资讯动态

基于STM32的ADS124S08高精度ADC驱动:SPI配置与校准详解

发布时间:2026/10/21 9:05:02 来源:尧图企业网站定制
简介针对STM32平台HAL库编写的高精度ADC驱动例程围绕ADS124s08这颗24位Σ-Δ模数转换器展开适用于工业数据采集、传感器检测和电池管理等需要低噪声测量的场景。资源包共含4个文件两个C源文件和两个头文件整体仅6KB代码量少、层次清晰可直接加入现有STM32CubeMX生成的工程进行二次开发。该例程已有891人学习下载适合正在学习SPI外设驱动或准备在项目中使用高精度ADC的嵌入式工程师参考。代码覆盖了SPI接口初始化、数据收发、寄存器读写与基本校准思路并提供了中断方式处理通信事件的示例能帮助理解HAL库下主从通信的配置细节也可作为后续扩展多通道采集功能的底层模板。整体来看代码量虽小但覆盖了从配置到采集的完整链路适合作为ADS124s08与STM32协作开发的入门模板。1. 为什么这枚 ADC 值得认真调第一次拿到带 ADS124S08 的样板时我没有急着移植例程而是先翻了数据手册的框图24 位 Δ-Σ 架构、内置可编程增益放大器PGA、两个 IDAC 恒流源、内部温度传感器、外加四个通用 GPIO——这基本就是为热电偶、RTD、称重传感器这类小信号测量场景量身定做的。STM32 配合 HAL 库驱动它核心工作不在 SPI 收发本身而在状态机、校准和时序管理。网上能找到的例程大多只演示「怎么读到一个数」但实际项目里真正让人头疼的是上电后寄存器配置顺序不对、DRDY 边沿没抓准、PGA 打开后输入共模范围超限、标定系数没做温度补偿。这些坑我都在某个温控模块上踩过一遍。如果你正准备用 STM32 HAL 驱动这枚 ADC这篇笔记值得你照着走一遍。2. 从芯片到 HAL 工程先把 SPI 和片选架构搞对2.1 硬件引脚和电源布置ADS124S08 是 24 位高精度 ADC但它的模拟性能上限取决于外围电路而不是芯片本身。官方评估板会把 AVDD、DVDD 分开供电模拟地和数字地单点连接REF 引脚用低噪声基准源驱动。实际做产品时常见做法是 AVDD 用 5V、DVDD 用 3.3V 或 5VDVDD 选择取决于主控 I/O 电平STM32F103 之类用 3.3V 更省事。电源去耦很关键AVDD 引脚对地放一个 10μF 钽电容加 0.1μF 陶瓷电容REF 引脚同样处理布局时电容尽量贴近引脚。SPI 接口是标准的四线制SCLK、DIN、DOUT、CS。除此之外这枚芯片还有 DRDY数据就绪和 START启动转换控制两根信号线很多例程把 START 直接接地DRDY 用 GPIO 中断或轮询。我的建议是 START 不要简单接地而是用 STM32 的一个 GPIO 控制后面讲多通道扫描时你会看到它的用处。典型引脚分配以 STM32F407 为例SPI1_SCK - PA5SPI1_MISO - PA6SPI1_MOSI - PA7片选 CS - PA4DRDY - PC4START - PC52.2 片选、DRDY、START 三根线的时序关系ADS124S08 的 SPI 时序有个容易忽视的点CS 必须在每条命令传输完成后拉高不能在整段读写过程中一直保持低电平。连续读取模式RDATAC下可以保持低但寄存器写入、单次读取数据、校准命令都要求 CS 在命令字节和响应字节之间有完整的高电平脉冲否则芯片内部状态机可能错乱。DRDY 是推挽输出数据准备好后拉低读取数据完成后自动拉高。如果开启连续转换模式DRDY 会周期性拉低。另一种做法是配置成单次转换模式用 START 引脚触发一次转换DRDY 下降沿表示转换完成读完数据后等待下一次触发。这种模式在低功耗应用中更常见。HAL 库下建议把 DRDY 接到外部中断输入EXTI下降沿触发在回调函数里置一个标志位。主循环检测到标志位后执行 SPI 读取。轮询 DRDY 也能用但主循环里如果有其他耗时任务可能错过数据就绪时刻。2.3 HAL SPI 模式配置时钟极性和相位别搞反ADS124S08 要求 SPI 工作在模式 1CPOL0, CPHA1即时钟空闲为低电平第二个边沿采样数据。很多例程翻车的第一个点就是这里——数据手册时序图里 SCLK 空闲拉低但第一个边沿是下降沿还是上升沿要看仔细我见过不止一个人把 CPOL/CPHA 配成 0,0 或者 1,0然后读回来的数据全是 0xFF。HAL 库的 SPI 初始化结构体里这三个字段是必填的hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; hspi1.Init.CLKPhase SPI_PHASE_2EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_16; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB;逻辑说明CLKPolarity对应 CPOLCLKPhase对应 CPHA。SPI_POLARITY_LOW表示空闲时时钟为低SPI_PHASE_2EDGE表示数据在第二个边沿被采样合起来就是 SPI Mode 1。NSS必须设为软件管理因为片选信号由普通 GPIO 控制不能让 SPI 硬件自动拉低否则会和 DRDY、START 的时序互相干扰。BaudRatePrescaler取值取决于主频ADS124S08 最高支持约 4MHz SCLK我一般从 1MHz 起步调试确认通信正常后再提高。片选控制用一个自定义函数包一层void ADS124S08_Select(void) { HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); } void ADS124S08_Release(void) { HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); }注意这两行之间不能夹延时CS 拉低后立刻就要开始发命令字节否则芯片可能误判命令起始位置。反过来命令发送完毕后要立刻拉高 CS不要等到下一轮操作再拉高。3. 寄存器配置顺序PGA、数据率、基准源一步错后面全歪3.1 上电默认状态与配置顺序ADS124S08 上电后 PGA 是旁路状态即 PGA_BYPASS输入直接进入 Δ-Σ 调制器此时增益为 1。如果传感器信号只有几毫伏不打开 PGA 读出来的数看起来就是一堆噪声。很多例程上来直接写寄存器但没注意默认状态对后续配置的影响。正确顺序是先发送 RESET 命令0x05等待至少 20ms 让内部振荡器稳定然后依次配置 STATUS、INPMUX、PGA、DATARATE、REF 寄存器最后再发送 START 信号或配置连续转换模式。这里有一个要点每次写寄存器后最好回读一次确认写入值生效避免 SPI 时序问题导致寄存器内容错位。回读命令是 RREG0x20 寄存器地址HAL 库实现如下uint8_t ADS124S08_ReadReg(uint8_t reg_addr) { uint8_t cmd[2] {0x20 | reg_addr, 0x00}; uint8_t resp[2] {0, 0}; ADS124S08_Select(); HAL_SPI_Transmit(hspi1, cmd, 2, 10); HAL_SPI_Receive(hspi1, resp, 2, 10); ADS124S08_Release(); return resp[1]; // 第一个字节是状态字节第二个是寄存器值 }逻辑说明RREG 命令格式是第一个字节0x20 | 寄存器地址第二个字节是(寄存器数量 - 1)。这里只读一个寄存器所以第二个字节写 0x00。发送命令后需要再产生 8 个时钟周期取回数据因此用Receive读两个字节丢弃第一个状态字节第二个才是寄存器实际内容。参数说明resp数组长度为 2是因为命令本身也占一个字节周期实际有效数据在第二个索引处。如果回读值和写入值不一致优先检查 CPOL/CPHA 配置和 SCLK 频率不要怀疑芯片坏了。3.2 INPMUX 选择输入通道和内部温度传感器INPMUX 寄存器控制模拟输入多路复用器。ADS124S08 有 8 个模拟输入引脚AIN0~AIN7可以配置成差分模式或单端模式。差分模式下INPMUX 高四位是正输入选择低四位是负输入选择。单端模式则是在 AINCOM 作为公共端的基础上选择正输入。温度传感器模式通过 INPMUX 寄存器的高四位选择TEMP_SENSOR_P、低四位选择TEMP_SENSOR_N来启用。配置示例热电偶差分输入接 AIN0/AIN1uint8_t mux_val (0x04 4) | 0x05; ADS124S08_WriteReg(0x02, mux_val);参数说明0x04是 AIN0 的通道编码0x05是 AIN1 的编码。高四位写在前面低四位写在后面组合成 0x45 写入 INPMUX 寄存器。如果要用内部温度传感器做冷端补偿mux_val 直接设成0x00部分型号编码不同以数据手册为准。这里有个新手容易绕进去的地方差分模式下正负输入的选择不是简单的「正输入引脚号减 1」而是按芯片手册里的编码表查。我第一次用的时候想当然写了AIN0 正、AIN1 负为(0x00 4) | 0x01实际编码表中 AIN0 对应 0x04 而不是 0x00导致读出来的电压全反了。3.3 PGA 增益、数据率和基准源的联动权衡PGA 寄存器控制增益可选 1、2、4、8、16、32、64、128。增益越大分辨率越高但输入共模范围和噪声也越差。ADS124S08 的 PGA 输入级是开关电容结构等效输入阻抗会随增益和数据率变化。如果传感器输出阻抗较高比如某些热电偶有几百欧内阻高增益下会引入明显的增益误差和失真。常见的补偿措施是在 ADC 输入引脚对地并联一个 0.01μF 到 0.1μF 的电容同时串联一个 1kΩ 到 10kΩ 的电阻组成低通滤波。这个 RC 时间常数不能太大否则 AD 采样期间电容充电不足动态误差会超过 PGA 本身精度。基准源选择也很关键。REF 寄存器控制内部基准和外部基准的选择。内部基准典型值是 2.048V温漂系数在几十 ppm/°C 级别精度一般够用。但对称重传感器这类需要高精度增益匹配的场景我会选外部精密基准源。注意如果选择了外部基准 REFP/REFN 输入模拟输入信号不能超过基准电压范围这是 Δ-Σ ADC 的硬约束。数据率寄存器 DATARATE 控制输出速率和数字滤波。低速下噪声更低但建立时间更长。示例中配置为 20SPSADS124S08_WriteReg(0x04, 0x14);0x14对应的具体速率要查数据手册的 DATARATE 寄存器表不同型号略有差异。选型建议热电偶测量用 20SPS 足够滤波深度好如果是高速称重或振动监测才需要提到 400SPS 以上但此时噪声会显著增大。3.4 连续模式与单次模式的切换ADS124S08 的转换模式由 START 引脚和 STATUS 寄存器共同决定。连续模式下START 拉高后芯片按设定数据率持续转换DRDY 周期性拉低单次模式下每次转换由 START 下降沿触发转换完成后 DOUT 拉低等待下一次触发。使用连续模式时命令发送顺序是先配置好所有寄存器再发送 SDATAC0x16停止连续读取然后写寄存器最后送 RDATAC0x14进入连续输出模式。连续模式下读取数据的时序更讲究片选必须在读取期间持续拉低字节之间不能插入其他操作否则数据流会错位。我的习惯是初学调试用单次模式逻辑清晰不容易错产品化后再切连续模式用 DMA 搬运。4. 从原始码到标定值24 位补码与增益误差的处理4.1 三字节原始数据的解析ADS124S08 的转换结果以 24 位二进制补码输出DOUT 引脚在 DRDY 拉低后输出三个字节高位在前。读取命令是 RDATA0x12时序如下uint8_t ADS124S08_ReadData(int32_t *value) { uint8_t rx_buf[3] {0, 0, 0}; ADS124S08_Select(); uint8_t cmd 0x12; HAL_SPI_Transmit(hspi1, cmd, 1, 10); HAL_SPI_Receive(hspi1, rx_buf, 3, 10); ADS124S08_Release(); *value ((int32_t)rx_buf[0] 16) | ((int32_t)rx_buf[1] 8) | ((int32_t)rx_buf[2]); if (*value 0x800000) { *value - 0x1000000; } return 0; }逻辑说明rx_buf[0]是最低地址也是最高字节左移 16 位拼到 int32 的高位第二个字节左移 8 位第三个字节直接放低位。拼完后如果最高位0x800000被置位说明是负数需要减0x1000000完成符号扩展。这就是二进制补码的展开过程相当于把 24 位有符号数转成 32 位有符号数。参数说明int32_t必须用固定宽度类型不能用int或long否则在 32 位和 16 位平台上行为不一致。0x1000000等于 2 的 24 次方这是补码转换的正确减数。4.2 电压换算公式拿到原始码之后换算成实际电压值的公式为V_in (RAW / 0x7FFFFF) * (2 * V_REF / PGA)其中0x7FFFFF是 24 位 ADC 的满量程码值。ADS124S08 是双极性 ADC输入为 0V 时输出码值约等于 0输入接近正满量程时输出接近0x7FFFFF接近负满量程时输出接近0x800000。单极性模式下公式略不同但热电偶和称重传感器一般都用双极性模式可以不用理会单极性那套换算。实际项目里为提高精度我会用标定系数而不是理论满量程。因为参考电压和 PGA 增益电阻都有误差理论公式算出来的电压和外部标准源测出来的值之间通常有 0.1% 到 1% 的偏差这对 24 位 ADC 来说是很大的误差了。标定方法在下一节展开。4.3 SYSOCAL 和 SYSGCAL 标定的执行步骤ADS124S08 提供了系统偏移校准SYSOCAL和系统增益校准SYSGCAL命令。校准流程是将 ADC 输入端短接偏移校准或接入已知标准电压增益校准发送 SYSOCAL 命令0x06或 SYSGCAL 命令0x07等待 DRDY 引脚由高变低表示校准完成读取 SYSOCAL0/1/2 和 SYSGCAL0/1/2 寄存器保存校准系数正常转换时用保存的系数对原始数据进行修正。校准命令发送代码void ADS124S08_Sysocal(void) { uint8_t cmd 0x06; ADS124S08_Select(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); ADS124S08_Release(); }逻辑说明SYSOCAL 命令只需一个字节不需要附带寄存器数据。校准过程通常在几十毫秒到几百毫秒之间取决于当前配置的数据率。数据率越低校准耗时越长。发送命令后要轮询 DRDY 引脚直到它拉低一次再拉高才表示校准完成。注意校准期间不要发送其他 SPI 命令否则芯片状态机被打断校准结果无效。4.4 标定系数的应用完成偏移和增益校准后实际电压修正公式为V_real (RAW - SYSOCAL) * V_ref / (SYSGCAL * PGA * 0x7FFFFF)简化做法是直接把 SYSOCAL 当作零点偏移减掉SYSGCAL 当作增益比例因子乘上去。大部分情况下不需要完整公式可以用两点标定代替在输入 0V 时记录码值 offset在输入标准电压 Vs 时记录码值 gain_ref那么任意输入的电压为V_in (RAW - offset) * Vs / (gain_ref - offset)这种方法比单独用 SYSOCAL/SYSGCAL 寄存器更直观也更容易调试。寄存器校准的好处是 ADC 内部自动完成不会占用 MCU 算力缺点是只能修正 ADC 本身的误差无法修正传感器、基准源和前端电路的误差。对于高精度测量我通常同时做先内部校准再外部两点标定。5. ADS124S08 实战避坑五个让人挠头的典型问题5.1 现象例程下载后读数恒定在 0x7FFFFF 或 0x800000原因INPMUX 配置把输入通道选成了未连接的引脚或者 PGA 增益设置不正确导致输入信号远超满量程。还有一种可能是 SPI 时钟极性配反了芯片根本没收到有效指令寄存器里全是复位默认值。解决先回读所有关键寄存器确认写入值已生效。然后用示波器抓 DRDY 引脚看是否有周期性脉冲。如果没有脉冲说明 ADC 没有开始转换回到上电配置顺序检查如果有脉冲但数据全满量程把输入信号直接短接到 AINCOM 或 GND看读数是否接近 0V 对应码值。如果短接后仍然满量程硬件连接或基准源电压有问题。5.2 现象读数每隔几秒跳一大截不是随机噪声原因热电偶信号本身是微伏级但 ADC 输入端有射频干扰或电源纹波耦合进来尤其是在未开启 PGA 的情况下。另一个常见原因是基准源引脚上电压波动参考不稳转换结果自然跟着跳。排查时先用万用表测 REFP/REFN 引脚电压波动超过 1mV 就要检查基准源电路。解决在模拟输入端并联 0.1μF 电容必要时串一个 10Ω 电阻限制充放电电流。检查 PCB 布局模拟输入线远离数字信号线。软件上提高数据率滤波深度减小数据率也能压住随机噪声但会让响应变慢适合慢速测量。5.3 现象多通道切换后第一笔数据明显偏大或偏小原因ADC 输入多路复用器切换后前端 RC 滤波电容上的残存电荷需要时间泄放。Δ-Σ 调制器也需要若干个转换周期才能稳定输出。很多例程切换通道后直接读数据第一笔自然不准。解决通道切换后丢弃前 2~3 个转换结果再取有效值。实现上可以在状态机里加一个「切换后预热」状态每次切换通道时重置采样计数void ADC_StartChannel(uint8_t ch) { ADS124S08_WriteReg(0x02, ch); channel_pending 2; } void ADC_OnDataReady(void) { if (channel_pending 0) { channel_pending--; return; } }逻辑说明channel_pending初始为 2每次 DRDY 中断进来时先减 1等于 0 之后才开始采数据。参数说明丢弃 2 笔是经验值数据率越低需要丢弃的笔数越多。如果设置 20SPS2 笔意味着一百毫秒的额外等待多数场景可接受。5.4 现象开启 PGA 后输出值线性度变差原因PGA 增益较大时输入共模电压超出允许范围。ADS124S08 的 PGA 输入级在增益为 128 时共模范围只有电源电压的很小一段接近 GND 或接近 AVDD 都会导致输出失真。用热电偶时信号本身就接近 GND更容易触到这个限制。解决把模拟输入参考点抬高常见做法是将 AINCOM 接到 AVDD/2 的偏置电压或者用电阻分压加运放跟随器生成一个人工参考地。增益不要盲目拉高热电偶信号几十毫伏64 倍增益已经够用。如果确实需要高增益考虑先用外部仪表放大器放大再送进 ADC。5.5 现象上用内部温度传感器做冷端补偿测出来偏差 5°C 以上原因ADS124S08 内部温度传感器精度有限典型误差在 ±2~3°C加上 ADC 自身温漂5°C 偏差并不罕见。内部传感器适合用来监测芯片温度不适合做精度要求高的热电偶冷端补偿。解决改用外部温度传感器如某高精度数字温度芯片或者 PT100 ADS124S08 自身 IDAC 驱动放在接线端子附近。内部传感器只在粗略补偿或诊断场景使用。如果坚持用内部传感器至少做一次两点校准并把校准系数存档到片外 Flash避免掉电丢失。6. 用好 START 引脚把单次触发模式做成低功耗采样状态机实际产品里MCU 不可能一直阻塞等待 ADC 转换结束。以我们给某温控系统写的固件为例主循环里还要处理按键、显示刷新和通信协议ADC 采样只能放在后台。我的做法是把 ADS124S08 配置成单次转换模式每次需要采样时拉高再拉低 START 引脚然后让 DRDY 下降沿触发 EXTI 中断在中断回调里读取数据并置标志位主循环按需消费这个标志位。核心代码片段void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin DRDY_Pin) { int32_t raw 0; ADS124S08_ReadData(raw); // 在中断里读取数据 adc_result raw; adc_data_ready 1; } } void ADC_StartSingleConversion(void) { HAL_GPIO_WritePin(START_GPIO_Port, START_Pin, GPIO_PIN_SET); HAL_Delay(1); HAL_GPIO_WritePin(START_GPIO_Port, START_Pin, GPIO_PIN_RESET); }逻辑说明START拉高后保持至少 1ms让内部电路稳定再拉低触发单次转换。转换完成后 DRDY 自动拉低触发中断。中断函数里直接调用读取函数因为 SPI 读取是阻塞式的耗时很短3 字节约 24 个时钟周期不会影响主循环的实时性。如果主循环有高优先级任务需要保护可以把读取搬出中断在回调里只置标志位主循环轮询到标志后再读。参数说明HAL_Delay(1)只是最小保持时间实际工程中建议用定时器或非阻塞延时替代避免在中断上下文调用 HAL_Delay。从那以后每次开发新的测量板我都会把 ADC 驱动拆成「寄存器配置层 数据读取层 应用标定层」三层结构寄存器配置层只负责写寄存器读数据层只处理 SPI 时序标定层负责换算电压。这样以后换主控芯片或者换传感器只需要改底层和标定层中间的状态机逻辑完全不用动。ADS124S08 这类高性能 ADC 真正难的不是读出一个数而是把「读数到数值」的链路做得可靠、可维护。希望这份笔记能帮你少走几步我走过的弯路也希望你上手顺利。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑