简介:针对STM32F407与TI ADS124S08的24位ADC驱动资源包,面向需要高精度模拟量采集的嵌入式开发者,可广泛应用于工业自动化、医疗检测与传感信号调理等场景。ADS124S08本身具备24位分辨率、内置可编程增益放大器和内部参考电压,能够直接处理毫伏级小信号,再配合STM32F407的浮点运算和外设接口,可搭建完整的数据采集前端。压缩包共268个文件,约16.44MB,以C源文件、头文件、标准外设库和Keil工程文件为主体,包含可直接编译的工程配置及编译输出文件,还有PDF、TXT等辅助文档,目录结构清晰,便于从底层驱动到应用示例对照学习。目前已有1007人学习下载,驱动代码和参考工程覆盖ADC初始化、通道选择、数据读取与处理等关键环节,能帮助开发者快速完成高精度转换功能集成,尤其适合对ADS124S08驱动不熟悉的开发者参考与二次开发。
1. ads124s08驱动的起点:24位模拟量怎么变成数字量
一个标着“24位模拟量”的ads124s08驱动,最让stm32f407工程师头疼的往往不是SPI硬件本身,而是上电后忘了从RDATAC连续读模式里退出来,写进去的寄存器全部无效。ADS124S08是TI的24位Δ-Σ ADC,内置PGA和2.048V基准,把现场传感器的模拟量经过调制器和数字滤波器后,变成24位二进制补码,从SPI串行读出。标题里的“ADS90数字量”说的就是这串数字输出,它跟PLC场景里常说的开关量不是一回事:开关量只有0和1,这里的数字量是一整个有符号整数,范围从-8388608到8388607。
这块芯片适合4/8通道、增益可配、采样率不超过4 kSPS的采集场景,比如应变桥、热电偶、称重和电池电压监测。做驱动的人真正要解决的,只是三件事:SPI时序对得上、寄存器写进去、数据读回来按电压公式还原。下文按这个顺序,把从stm32f407接线到能稳定出数的完整路径拆开讲。
2. ADS124S08与stm32f407的SPI握手:模式、引脚与时序
2.1 Δ-Σ架构决定的数据流:ADC输出是数字量,不是开关量
ADS124S08内部先由调制器把模拟输入变成高码率比特流,再由数字滤波器抽取成低速率的24位结果。这个“抽取”过程意味着,转换结果不是瞬时电压,而是过去一段时间窗口的平均值,芯片因此天然有了抗混叠能力。代价是每次转换完成时间比SAR型ADC长得多,数据从DRDY引脚的下落沿通知你“新的一帧准备好了”。
很多第一次从ADS1115这类芯片转过来的工程师,会习惯性在读取前先发一条“读转换结果”命令。ADS124S08上电默认进入的是RDATAC连续读模式,此时不能执行RREG/WREG之外的命令,想配置寄存器必须先发SDATAC退出。反过来,有人一上来就发RDATA读数据,发现读回来的字节全是0xFF,多半也是这个原因。这个顺序问题,是整套驱动里最容易被忽略的启动条件。
2.2 MODECN决定SPI模式:模式1还是模式3,先量电平再写代码
ADS124S08的数据手册里有一个专门管SPI模式的引脚MODECN。MODECN拉低时,芯片工作在SPI模式1,即CPOL=0、CPHA=1,SCLK空闲为低,数据在第二个边沿被采样;MODECN拉高时,芯片工作在SPI模式3,即CPOL=1、CPHA=1,SCLK空闲为高。两个模式只是极性不同,采样沿都是第二个边沿,所以MISO上的数据建立时间要求差不多。
上电第一步,拿万用表量MODECN引脚电平,再对应配置stm32f407的SPI。原理图里这个脚常通过10kΩ电阻接GND或VDD,也有板子直接悬空,悬空时电平不确定,是通信不稳定的典型来源。写代码时把这一项单独做成宏,换板子时只改宏,不碰初始化函数:
/* 0: MODECN=0, SPI模式1(CPOL=0,CPHA=1) */ /* 1: MODECN=1, SPI模式3(CPOL=1,CPHA=1) */ #define ADS124S08_SPI_MODE_1 1uSCLK频率也在这里一起定。ADS124S08的SCLK上限取决于数据手册给出的电气参数,我一般把F407这边的SPI时钟压在4 MHz以下,留给走线电容和电平转换余量。SPI2挂在APB1总线上,主频168MHz时APB1是84MHz,用32分频得到2.625 MHz,稳妥够用。
2.3 F407引脚分配:SPI2还是SPI3,避开片选与中断冲突
stm32f407的SPI1挂在APB2上,速度上限比SPI2/SPI3高,但SPI1引脚与很多板载外设复用,留给ADC的位置往往不好。常见做法是把ADS124S08放到SPI2或SPI3上,用软件NSS控制片选,DRDY接一个支持外部中断下降沿的GPIO。我的一块自制板上用的是SPI2,引脚分配如下:
| F407引脚 | 功能 | ADS124S08引脚 | 说明 |
|---|---|---|---|
| PB13 | AF5/SPI2_SCK | SCLK | 时钟极性由MODECN定 |
| PB14 | AF5/SPI2_MISO | DOUT | 数据输出,芯片推挽输出 |
| PB15 | AF5/SPI2_MOSI | DIN | F407写寄存器用 |
| PB12 | GPIO输出 | CS | 每帧操作前拉低,帧结束拉高 |
| PA1 | GPIO下降沿中断 | DRDY | 开漏输出,板子上必须带上拉电阻 |
注意DRDY是开漏结构,没有上拉时,即使ADC正常转换,F407也永远等不到下降沿。这是硬件上最隐蔽的坑,原理图评审时一定要确认这颗电阻存在。CS不要交给SPI外设的硬件NSS管理,ADS124S08的每个命令、每次数据读取都是一个独立的片选帧,帧内字节数各不相同,用GPIO手动拉放最可靠。
3. stm32f407侧接线与SPI2初始化:最小可运行配置
3.1 电源与基准:AVDD、DVDD、VREF和输入滤波的硬规则
ADS124S08的数字电源和模拟电源是分开的,DVDD负责接口电平,AVDD负责内部模拟电路。这两路电源的电压可以不同,但去耦都要做到位:每个电源引脚就近放一个100nF陶瓷电容,再在电源入口放10µF钽电容或瓷片电容。模拟输入引脚附近不要走数字信号线,尤其是SCLK这类沿很陡的信号,串扰进去会直接体现在低几位跳动上。
参考电压决定满量程。用内部2.048V基准时,REFP和REFPN之间建议并一颗1µF低ESR电容;噪声敏感的场合换成外部基准芯片,把REFMUX寄存器切到外部基准。初次调试不接传感器时,把模拟输入直接短接到AVSS,这时读出的码值应当接近0,这是判断硬件通路是否干净的最快办法。
3.2 用HAL库配置SPI2:8位、MSB first、模式1
下面这段初始化代码基于STM32CubeMX生成的工程,手写调整了几个关键参数。ADS124S08的SPI帧是8字节整数倍,所有寄存器、命令、数据都按字节传输,确认数据位宽为8位即可。
void MX_SPI2_Init(void) { GPIO_InitTypeDef gpio = {0}; SPI_InitTypeDef spi = {0}; __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_SPI2_CLK_ENABLE(); /* PB13=SCK, PB14=MISO, PB15=MOSI, 全部复用为AF5 */ gpio.Pin = GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15; gpio.Mode = GPIO_MODE_AF_PP; gpio.Pull = GPIO_NOPULL; gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; gpio.Alternate = GPIO_AF5_SPI2; HAL_GPIO_Init(GPIOB, &gpio); /* CS用普通GPIO,手动控制,不交给SPI硬件NSS */ gpio.Pin = GPIO_PIN_12; gpio.Mode = GPIO_MODE_OUTPUT_PP; gpio.Pull = GPIO_PULLUP; gpio.Speed = GPIO_SPEED_FREQ_MEDIUM; HAL_GPIO_Init(GPIOB, &gpio); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); spi.Instance = SPI2; spi.Init.Mode = SPI_MODE_MASTER; spi.Init.Direction = SPI_DIRECTION_2LINES; spi.Init.DataSize = SPI_DATASIZE_8BIT; spi.Init.CLKPhase = SPI_PHASE_2EDGE; /* CPHA=1, 第二边沿采样 */ spi.Init.CLKPolarity = SPI_POLARITY_LOW; /* CPOL=0, 对应MODECN=0 */ spi.Init.NSS = SPI_NSS_SOFT; spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; /* 84MHz/32=2.625MHz */ spi.Init.FirstBit = SPI_FIRSTBIT_MSB; spi.Init.TIMode = SPI_TIMODE_DISABLE; spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; HAL_SPI_Init(&spi); }代码里CLKPolarity和CLKPhase这一对参数要跟MODECN电平严格对应。上面写的CPOL=0对应MODECN拉低;如果量到MODECN为高,只把SPI_POLARITY_LOW改成SPI_POLARITY_HIGH,芯片会切到真正匹配的模式。注释里也标了APB1频率,84MHz除以32得到2.625MHz,这个频率对大多数ADS124S08应用都留有余量。SCLK上的信号完整性如果很差,先降低分频系数,不要急着改输入滤波电路。
3.3 最小回环自检:先证明F407的SPI通路是好的
ADS124S08还没焊上去之前,可以先把SPI2的MOSI和MISO用一根短导线短接,做一次自发自收测试。F407的SPI在全双工模式下,发送一个字节的同时收到对端返回的字节,把MOSI短到MISO后,发什么就应收到什么。测试代码不需要驱动芯片,只验证GPIO复用、时钟配置和SPI外设本身是否正常。
uint8_t spi2_loopback_test(void) { uint8_t tx = 0xA5; uint8_t rx = 0; HAL_SPI_TransmitReceive(&hspi2, &tx, &rx, 1, 100); return (tx == rx) ? 0 : 1; }短接导线要尽量短,直接插在排针上就行。返回0说明SPI2通路正常,这时再去查ADS124S08电源和DRDY;返回1则先查时钟树里SPI2是否被使能、GPIO是否复用到了AF5。这个自检能省掉大量“为什么读回全FF”的排查时间。
4. ads124s08驱动实现:寄存器读写、初始化序列与24位数据解析
4.1 命令宏和字节读写原语先写好
ADS124S08的命令集非常规整,读寄存器、写寄存器、读数据、复位各是一个单字节命令。驱动底层只要封装一个SPI收发函数,上层所有操作都从这个函数展开,禁止在业务代码里到处直接调用HAL库。
#define ADS124S08_CMD_RREG 0x20u /* 读寄存器,后续字节为寄存器地址 */ #define ADS124S08_CMD_WREG 0x40u /* 写寄存器,后续字节为寄存器地址 */ #define ADS124S08_CMD_RDATA 0x12u /* 读一次转换结果,24位 */ #define ADS124S08_CMD_RDATAC 0x14u /* 连续读模式,上电默认 */ #define ADS124S08_CMD_SDATAC 0x16u /* 退出连续读 */ #define ADS124S08_CMD_RESET 0x06u /* 复位 */ static uint8_t ads124s08_spi_byte(uint8_t tx) { uint8_t rx = 0; HAL_SPI_TransmitReceive(&hspi2, &tx, &rx, 1, HAL_MAX_DELAY); return rx; }ads124s08_spi_byte每次只收发一个字节,牺牲一点效率保逻辑清晰。所有命令、寄存器写值、数据字节都走这个函数,MISO上收到的返回字节就是上一次发送期间芯片输出的内容。RREG读寄存器时,第二个字节是“后续要读的字节数减1”,这个减1的规则源自TI的SPI协议,写0表示读1个字节,写2表示读3个字节,初学时最容易漏。
4.2 初始化序列:SDATAC、WREG、回读校验
上电或复位后,ADS124S08会默认进入RDATAC连续读模式。此时执行RREG之外的命令会被忽略,所以初始化第一步永远是退出连续读模式,再写寄存器。下面是完整的初始化流程:
void ads124s08_init(void) { uint8_t chk = 0; /* 复位芯片,之后等待内部上电稳定 */ ADS124S08_CS_L(); ads124s08_spi_byte(ADS124S08_CMD_RESET); ADS124S08_CS_H(); HAL_Delay(2); /* 退出RDATAC,允许写配置寄存器 */ ADS124S08_CS_L(); ads124s08_spi_byte(ADS124S08_CMD_SDATAC); ADS124S08_CS_H(); /* INPMUX=0x0A:正输入AIN0,负输入AVSS(单端模式) */ ads124s08_wreg(0x01, 0x0A); /* PGA=0x30:增益1倍,数据速率保持默认;低4位查手册数据速率表 */ ads124s08_wreg(0x02, 0x30); /* 回读校验,确认SPI写真的写进去了 */ chk = ads124s08_rreg(0x01); if (chk != 0x0A) { /* 回读失败:先查CS波形,再查SPI模式 */ } }INPMUX寄存器的高4位选正输入,低4位选负输入,0x0A只是AIN0对AVSS这一种接法。如果板子上信号接的是AIN3,高位改成3;如果做差分测量,低位改成另一个通道编号。PGA寄存器的高3位是增益,001代表1倍,低4位是数据速率代码,不同代码对应的SPS值要查数据手册里的速率表,不同增益下最大速率也不一样,不建议靠记忆硬填。
这里把CS每一帧都拉高拉低,是因为ADS124S08的RREG和WREG都以CS下降沿为起始条件。回读校验这步看起来多余,但对排查硬件问题非常有用:写进去再读出来不一致,基本可以确定是SPI模式、CS时序或MISO通路三者之一出了问题。
4.3 读24位数据:DRDY下降沿触发与二进制补码处理
转换完成后DRDY引脚会产生一个下降沿。读取数据时,先等DRDY变低,再拉低CS,发送RDATA命令,随后连续收3个字节,就是当前的24位转换码。读完后DRDY自动回高,等待下一次转换。
uint32_t ads124s08_read_single(void) { uint32_t raw = 0; /* 等待DRDY拉低,表示新数据已就绪 */ while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) != GPIO_PIN_RESET) { /* 这里建议加超时计数,避免死等 */ } ADS124S08_CS_L(); ads124s08_spi_byte(ADS124S08_CMD_RDATA); raw |= (uint32_t)ads124s08_spi_byte(0xFF) << 16; raw |= (uint32_t)ads124s08_spi_byte(0xFF) << 8; raw |= (uint32_t)ads124s08_spi_byte(0xFF); ADS124S08_CS_H(); return raw; }发送RDATA后,芯片从DOUT引脚依次移出最高字节、中间字节、最低字节。读数据时MOSI上发的0xFF是空指令,只是为了产生SCLK时钟,芯片并不关心MOSI内容。等待DRDY的地方要加超时,常见写法是配一个SysTick计数器,超过数据速率对应周期数就报错退出,避免SPI卡死在死循环里。
需要注意,RDATA命令在RDATAC退出后才能使用。如果坚持用连续读模式,就不要发RDATA,而是每检测到一次DRDY下降沿直接读3个字节,省掉命令字节,但此时任何配置寄存器的写入都会被芯片拒绝。
4.4 把原始码换算成实际电压:单端与差分的差别
24位数据是有符号二进制补码,满量程对应0x7FFFFF,负满量程对应0x800000。换算成电压前先做符号扩展,再用满量程值归一化。stm32f407自带FPU,单精度float足够完成这个计算,没必要用double。
float ads124s08_raw_to_volt(uint32_t raw, float vref, float pga_gain) { /* 24位补码转有符号整数 */ int32_t s = (int32_t)(raw << 8) >> 8; /* 单端输入:电压 = 码值/2^23 * 参考电压 / 增益 */ /* 差分输入时,满量程是±VREF,要先乘以2再除以增益 */ return ((float)s / 8388608.0f) * vref / pga_gain; }符号扩展那行是驱动里最容易写错的地方,raw << 8把24位数据挪到32位整数的最高字节,>> 8用算术右移把符号位填满。若直接用浮点除法算,0x800000会被当成8388608而不是-8388608,电压值就会差一个满量程。单端输入时,电压范围从0到VREF对应码值0到0x7FFFFF;差分输入时,范围是-VREF到+VREF,公式里的vref要换成2倍参考电压,这一点在硬件设计阶段就要想清楚,软件改起来容易,硬件接线反了就只能飞线。
5. 用DRDY波形和内部温度传感器做整链自检
5.1 温度传感器读出的数字量怎么验证SPI和基准
配置完增益和输入通道后,第一件事不是接传感器,而是读内部温度传感器。ADS124S08内部有一个温度传感器,切换INPMUX把负输入端选到内部温度传感器通道,读回的数据就是当前结温对应的码值。这个码值即使有误差,也一定在某个合理区间内,不会因为外部接线而改变。
/* 切换到内部温度传感器通道后执行一次转换 */ ads124s08_wreg(0x01, 0x20 | 0x0B); /* 正端=内部温度传感器, 负端=AVSS */ ads124s08_wreg(0x02, 0x30); /* 增益1倍不变 */ uint32_t t_raw = ads124s08_read_single();读回来的码值如果在预期温度范围内,说明SPI通信、寄存器写入、参考电压、DRDY中断整条链路都是通的,问题只可能出在外部输入信号上。这一步把“芯片问题”和“电路问题”一刀切开,比拿示波器逐脚量效率高得多。
5.2 两个必看的波形:DRDY下降沿与SCLK空闲电平
温度传感器读数正常后,再看两处波形确认时序余量。第一处是DRDY下降沿后、发起RDATA命令之前的间隔,间隔太短说明主循环调度太慢,数据可能被下一次转换覆盖;第二处是SCLK空闲电平,用示波器DC耦合看,空闲时应是低电平,与MODECN设定一致。
| 现象 | 优先查什么 | 处理方式 |
|---|---|---|
| RREG回读全0xFF | CS时序和SPI模式 | 量SCLK空闲电平,核对MODECN |
| DRDY一直为高 | 开漏上拉电阻 | 确认DRDY引脚有上拉 |
| 读数固定为0 | START引脚/参考电压 | 量START电平和VREF引脚步电压 |
| 数值偏移很大 | INPMUX寄存器 | RREG回读0x01与写值比对 |
| 低几位乱跳 | 输入滤波/电源去耦 | 输入引脚加RC,AVDD去耦加强 |
5.3 用温度传感器代外部信号做最终验证
比较稳妥的收尾动作是把内部温度传感器换算结果跟热成像仪或板载NTC对比,误差在几摄氏度内即可。这一步能同时验证参考电压和增益配置。外部信号接入前,先在软件里把ads124s08_raw_to_volt的输出打印到串口,用手触摸输入走线观察电压变化方向,确认极性正确后再接传感器,避免接反导致芯片输入超过共模范围。
本文还有配套的精品资源,点击获取