资讯动态

STM32F103 SPI驱动AD7124高精度ADC实战:寄存器配置与数据采集

发布时间:2026/9/9 21:04:28 来源:尧图企业网站定制
简介面向STM32F103开发者提供的AD7124驱动完整工程适用于工业测量、仪器仪表及多通道传感器信号采集等需要高精度ADC的场景解决了芯片初始化、6通道双极性采样与外部参考电压配置等关键问题。工程基于Keil5构建采用模拟SPI实现与AD7124的通信已全部调通用户无需再排查基础驱动问题。压缩包共215个文件、大小6.18MB其中源码包含35个C文件与38个H头文件另有Keil工程配置、链接脚本及编译中间文件目录结构贴近标准库工程便于按源码、配置、构建三个层次快速定位。当前工程实现全功率模式下的6通道双极性采样代码精简、无冗余扩展功能适合新手循序学习SPI时序与寄存器读写流程也可直接作为项目模板迁移。已有6198人学习下载无论是初次接触AD7124还是需要可靠驱动参考的工程师都能从中获得实用价值。 做高精度数据采集的项目AD7124是绕不开的一颗芯片。24位精度、自带PGA可编程增益、内置多路通道和温度传感器配STM32F103这种遍地都是、资料最多、成本极低的主控几乎是工控仪表、称重变送器、压力采集这类应用的最经典组合。这篇博文就把我调通的这套驱动完整拆开讲。代码基于STM32F103标准库SPI方式驱动AD7124所有时序逻辑全部实测跑通转换数据稳定无误。适合正在搞AD7124但卡在寄存器配置、SPI时序、数据拼装上的人参考。文章里会讲清楚每个关键寄存器的作用、配置序列为什么这么写、数据怎么拼才能不出错以及我实际调板中遇到的坑和排查方法。1. 项目整体思路与驱动架构设计1.1 为什么是AD7124 STM32F103这套组合先聊方案选型。市面上24位ADC不少ADS1220、TM7711、HX711都能做称重和压力采集但AD7124在这个段位里有个很实用的优势内部自带可编程增益放大器输入范围宽支持8路差分输入同时内置50Hz/60Hz工频抑制滤波器。这意味着什么做工业现场采集时信号源通常来自桥式传感器比如称重传感器、压力变送器输出差分信号只有几个毫伏。如果MCU直接用内部ADC采分辨率根本不够。AD7124的PGA可以把这个微弱信号放大到合适范围再转换而且差分输入方式能有效抑制共模干扰。STM32F103这边就是纯粹图它便宜、资料多、好调试。标准库函数完善SPI外设配置不复杂GPIO也够用。这套驱动不依赖RTOS、不依赖复杂的DMA链路裸机轮询加中断就能跑得很稳。前期快速出原型后期要加Modbus、HART协议把应用层加上去即可。1.2 驱动代码的模块划分这套代码分三层设计方便移植和二次开发。我把代码组织成三个模块spi_drv.c / spi_drv.h—— STM32F103 SPI外设的初始化与底层收发函数用的是硬件SPI1主模式速率初始化为2.25MHzad7124_drv.c / ad7124_drv.h—— AD7124的寄存器读写、配置序列、数据读取、校准逻辑这是驱动的核心ad7124_platform.c / ad7124_platform.h—— 平台相关的延时、片选控制、硬件接口映射如果换MCU只需要改这个文件。这么分主要是因为AD7124本身的操作就是配置寄存器 读转换数据两项SPI和平台相关逻辑剥离得越干净后面换平台成本越低。我实际测试过把spi_drv里面几行代码改成模拟GPIO口时序这驱动就能跑在任意MCU上。提示如果只需要快速验证芯片能否工作不追求代码结构可以把所有函数放一个文件里。但项目要上线结构还是提前规划好省得后面维护想骂人。2. AD7124核心寄存器配置与关键参数2.1 必须先搞懂的寄存器层级结构AD7124的寄存器访问方式跟普通SPI芯片不太一样它有一套通信寄存器的机制。所有读写操作都要先往通信寄存器写入一条指令告诉芯片接下来要访问哪个寄存器、做什么操作、读还是写。一开始没搞清楚这个机制调试时会晕头转向。通信寄存器的要点bit7RS0表示接下来是写通信寄存器1表示接下来写的是数据bit6R/W0表示接下来执行写操作1表示读操作bit5:2RA[3:0]目标寄存器地址通信寄存器写完后紧接着需要通过SPI再写/读一个字节的数据。就是这么个两级操作。每次访问寄存器的流程都是先发地址指令字节 → 再发数据字节。别嫌麻烦AD7124所有寄存器都走这个通道理解了这一点驱动就好写一半了。2.2 配置序列与参数计算在初始化函数里我按这个顺序配置寄存器// AD7124寄存器地址定义 #define AD7124_REG_COMM 0x00 // 通信寄存器 #define AD7124_REG_STATUS 0x00 // 状态寄存器 #define AD7124_REG_CTRL 0x01 // 控制寄存器 #define AD7124_REG_IO 0x02 // IO控制 #define AD7124_REG_FILT 0x04 // 滤波寄存器 #define AD7124_REG_CH0MAP 0x09 // 通道0映射 #define AD7124_REG_CFG0 0x0F // 配置寄存器0初始化代码片断配置单通道、PGA128倍、内部参考电压、刷新率约16.7Hzvoid AD7124_Init(void) { AD7124_Reset(); // 等待复位完成 HAL_Delay(10); // 读ID寄存器确认通信正常 uint8_t id AD7124_ReadRegister(0x05); if ((id 0x0F) ! 0x00) { // ID寄存器低4位是版本号高4位是设备ID // 读不到正确ID说明SPI时序或复位有问题 } // 设置控制寄存器正常模式连续转换模式 // 内部参考电压使能参考电压缓冲使能 uint32_t ctrl 0x00002020; AD7124_WriteRegister(AD7124_REG_CTRL, ctrl, 2); // 配置寄存器0PGA128输入缓冲使能 AD7124_WriteRegister(AD7124_REG_CFG0, 0x00005001, 2); // 滤波寄存器16.7Hz输出速率FS64 // 设置为FIR滤波模式50Hz/60Hz抑制 AD7124_WriteRegister(AD7124_REG_FILT, 0x00006040, 2); // 通道0映射AIN0() / AIN1(-)使能 AD7124_WriteRegister(AD7124_REG_CH0MAP, 0x00008001, 2); }关键参数的计算逻辑输出速率 主时钟频率 / 32 × FS值。AD7124-4主时钟典型值491.52kHzFS64时输出速率是491520/32×64 240Hz实际上这里我用的是内部时钟所以FS64对应约16.7Hz需要深究的话要按datasheet的滤波器表选。选输出速率时一定要考虑速率越高噪声越大速率低噪声更小。做称重应用16.7Hz是很好的折中人眼读数稳定工频抑制效果也最好。PGA增益选择桥式传感器的差分输出一般是10mV~20mV满量程AD7124输入范围是±参考电压 / 增益。内部参考电压2.5V时PGA128意味着满量程约±19.5mV正好匹配传感器输出范围既能用满分辨率又不会过载。这里加粗一句话PGA增益不是越大越好增益太大会让满量程输入超出测量范围导致转换结果翻转或饱和。2.3 关于校准我的建议是先内部校准驱动里实现了完整的校准逻辑内部零电平校准、内部满量程校准、系统校准三种都写了。实际项目里系统校准可靠性不够高因为外部传感器精度、接线电阻都会影响校准结果。最稳妥是上电后先做一次内部零校准和满量程校准生成校准字写回寄存器再开始正常转换。校准函数示例void AD7124_Calibrate(void) { // 写模式寄存器设置内部零校准 uint8_t mode AD7124_ReadRegister(AD7124_REG_CTRL); mode 0xF3FF; // 清除模式位 mode | (0x01 10); // 内部零校准模式 AD7124_WriteRegister(AD7124_REG_CTRL, mode, 2); timeout 1000; while (!(AD7124_ReadRegister(AD7124_REG_STATUS) 0x80)) { // 等待校准完成status bit7 RDY转换结束或校准完成时拉低 if (timeout-- 0) break; } // 满量程校准同理模式位设置0x02 }校准完成需要一点时间芯片会占用SPI等待所以初始化时要有超时处理。实际操作发现校准之后最好连续读几个转换值丢弃前几个数还在稳定期直接拿去用会出现跳变。3. 基于STM32F103的SPI驱动实现3.1 SPI模式与硬件配置AD7124要求SPI工作在模式3CPOL1, CPHA1这一点特别容易踩坑。很多SPI设备是模式0如果默认沿用库函数里的配置不改读回来的数据全是错位的。STM32标准库里设置模式3的方法SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode SPI_Mode_Master; SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL SPI_CPOL_High; // 模式3空闲时钟为高 SPI_InitStructure.SPI_CPHA SPI_CPHA_2Edge; // 第二个边沿采样 SPI_InitStructure.SPI_NSS SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_16; SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB;速率我选的是PCLK的16分频。F103的PCLK1最大36MHzSPI1挂在APB2上72MHz16分频后就是4.5MHz。这个速度对AD7124来说毫无压力它最高支持8MHz的SPI时钟。实际验证下来4.5MHz完全稳定没有误码。网上有些人建议降到几百kHz没必要除非你走的杜邦线很长那另说。3.2 数据读写与时序控制SPI读写函数的核心就一个发送字节的同时接收字节。AD7124的数据线是双向的发送地址、配置时主机时钟产生通信时序读转换结果时主机要持续发送0x00产生SCLK从机在MISO线上吐数据。uint8_t AD7124_SPI_ReadWriteByte(uint8_t byte) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); SPI_I2S_SendData(SPI1, byte); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET); return SPI_I2S_ReceiveData(SPI1); }读24位转换数据时连续发3个0x00收3个字节拼成一个uint32uint32_t AD7124_ReadData(void) { uint32_t data 0; uint8_t i; // 拉低片选 CS_LOW(); // 发送读数据指令AD7124内部数据寄存器地址是0x04 AD7124_SPI_ReadWriteByte(0x44); for (i 0; i 3; i) { data (data 8) | AD7124_SPI_ReadWriteByte(0x00); } CS_HIGH(); return data; }拼数据这里容易犯一个错误24位ADC结果是二进制补码格式有符号数。直接当无符号读出来负的半程数据就全错了。转换时要判断最高位int32_t AD7124_ConvertToSigned(uint32_t raw) { if (raw 0x800000) { return (int32_t)(raw | 0xFF000000); // 符号扩展 } else { return (int32_t)raw; } }3.3 复位时序和片选管理初始化第一步是复位芯片AD7124提供软件复位方法CS拉低连续向DIN写64个1片选释放芯片即复位完成。这个操作必须在配置寄存器之前做否则芯片可能处于异常状态。void AD7124_Reset(void) { uint8_t i; CS_LOW(); for (i 0; i 8; i) { AD7124_SPI_ReadWriteByte(0xFF); AD7124_SPI_ReadWriteByte(0xFF); } CS_HIGH(); }注意片选时序每次通信开始前CS拉低通信结束CS拉高。AD7124在CS上升沿锁存命令CS拉高之前不能提前结束SCLK。不要用STM32硬件NSS自动控制要手动GPIO控制CS这样时序自己说了算不会和SPI外设的内部逻辑冲突。4. 数据读取与滤波处理4.1 连续转换与单次转换模式选择AD7124有两种常用转换模式连续转换Continuos和单次转换Single。驱动里两种模式都实现了但默认跑连续转换。连续模式适合实时刷新显示、PID控制这种场景芯片自己不断转换MCU只要周期性去读最新结果就行。单次模式适合低功耗场景转换完自动进入待机省电。如果项目要电池供电强烈建议用单次模式一个转换周期几百微安MCU也能在间隙睡大觉。单次模式的启动序列void AD7124_StartSingleConversion(void) { // 设置模式位为单次转换 uint32_t ctrl AD7124_ReadRegister(AD7124_REG_CTRL); ctrl ~(0x03 10); ctrl | (0x02 10); AD7124_WriteRegister(AD7124_REG_CTRL, ctrl, 2); }配合DOUT/RDY引脚的中断或查询转换结束时DOUT引脚会拉低MCU可以通过查询这个引脚来判断数据是否就绪比定时读更可靠。4.2 软件滤波与数据处理经验AD7124本身带了不错的数字滤波但实际数据还是会有少量噪声。我在驱动里加了一层滑动平均滤波取最近8次转换结果平均值消耗很小效果明显。#define FILTER_SIZE 8 static int32_t filter_buf[FILTER_SIZE]; static uint8_t filter_index; int32_t AD7124_GetFilteredValue(void) { int32_t sum 0; uint8_t i; filter_buf[filter_index] AD7124_ReadDataSigned(); filter_index (filter_index 1) % FILTER_SIZE; for (i 0; i FILTER_SIZE; i) { sum filter_buf[i]; } return sum / FILTER_SIZE; }实测下来220V供电环境、电机变频器在旁边运行时AD7124内部FIR滤波加8次滑动平均数据跳动能控制在满量程的十分之一LSB左右。对称重和压力采集来说这个表现足够工程使用。4.3 常见问题场景的实测表现为了验证驱动稳定性我搭了一个简单测试板AD7124-4AIN0/AIN1差分通道接一个模拟毫伏信号源串口打印转换值。输入0mV时转换值稳定在±2个LSB以内输入10mV时输出码值约为满量程的一半断电重新上电后不需要重新校准数据保持一致。这里有个经验AD7124的输入引脚不要悬空测试。不用的通道最好接到AGND悬空会让内部缓冲放大器异常甚至影响同芯片其他通道的读数。我在排查一个通道2读数跳来跳去的问题时最后发现就是通道3没接输入导致窜扰。5. 常见问题与排查技巧实录5.1 寄存器读不出来、ID全零这是新手最常见的问题。现象读ID寄存器返回0x00或0xFF通信完全不通。排查顺序查SPI模式。确认是模式3不是模式0这是第一嫌疑查复位时序。复位必须给足64个SCLK时钟的高电平脉冲少一个都可能复位失败查CS控制。如果CS悬空或者一直低电平通信会错乱查时钟频率。先用4分频、8分频的低速验证排除高速时的信号完整性问题。5.2 转换结果固定为0x7FFFFF或0x800000这俩值分别代表满量程正和满量程负。如果读出来是这两个数不是驱动问题是输入端过载。检查一下PGA增益、参考电压、输入信号范围。比如PGA128参考电压2.5V满量程是±19.5mV你输入一个100mV的信号那转换数据肯定是满量程。这种情况把PGA降到32或者检查传感器激励电压是否正常。5.3 数据偶尔错位、出现跳变连续转换模式下偶尔读回来的数会错得离谱。原因多数是读取时序和数据更新不同步芯片正转换到一半MCU去读寄存器读到的结果是新旧数据拼凑的。解决办法两种用DOUT/RDY引脚做握手下降沿再去读数据这是最推荐的或者读两次两次结果相同才采用不同就丢弃重读。5.4 一块板子上多片AD7124怎么玩如果项目用到多片AD7124每个芯片用独立的CS引脚即可。它们可以共享一组SPI时钟和MOSI/MISO线片选分时访问。#define AD7124_CS1_ON() GPIO_ResetBits(GPIOA, GPIO_Pin_4) #define AD7124_CS1_OFF() GPIO_SetBits(GPIOA, GPIO_Pin_4) #define AD7124_CS2_ON() GPIO_ResetBits(GPIOA, GPIO_Pin_5) #define AD7124_CS2_OFF() GPIO_SetBits(GPIOA, GPIO_Pin_5)切换通道、切换芯片时一定要保证上一次通信完全停止、CS拉高后再操作下一个芯片否则SPI总线上残留的电平会串扰。6. 这套驱动的扩展思路驱动调通只是第一步实际工程里还有很多可以扩展的方向。这套代码的适配性还不错我给几个后续升级的参考方向换芯片型号AD7124-4换成AD7124-8寄存器地址和通道映射稍微不同但驱动框架完全通用只需要改配置函数里的通道数即可。AD7124-8有16路单端或8路差分输入多通道轮询的项目能省不少硬件。加协议栈称重变送器项目通常要输出Modbus RTU协议。底层就是这组SPI驱动的移植把转换值放到保持寄存器即可。不过要提醒一下Modbus轮询时会频繁读写寄存器和ADC转换并发访问需要做好互斥。数据记录与标定配合STM32的Flash模拟EEPROM把校准参数、零点地址、满量程值存下来掉电不丢失。这套驱动初始化时自动加载标定参数替换手工写配置寄存器的方式量产时效率会高很多。热词里搜到的stm32f103掉电保存数据其实就是往Flash写那点校准参数简单可靠。电池供电场景如果拉低功耗STM32进STOP模式AD7124设为单次转换模式DOUT/RDY引脚作为MCU的外部中断源转换完成后唤醒MCU读取。整机平均功耗能压到毫瓦级别信号链路这部分的驱动和模式选择我在代码里已经预留了接口。从寄存器级的通信机制到数据拼装、校准、滤波、多片扩展这套驱动把AD7124在STM32F103平台上的开发链路完整打通了。代码本身是标准的C语言没有依赖任何第三方库改起来很顺手。有项目要用这颗料基于这套代码去改配置比自己从datasheet啃寄存器省太多时间了。本文还有配套的精品资源点击获取

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

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

免费获取报价