首页 / 资讯中心 / 文章详情

STM32F103驱动AD7124实战:从SPI配置到电压采集的完整指南

STM32F103驱动AD7124实战:从SPI配置到电压采集的完整指南 ★ FEATURED ARTICLE
简介这份资源是面向STM32开发者与嵌入式新手的AD7124高精度ADC驱动代码基于STM32F103平台采用模拟SPI方式实现工程基于Keil5搭建并已完整调通。代码聚焦6通道、双极性、全功率采样与外部参考电压配置未开启多余功能便于读者直接移植到自己的采集项目中也适合作为学习24位ADC寄存器配置与通信时序的参考。压缩包共215个文件约6.18MB包含35个c源文件、38个h头文件以及大量编译中间产物d、o、crf文件、Keil工程文件uvprojx、uvoptx、map映射文件与hex固件等覆盖从源码到可执行文件的完整工程结构。目前已有6215人学习下载。作者在描述中坦言调试过程曲折因此代码经过实际验证新手无需怀疑其可用性可直接对照工程理解AD7124初始化、通道切换与数据读取流程并在此基础上扩展硬件SPI或更多通道应用。1. 拿到一份调通的 AD7124 驱动先别急着往板子上怼如果你手头正捏着一块 STM32F103 的板子旁边还躺着一颗 AD7124 这颗 24 位 Σ-Δ 型 ADC那你大概率已经体会过那种“手册翻烂了、寄存器配了半天、读回来全是 0xFFFFFF”的绝望。这份资源就是冲这个场景来的一套基于 STM32F103 的 AD7124 完整驱动代码作者标注“已全部调通”意味着 SPI 时序、寄存器配置、通道切换、数据读取这些最容易翻车的地方已经有人替你趟过一遍了。它适合两类人一类是刚接手高精度采集项目、想先跑通再研究的嵌入式新手另一类是手上有现成硬件、懒得从零啃 80 页寄存器手册、想直接拿一份能跑的参考实现来改的熟手。关键词 ADC、AD7124、STM32F 驱动基本就是这份代码的全部标签。2. 先看清这套驱动到底封装了什么从 SPI 到通道扫描的完整链路2.1 硬件接口层SPI 时序和片选是怎么接的AD7124 和 STM32F103 之间走的是标准 4 线 SPI但有几个细节如果没处理好读出来的数据就是玄学。常见做法是STM32 做 SPI 主机AD7124 做从机SCLK 空闲电平根据 CPOL/CPHA 配置通常用 Mode 3CPOL1CPHA1或者 Mode 1具体看你的时序图。片选 CS 建议用普通 GPIO 手动拉低拉高而不是用硬件 NSS因为 AD7124 的寄存器读写需要严格的帧边界硬件 NSS 有时候会在字节间隙抖动导致命令解析错位。下面这段是 SPI 初始化和字节收发的基础封装基于 STM32 标准外设库的写法// spi.c - AD7124 SPI 底层收发 #include spi.h void AD7124_SPI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; // 使能 SPI 和 GPIO 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); // SCLK(PA5) / MOSI(PA7) 复用推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // MISO(PA6) 浮空输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); // CS 用 PA4 普通推挽输出手动控制 GPIO_InitStructure.GPIO_Pin GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_SetBits(GPIOA, GPIO_Pin_4); // 默认拉高 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; // 空闲高 SPI_InitStructure.SPI_CPHA SPI_CPHA_2Edge; // 第二边沿采样 SPI_InitStructure.SPI_NSS SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_64; // 约 1.1MHz SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial 7; SPI_Init(SPI1, SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); } // 收发一个字节返回读到的数据 uint8_t AD7124_SPI_ReadWriteByte(uint8_t txData) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); SPI_I2S_SendData(SPI1, txData); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET); return SPI_I2S_ReceiveData(SPI1); }逻辑说明SPI 速率这里用了 64 分频STM32F103 主频 72MHz 时 SCLK 约 1.1MHzAD7124 手册允许的最高 SCLK 通常远高于此但低速更稳适合长线或干扰环境。CPOL/CPHA 选的是 Mode 3如果你发现读回全 0 或全 1第一个要改的就是这两个参数。CS 手动控制意味着每次读写寄存器前要拉低 PA4结束后拉高这个动作在寄存器操作函数里统一做。2.2 寄存器读写24 位数据怎么拆成 3 个字节AD7124 的寄存器是 8 位地址 24 位数据的结构通信寄存器决定下一步是读还是写、操作哪个寄存器。常见做法是封装两个函数写寄存器和读寄存器内部处理 CS 拉低、发命令、收发数据、CS 拉高。// ad7124_reg.c - 寄存器读写 #include ad7124.h // 写寄存器addr 为寄存器地址data 为 24 位数据 void AD7124_WriteRegister(uint8_t addr, uint32_t data) { AD7124_CS_LOW(); // 通信寄存器写操作地址 addr AD7124_SPI_ReadWriteByte(AD7124_COMM_REG_WR | addr); // 依次发 24 位数据高字节在前 AD7124_SPI_ReadWriteByte((data 16) 0xFF); AD7124_SPI_ReadWriteByte((data 8) 0xFF); AD7124_SPI_ReadWriteByte(data 0xFF); AD7124_CS_HIGH(); } // 读寄存器addr 为寄存器地址返回 24 位数据 uint32_t AD7124_ReadRegister(uint8_t addr) { uint32_t data 0; AD7124_CS_LOW(); // 通信寄存器读操作地址 addr AD7124_SPI_ReadWriteByte(AD7124_COMM_REG_RD | addr); // 读回 24 位 data | (uint32_t)AD7124_SPI_ReadWriteByte(0xFF) 16; data | (uint32_t)AD7124_SPI_ReadWriteByte(0xFF) 8; data | (uint32_t)AD7124_SPI_ReadWriteByte(0xFF); AD7124_CS_HIGH(); return data; }参数说明AD7124_COMM_REG_WR和AD7124_COMM_REG_RD是通信寄存器的读写标志位通常是 0x00 和 0x40具体值要对照手册确认。addr是寄存器地址比如配置寄存器 0x01、数据寄存器 0x02 等。注意每次读写之间 CS 必须拉高否则 AD7124 会把下一次通信当成当前帧的延续导致命令错位。这个坑我在早期调试时踩过现象是读配置寄存器返回的值总是偏移一个字节。2.3 通道配置与扫描怎么让 AD7124 轮流采多路信号AD7124 有多路模拟输入可以通过配置寄存器设置每个通道的使能、增益、滤波等参数。常见做法是初始化时把用到的通道写进配置寄存器然后设置 ADC 控制寄存器为连续转换模式数据寄存器会自动更新。// ad7124_init.c - 通道配置示例 void AD7124_SetupChannels(void) { // 配置寄存器 0使能通道 0增益 1单极性 // 具体位定义参考手册这里只写关键字段 uint32_t cfg0 0; cfg0 | (0 12); // 增益 1 cfg0 | (0 11); // 单极性 cfg0 | (0 10); // 使能缓冲 AD7124_WriteRegister(AD7124_CFG_0_REG, cfg0); // 配置寄存器 1通道 1 uint32_t cfg1 cfg0; // 同样参数 AD7124_WriteRegister(AD7124_CFG_1_REG, cfg1); // 通道寄存器使能 AIN0/AIN1 对应通道 0AIN2/AIN3 对应通道 1 AD7124_WriteRegister(AD7124_CH_0_REG, 0x0001); // 使能正输入 AIN0负输入 AIN1 AD7124_WriteRegister(AD7124_CH_1_REG, 0x0043); // 使能正输入 AIN2负输入 AIN3 // ADC 控制寄存器连续转换模式内部基准 uint32_t adc_ctrl 0; adc_ctrl | (0 11); // 连续转换 adc_ctrl | (1 6); // 内部基准使能 AD7124_WriteRegister(AD7124_ADC_CTRL_REG, adc_ctrl); }逻辑说明通道寄存器的低 4 位是正输入选择接着 4 位是负输入选择最高位是使能位。上面0x0001表示通道 0 使能正输入 AIN0负输入 AIN10x0043表示通道 1 使能正输入 AIN2负输入 AIN3。实际项目中通道号对应的引脚要查原理图别照抄。ADC 控制寄存器里连续转换模式让芯片自己轮询使能的通道数据寄存器会依次更新你只需要在需要的时候读数据寄存器即可。2.4 数据读取与转换24 位原始码怎么变成电压值读数据寄存器得到的是 24 位原始码要转成电压需要知道基准电压和增益。常见做法是用内部基准 2.5V增益为 1 时电压 (原始码 / 2^23) * 2.5V单极性。如果是双极性公式略有不同。// ad7124_read.c - 读取并转换 float AD7124_ReadVoltage(void) { uint32_t raw AD7124_ReadRegister(AD7124_DATA_REG); // 单极性内部基准 2.5V增益 1 float voltage ((float)raw / 8388608.0f) * 2.5f; // 2^23 8388608 return voltage; }参数说明8388608.0f是 2 的 23 次方对应 24 位 ADC 单极性满量程。如果增益不是 1分母要乘增益如果基准是外部 5V把 2.5f 换成 5.0f。注意读数据寄存器之前最好检查状态寄存器的 RDY 位确保数据已更新否则可能读到旧值。这个细节在连续转换模式下尤其重要我一般会在读之前加一个超时等待。3. 把代码跑起来从工程搭建到第一个电压读数3.1 工程目录怎么组织别把所有文件堆在 main.c 里拿到一份驱动代码第一件事是看目录结构。这套代码常见做法是分成三层底层 SPI 驱动、AD7124 寄存器操作层、应用层。目录大概长这样project/ ├── CMSIS/ // STM32 内核文件 ├── STM32F10x_StdPeriph_Driver/ // 标准外设库 ├── User/ │ ├── main.c // 主循环初始化 读数据 │ ├── spi.c / spi.h // SPI 底层 │ ├── ad7124.c / ad7124.h // AD7124 驱动 │ └── delay.c / delay.h // 延时函数 └── Project.uvprojx // Keil 工程文件如果你用的是 HAL 库把 spi.c 换成 HAL 的 SPI 初始化即可寄存器操作层不用大改。关键是别把 AD7124 的寄存器配置直接写在 main 里否则换一颗 ADC 或者改通道时你会想哭。3.2 初始化顺序先 SPI 再 AD7124 最后开转换初始化顺序错了后面全是白费。常见做法是系统时钟配置STM32F103 通常 72MHz延时函数初始化SPI 初始化AD7124 复位拉低 RESET 引脚或者发复位命令写配置寄存器、通道寄存器写 ADC 控制寄存器启动连续转换等待 RDY 位读数据// main.c - 初始化流程 int main(void) { Delay_Init(); AD7124_SPI_Init(); AD7124_Reset(); // 硬件复位或软件复位 AD7124_SetupChannels(); // 配置通道和 ADC AD7124_StartConversion(); // 启动连续转换 while (1) { if (AD7124_DataReady()) // 检查 RDY { float v AD7124_ReadVoltage(); printf(Voltage: %.6f V\r\n, v); } } }逻辑说明AD7124_Reset()通常是把 RESET 引脚拉低一段时间再拉高或者写通信寄存器的复位命令。AD7124_DataReady()读状态寄存器的 RDY 位为 0 表示数据就绪。主循环里不要用死延时等数据用 RDY 查询或者 DRDY 引脚中断更靠谱。3.3 用串口打印验证第一个读数出来之前别急着接传感器代码跑起来后先别接传感器把 AD7124 的输入短接到地或者基准看读数是否接近 0 或满量程。常见做法是短接到地读数应该接近 0x000000电压接近 0V短接到基准读数应该接近 0x7FFFFF单极性电压接近 2.5V如果读数跳得厉害检查基准电压是否稳定、SPI 速率是否过高、电源是否干净串口打印用 printf 重定向到 USART1波特率 115200。如果打印出来全是 0 或者 0xFFFFFF先查 SPI 模式再查 CS 时序最后查寄存器地址有没有写错。4. 避坑与排查那些让读数变成玄学的瞬间4.1 读回全 0 或全 1SPI 模式或 CS 时序错了现象读任何寄存器都返回 0x000000 或 0xFFFFFF。原因SPI 的 CPOL/CPHA 和 AD7124 不匹配或者 CS 在字节之间被拉高导致帧断裂。解决把 CPOL/CPHA 改成 Mode 1 或 Mode 3 试一遍CS 确保在整个读写过程中保持低电平读写完成后再拉高。4.2 数据跳动大基准不稳或滤波没配现象电压读数跳动几十毫伏甚至上百毫伏。原因内部基准还没稳定就启动转换或者滤波寄存器配置的采样率太高。解决启动转换前加 10ms 以上延时等基准稳定把滤波寄存器配成较低的输出速率比如 10SPS牺牲速度换稳定。4.3 通道切换后读到旧值没等 RDY 就读现象切换通道后第一次读到的还是上一个通道的值。原因连续转换模式下通道切换后需要等一个完整的转换周期数据寄存器才会更新。解决每次读之前检查 RDY 位或者用 DRDY 引脚中断触发读取。4.4 通信完全没反应RESET 引脚没接或没拉高现象SPI 发什么都是 0AD7124 像死了一样。原因RESET 引脚悬空或者一直拉低芯片处于复位状态。解决RESET 引脚接 GPIO初始化时拉低至少 100ns 再拉高或者直接接 3.3V 上拉。4.5 读数据寄存器返回 0x800000溢出或负满量程现象读数据寄存器得到 0x800000。原因输入电压超出量程或者双极性模式下负满量程。解决检查输入电压是否在基准范围内确认配置寄存器的极性设置和增益是否匹配实际信号。5. 二次开发怎么改换通道、换基准、换 MCU 的实操思路拿到这份代码你大概率不会原封不动用总得改点东西。我一般会从三个方向下手换通道、换基准、换 MCU。换通道最简单改AD7124_SetupChannels()里的通道寄存器值就行。比如你要用 AIN4 和 AIN5 作为差分输入就把通道寄存器的正输入改成 4负输入改成 5。注意通道号对应的物理引脚要查原理图别照着代码里的注释抄。改完通道后配置寄存器也要对应改增益和极性否则读数会差一个系数。换基准分两种情况用内部基准还是外部基准。内部基准 2.5V 在 ADC 控制寄存器里使能代码里已经写了。如果你用外部基准比如 REFIN1 接 5V那要把 ADC 控制寄存器的内部基准关掉同时电压换算公式里的 2.5f 改成 5.0f。外部基准的好处是精度更高但布线要讲究基准芯片的退耦电容不能省。换 MCU 是改动最大的。如果你从 STM32F103 换到 F4 或者 G0SPI 底层要重写但 AD7124 寄存器操作层几乎不用动。我的习惯是把 SPI 收发封装成两个函数指针初始化时根据 MCU 类型赋值这样上层驱动完全不用改。下面是一个简单的函数指针封装示例// ad7124_port.h - 移植层接口 typedef struct { void (*cs_low)(void); void (*cs_high)(void); uint8_t (*spi_rw)(uint8_t tx); void (*delay_ms)(uint32_t ms); } AD7124_Port_t; extern AD7124_Port_t ad7124_port; // 在 main.c 里根据 MCU 填充 AD7124_Port_t ad7124_port { .cs_low AD7124_CS_LOW, .cs_high AD7124_CS_HIGH, .spi_rw AD7124_SPI_ReadWriteByte, .delay_ms Delay_ms };这样换 MCU 时只需要重新实现这几个函数AD7124 的寄存器读写和通道配置代码原封不动。验证方法也简单短接到地读 0短接到基准读满量程中间接一个分压电阻看读数是否线性。我一般会拿一个 1.5V 电池串两个等值电阻分压理论值 0.75V实测偏差在 1mV 以内就算通过。从那以后我每次拿到新的 ADC 驱动都强制先跑一遍“短接到地、短接到基准、分压验证”这三步确认底层通了再往上叠业务逻辑。希望这份代码帮你省下啃手册的那几天。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站