
简介这份资源是面向STM32F103平台、针对AD7124高精度ADC芯片的完整驱动代码工程适合正在调试该芯片却苦于缺少参考的嵌入式开发者与新手。作者基于Keil5环境采用模拟SPI方式实现通信测试代码聚焦6通道、双极性、全功率采样并启用外部参考电压未堆砌冗余功能工程已全部调通可直接作为可靠起点。压缩包共215个文件约6.18MB包含35个c源文件、38个h头文件及大量编译中间产物d、o、crf等另有uvprojx工程文件、map映射文件、hex烧录文件与sct分散加载配置覆盖从源码到可执行输出的完整链路。目前已有6215人学习下载热度较高。读者可借此省去从零摸索寄存器时序与采样配置的时间快速理解AD7124在STM32F103上的驱动框架与排错思路降低调试门槛。1. 拿到一份「已全部调通」的 AD7124 驱动先别急着烧录手上这份 AD7124 驱动代码基于 STM32F103标题写着「已全部调通」。做过高精度采集的人都知道这句话含金量不低——AD7124 这颗 24 位 Σ-Δ ADC寄存器多、模式多、时序讲究SPI 读回来全是 0xFFFFFF 或者 0x000000 是常态。它解决的是小信号、高分辨率、多通道差分采集的场景比如称重、热电偶、RTD、压力桥路。适合谁适合已经能跑通 STM32 基础 SPI、但被 AD7124 的配置寄存器和校准流程卡住的嵌入式工程师。但「调通」是别人的板子调通换到你的硬件上第一件事不是烧录而是先搞清楚这份代码的配置假设和你的电路差在哪。2. AD7124 驱动骨架拆解SPI 时序、寄存器映射与状态机2.1 为什么 AD7124 不能当普通 SPI 从机用很多人第一反应是拿 STM32 的硬件 SPI 直接怼上去结果读 ID 都读不对。AD7124 的 SPI 有几个硬性约束CPOL1、CPHA1也就是空闲时钟高电平、第二个边沿采样。STM32F103 的 SPI1 配成SPI_CPOL_High | SPI_CPHA_2Edge才对得上。另外它的通信寄存器是 8 位但数据寄存器是 24 位读写要按「先写通信寄存器选目标寄存器再连续读/写 N 字节」的节奏走。如果你用 HAL 的HAL_SPI_TransmitReceive一次性发 4 字节大概率错位。常见做法是封装两个底层函数一个写通信寄存器一个读写数据。我一般会把 CS 拉低、发通信字、再操作数据、最后拉高 CS 这一整套放在一个函数里避免中间被中断打断导致帧错位。// AD7124 底层读写通信寄存器 数据寄存器 // hspi: 已配置为 CPOL1, CPHA1, 软件 NSS // addr: 寄存器地址(0x00~0x3F), rw: 0写 1读, len: 数据字节数(1~3) uint8_t AD7124_ReadReg(SPI_HandleTypeDef *hspi, uint8_t addr, uint8_t *buf, uint8_t len) { uint8_t comm (addr 2) | 0x40; // bit70 写通信寄存器, bit61 读操作 uint8_t dummy 0xFF; HAL_GPIO_WritePin(ADC_CS_PORT, ADC_CS_PIN, GPIO_PIN_RESET); HAL_SPI_Transmit(hspi, comm, 1, 100); // 先写通信寄存器 for (uint8_t i 0; i len; i) { HAL_SPI_TransmitReceive(hspi, dummy, buf[i], 1, 100); // 逐字节读 } HAL_GPIO_WritePin(ADC_CS_PORT, ADC_CS_PIN, GPIO_PIN_SET); return 0; }逻辑说明通信寄存器 bit7 固定为 0写通信寄存器bit6 是读写标志bit5~0 是寄存器地址。读操作时 bit6 置 1。参数len根据目标寄存器决定——状态寄存器 1 字节配置寄存器 2 字节数据寄存器 3 字节。注意每次操作都要完整拉 CS不能中途松手。2.2 寄存器初始化顺序别跳步跳了就等着读全 0AD7124 上电后必须按固定顺序配置顺序错了它不报错只是默默给你垃圾数据。这份驱动的初始化流程大致是复位 → 读 ID 确认通信 → 配置各通道 → 配置 ADC 控制寄存器 → 配置滤波器 → 使能通道 → 启动转换。复位有两种硬件复位拉 RESET 引脚和软件复位连续写 64 个 1。代码里一般用软件复位省一个 IO。复位后所有寄存器回默认值此时读 ID 寄存器应该得到 0x14AD7124-8或 0x16AD7124-4这个值对不上后面全白搭。// 软件复位 ID 校验 void AD7124_Reset(SPI_HandleTypeDef *hspi) { uint8_t tx[8] {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; HAL_GPIO_WritePin(ADC_CS_PORT, ADC_CS_PIN, GPIO_PIN_RESET); for (int i 0; i 8; i) { HAL_SPI_Transmit(hspi, tx, 8, 100); // 连续 64 个 1 } HAL_GPIO_WritePin(ADC_CS_PORT, ADC_CS_PIN, GPIO_PIN_SET); HAL_Delay(1); // 等内部振荡器稳定 } uint8_t AD7124_CheckID(SPI_HandleTypeDef *hspi) { uint8_t id 0; AD7124_ReadReg(hspi, 0x05, id, 1); // ID 寄存器地址 0x05 return id; // 期望 0x14 或 0x16 }参数说明复位后至少延时 1ms 再通信内部振荡器起振需要时间。ID 寄存器低 4 位是版本高 4 位是型号判断时用(id 0xF0)更稳。2.3 通道与滤波器配置决定你读到的是不是有效值AD7124 有 8 个配置寄存器AD7124-8每个通道可以绑定一个配置。配置寄存器里最关键的是增益、基准源选择、输入缓冲。滤波器寄存器决定输出数据率和建立时间。很多人读出来的值跳得厉害八成是滤波器没配对——Sinc4 滤波在 50Hz/60Hz 工频抑制上表现好但建立时间长单周期滤波快但噪声大。参数典型值影响增益1~128小信号必须放大但放大后满量程缩小基准源内部 2.5V / 外部 REFIN内部基准温漂大精密场合用外部滤波类型Sinc4 / 单周期Sinc4 抑工频单周期快输出速率10SPS~19200SPS越低越稳越高越跳建立时间与滤波和速率相关切换通道后要等建立完再读配置通道时先写配置寄存器再写通道寄存器把通道映射到配置最后使能通道。顺序反了通道使能时配置还没生效读出来就是默认增益 1、内部基准小信号直接淹在噪声里。3. 从零跑通采集STM32F103 工程搭建与实测步骤3.1 工程文件组织与 CubeMX 关键配置拿到代码包先别一股脑塞进现有工程。我一般会新建一个ad7124_driver分组把驱动文件ad7124.c/ad7124.h和板级支持ad7124_port.c分开。驱动层不碰 HAL 具体外设只依赖几个函数指针或宏这样换 MCU 只改 port 层。CubeMX 里 SPI1 配置Mode 选 Full-Duplex MasterData Size 8 BitsCPOL HighCPHA 2EdgeNSS 选 SoftwarePrescaler 先设 64约 1.1MHzAD7124 最高支持 5MHz但 F103 走线长时降速更稳。GPIO 里把 CS 和 RESET 配成推挽输出初始电平都拉高。// ad7124_port.h 里做硬件抽象 #define ADC_CS_PORT GPIOA #define ADC_CS_PIN GPIO_PIN_4 #define ADC_RESET_PORT GPIOA #define ADC_RESET_PIN GPIO_PIN_3 #define ADC_SPI_HANDLE hspi1这样驱动里只调AD7124_ReadReg(ADC_SPI_HANDLE, ...)换板子只改这个头文件。3.2 单通道连续采集的完整调用链跑通的最小闭环是初始化 → 配通道 0 → 启动连续转换 → 等 RDY 引脚或查状态寄存器 → 读数据 → 转电压。RDY 引脚接法看你的板子没接就用状态寄存器的 RDY 位轮询。void AD7124_SetupChannel0(void) { uint8_t cfg[2] {0x00, 0x00}; // 配置寄存器: 增益1, 内部基准, 缓冲开 AD7124_WriteReg(ADC_SPI_HANDLE, 0x19, cfg, 2); // CFG0 地址 0x19 uint8_t ch[2] {0x01, 0x80}; // 通道0: 使能, 接 AIN0/AIN1 AD7124_WriteReg(ADC_SPI_HANDLE, 0x09, ch, 2); // CH0 地址 0x09 uint8_t filt[3] {0x00, 0x00, 0x60}; // 滤波器: Sinc4, 输出速率约 10SPS AD7124_WriteReg(ADC_SPI_HANDLE, 0x21, filt, 3); // FILT0 地址 0x21 uint8_t adc_ctrl[2] {0x00, 0x0C}; // 连续转换模式, 内部基准使能 AD7124_WriteReg(ADC_SPI_HANDLE, 0x01, adc_ctrl, 2); // ADC_CTRL 地址 0x01 } int32_t AD7124_ReadData(void) { uint8_t raw[3] {0}; uint8_t status 0; do { AD7124_ReadReg(ADC_SPI_HANDLE, 0x00, status, 1); // 状态寄存器 } while (status 0x80); // 等 RDY 位变低 AD7124_ReadReg(ADC_SPI_HANDLE, 0x02, raw, 3); // 数据寄存器 0x02 int32_t code ((int32_t)raw[0] 16) | ((int32_t)raw[1] 8) | raw[2]; if (code 0x800000) code | 0xFF000000; // 24 位补码转 32 位 return code; }逻辑说明状态寄存器 bit7 是 RDY低电平有效所以while (status 0x80)是等它变 0。数据寄存器是 24 位读回来要拼成 int32 并做符号扩展。电压换算V code * VREF / (增益 * 2^23)内部基准 2.5V、增益 1 时满量程约 ±2.5V。3.3 用已知电压验证别信「能读数」要信「读得准」跑通不代表读准。我一般会拿一个基准电压源或者分压后的电池电压做验证。比如用两节干电池分压到 1.0V 左右接 AIN0/AIN1读回来换算成电压和万用表对比。偏差在几个 LSB 以内算正常偏差几百 mV 就是配置错了。验证步骤先短接 AIN0 和 AIN1读出来应该接近 0有几十个 LSB 的失调正常再接入已知电压看线性度。如果短接都读几百 mV检查增益和基准配置如果读数乱跳检查滤波器和 SPI 速率。提示AD7124 的模拟输入范围是±VREF/增益差分输入时 AIN0 和 AIN1 都要在允许的共模范围内超了读出来是满量程或负满量程不是真实值。4. 避坑与排查调 AD7124 最容易翻车的五个地方4.1 读回来全是 0xFFFFFF 或 0x000000现象初始化后读数据寄存器永远是满量程或零。原因SPI 模式不对或者通信寄存器写错导致根本没选中数据寄存器。解决先用示波器看 CS、SCLK、MOSI 波形确认 CPOL/CPHA再单独读 ID 寄存器ID 不对就先修通信别往下走。4.2 读数跳得厉害像随机数现象电压值上下跳几百个 LSB。原因滤波器输出速率设太高或者基准源用了内部基准且没使能缓冲。解决把输出速率降到 10SPS 或更低滤波器选 Sinc4基准改外部 REFIN并在配置寄存器里打开基准缓冲。4.3 切换通道后第一笔数据是错的现象多通道轮询时刚切过去的通道读出来是上一个通道的值。原因Σ-Δ ADC 切换通道后有建立时间没等建立完就读。解决切换通道后丢弃第一笔或前两笔数据或者查状态寄存器的 RDY 位等它真正更新。4.4 校准后反而更不准现象跑了内部零刻度校准和满刻度校准结果偏差更大。原因校准时的输入条件不对零刻度校准要求输入短接满刻度校准要求输入接近满量程。解决校准前先把输入接到对应电平校准完再切回正常测量或者干脆用系统校准在已知信号下算修正系数。4.5 长时间运行后数据漂移现象跑几小时后读数慢慢偏。原因内部基准温漂或者芯片自热。解决换外部基准降低激励电流PCB 上模拟部分远离发热源。AD7124 本身功耗不高但配置成高增益、高速度时功耗上去了自热会影响精度。5. 进阶把这份驱动改成多通道轮询 软件滤波的稳定采集单通道跑通只是起点实际项目多半是多通道轮询。AD7124 支持序列器模式可以自动按顺序扫多个通道但配置起来寄存器操作多调试时容易乱。我一般先用软件轮询手动切通道、等建立、读数据逻辑清晰出问题好定位。多通道的关键是维护一个通道-配置映射表每个通道有自己的增益和滤波需求。比如通道 0 接称重传感器要增益 128通道 1 接 RTD 要增益 16那就配两个配置寄存器通道寄存器分别指向它们。切换时先写通道寄存器再等建立时间再读。typedef struct { uint8_t ch_reg_val; // 通道寄存器值 uint8_t cfg_idx; // 绑定的配置索引 uint8_t settle_cnt; // 需要丢弃的样本数 } AD7124_ChannelMap; AD7124_ChannelMap ch_map[2] { {0x01, 0, 2}, // 通道0, 配置0, 丢2笔 {0x02, 1, 2}, // 通道1, 配置1, 丢2笔 }; int32_t AD7124_ScanChannel(uint8_t idx) { AD7124_WriteReg(ADC_SPI_HANDLE, 0x09, ch_map[idx].ch_reg_val, 2); for (int i 0; i ch_map[idx].settle_cnt; i) { AD7124_ReadData(); // 丢弃建立期样本 } return AD7124_ReadData(); }软件滤波方面我习惯在驱动上层加一个滑动平均或中值滤波。AD7124 本身输出速率低的时候噪声已经很小但工频干扰残留还是有的。滑动平均窗口取 8 或 16对缓变信号效果好如果信号有突变中值滤波更合适。别在驱动层做滤波放应用层方便调参。验证方法拿一个标准信号源或者用高精度万用表对比跑 24 小时看漂移。我一般会记录每小时的平均值和标准差标准差突然变大就是有干扰进来了。从那以后我每次改滤波器参数都强制先跑一遍短接和满量程两点验证确认没翻车再上真实信号。希望帮到你。本文还有配套的精品资源点击获取