资讯详情

资讯详情

STM32 HAL库驱动ADS124S08:24位ADC数据采集例程详解

简介面向 STM32 开发者的 HAL 库驱动 ADS124s08 例程解决高精度 24 位 Σ-Δ ADC 的 SPI 接入与数据采集问题适用于工业信号采集、传感器监测、电源监控等对噪声和精度要求较高的场景。压缩包仅 6KB共 4 个文件包含 2 个 C 源文件和 2 个头文件C 文件负责 SPI 底层通信和 ADS124s08 寄存器读写头文件提供接口声明与关键定义整体结构紧凑便于对照学习并移植到自己的工程。目前已有 891 人学习下载。示例覆盖 SPI 主机模式初始化、HAL_SPI_TransmitReceive 收发流程、寄存器读写函数、中断传输方式以及校准与数据处理思路开发中可参考其接口设计直接改写驱动或结合外部传感器搭建高精度采集链路。对于正在使用 STM32 做嵌入式测量的工程师这份代码能显著缩短底层调试时间是快速验证 ADS124s08 采集链路的好起点。1. 从“测不准”说起ADS124S08例程到底帮你省掉哪几步做高精度采集的人应该都遇到过这种场景芯片选型时看中一颗24位ADC可焊到板子上以后读回来的裸数据一直在跳最后几位完全没有意义。ADS124S08就是一颗典型的“纸面很强、落地很磨人”的芯片24位ΔΣ架构内置PGA增益最高128倍内部基准2.5V还集成了恒流源用于RTD测温。但它的驱动方式不是简单写寄存器而是命令制SPI加上DRDY状态机稍微一个时序没对齐读回来的全是0xFF。这套基于STM32 HAL库的ADS124S08例程把复位、寄存器配置、等待数据就绪、连续取数、结果换算这一整条链路封装好了适合做称重变送器、温度采集模块、工业模拟量输入板这类场景。拿到手不用从数据手册开始啃直接在HAL库工程里把驱动文件挂上去就能跑。2. 工程全景硬件接线、目录结构与寄存器读写地基2.1 硬件连接先把SPI四根信号和DRDY/CS/RESET接对ADS124S08是纯SPI从机MCU端只需要占用一组SPI外设加三根普通GPIO。SPI的四根信号是SCLK、DINMOSI、DOUTMISO、CS此外还有两个状态控制脚DRDY和RESET。DRDY是转换数据就绪标志低电平有效必须接到支持外部中断的引脚上靠下降沿通知MCU来取数RESET是硬件复位脚平时拉高复位时拉低至少几百微秒即可。START和STOP两个功能可以通过SPI命令发也可以引出来用GPIO控制例程里直接用命令字操作省两个引脚。STM32端建议的引脚分配见下表工程里各个脚都可以在CubeMX里任意重映射只要保证SPI的SCK、MOSI、MISO在同一组SPI外设上就行。ADS124S08信号方向STM32端建议引脚说明SCLK输入PA5SPI时钟DIN输入PA7SPI MOSIDOUT输出PA6SPI MISOCS输入PB0片选软件控制DRDY输出PB1数据就绪接EXTIRESET输入PB2复位控制接线时最容易忽略的是CS的时序。ADS124S08要求CS拉低后必须等待t_CSSC片选建立时间再传数据这个时间在数据手册里通常给的是纳秒级MCU的GPIO翻转延迟远大于它所以代码里不用特意加延时但前提是CS不要和SPI时钟共用同一个引脚。2.2 目录结构HAL库工程外挂驱动文件夹这个例程不是从零手写寄存器而是基于CubeMX生成的HAL库工程驱动层单独放在App目录下方便移植到不同的STM32型号。整个目录结构是这样的HAL_ADS124S08_Driver/ ├── Core/ │ ├── Inc/ │ │ ├── ads124s08.h │ │ └── ads124s08_config.h │ └── Src/ │ ├── ads124s08.c │ └── main.c ├── Drivers/ │ ├── CMSIS/ │ └── STM32F4xx_HAL_Driver/ └── MDK-ARM/ads124s08.h对外暴露的是初始化、单次转换、连续转换、校准这几个函数ads124s08_config.h管硬件绑定里面定义CS、RESET的GPIO操作宏以及SPI句柄的 extern 声明。这样做的好处是换平台时只需要改config头文件里的引脚映射和SPI句柄驱动算法层不用动。main.c里只负责初始化外设和调用驱动接口业务逻辑和芯片时序被隔离开这是嵌入式项目里最简单也最实用的一层分包方式。2.3 寄存器读写命令制SPI和HAL全双工模型ADS124S08的SPI不是“发地址取数据”的传统外设寄存器模型而是先发命令字节再根据命令内容传输数据。读单个寄存器要发两帧第一帧是RREG命令第二帧是由主机发起时钟、从机把寄存器内容放到DOUT上。写寄存器则是WREG命令加一个数据字节其中WREG不仅可以写单个寄存器还可以一次连续写多个寄存器这正是初始化配置比较舒服的地方。裸机驱动里最常犯的错是把SPI当半双工用发命令时不管MISO上的数据读数据时又只调用接收函数。HAL库里SPI是全双工外设发送和接收永远同时发生所以例程里所有寄存器操作都封装成了“发一个命令字节同时收一个应答字节”的模型uint8_t ADS124S08_ReadReg(uint8_t regAddr) { uint8_t cmd ADS124S08_CMD_RREG | (regAddr 0x1F); uint8_t dummy 0xFF; uint8_t val 0; ADS124S08_CS_LOW(); // 主机发出RREG命令字节从机同时开始准备寄存器内容 HAL_SPI_Transmit(hspi1, cmd, 1, 100); // 第二个字节由主机提供时钟从机在DOUT上返回寄存器值 HAL_SPI_TransmitReceive(hspi1, dummy, val, 1, 100); ADS124S08_CS_HIGH(); return val; }这个函数有几个地方值得注意。第一个是命令字节的组成RREG命令的高5位固定低5位是寄存器地址所以用|运算把地址拼进去第二个是第二帧用了TransmitReceive因为主机必须产生SCLK时钟才能把数据从DOUT上“推”出来用纯Receive一样可以但要保证传入的发送缓冲区是有效地址第三个是CS在每个命令序列结束后拉高ADS124S08以CS上升沿锁存命令边界如果一帧没传完就拉高CS这次命令会被丢弃。写寄存器函数与读函数对称只是把WREG命令和值连续发出去。3. 核心代码拆解从初始化到电压换算的完整链路3.1 初始化时序复位、配置寄存器、消费首个数据就绪信号ADS124S08上电后默认处于连续转换模式DRDY会在复位完成后拉低一次表示数据已经转换好、等待主机读取。很多第一次用这颗芯片的人会在这里卡住——明明是“初始化”为什么DRDY一上来就是低的例程的做法是给初始化函数设计一个固定的四步流程硬件复位、配置寄存器、发STOP命令进入待机、读取或清除首个转换结果。这四步做完芯片才处于一个可预测的状态。void ADS124S08_Init(ADS124S08_Handle_t *hdev) { // 1. 硬件复位RESET拉低至少1ms后再拉高 ADS124S08_RESET_LOW(); HAL_Delay(1); ADS124S08_RESET_HIGH(); HAL_Delay(1); // 2. 上电后DRDY会拉低代表第一个数据已就绪 // 这里先发STOP命令停掉连续转换防止数据被覆盖 ADS124S08_SendCommand(ADS124S08_CMD_STOP); // 3. 依次写配置寄存器数据速率、PGA增益、输入MUX、内部基准 ADS124S08_WriteReg(ADS124S08_REG_CONFIG1, hdev-dataRate); ADS124S08_WriteReg(ADS124S08_REG_CONFIG2, hdev-pgaGain); ADS124S08_WriteReg(ADS124S08_REG_CONFIG3, hdev-inputMux); ADS124S08_WriteReg(ADS124S08_REG_CONFIG4, hdev-refSource); // 4. 发RDATA读取旧数据把DRDY状态清掉 ADS124S08_SendCommand(ADS124S08_CMD_RDATA); ADS124S08_ReadRawCode(hdev, hdev-discard); }这里多说一句配置顺序。CONFIG1对应数据速率例程里支持的档位从2.5SPS到4000SPS工业场景最常用的三个档位是20SPS、60SPS和1000SPS。CONFIG2控制PGA增益和输入极性增益从1到128共九档增益越高噪声越小但有效位数也会受前端信号限制。CONFIG3是输入多路复用器决定当前采样的是哪一路模拟输入ADS124S08共有8个模拟输入引脚单端模式可以选8路差分模式只能选4对。CONFIG4负责基准源和内部恒流源开关默认选内部2.5V基准。初始化中最后一步的RDATA最容易被人忽略。如果不读走这个残留数据DRDY会一直保持低电平后续用轮询方式等待下降沿就永远等不到。例程里故意用一个discard变量接住它明确告诉阅读代码的人这不是有效数据这是状态机复位必须走的一步。3.2 单次转换START命令、等待DRDY、三字节拼接单次转换流程对应称重仪表里“按一次键采一次样”的场景。主机先发STOP命令确保从连续模式下退出再发START命令启动单次转换然后等待DRDY下降沿。DRDY一到说明转换完成且数据锁存到了移位寄存器这时候发RDATA命令并读三个字节三字节依次是最高有效位、中间字节、最低有效位。uint8_t ADS124S08_SingleConversion(ADS124S08_Handle_t *hdev, int32_t *outCode) { uint8_t buf[3]; int32_t raw 0; // 先确保不在连续模式再启动一次新转换 ADS124S08_SendCommand(ADS124S08_CMD_STOP); ADS124S08_SendCommand(ADS124S08_CMD_START); // 轮询DRDY下降沿超时返回错误 uint32_t timeout HAL_GetTick(); while (ADS124S08_DRDY_READ() ! GPIO_PIN_RESET) { if (HAL_GetTick() - timeout 500) { return HAL_ERROR; } } // 读回24位转换结果 ADS124S08_CS_LOW(); uint8_t cmd ADS124S08_CMD_RDATA; HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_SPI_Receive(hspi1, buf, 3, 100); ADS124S08_CS_HIGH(); // 把三字节拼成有符号24位整数 raw ((int32_t)buf[0] 16) | ((int32_t)buf[1] 8) | buf[2]; if (raw 0x800000) { raw | 0xFF000000; // 符号扩展 } *outCode raw; return HAL_OK; }这段代码的关键在最后的符号扩展。ADS124S08的输出是二进制补码格式正数的最高位是0负数的最高位是1。数据手册给出的有效位是24位但C语言里int32_t是32位所以拼完以后必须判断bit23如果为1就在高8位补1否则outCode会被当成一个正整数随后所有电压换算结果都会偏移一半满量程。这种问题在万用表、温度表调试时最容易暴露正读数看起来正常负温度一下就变成几百万的离谱值。轮询DRDY的方式还有一个隐患如果系统里同时跑着RTOS或者多个中断轮询循环可能会被更高优先级任务抢占导致DRDY已经拉低但代码还在等。稳妥的做法是把DRDY接成EXTI下降沿中断在中断里置一个标志位主循环查询标志位。例程里默认用的是轮询二次开发时改成中断唤醒的成本很低后面会具体讲。3.3 连续转换DRDY事件驱动与数据消费节奏连续转换模式是ADS124S08最常用的工作方式适合振动监测、电网波形记录这类需要持续采样的场景。初始化完成后直接发START命令芯片就会按照CONFIG1配置的数据速率自动完成转换每个转换周期结束时DRDY拉低一次。主机的任务只有一个紧跟DRDY节奏每次下降沿读回三个字节。连续模式下读数据可以省掉每次发RDATA命令的步骤。DRDY拉低后转换数据已经自动锁存到输出移位寄存器主机拉低CS并直接产生24个SCLK就能收完数据。这样做能省一次命令传输的时间但代码上要小心如果主机提前拉低CS去读而DRDY还没拉低读回来的会是上一次的旧数据。例程里的实现是坚持“DRDY下降沿再读”的原则并且每次读完都重新等待下一次下降沿不欠任何一个周期uint8_t ADS124S08_ContinuousRead(ADS124S08_Handle_t *hdev, int32_t *outCode) { // 等待DRDY下降沿确保当前转换周期已完成 uint32_t timeout HAL_GetTick(); while (ADS124S08_DRDY_READ() ! GPIO_PIN_RESET) { if (HAL_GetTick() - timeout 1000) { return HAL_ERROR; } } // 连续模式下DRDY拉低即数据可用直接产生SCLK读三字节 ADS124S08_CS_LOW(); HAL_SPI_Receive(hspi1, hdev-rawBuf, 3, 100); ADS124S08_CS_HIGH(); // 拼接与符号扩展 hdev-raw ((int32_t)hdev-rawBuf[0] 16) | ((int32_t)hdev-rawBuf[1] 8) | hdev-rawBuf[2]; if (hdev-raw 0x800000) { hdev-raw | 0xFF000000; } *outCode hdev-raw; return HAL_OK; }ADS124S08的数据手册里对连续模式的DRDY行为描述得比较细数据速率越低DRDY低电平持续的时间越长数据速率越高DRDY每次拉低的脉冲宽度越窄。当SPS配置到4000SPS时DRDY低电平脉冲只有几十微秒如果MCU主循环里有其他耗时操作非常容易漏掉脉冲。这时候有两个选择一是用DRDY中断锁存时间戳延后读取也是有效数据二是把SPS降下来。前者适合做精确采样的系统后者适合做常规仪表。3.4 数值换算24位有符号码怎么变成真实的mV拿到原始码之后最后一步是把int32_t转换成实际的物理电压。ADS124S08的差分输入满量程是±Vref/PGAVref默认2.5VPGA增益可配置。24位ADC把整个测量范围映射到-8388608到8388607之间每一个LSB对应的电压就是满量程除以2的23次方。float ADS124S08_CodeToVoltage(int32_t rawCode, float vref, uint16_t pga) { // 满量程范围 vref / pga float fsRange vref / (float)pga; // 每个LSB对应的电压 满量程 / 2^23 float lsbVolt fsRange / 8388608.0f; // 直接乘以LSB电压即可得到实际输入电压 return (float)rawCode * lsbVolt; }举个例子Vref2.5VPGA16满量程就是±156.25mV一个LSB约等于0.0186uV。如果rawCode是8388607对应的输入电压就是满量程正向极限如果rawCode是-8388608就是负向极限。增益从1变到128时满量程从±2.5V缩到±19.5mV但LSB分辨率也从0.298uV提升到0.00233uV。这些参数在config头文件里都预留了宏定义不需要每次重新算。要注意的是精度问题。上面这个换算公式用的是float在STM32F4上float精度足够覆盖24位ADC的计算需求但在只有M0内核的型号上浮点运算会占用大量CPU时间建议改成定点运算先乘以一个放大系数再右移。例程默认用float是优先保证代码可读性二次开发时如果发现CPU占用过高可以把Vref和PGA预计算成uint32_t格式的定点数。4. 避坑指南调板时遇到的几个“玄学”问题4.1 SPI模式记错回读全FF不是芯片坏了现象上电初始化后读任意寄存器返回的都是0xFF或者0x00换一个寄存器也一样SPI波形上看MOSI有输出、MISO一直是高电平。原因ADS124S08要求SPI工作在CPOL0、CPHA1的模式下。CPOL0代表SCLK空闲为低CPHA1代表数据在SCLK的第二个边沿被采样。很多从别的芯片移植过来的驱动直接沿用CPOL0、CPHA0主机在第一个边沿采集DOUT而从机是在第二个边沿才把数据放上总线两边永远没对齐。解决核对数据手册里的时序图把CubeMX的SPI参数改成Clock Polarity为Low、Clock Phase为2 Edge。改完再看波形MISO上的数据就会跟着命令字节的时间轴正确输出了。这也是这个例程在设计时特意把SPI配置写成注释放在初始化函数顶部的原因换芯片平台时第一件事就是核对这四个参数。4.2 DRDY一上电就拉低其实是“数据等你读”现象程序刚跑起来DRDY引脚已经是低电平用轮询方式等下降沿永远等不到程序卡死在等待循环里。原因ADS124S08上电默认就进入连续转换模式芯片复位完成后自动完成第一次转换DRDY拉低表示第一个数据已经在输出寄存器里等着被读走。如果初始化里不发RDATA这个低电平就一直挂着。解决在初始化函数的最后主动消费掉这个数据。例程里专门用一步ADS124S08_ReadRawCode把这个残留数据读走读完之后DRDY自动回到高电平后续的转换周期才会产生新的下降沿脉冲。同理在代码调试时如果发现DRDY状态不正常先怀疑是不是上一次数据没读完而不是怀疑芯片坏了。4.3 通道切换后第一包数据是脏的现象通过CONFIG3寄存器切换输入通道比如从AIN0切到AIN4后读到的第一个数据明显偏大或偏小连续读几包后恢复正常。原因ΔΣ ADC内部有数字滤波器和调制器切换MUX后等效于改变了输入信号滤波器需要经历几个转换周期才能收敛到新值。第一包数据往往是新旧输入信号混合的结果。解决在切换通道后丢弃前两包转换结果。例程里提供了一个ADS124S08_SetInputMux接口内部会自动读取两个无用数据然后再启动正式的转换序列。如果采样速率很高也可以在配置寄存器后发SYNC命令强制同步不过最简单可靠的做法还是软件层面丢点。4.4 HAL全双工的隐藏行为命令发送时会顺带收一包垃圾现象用HAL_SPI_Transmit发送START命令后紧接着用HAL_SPI_Receive读数据读回来的三字节里第一个字节总是错的后面两个字节正常。原因HAL库的SPI是全双工外设Transmit函数在发送命令字节的同时MISO上其实也在接收数据只是这个数据被丢弃了。下一个Receive调用产生的时钟是从零开始的如果从机在命令阶段已经把一个字节推上了总线这个字节就会被当成有效数据读走。解决命令阶段就统一用HAL_SPI_TransmitReceive哪怕只关心发送内容也把接收缓冲区的地址传进去。例程里所有命令发送函数都严格遵循这个原则避免在序列中间插入一个无意识的空时钟周期。而且ADS124S08对CS拉高拉低的次数敏感每多一次CS动作芯片状态机的流转都要多等一个周期不必要的全双工操作会直接影响时序容错。5. 跑起来CubeMX配置、逻辑分析仪核对与现场调参5.1 CubeMX里SPI和GPIO的一组推荐配置拿到例程后第一步是在CubeMX里重建一遍外设初始化。SPI配置建议这样填Mode选Full-Duplex MasterClock Speed选1MHz起步确认能通讯后再慢慢往上升CPOL选LowCPHA选2 Edge这是ADS124S08的硬性要求Data Size选8位First Bit选MSB First。这里唯一要留意的是PrescalerSTM32F407的APB2时钟是84MHz如果选/256就是328KHz选/42就是2MHz。例程默认建议从1MHz附近开始因为ADS124S08的SPI时钟上限和数据手册里的sclk参数有关低频永远不会错高频要看芯片版本。GPIO这里有两处容易翻车。一处是CS必须配成GPIO Push-Pull Output初始电平设为High因为片选低有效CS拉高表示芯片未选中。另一处是DRDY必须配成External Interrupt模式并开启下降沿触发同时把GPIO的上拉电阻打开。DRDY是开漏输出外部没有上拉电阻时STM32内部上拉可以保证引脚稳定在高电平否则松开的时候电平悬浮中断容易误触发。RESET用普通推挽输出默认拉高只有复位瞬间拉低。5.2 用逻辑分析仪核对命令时序先把这块板子当成一个纯SPI从机来验证不跑业务代码。用逻辑分析仪同时抓CS、SCLK、MOSI、MISO、DRDY五根线然后执行一个最简单的操作读ID寄存器。正常波形应该是CS拉低主机发出RREG命令字节紧接着一个空字节时钟MISO上同步返回ID值最后CS拉高。如果MISO一直为高八成是SPI模式错了如果返回的值和手册不一致核对地址位有没有拼错。逻辑分析仪还有一个用途是观察DRDY脉冲和数据速率的关系。SPS配置为20时DRDY每50ms拉低一次低电平持续约一个SCLK周期SPS配置为4000时DRDY每250us就拉低一次。这个参数直接决定了主循环应该用哪种取数策略。例程里把SPS和DRDY的关系写成了注释低于100SPS可以放心用轮询高于1000SPS建议用中断否则一次中断处理逻辑稍长就会漏脉冲。5.3 采样率、PGA与工频滤波的取舍实际调试时最常调的两个参数是采样率和PGA增益。ADS124S08的数据速率在2.5SPS到4000SPS之间数字滤波器本身就是sinc结构在某些特定频率上会形成陷波。工业现场最常见的干扰是50Hz工频这时候把采样率配成20SPS或60SPS滤波器会在50Hz附近形成较深的陷波读数跳动会明显减小。这是我调温度变送器时的血泪经验一开始配成1000SPS想看实时响应结果50Hz干扰纹丝不动进到数据里显示值一直抖把速率换到60SPS后串口输出立刻稳了。PGA增益的选择则要看传感器输出幅度。称重传感器的满量程输出通常只有2mV/V激励电压5V时满量程只有10mV必须配合128倍增益才能用满24位ADC的分辨率而采集0到10V的工业模拟量信号时前端已经做了分压衰减PGA设1倍就够了。PGA调太大还会把前端噪声一起放大表现为数据分布变宽。例程里提供了一个小测试函数把输入端短接看噪声底PGA从1逐步加到128观察码值分布宽度找出噪声开始恶化的拐点那个增益就是当前平台的甜点值。6. 二次开发把例程扩展成自己的采集前端6.1 改造成定时器同步采样用DRDY中断做连续采样数据之间天然均匀间隔但每次转换完成到MCU真正读取之间还有延迟如果对采样时刻的一致性要求很高可以把定时器触发和SPI读取组合起来。做法是用一个定时器产生固定频率的PWM信号接到ADS124S08的START引脚或者更简单的方式是在定时器中断里查DRDY再读数据。后者在SPS不高的时候完全够用且不需要动硬件。改成定时器同步采样时要注意ADS124S08的转换周期是从START命令或外部START引脚上升沿开始的理论上外部触发能够获得更精确的采样时刻。例程里保留了START引脚的GPIO控制接口二次开发时只需把定时器的比较通道映射到这个引脚上再把初始化里的软件START换成外部触发其余代码全部复用。6.2 加入PT100的IDAC激励ADS124S08内置两个恒流源输出引脚这是做RTD测温的关键特性。PT100在0摄氏度时电阻100欧姆设置1mA激励电流后两端电压100mVPGA增益设8倍后满量程约800mV配合24位分辨率可以轻松做到0.01摄氏度级别的测量精度。改造思路是把IDAC1接到PT100的一端PT100串联一个精密参考电阻到地用差分输入同时采PT100和参考电阻两端的电压通过电压比值计算出实际电阻值从而消除恒流源自身漂移的影响。例程里的CONFIG4寄存器就是为这个预留的头文件里已经有ADS124S08_IDAC1_CHANNEL和ADS124S08_IDAC2_CHANNEL的枚举定义。加上四线制接法后导线电阻引入的误差可以被完全消除这是目前低成本方案里精度较高的做法。6.3 多通道扫描的正确姿势如果前端有8路传感器需要轮询采集直接在主循环里切换MUX再读数据会得到一个常见的坑切换瞬间引入的毛刺会被记录成有效数据。正确的扫描流程是先写CONFIG3切换通道然后丢弃两个转换周期再启动正式转换读取。通道越多这个“切换后丢包”的机制越重要否则上位机画出来的曲线每切一次通道就出现一个尖峰。我自己的习惯是在驱动层封装一个ADS124S08_ScanChannels函数内部维护通道表、当前索引和丢弃计数每个通道占用三个转换周期第一个周期丢弃第二个周期稳定第三个周期取数。这样上层业务代码完全感受不到通道切换的存在拿到的每一组数据都是稳定后的结果。这几处改造做下来这个例程基本就从一个“能跑的驱动”升级成了一个“能上产线的采集前端”。我用过不少ADC驱动ADS124S08的寄存器模型不复杂难的全在时序细节上。从那以后我每次拿到一颗新的TI ADC第一件事不是写代码而是把数据手册里的时序图和SPI模式表打印出来贴在调试台旁边确认完再动键盘。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →