尧图精选

ADS1248驱动与SPI时序调试:24位Δ-Σ ADC稳定读取指南

🕒 发布时间:2026/10/2 4:09:30 📁 来源:尧图网络
简介这是一份面向嵌入式开发者的ADS1248芯片SPI驱动源码包聚焦16位高精度ADC的底层通信实现适合需要快速集成传感器采集、工业自动化或医疗测量功能的单片机项目。驱动采用C语言编写包含ads1248.c与ads1248.h两个核心文件分别实现SPI读写、PGA增益配置、数字滤波器设置以及转换启停等函数并配套README说明文档便于理解调用流程与引脚连接。压缩包仅3个文件、大小约4KB轻量简洁可直接复制到工程中使用。已有310人学习下载。对不熟悉ADS1248寄存器操作或SPI时序的开发者而言这份驱动省去了查阅数据手册和调试底层通信的步骤能显著缩短高精度模拟采集模块的开发周期。若配合手册使用还可借此掌握多通道单端输入、可编程增益放大器及低功耗模式等典型配置方法。1. ADS1248 驱动代码包背后的第一课24 位精度不是靠接对线就有的从网上下到一个叫 ADS1248-Drivers-master_spi_ads1248_ 的驱动包第一步往往是高兴第二步是翻车——很多人把里面的 .c 文件拷进工程、把 STM32CubeMX 里的 SPI 速度直接拉到 18M读回来的数据却全在乱跳。ADS1248 是一颗 24 位 Δ-Σ ADC标称精度看着美好但能不能把 24 位分辨率变成真实测量精度取决于驱动里的 SPI 时序、寄存器参数和硬件布局跟引脚号没关系。这套驱动代码包正好可以作为入口带你从“能读到数”走到“读得准”。适合正在做高精度数据采集、称重、温压测量的嵌入式工程师也适合被 SPI 通信折腾到怀疑人生的新手。2. ADS1248 为什么难驱动24 位 Δ-Σ ADC 的硬件边界与 SPI 时序ADS1248 不是拿出来就有高精度的芯片。它的精密性来自一整套模拟链路输入多路选择器、可编程增益放大器PGA、24 位 Δ-Σ 调制器、数字滤波器再加上片内温度传感器和参考电压源。任何一个环节用错读回来的数据都会让你误以为芯片是坏的。2.1 普通 16 位 ADC 和 ADS1248 差在哪噪声、PGA 和内置参考先明确一个事实普通 16 位 SAR ADC 的真实有效位数往往只有 12 位左右因为量化噪声和参考源噪声会吃掉低几位。ADS1248 能到 24 位靠的是过采样和数字滤波把带内噪声压得很低。但这不是免费的数字滤波器需要时间所以数据速率越低噪声越小。5SPS 下面噪声远小于 2000SPS 下面这是 Δ-Σ 结构本身的特性不要拿 SAR 芯片“速率越高越准”的思路来套。PGA 增益范围 1 到 128对热电偶这种几十微伏到几十毫伏的小信号特别重要。没有 PGA24 位分辨率只是数字上的花架子小信号会被前端噪声盖掉。但 PGA 不是白放的增益越大输入阻抗越低、失调和温漂的影响越明显对外部信号源阻抗也更敏感。用 128 倍增益去接一个 10kΩ 源阻抗的传感器读数基本不可能稳定。内置参考是 2.048V温漂典型在 10ppm/°C 这个量级很多工业场景够用。但对长期漂移要求高的称重仪表我会选外部精密参考因为内部参考的绝对值误差还能用软件校准温漂却很难通过单点校准补回来。内部温度传感器也别当摆设热电偶冷端补偿直接读它最省事。2.2 SPI 通信协议里的模式 1 陷阱CPOL/CPHA 与数据输出时序这是驱动包最难搞的部分。ADS1248 的 SPI 要求模式 1也就是 CPOL0、CPHA1空闲时时钟为低数据在第二个边沿下降沿被采样。常见误区是直接用 STM32CubeMX 默认的模式 0CPOL0、CPHA0结果命令发出去、数据读回来全是 0xFF 或者乱码。看 SPI 时序图会清楚很多模式 0 和模式 1 就差一个相位的采样沿命令字相同但 DOUT 上的数据会错开半个位周期。示波器抓 SCLK 和 DOUT 时只要对着手册上的 SPI 时序图比对一次就能发现是采样沿选错。很多下载到的驱动包默认按模式 0 写是因为作者用某颗国产 MCU 的硬件 SPI 时发现模式 0 也“能跑”实际是把 CPOL 和 CPHA 的组合理解反了。我的习惯是任何一颗 SPI 芯片第一件事就是查手册里的时序图确认是模式几再在 CubeMX 里选。ADS1248 的命令分两类一类是单字节命令比如 RESET0x06、START0x08、WAKEUP0x02另一类是寄存器读写命令 RREG0x20 地址和 WREG0x40 地址。读寄存器时第二个字节必须写“寄存器数量减 1”这个字节太容易踩坑——只读一个寄存器写 0x00 没错写 0x01 就会多读一个寄存器后面的数据全错位。2.3 软件片选、硬件片选和 DRDY板级接法决定驱动复杂度很多人喜欢用 STM32 的硬件 NSS 自动控制片选这在只有一个从机的板子上问题不大但 ADS1248 的 CS 要求在每笔 SPI 事务结束后拉高一段时间硬件 NSS 在多从机共线时很难把时序做干净。我一般用 GPIO 软件片选把 CS 当作普通推挽输出读写前拉低、结束再拉高干净利落。这个选择也会直接影响后续能不能和 W25Q64 这类 SPI Flash 共用总线。DRDY 是数据就绪输出转换完成后由高变低读取数据后恢复高。它最好接 MCU 的外部中断引脚下降沿触发如果只是用 GPIO 查询驱动里务必要加超时。否则初始化没配好、SPI 又卡住的时候整个程序会死在 while 等 DRDY 的循环里。上电时序也要注意模拟电源和数字电源要同时上电RESET 引脚建议由 MCU 的 GPIO 控制不要在 PCB 上直接悬空。3. 把 Drivers 包里的思路移植到 STM32从 CubeMX 建工程到连续读出 24 位数据标题里那个 Drivers 目录结构习惯上会是底层 SPI 加寄存器层再加应用层的三层结构。很多下载包把三个层次全塞进一个文件里移植时看着省事后面调参数就痛苦了。我更推荐自己写一套最小驱动核心就四个函数底层发一个字节、写寄存器、读寄存器、读转换数据。3.1 最小系统与 CubeMX 配置先以 1MHz SPI 把时序跑起来第一步不是写代码是先把 CubeMX 的 SPI 配置做对。使用 STM32F103 或 F407任意外设 SPI 都行关键是这三个参数波特率预分频先选大一点让 SPI 时钟在 1MHz 左右时钟极性 CPOL 设为 Low时钟相位 CPHA 设为 2 Edge也就是模式 1。CS 和 DRDY 都配成 GPIOCS 推挽输出DRDY 输入可选的 RESET 也配成 GPIO 推挽输出。先跑低速的原因很简单ADS1248 的 24 位数据输出不像 Flash 那么抗造高速下 PCB 走线寄生电容会让沿变缓初期排查时分不清是时序问题还是硬件问题。低速调通后再把波特率升上去比如先改到 4MHz逐级验证。下面这段是把整个驱动和 HAL 耦合收口的底层函数所有上层函数都只调它。#define ADS1248_CS_LOW() HAL_GPIO_WritePin(ADS1248_CS_GPIO_Port, ADS1248_CS_Pin, GPIO_PIN_RESET) #define ADS1248_CS_HIGH() HAL_GPIO_WritePin(ADS1248_CS_GPIO_Port, ADS1248_CS_Pin, GPIO_PIN_SET) static uint8_t adc_spi_byte(uint8_t tx) { uint8_t rx 0; while (HAL_SPI_GetState(hspi2) ! HAL_SPI_STATE_READY) {} if (HAL_SPI_TransmitReceive(hspi2, tx, rx, 1, 100) ! HAL_OK) { return 0xFF; } return rx; }这个函数同时发送一个字节并接收一个字节SPI 全双工模式下必须这么写因为读数据时也要给 SCLK 提供时钟。超时时间我习惯设 100ms调试时如果频繁返回 0xFF多半是模式没配对或时钟没起来。这个函数只依赖 hspi2换 SPI 外设时只改这一处。3.2 寄存器级驱动层WREG/RREG 命令序列与初始化函数寄存器层要按手册的命令字实现读写重点是把“命令字节、数量字节、数据字节”的顺序写对。写寄存器时发送 WREG 命令后跟数量字节再跟数据读寄存器时发送 RREG 命令后跟数量字节再读一个字节。每次事务都要拉低 CS 开始、拉高 CS 结束中间不能停太久。#define ADS1248_CMD_RESET 0x06 #define ADS1248_CMD_START 0x08 #define ADS1248_CMD_RREG 0x20 #define ADS1248_CMD_WREG 0x40 void ADS1248_Reset(void) { ADS1248_CS_LOW(); adc_spi_byte(ADS1248_CMD_RESET); ADS1248_CS_HIGH(); HAL_Delay(10); } void ADS1248_WriteReg(uint8_t addr, uint8_t value) { ADS1248_CS_LOW(); adc_spi_byte(ADS1248_CMD_WREG | (addr 0x07)); adc_spi_byte(0x00); // 数量字节写 1 个寄存器所以是 0x00 adc_spi_byte(value); ADS1248_CS_HIGH(); } uint8_t ADS1248_ReadReg(uint8_t addr) { uint8_t value 0; ADS1248_CS_LOW(); adc_spi_byte(ADS1248_CMD_RREG | (addr 0x07)); adc_spi_byte(0x00); value adc_spi_byte(0x00); ADS1248_CS_HIGH(); return value; }数量字节是 8 位寄存器个数减 1写一个寄存器必须是 0x00。总有人拿着别的芯片例程写成 0x01结果 ADC 把后一个寄存器也覆盖掉。读回校验是很好的习惯写完寄存器立刻读回来对比能排除大部分 SPI 配置问题。初始化函数按典型顺序做四件事等电源稳定、复位、配置寄存器、启动转换。寄存器配置先用保守参数后面再调void ADS1248_Init(void) { ADS1248_CS_HIGH(); HAL_Delay(50); // 等待电源和内部参考稳定 ADS1248_Reset(); ADS1248_WriteReg(0x00, 0x00); // MUX0关闭断电检测,输入由MUX1决定 ADS1248_WriteReg(0x01, 0x00); // VBIAS关闭所有偏置电压 ADS1248_WriteReg(0x02, 0x00); // MUX1默认差分通道 AIN0/AIN1 ADS1248_WriteReg(0x03, 0x34); // SYS0PGA8, 数据速率 20SPS ADS1248_CS_LOW(); adc_spi_byte(ADS1248_CMD_START); ADS1248_CS_HIGH(); }0x34 是我常用的一个工程起点高四位 0011 对应 PGA 增益 8 倍低四位 0100 对应 20SPS。这个值在热电偶和大部分模拟小信号的场景下都够用后续要改只需要动 SYS0 这一个寄存器。MUX1 的 0x00 默认配置取决于你的板卡输入通道别照抄完就以为选对通道了。3.3 连续转换读取DRDY 查询、24 个时钟与二进制补码拼解数据读取是驱动里最关键的部分。ADS1248 在连续转换模式下DRDY 会周期性地拉低表示新数据就绪。读取时要等 DRDY 变低然后拉低 CS连发 24 个时钟把三个字节从 DOUT 移出来。不需要发送读数据命令直接给时钟就能拿到最新转换结果这是 ADS1248 给硬件设计的一个方便点。int ADS1248_WaitData(uint32_t timeout_ms) { uint32_t start HAL_GetTick(); while (HAL_GPIO_ReadPin(ADS1248_DRDY_GPIO_Port, ADS1248_DRDY_Pin) ! GPIO_PIN_RESET) { if (HAL_GetTick() - start timeout_ms) { return -1; } } return 0; } int ADS1248_ReadData(int32_t *raw) { uint8_t buf[3]; if (ADS1248_WaitData(100) ! 0) { return -1; } ADS1248_CS_LOW(); buf[0] adc_spi_byte(0x00); buf[1] adc_spi_byte(0x00); buf[2] adc_spi_byte(0x00); ADS1248_CS_HIGH(); *raw (int32_t)(((uint32_t)buf[0] 16) | ((uint32_t)buf[1] 8) | ((uint32_t)buf[2])); return 0; }这里最容易错的是符号扩展。buf[0] 最高位为 1 时整个 24 位数是负数直接拼成 int32_t 会得到一个大正数。ADS1248 输出的是二进制补码所以我把三个字节先拼成 uint32_t再强制转成 int32_t用 C 语言的位模式转换直接解释成负数逻辑简单也不会漏。电压换算公式我一般这样写float ADS1248_ToVoltage(int32_t raw, float vref, float pga) { return (float)raw * (2.0f * vref / pga) / 8388608.0f; }公式的含义是满量程对应 ±VREF/PGA24 位补码的满码范围是 ±2 的 23 次方所以每个 LSB 对应 2VREF/(PGA×2^24)。如果你的项目要求更高精度这里可以按手册的 LSB 定义再核对一次不同版本手册表述略有差异。4. ADS1248 的六个必调参数与寄存器配置增益、速率、滤波器要一起看驱动能读出数只是第一步参数配不好读出来的数没法用。ADS1248 的寄存器配置里最要命的不是命令字而是增益、数据速率、滤波器和参考源之间的耦合关系。4.1 数据速率与 PGA 增益联动为什么“越高越好”在这里不成立很多人上来就把数据速率设到最高理由是“采样快一点数据多点求平均更准”。这在 Δ-Σ ADC 上是反向工程数字滤波器带宽跟随数据速率变化速率越高通带越宽带内噪声越多。更关键的是PGA 增益越高内部放大器可用带宽越小高增益配高数据速率时信号根本没建立完就被采样了。工程上我一般按“信号带宽的 5 到 10 倍”选数据速率。热电偶信号本身只有几 Hz20SPS 已经足够称重传感器动态响应慢10SPS 更合适还能顺便把工频干扰压掉。增益的选择反过来先用示波器看信号幅值再用“目标满量程÷输入信号幅值”估算增益别直接上 128。4.2 SYS0 寄存器典型配置三组测量场景的参数对照表SYS0 寄存器是 ADC 配置的核心高四位选 PGA 增益低四位选数据速率。下面这三组配置是我在实际板卡上验证过的起始点不是唯一答案但足够新手下第一版固件测量场景输入信号典型范围PGA数据速率参考源选择注意事项热电偶测温±10mV 级别1285SPS内部 2.048V开启内部温度传感器做冷端补偿称重传感器±10mV 到 ±20mV64 到 12810SPS内部 2.048V传感器激励源要单独滤波压力变送器 4-20mA1V 到 5V250Ω 采样电阻1 到 420SPS外部 2.5V 参考输入信号幅值大重点是共模电压配好之后一定要读回 SYS0 确认。很多代码包初始化函数里写了一大串寄存器实际 SPI 模式不对数据根本没写进去。读回校验会立刻暴露问题。4.3 内部参考和外部参考的取舍2.048V、漂移和参考去耦内部参考 2.048V 省事但精度和温漂不是顶级。做批量产品时每一片 ADS1248 的内部参考都存在个体差异靠软件校准能拉回增益误差却很难拉回温漂。外部参考一般选 2.5V 或 4.096V前提是 ADC 模拟供电要足够高比如 5V 供电、2.5V 参考就比 2.048V 更安全。外部参考的 PCB 设计比选型更重要。参考输出端要放 10μF 钽电容并联 0.1μF 陶瓷电容去耦电容离 REF 引脚越近越好。千万不要用普通电阻分压直接从电源取参考电压ADC 内部开关电容采样会在参考引脚上抽取瞬态电流分压网络的阻抗稍微高一点参考电压就被调制出毛刺读数低几位立刻开始跳。4.4 输入通道与 MUX 的设置单端、差分和伪差分ADS1248 有 8 个输入引脚可以配成差分或单端但严格说都是伪差分所有输入都以 AGND 为参考。所以共模电压必须在 GND0.1V 到 AVDD-0.1V 这个范围内超过了一点点PGA 就会饱和。工程师常在这里翻车以为只要信号差值是安全的就行忽略了输入引脚对地的绝对电压。MUX0 和 MUX1 两个寄存器配合决定输入方式。MUX0 还带一个断电检测位如果软件里不小心把它设成 1输入通道会被切到内部检测路径读数完全错误。初始化前最好把这个寄存器读回来看一眼。5. ADS1248 驱动调试避坑五条高频翻车记录与排查路径下面这五条问题是我在十来个项目里见过最多的每条都按现象、原因、解决的顺序写清楚。照着排查大多能在半小时内定位。5.1 读出全 0先查 SPI 模式别急着怀疑芯片现象写寄存器后读回来全是 0x00转换数据也全是 0片选和电源都正常。原因八成是 SPI 模式配置成模式 0 了数据采样沿和 ADS1248 要求的模式 1 差半个时钟周期。也有少量情况是 SPI 时钟极性反了DOUT 数据在错的沿被采到。硬件上 CS、SCLK、DIN、DOUT 四根线全部接反的可能性也有但少见。解决先在 CubeMX 里把 Mode 改成 CPOLLow、CPHA2 Edge重新生成代码。再用示波器或逻辑分析仪抓 SCLK 和 DOUT对比手册里的 SPI 时序图。抓不到就写一个只读固定寄存器 0x00 的函数用 1MHz 时钟反复读一般几十秒就能确认。5.2 DRDY 悬空导致程序卡死超时机制和上拉接法现象程序运行到等待 DRDY 的循环里永远出不来看门狗也不生效整机假死。原因DRDY 引脚悬空或者外部中断没配置成下降沿触发。ADS1248 的 DRDY 在每个转换周期都会拉低一次如果 MCU 这边没接该引脚驱动里 while 轮询状态永远为高就会卡死。解决DRDY 必须接 MCU GPIO 或 EXTI。更稳妥的做法是驱动里 WaitData 函数带超时参数超时后返回错误码并复位 SPI 外设。这个后悔药一定要提前吃否则量产现场出问题靠示波器很难立刻定位。5.3 读数低几位乱跳参考电压噪声和地环路才是黑匣子现象输入一个稳定的电压源读数高几位很稳低三位在上下跳动换算成电压有几十微伏的摆动。原因24 位分辨率下1 个 LSB 对应的电压是微伏级别PCB 上的地环路噪声、参考电压纹波、开关电源的尖峰都会被 ADC 看到。普通万用表量不出来的噪声在 24 位 ADC 面前被放得很大。解决先用内部参考对比外部参考排除参考噪声。然后把模拟地和数字地在 ADC 引脚附近单点连接模拟电源用 LDO 加 LC 滤波参考去耦电容加大到 10μF。软件上开数字滤波器或者做多次采样平均是最后的救命手段但治标不治本。5.4 高增益下偶发大毛刺输入阻抗与 MUX 切换后的沉降现象PGA 增益设到 128输入悬空或接高阻传感器时数据偶尔跳出一个明显偏大的尖峰过几个采样点又恢复。原因PGA 输入阻抗有限高增益下输入引脚对地的阻抗更低外部信号源阻抗高时采样电容建立不充分。MUX 切换通道后前几个转换周期内部模拟链路还在沉降数据当然不可靠。解决把传感器输出阻抗降到 1kΩ 以下或在 ADC 前面加一级低噪声运放缓冲软件上每次切换通道后丢弃前两个转换结果。驱动包里如果提供“丢弃 N 次”的配置直接设成 2 到 4 次毛刺基本消失。5.5 上电后寄存器写不进去POR 时间与复位命令时序现象初始化函数写了好几串寄存器读回却全是默认值像是 SPI 根本没通信。原因上电后芯片还在内部复位MCU 这边立刻就开始写寄存器命令被丢掉了。还有一种情况是板子上 ADS1248 和 W25Q64 挂同一根 SPI 总线CS 控制没处理好上一笔 Flash 事务结束后 CS 没拉高足够长把 ADC 这边的 CS 时序污染了。解决初始化前先延时 50ms 以上再主动发送 RESET 命令延时 10ms最后写寄存器并逐个读回校验。多从机共用 SPI 总线时每笔事务结束后 CS 拉高至少几百纳秒我用逻辑分析仪测过很多翻车问题就是 CS 拉高时间不够。6. 用电压源校准 ADS1248从粗测到两点校准把驱动变成可交付模块驱动稳定后最重要的一件事是用真实电压源验证精度而不是拿万用表怼着电源看个大概。我会准备一个精度 0.05% 以上的可调电压源先输入 0V再输入参考电压的一半分别记录原始码并换算成电压。零输入时读数应该很接近 0但不会刚好是 0因为失调电压和 PGA 偏置电流会让输出码偏几个 LSB。半量程输入时读数与理论值之间的比例偏差就是增益误差。粗测的判据是换算电压误差在满量程的 0.1% 以内说明驱动链路基本健康。如果差得远先怀疑参考电压实际值再怀疑输入信号源精度。粗测通过后做两点校准把增益误差和失调误差固化成软件参数比在硬件上死磕精密电阻更划算。我会在驱动里定义一个校准结构体typedef struct { float k; // 增益补偿系数 float b; // 偏移量单位 mV } ADS1248_Cal; float ADS1248_ApplyCal(int32_t raw, float vref, float pga, ADS1248_Cal *cal) { float mv (float)raw * (2.0f * vref / pga) / 8388608.0f * 1000.0f; return mv * cal-k cal-b; }校准流程是对标准电压源输入两个点比如 0V 和 1V记录原始码算出 mv然后解二元一次方程求出 k 和 b。生产线上每块板跑一遍把结果存进外部 Flash 或 MCU 的 Flash 参数区产品一致性会好很多。很多高精度仪表厂都是这么做的区别只在于校准点的数量和自动化的程度。三个提升有效位数的小习惯也提一下第一PCB 布局时模拟输入走线越短越好差分走线要平行且等长远离 SCLK 和 DIN 这类跳变信号第二SPI 读取如果频率很高可以用 DMA 加 DRDY 中断让 CPU 在等待转换期间去做别的事数据稳定读取反而更好第三每读完一个数据不要急着用先做一次中值滤波或滑动平均滤掉偶发毛刺后再进入控制链路。我习惯把校准常数、SYS0 配置和输入通道选择全部写进一个头文件每换一块新板子先跑十分钟零输入稳定性测试再决定固件是否定版。这套流程看着麻烦但能把 24 位 ADC 的潜力真正挖出来。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →