尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于STM32硬件SPI接口驱动AD7124的高精度数据采集实现

基于STM32硬件SPI接口驱动AD7124的高精度数据采集实现 简介本资源是一套面向嵌入式开发工程师与STM32初学者的AD7124高精度模数转换芯片驱动实现方案聚焦于利用STM32硬件SPI外设完成稳定、可靠的底层通信控制解决工业传感、精密测量等场景中对24位Σ-Δ型ADC芯片的集成难题。压缩包共含7个源文件4个头文件.h负责寄存器定义与接口声明3个C文件.c实现SPI读写、状态校验及设备初始化逻辑总大小仅9KB结构精简、模块职责清晰便于快速移植与二次开发。已有664人学习下载适用于课程设计、毕业设计及小型测控项目开发。读者可直接获取完整可编译的驱动框架包含带设备状态检查的寄存器读写函数AD7124_ReadRegister/WriteRegister及无检查快速访问接口NoCheckRead/WriteRegister并配套ad7124_regs.h寄存器映射定义与通信层抽象封装显著降低AD7124配置门槛与调试成本。1. 项目概述为什么选择AD7124以及这块芯片到底能干什么最近在做一个高精度数据采集项目需要同时采集多路传感器信号而且对噪声和温漂要求比较苛刻。对比过几款主流的24位ADC之后最终锁定了ADI的AD7124-4。说实话这块芯片在工业级别的高精度采集场景里确实非常能打无论是做称重、温度采集、压力变送器还是振动监测它都能胜任。AD7124系列有AD7124-4和AD7124-8两个型号区别主要在于通道数量。AD7124-4有4个差分输入或者7个单端输入AD7124-8则扩展到8个差分输入或15个单端输入。我这次用的是AD7124-4因为它带有一个2.5V的基准电压源内部这个基准温漂能控制在10ppm/°C左右精度相当不错。另外它还内置了可编程增益放大器PGA增益可以在1到128之间调整这意味着µV级别的微弱信号也能被有效放大到ADC可以分辨的范围。这款芯片的输出数据速率最高可以达到19200 SPS但常见的使用场景一般不会跑满因为速率越高噪声越大。比如在100 SPS的输出速率下配合数字滤波器有效分辨率能到20位以上这在实际项目中已经非常够用了。它还支持多种滤波模式包括sinc3、sinc4和FIR滤波器用户可以根据自己的需求在带通和建立时间之间做取舍。回到标题本身这是一个“STM32硬件SPI接口驱动AD7124”的项目。之所以强调硬件SPI而不是软件模拟SPI是因为硬件SPI可以配合DMA实现高速、无阻塞的数据采集CPU占用率极低这对于需要同时处理显示、通信、控制逻辑的单片机系统来说是非常关键的。下面我会从硬件接线、SPI参数配置、寄存器初始化、实际代码实现和调试经验这几个方面把整个驱动过程完整讲清楚。2. 硬件SPI接口设计接线方式与硬件层面的经验总结2.1 四线SPI接线与片选信号处理AD7124的SPI接口是标准的四线制SCLK、DIN、DOUT/RDY、CS。STM32作为主机AD7124作为从机。接线看起来很简单但在实际项目中如果不注意细节很容易埋下通信不稳定的隐患。我的推荐接法是这样的STM32的SCK接到AD7124的SCLKSTM32的MOSI接到AD7124的DINSTM32的MISO接到AD7124的DOUT/RDY任意一个GPIO接到AD7124的CS用软件控制片选AD7124的DRDY如果单独引出接到STM32的一个外部中断引脚用于判断数据是否准备好特别注意SCLK和DIN之间、以及DOUT和CS之间的关系。AD7124的SDO信号在CS为高电平时会变成高阻态所以当多个SPI设备共享同一条MISO总线时可以通过CS来分时占用总线不会冲突。这里有个很多新手会踩的坑STM32的SPI硬件NSS引脚和AD7124的CS到底怎么配合。我建议直接把NSS配置成软件模式SSM1用普通GPIO控制CS。原因有两个第一是STM32的硬件NSS在主模式下往往会产生多余的时序导致通信出现未知的问题第二是软件控制CS的时序更灵活方便插入一些特殊操作比如AD7124的60ms上电复位时序处理。如果你使用硬件NSS需要仔细确认NSS的输出时机是否和AD7124的时序要求吻合实测下来直接用GPIO最省心。2.2 DOUT/RDY复用引脚的特殊用途AD7124的DOUT/RDY引脚很有意思它是一个复用引脚。在CS拉低之后这个引脚首先会输出DSYNC/RDY状态表示上一次转换是否完成而在STM32向它写入命令时这个引脚又作为DIN的数据入口。更关键的是在CS拉低期间如果该引脚输出低电平表示ADC转换完成数据已经准备好了。也就是说这个引脚不仅承担数据输出的功能还承担了“数据就绪”标志的功能。你可以在每次读取数据之前先检查这个引脚的电平如果为低说明可以读取转换结果了。当然如果你使用的是连续转换模式并配合sinc3滤波器也可以不检查RDY直接读取因为按转换周期算时序上往往是够的。但严谨地讲最稳妥的方式是接一个额外的GPIO到STM32的外部中断用下降沿触发来同步读取。2.3 SPI时钟极性和相位匹配AD7124的SPI时序要求CPOL1、CPHA1也就是模式3。如果你用模式0CPOL0, CPHA0去读大概率读出来数据是错位的而且错位的方式往往是很隐蔽的——比如数据看起来要么全是0xFF要么每隔几个bit多一位。SPI模式3意味着时钟空闲时为高电平数据在时钟的第二个边沿上升沿被采样。用STM32的HAL库配置时需要把ClockPolarity设为SPI_POLARITY_HIGH把ClockPhase设为SPI_PHASE_2EDGE。这两个参数如果设错初始化之后的读取结果可能是完全不正常的。另外还有一个细节是SPI的位宽。AD7124的通信寄存器是8位宽的读写数据时一个字节一个字节地操作所以STM32的SPI数据位宽要设置为8位。千万不要设置成16位模式然后一次发送两个字节那样虽然在某些场景碰巧能工作但在时序要求严格的寄存器地址切换时会出问题。3. AD7124寄存器体系先搞清楚这些才能写出正确的驱动3.1 通信寄存器与SPI交互规则AD7124的SPI通信方式特别有一点需要注意每次访问寄存器之前都必须要先写通信寄存器来指定要访问的寄存器和接下来要执行的操作是读还是写。通信寄存器本身是8位的在写入通信寄存器之后紧接着的SPI传输读取或写入的字节数取决于你之前访问的寄存器宽度。通信寄存器的格式如下Bit 7写使能位必须为0才能写入通信寄存器Bit 6R/W位0表示写操作1表示读操作Bit 5至Bit 3寄存器地址Bit 2至Bit 0保留位写0例如要向通道寄存器0地址0x09写入配置那么先发送0x09写操作地址为0x09然后再写入该寄存器的内容。如果是读取则先发送0x49读操作地址为0x09然后由主机发送填充字节来产生SCLK从机则将数据放在DOUT上。在连续读取模式下AD7124允许在通信寄存器中指定地址后连续读多个寄存器这对于高效读取通道数据和状态寄存器很有价值。不过如果对寄存器不太熟悉建议先用单次读写的模式搞清楚每个寄存器的值然后再考虑连续读取。3.2 控制寄存器和状态寄存器的配置要点控制寄存器地址0x00是AD7124最核心的寄存器之一它控制着ADC的电源模式、转换模式、时钟源和基准电压等内容。常见的配置值为0x001000表示连续转换模式、内部64MHz时钟。如果你想用单次转换模式则需要设置MODE位为001这种情况下ADC转换一次后会自动进入待机模式功耗更低适合电池供电的仪表设备。状态寄存器地址0x00读操作时是只读的它包含了RDY位、通道ID和错误标志位。RDY位为0表示转换数据已就绪为1表示正在转换或未就绪。在实际排查中如果发现读出来的数据一直是0xFFFFFF或者0x000000可以先读一下状态寄存器看RDY位是否一直在变化。如果RDY位恒定不变说明ADC可能没有进入正确的转换状态或者是SPI的通信本身就有问题。3.3 通道寄存器与配置寄存器一个通道一个配置AD7124-4有4个差分输入通道和多个配置组。通道寄存器地址从0x09到0x0F用来选择哪个输入引脚连接到ADC的模拟输入以及使用哪个配置寄存器来设置PGA和滤波。配置寄存器地址从0x01到0x08则包含PGA增益、基准电压源选择、模拟输入缓冲等设置。一个典型的配置流程是选择Ain0和Ain1作为差分输入将通道0映射到配置寄存器0配置寄存器0中设置PGA增益为1基准电压选内部2.5V然后设置滤波寄存器选择sinc3滤波器输出速率设置为100 SPS。这样组合下来通道0就可以以差分方式测量Ain0和Ain1之间的电压差。这里需要注意如果某个通道没有在通道寄存器中被启用那么这个通道是不会进行转换的。有时候你配置了半天发现那个通道读出来的数据一直不变往往就是通道寄存器里的使能位没有置1。3.4 滤波器寄存器与输出数据速率滤波器寄存器地址从0x21到0x28决定输出数据速率和滤波方式。AD7124的滤波器选项比较多包括sinc3、sinc4和FIR等。每种滤波器对应的输出数据速率范围有所不同越高的输出速率对应越宽的带宽但同时噪声也会增加。在这里sinc3滤波器是默认推荐的选择。在sinc3模式下输出数据速率可以从1 SPS到9600 SPS内部时钟64MHz下。对于50Hz和60Hz工频干扰的抑制你可以把输出速率设置为50 SPS或者60 SPS这样sinc3滤波器的陷波点正好落在工频频率上可以有效地滤除工频干扰。我实际测试过在设置成50 SPS之后50Hz的干扰衰减非常明显效果远好于用软件再做一次均值滤波而且还不占用CPU资源。4. 程序实现从零开始写一个完整的AD7124驱动程序4.1 底层SPI发送接收函数封装先把底层的SPI读写函数封装好。这里使用STM32的HAL库因为HAL库对SPI的操作封装得比较完善代码可移植性也好。如果你用的是标准外设库思路也是类似的只是API名称不同。// spi_drv.c void AD7124_SPI_Init(void) { // 初始化GPIO和SPI外设 // SPI1: SCKPA5, MISOPA6, MOSIPA7, CSPA4 GPIO_InitTypeDef GPIO_InitStruct {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_SPI1_CLK_ENABLE(); // 注意这里的GPIO配置用了AF模式 GPIO_InitStruct.Pin GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Pull GPIO_PULLUP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate GPIO_AF5_SPI1; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); // CS引脚配置为普通推挽输出 GPIO_InitStruct.Pin GPIO_PIN_4; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_PULLUP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // SPI1配置模式38位主模式时钟分频64 hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_HIGH; hspi1.Init.CLKPhase SPI_PHASE_2EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_64; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial 10; HAL_SPI_Init(hspi1); }注意这里SPI的时钟分频我用了64分频。STM32F103的APB2时钟是72MHz64分频后SCLK约为1.125MHz完全满足AD7124的最高SPI时钟要求数据手册标称最大50MHz但实际使用时偏低的时钟更稳定。如果ADC器件距离MCU较远或者PCB布线质量一般适当降低SPI时钟可以有效避免通信错误。然后是发送和接收函数uint8_t AD7124_SPI_WriteRead(uint8_t data) { uint8_t rxData 0; HAL_SPI_TransmitReceive(hspi1, data, rxData, 1, 100); return rxData; }使用HAL_SPI_TransmitReceive可以同时完成发送和接收很多场景下比分开调用Transmit和Receive更稳定。需要特别提醒HAL库的HAL_SPI_Receive函数在某些版本上需要手动操作CS而且在接收模式下MISO的数据会被采样到接收缓冲区这时MOSI上是空闲电平而不是全0所以如果你的代码里通过MOSI发送0x00来触发时钟这种方式可能在某些库版本下会有问题。我在调试时就遇到过这个坑直接用HAL_SPI_Receive读数据结果读到的一直是0xFF。后来改成HAL_SPI_TransmitReceive把发送字节设为0x00问题就解决了。4.2 AD7124寄存器读写驱动基于上面的底层函数封装AD7124的寄存器读写操作。注意AD7124的寄存器是16位宽或者24位宽不等所以需要根据具体寄存器类型决定写入的字节数。// ad7124_drv.c void AD7124_WriteRegister(uint8_t regAddr, uint8_t *data, uint8_t len) { uint8_t cmd 0; // Bit70表示写使能Bit60表示写操作 cmd (regAddr 0x3F); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); AD7124_SPI_WriteRead(cmd); for (int i 0; i len; i) { AD7124_SPI_WriteRead(data[i]); } HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); } void AD7124_ReadRegister(uint8_t regAddr, uint8_t *data, uint8_t len) { uint8_t cmd 0; // Bit70表示写使能Bit61表示读操作 cmd 0x40 | (regAddr 0x3F); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); AD7124_SPI_WriteRead(cmd); for (int i 0; i len; i) { data[i] AD7124_SPI_WriteRead(0x00); // 发送0x00产生SCLK同时读取MISO数据 } HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); }这两个函数是所有上层操作的基础。在写代码的时候我习惯把CS的拉低和拉高操作直接放在寄存器读写函数里这样上层调用时不需要关心CS状态可以避免因CS状态错误而导致的通信错乱。4.3 AD7124初始化序列一个完整的配置实例下面这个初始化序列是我在测量一个电桥传感器称重传感器时实际使用的配置。电桥输出一般为±10mV到±20mV所以需要开PGA放大增益设置为128倍差分输入接到Ain0和Ain1。uint8_t AD7124_Init(void) { // 复位ADC AD7124_WriteRegister(0xFF, NULL, 0); // 写0xFF到通信寄存器触发复位 HAL_Delay(10); // 等待复位完成 // 配置ADC控制寄存器连续转换模式内部64MHz时钟 uint8_t ctrlData[2] {0x00, 0x10}; AD7124_WriteRegister(AD7124_REG_CTRL, ctrlData, 2); // 配置配置寄存器0PGA128选择内部2.5V基准开启AIN输入缓冲双极性模式 uint8_t cfgData[2] {0x78, 0x02}; AD7124_WriteRegister(AD7124_REG_CFG0, cfgData, 2); // 配置滤波器寄存器0sinc3滤波器输出速率约50 SPS选择FS[10:0] 0x600 uint8_t filtData[2] {0x06, 0x00}; AD7124_WriteRegister(AD7124_REG_FILT0, filtData, 2); // 配置通道寄存器0启用通道0选择Ain0()和Ain1(-)映射到配置寄存器0 uint8_t chData[2] {0x00, 0x01}; AD7124_WriteRegister(AD7124_REG_CH0, chData, 2); // 配置IO控制寄存器设置CH1-0引脚为高电平关闭内部偏置电压 uint8_t ioData[2] {0x00, 0x10}; AD7124_WriteRegister(AD7124_REG_IO, ioData, 2); return 1; }上面的配置值看起来可能有点抽象我逐个拆解一下。配置寄存器0的0x7802转成二进制是0111 1000 0000 0010其中Bit[7:4]为0111表示PGA增益为128Bit[6]为1表示使能AIN0的输入缓冲Bit[5]为1表示使能AIN1的输入缓冲Bit[2]为1表示基准电压选择为内部2.5VBit[1]为0表示双极性模式ADC转换结果以二进制补码形式输出。滤波器寄存器0的0x0600其中Bit[14:12]为001表示选择sinc3滤波器Bit[10:0]为0x600对应十进制1536在内部时钟64MHz、sinc3滤波器下输出数据速率大概在50 SPS附近。具体FS值的计算可以参考数据手册中的公式不过实测这个值可以得到50Hz陷波效果对于工频干扰比较严重的环境非常有利。通道寄存器0的0x0001最后一个bit为1表示通道0使能。前面的Bit[20:16]为00000表示选择Ain0作为正输入Bit[12:8]为00001表示选择Ain1作为负输入Bit[7:5]为000表示使用配置寄存器0。4.4 读取ADC转换数据的核心函数初始化完成后读取数据就很简单了。两种常见的读取方式手动等待DRDY下降沿或者用轮询方式检查状态寄存器的RDY位。下面给出手动读取数据不依赖外部中断的示例。uint32_t AD7124_ReadData(void) { uint8_t data[3] {0}; uint32_t rawData 0; // 读取DATA寄存器地址0x02 AD7124_ReadRegister(AD7124_REG_DATA, data, 3); // 组合成24位数据 rawData ((uint32_t)data[0] 16) | ((uint32_t)data[1] 8) | data[2]; // 如果是双极性模式需要处理符号位 if (rawData 0x800000) { rawData | 0xFF000000; // 符号扩展 } return rawData; }注意这里读取DATA寄存器的操作必须在CS拉低后先写命令字节0x02读DATA寄存器然后连续读3个字节。AD7124的DATA寄存器是24位的高位在前。如果数据是负值需要把符号位扩展到32位否则直接强制转换成有符号整数时会出错。等一下有个细节DD。你如果用的是连续转换模式在每次读取数据前最好确认RDY状态。最简单的方法是读状态寄存器的最低一位如果是0就说明数据更新了。在代码中可以这样处理// 等待新数据就绪 uint8_t status 0; do { AD7124_ReadRegister(AD7124_REG_STATUS, status, 1); } while (status 0x80); // RDY位为1表示忙不过这种轮询方式会阻塞CPU如果系统还有别的任务更推荐使用外部中断方式。将AD7124的DOUT/RDY引脚接到STM32的EXTI引脚下降沿触发中断在中断中设置一个标志位在主循环中检测到标志位后再去读取数据。这种方式既省电又能保证数据不会因为读取太晚而丢失。5. 实际调试经验那些数据手册里不会写的坑5.1 DRDY引脚信号异常为什么CS拉低后DOUT拉低没有出现刚拿到板子的时候我按照标准接线连好之后第一个遇到的问题是CS拉低之后DOUT/RDY引脚一直保持高电平没有出现预期的低电平脉冲。查了很多资料最后发现是因为AD7124在上电后默认处于待机模式需要通过SPI写入控制寄存器设置成连续转换模式后DRDY引脚才会有反应。也就是说如果你的ADC在初始化之前就想去用示波器看DOUT波形通常会一无所获。先确认初始化序列完整跑完之后再去观察DRDY的信号。5.2 读出来的数据永远是0xFFFFFF这个坑比较经典。如果你读到的结果是0xFFFFFF或者是全0先不要怀疑芯片坏了大概率是SPI的相位配置不对。AD7124要求CPOL1、CPHA1可是STM32的HAL库默认的SPI初始化往往是CPOL0、CPHA0如果忘记改就会出现这种问题。还有一种可能是CS的时序问题。有些芯片要求CS拉低之后要先等一小段时间再发起SPI时钟AD7124虽然对这个要求不严但如果SCLK速率太高且CS拉低后立即发送命令可能有极小概率出现通信错误。建议在CS拉低之后插入几个nop或者一个微秒级的延时稳定之后再发数据。5.3 单次转换模式读取不到数据如果你用的是单次转换模式读取数据前必须确认转换已经完成。单次转换模式下写入控制寄存器启动转换之后如果立刻去读DATA寄存器很可能读到的还是上一次的旧数据。解决办法是利用DRDY引脚的中断等DRDY拉低之后再去读取。也可以用轮询状态寄存器的方式等待RDY位清零。前面提到的外部中断法在单次转换模式下尤其重要。因为单次转换模式下转换完成一次之后ADC会回到待机模式此时DRDY会恢复到高电平。如果你在主循环里一味地轮询RDY可能就会永远等不到数据。5.4 输出速率设置过高导致数据不稳定在做振动信号采集时我把输出数据速率调到了4800 SPS然后发现数据非常不稳定跳动幅度远大于手册上给出的指标。后来分析了一下我发现高SPS下ADC的噪声本来就比较大如果你不配合合适的滤波算法直接使用原始数据就会觉得很“飘”。解决方法是根据信号带宽合理选择SPS不要一味求高在软件层面增加均值滤波或滑动滤波使用差分输入模式避免共模干扰被PGA放大5.5 参考电压引脚一定要加去耦电容AD7124的REFIN和REFIN-引脚如果使用外部基准源一定要在靠近引脚的位置加上10µF和0.1µF的去耦电容。如果使用内部2.5V基准则需要在REFOUT引脚上放置10µF电容。我在最初画PCB时因为空间紧张REFIN引脚旁只放了一个0.1µF的电容导致测量结果的RMS噪声明显偏大。后来补上10µF电容后数据立刻变得稳定了许多。5.6 VBIAS引脚功能容易忽略AD7124-4有一个VBIAS功能可以给某个输入通道提供共模电压用于单端输入时偏置。但这个功能默认是关闭的如果你用单端测量且传感器是浮空的输入信号可能会偏出共模范围导致转换结果错误。需要测量单端信号时可以在IO控制寄存器中设置对应的VBIAS位实现“伪差分”测量。6. 工程上的性能优化DMA与多通道扫描的加速方案6.1 DMA方式读取数据如果系统需要以较高的速率持续采集数据强烈建议使用SPIDMA的方式。传统的HAL_SPI_TransmitReceive是阻塞式的每读一次数据CPU都要等SPI完全传完才返回。在连续转换模式下如果输出速率是9600 SPS每次转换周期约104µs读取3个字节的时间大约是3µs看起来占用不大但如果再加上寄存器读取时的命令字节和可能的状态查询CPU占用率会明显上升。改用DMA之后CPU只需要发起传输然后去处理别的事情传输完成后触发中断即可。一个简单的做法是使用HAL_SPI_TransmitReceive_DMA在完成中断中解析数据。不过要注意DMA模式下接收缓冲区和发送缓冲区的生命周期管理容易出错建议使用静态数组或者环形缓冲区。// 使用DMA读取DATA寄存器的示例 uint8_t spiTxBuf[4] {0x02, 0x00, 0x00, 0x00}; uint8_t spiRxBuf[4] {0}; HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); HAL_SPI_TransmitReceive_DMA(hspi1, spiTxBuf, spiRxBuf, 4); // 在SPI DMA完成中断中 // 将spiRxBuf[1]到spiRxBuf[3]组合成24位数据这个方案在COMSOL这类连续高速采集场景里非常常用。实测下来STM32F103在72MHz主频下用SPI DMA读AD7124在19200 SPS的极限速率下也能稳稳跑通CPU占用率不到20%。6.2 多通道扫描时的通道切换策略AD7124支持多通道扫描模式你可以在通道寄存器中同时使能多个通道ADC会自动按顺序转换。在读取数据时每次读取DATA寄存器后还需要检查状态寄存器中的通道ID确认当前读到的数据属于哪个通道。我在做3通道温度采集时遇到的问题是一开始的通道顺序和寄存器中配置的通道顺序不完全一致。后来才发现AD7124的通道转换顺序是按通道编号从低到高排列的而且如果你在转换过程中修改了通道使能可能会出现一两个周期的错乱数据。解决办法是先停机写入命令再修改通道使能然后重新启动转换。6.3 片选信号与连续转换模式的坑这是一个容易被忽略的点看数据手册的时候你会发现AD7124在连续转换模式下如果你在读数据的时候反复拉高CS、拉低CSADC的转换不会受影响但如果你在CS拉低期间执行了写操作比如写了通信寄存器转换顺序可能会被打乱。因此在多通道扫描时建议不要频繁切换寄存器或者确保每次切换之后都丢弃当前周期的一两个数据点进行校准。7. 数据后处理与工程化建议7.1 24位数据到真实电压的换算拿到24位原始数据之后如何换算成真实电压这个公式很简单电压 (原始数据 / 2^23) × 满量程电压 / PGA增益其中满量程电压等于基准电压的两倍双极性模式。如果你的基准电压是2.5VPGA增益是128那么计算公式就是电压 (原始数据 / 8388608) × 2.5 × 2 / 128 (原始数据 / 8388608) × 0.0390625 V如果是称重传感器场景还可以做线性标定把电压值转换成实际重量。标定的方式就是在空载和满载下分别记录ADC的原始值然后做两点标定得到斜率和截距。注意温度变化会影响PGA的增益精度和基准电压所以如果做高精度称重还需要考虑温度补偿。7.2 数字滤波与工频抑制AD7124内置滤波器已经能处理大部分噪声但实际项目中还是建议在软件里做一层简单的均值滤波或者中值滤波。我常用的做法是对原始数据做16次采样取平均这样能明显降低随机噪声而且不会让系统响应变慢太多。如果对实时性要求不高还可以做滑动窗口平均既能保持连续性又能有效平滑输出数据。对于50Hz工频干扰最好的方案是把AD7124的输出速率设置为50 SPS利用sinc3滤波器的陷波点。实测下来在工业现场有大量电机和变压器的情况下设置50 SPS后采集到的数据远比100 SPS加软件滤波更干净。当然代价是时间分辨率变低不适合快速变化的信号。7.3 PCB布线与抗干扰建议最后简单聊一下硬件设计的一些注意事项。AD7124的模拟输入引脚是高阻抗输入走线越短越好否则容易引入噪声。在PCB布局时把模拟部分和数字部分分开AD7124尽量靠近传感器接口SPI信号线不要和模拟输入线平行走线以避免串扰。电源去耦也很关键AVDD和DVDD都要放10µF0.1µF的去耦电容模拟地和数字地建议用磁珠或0Ω电阻单点连接。SPI信号线上可以串联22Ω到33Ω的电阻改善信号完整性。实测中SPI时钟频率超过10MHz时串阻的效果非常明显。8. 完整可用的源代码包结构说明如果你拿到的是“基于stm32单片机硬件SPI接口驱动AD7124模数转换芯片程序源代码.zip”这个压缩包一般是这样的结构project/ ├── Core/ │ ├── Inc/ │ │ ├── ad7124_drv.h │ │ ├── spi_drv.h │ │ └── main.h │ └── Src/ │ ├── ad7124_drv.c │ ├── spi_drv.c │ └── main.c ├── Drivers/ │ ├── CMSIS/ │ └── STM32F1xx_HAL_Driver/ ├── MDK-ARM/ │ ├── project.uvprojx │ └── ... └── README.md主文件main.c中的典型测试逻辑一般是这样int main(void) { HAL_Init(); SystemClock_Config(); AD7124_SPI_Init(); AD7124_Init(); while (1) { uint32_t rawData AD7124_ReadData(); // 将rawData转换为电压值 float voltage (float)rawData / 8388608.0f * 2.5f * 2.0f / 128.0f; // 通过串口打印或者显示到OLED HAL_UART_Transmit(huart1, (uint8_t *)voltage, sizeof(voltage), 100); HAL_Delay(100); // 采样周期10Hz } }在实际使用这份代码时你需要根据自己的STM32型号和SPI外设修改spi_drv.c中的引脚定义和SPI句柄。不同型号的STM32SPI外设的时钟源和引脚复用关系都不一样最稳妥的方式是在芯片原厂的例程基础上进行修改。9. 常用寄存器速查表这里整理一张我在调试中经常对照的寄存器地址表方便大家快速查阅。寄存器名称地址宽度说明通信寄存器0x008位每次SPI操作前必须先写状态寄存器0x008位读操作时获得状态信息ADC控制寄存器0x0116位设置操作模式、时钟、电源数据寄存器0x0224位当前转换结果IO控制寄存器0x0316位GPIO和VBIAS控制配置寄存器0-70x08-0x0F16位PGA增益、基准、缓冲设置滤波器寄存器0-70x10-0x1724位滤波方式、输出速率偏移寄存器0-70x18-0x1F24位系统校准偏移满量程寄存器0-70x20-0x2724位系统校准增益通道寄存器0-70x28-0x2F16位通道映射和使能特别说明一下寄存器地址是基于6位地址但我上面列出的地址是加上命令格式后的值。如果你直接对地址0x01写数据其实是写到了ADC控制寄存器因为通信寄存器的0x01低6位地址是0x01Bit7为0Bit6为0。如果你要先写0x80到通信寄存器复位命令然后等待一段时间那是在复位整个ADC。10. 一些个人体会和小技巧我在这个项目里踩过不少坑有几个小技巧值得分享。第一第一次调试新硬件的时候建议先把SPI通信速率降到最低比如100kHz左右并把所有寄存器配置改为基础值比如PGA1输出速率最低档先确保SPI通信链路是通的。通信通了再逐步提升速率和配置这样可以避免把“配置错误”和“通信不稳定”混在一起排查事半功倍。第二调试阶段在代码中加一个“回读寄存器”的功能。写完某个关键寄存器之后马上读出来和写入值对比一下如果写入和读出不一致说明SPI通信有问题或者寄存器地址不对。这个方法在排查AD7124这类寄存器较多的芯片时极其管用。第三AD7124的复位有讲究。往通信寄存器写0xFF可以触发复位但复位需要时间至少要等待10ms再开始配置否则后续的寄存器写入可能会失败。数据手册上虽然标注了复位时间但实际温度偏低时复位时间可能延长一些。第四如果你用到多个AD7124可以考虑共用一个SPI总线各自通过CS片选。但注意SPI总线上如果某个从机的DOUT/RDY没有释放也就是CS没有拉高会导致总线冲突所以必须确保每次操作完成后都把CS拉高并且在所有从机初始化完成后才能开始采集。还有一个办法是用外部中断检测DOUT/RDY但多个ADC时每个ADC需要独立的DOUT引脚或者用额外的GPIO来读取。第五关于代码风格的取舍。ADI官方提供了针对AD7124的驱动程序代码写得很完备但如果你只做简单应用直接移植官方驱动反而会显得臃肿。我自己习惯精简成一个ad7124_drv.c文件只保留用到的函数代码量大概200行左右维护起来很轻松。最后想说的是AD7124是一块学习价值很高的ADC芯片把它的驱动搞明白以后再看ADS1256、MCP3561这类Σ-Δ型ADC都会轻松很多因为它们在寄存器架构和SPI交互方式上都有很多相似之处。希望通过这篇文章你不仅能跑通STM32硬件SPI驱动AD7124还能真正理解每一行配置的意义然后在自己的项目里灵活运用。本文还有配套的精品资源点击获取
返回列表