多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

ADS131M04同步采样ADC驱动开发:SPI时序与寄存器配置实战

ADS131M04同步采样ADC驱动开发:SPI时序与寄存器配置实战 简介一套基于STM32F401微控制器及其硬件SPI接口驱动TI ADS131A04高精度四通道同步采样ADC的嵌入式代码包面向医疗、工业测量及科研数据采集等需要低噪声、高分辨率信号的开发者旨在解决高精度模拟量数字化与底层通信配置的常见问题。资源包仅有两个文件分别为一个C源文件和一个头文件压缩后约2KB代码精简可直接嵌入现有STM32工程。源文件封装了ADS131_Init、启动转换与数据读取等核心接口覆盖SPI主模式配置、寄存器初始化、全双工通信时序以及连续/单次转换模式并预留增益、滤波器、采样率等参数调节空间方便针对不同传感器与应用场景灵活调整。代码还涉及中断与唤醒功能的实现思路便于在非采样时段进入低功耗状态对便携或电池供电设备有实用参考价值。当前已有4997人学习该资源适合正在调试高精度ADC驱动或需要快速集成四通道同步采集的嵌入式工程师借鉴使用。 做数据采集系统的时候我常推荐同步采样ADC比如TI的ADS131系列。四通道、24位、ΔΣ架构一颗芯片就能把多路模拟量采集搞定在电力监测、振动分析、多路传感器同步采集这些场景里非常好用。但真到写驱动的时候不少人都被那套寄存器命令和SPI帧结构绕晕了——我最初也被这颗芯片折腾过两天。这篇文章把我实际调通ADS131M04的代码和调试经验整理出来内容包括驱动初始化、数据读取、常见问题排查正在写ADS131相关代码的同行可以直接参考。1. 选型思路与代码架构设计1.1 ADS131系列到底能干什么ADS131是TI的同步采样24位ΔΣ ADC家族常见型号有ADS131M04四通道、M06六通道、M08八通道还有带DSP功能的ADS131E04等。它们共同的特点是多个通道真正同步采样而不是模拟多路开关轮询。这一点对电力三相参数计算、电机振动特征分析、或者需要对比多路传感器时序关系的项目非常重要。M04每通道内置PGA可编程增益放大器增益范围1到128输入范围是±1.2V对应内部参考电压。输出数据率通过过采样率OSR配置典型范围从1kSPS到64kSPS左右。它还带有偏置校准寄存器和增益校准寄存器做精度标定时不需要额外加模拟调理电路直接在数字域修正非常方便。1.2 代码分层驱动最好别裸奔我的习惯是把代码拆成三层硬件抽象层HAL、芯片驱动层、应用层。HAL只负责SPI读写、延时、GPIO控制这些跟芯片无关的东西驱动层封装ADS131M04的寄存器操作、初始化、读数据应用层只跟具体的业务打交道比如电压换算、滤波、上传数据。这样做的原因是ADS131系列寄存器多初始化参数也多如果全糅在一段main函数里后续换MCU平台几乎要重写。分层之后换平台只需要改HAL层那几百行驱动层可以直接复用。像我们后期从STM32F103换到GD32E230驱动代码一行没改。2. 驱动初始化寄存器配置与采样参数计算2.1 SPI时序是第一个大坑ADS131M04使用串行外设接口SPI通信但它对时序比较挑剔。我调试时踩过的第一个坑就是用STM32默认的SPI配置去读芯片ID返回的全是0xFF。后来查手册才发现芯片支持SPI模式0和模式1但要求CS拉低期间整个命令响应序列都必须连续拉低中间不能让CS弹起。另外时钟频率不能太高数据手册建议最大20MHz但实际设计时我一般控制在8MHz以下特别是PCB走线比较长的时候高频下波形边沿容易过冲。以STM32的HAL库为例我会这么配置SPISPI_HandleTypeDef hspi1; void ads131_spi_init(void) { 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_LOW; hspi1.Init.CLKPhase SPI_PHASE_1EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_16; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; HAL_SPI_Init(hspi1); HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET); // CS默认拉高 }注意这里NSS使用软件控制CS引脚最好用普通GPIO手动拉不要用硬件NSS。因为硬件NSS在每次传输结束会自动拉高而ADS131的帧传输需要在整个读操作期间CS保持低电平用硬件NSS会导致帧错乱。2.2 关键寄存器配置时钟、增益、配置ADS131M04的寄存器地址不多但每个都很关键。最常用的是这几个寄存器地址作用CLOCK0x00配置时钟分频、OSR、PGA延迟等GAIN0x01设置各通道PGA增益CFG0x02配置CRC、帧格式、中断输出THRSHLD0x03阈值检测相关CH_EN0x0F通道使能控制寄存器操作用到两条命令RREG读寄存器WREG写寄存器。命令字节格式很统一读寄存器命令字节是0x20加上寄存器地址写寄存器是0x60加上寄存器地址紧跟一个字节表示写入的寄存器个数减1后面跟着要写的数据。我封装成两个基础函数uint8_t ads131_read_reg(uint8_t addr) { uint8_t tx[2] { (uint8_t)(0x20 | addr), 0x00 }; uint8_t rx[2] { 0, 0 }; HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(hspi1, tx, rx, 2, 10); HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET); return rx[1]; } void ads131_write_reg(uint8_t addr, uint8_t data) { uint8_t tx[3] { (uint8_t)(0x60 | addr), 0x00, data }; uint8_t rx[3] { 0, 0, 0 }; HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(hspi1, tx, rx, 3, 10); HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET); }这里有个细节发送命令后收到的字节是上个寄存器的内容也就是说读寄存器时第一个返回字节是无效的第二个字节才是目标寄存器的值。写寄存器时第二个字节是固定的0x00表示要写的寄存器个数减1。2.3 采样率与增益怎么算ADS131M04内部有一个PLL可以把外部时钟倍频到调制器工作频率。数据率的计算公式是fDATA fCLKIN / (OSR × 4)其中fCLKIN是CLOCK寄存器配置后的内部时钟典型基准是8.192MHz。我项目里用8.192MHz晶振OSR配置为1024算出来的数据率就是8.192MHz / (1024 × 4) 2kSPS这个速率非常适合电力谐波分析。增益的选择要结合信号幅度来定。PGA增益越高小信号越灵敏但噪声也会按比例放大。比如增益1时满量程是±1.2V可分辨的最小电压约0.143μV增益128时输入范围变成±9.375mV但噪声也大概被放大128倍。我一般先算传感器输出范围留出50%以上裕量再选增益而不是直接选最大增益。初始化时我通常会统一设置所有通道增益为1void ads131_init(void) { ads131_write_reg(0x00, 0x00); // CLOCK: 默认配置再按需修改 ads131_write_reg(0x01, 0x00); // GAIN: 所有通道增益为1 ads131_write_reg(0x02, 0x10); // CFG: 关闭CRC禁用帧状态字 ads131_write_reg(0x0F, 0xF0); // CH_EN: 使能4个通道 }写完之后建议回读一遍确认寄存器真的写进去了。如果读回来是0xFF基本是SPI配置问题如果是0x00要检查上电时序和复位引脚状态。3. 数据读取从SPI字节到工程电压3.1 数据帧结构解析ADS131M04一次读取会输出一组固定的数据帧。默认情况下每帧是4个通道的24位数据MSB先行输出。如果使能了CRC和状态字帧长度会变长配置阶段也更多。我建议调试初期先把CRC和状态字全部关掉让帧结构最简单等通信完全稳定再打开这些功能也不迟。读取一帧数据的代码示例如下#define ADS131_CHANNEL_NUM 4 void ads131_read_frame(int32_t *ads_data) { uint8_t tx[12] {0}; uint8_t rx[12] {0}; HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(hspi1, tx, rx, 12, 10); HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET); for (int i 0; i ADS131_CHANNEL_NUM; i) { uint8_t *p rx[i * 3]; int32_t val ((int32_t)p[0] 16) | ((int32_t)p[1] 8) | p[2]; if (val 0x800000) { val | 0xFF000000; // 24位补码扩展为32位 } ads_data[i] val; } }这里需要特别注意ADS131M04的数据帧前面没有任何命令字节只要CS拉低芯片就会自动把当前ADC数据放到MISO线上。所以接收缓冲区直接就能拿到4个通道各3字节的原始数据。如果帧格式配置成带状态字那么这个函数就不适用了。3.2 读取时机与DRDY中断什么时候读数据答案是看DRDY引脚。芯片每次完成一次ADC转换DRDY引脚会拉低表示新数据已就绪。之后必须在这个低电平期间完成数据读取否则可能导致数据指针指向下一帧造成错位。我对这里的处理方式是DRDY引脚连接到MCU的外部中断引脚下降沿触发中断。中断里置一个标志位主循环检测到标志后才去读数据读完后DRDY会自动释放。这样比轮询DRDY更节省CPU也保证了不会漏掉转换完成事件。唯一要注意的是SPI传输本身需要一定时间如果数据率很高比如64kSPS那么每个周期只有约15μs一定要确认SPI速度足够快否则数据没读完下一轮就来了。3.3 24位补码转成实际电压芯片输出的原始数据是24位二进制补码满量程范围是-8388608到8388607对应输入电压范围±1.2V/VREF。转换成实际电压值的公式是V_input (raw_value / 8388608.0) × VREF / PGA_gain我用一个函数统一换算float ads131_raw_to_voltage(int32_t raw_value, float gain) { const float VREF 1.2f; return (float)raw_value * VREF / 8388608.0f / gain; }举个例子增益1时如果读到的raw_value是4194304那么电压大约是0.6V正好是满量程的一半。这里有个实际经验不要用两个整数相除再乘系数那样会丢掉精度。最好先转成float或double再做乘法我一般用float在嵌入式平台就够用了除非做高精度计量才需要double。4. 实测调试记录与噪声优化4.1 先写一个读ID自检函数拿到芯片后我永远不会直接开始读数据写一个芯片ID读取函数是最可靠的自检手段。ADS131M04的ID寄存器地址是0x0F其实这个寄存器同时兼作通道使能高4位固定为版本号。读出来数值一般是0xF0左右具体看版本批次这个自检能同时验证SPI通信、CS控制、上电状态是否正常。uint8_t ads131_check_id(void) { uint8_t id ads131_read_reg(0x0F); if ((id 0xF0) 0xF0) { return 1; // 通信正常 } return 0; }我第一次调试时串口打印读回的id始终是0x00排查了SPI极性、CS时序都没发现问题最后发现是芯片的复位引脚一直没拉高芯片处于复位状态。这种基础问题最容易忽略所以自检函数一定要先跑通。4.2 观察DRDY与数据流在示波器上同时抓DRDY下降沿、CS低电平窗口、MISO波形是判断时序最直接的办法。正常工作时DRDY每个采样周期拉低一次然后CS拉低开始读数据MISO上会连续出现4个通道各24位的数据。我第一次把CS拉低时间提前了结果读到的前几个字节总是不对——那时我才意识到必须等DRDY拉低后才能发起读取。如果示波器上看到DRDY频率异常比如不是预期的2kSPS而是很乱那基本是时钟配置错误。这时候要回头检查CLOCK寄存器的OSR和分频系数。可以先用最简单的配置OSR256验证基本功能确认能出数后再追求高精度。4.3 噪声优化三板斧ADS131M04本身的噪声性能很好但外部电路设计不当会把噪声搞得很严重。我实测发现芯片内部参考电压足够稳定除非对精度有变态要求否则不需要外接参考芯片。下面这几条是我实际项目里最有效的优化手段第一模拟电源AVDD和数字电源DVDD要分开滤波AI0~AI3输入附近不要走数字信号线。第二SPI总线上串接22Ω~33Ω电阻可以抑制振铃特别是在MCU引脚驱动能力比较强的场合。第三DRDY引脚上的干扰也会导致读取时机错乱建议加一个10kΩ上拉电阻同时在代码里做连续多次读取校验。增益的配置对噪声同样影响巨大。我做过一组对比输入信号约2mV增益1时噪声底大约几十μV量级改成增益64后等效噪声明显降低折算到输入端。如果你的信号最终要放大处理不如直接用PGA放大而不是后级再加放大器这样可以减少一个噪声源。5. 常见问题排查速查表5.1 通信没反应读寄存器返回0xFF或0x00是最常见的初始化失败特征。我遇到过的情况有SPI极性配置错了、CS没有用软件控制、芯片还在复位状态、电源引脚接触不良。排查顺序建议是先确认电源电压在2.7V~3.6V范围内再用逻辑分析仪抓CS和MOSI波形确认命令字节发出去了最后检查复位引脚。现象可能原因排查方法读ID返回0xFFSPI模式不对、CS时序错误检查时钟极性和相位用逻辑分析仪抓波形读ID返回0x00芯片未退出复位、电源异常检查RESET引脚电平和电源电压读ID正常但初始化写不进去寄存器写保护未解除按手册确认WRITE命令格式回读验证5.2 数据全零或长时间不更新数据一直为零但读ID正常这说明SPI通信没问题问题出在转换环节。常见原因有三个通道没有使能、DRDY信号没接对、或者CLOCK寄存器配置导致数据率太低。我看过有人把DRDY引脚悬空靠主循环死等延时去读结果时序永远对不上数据全是乱码。最稳定的做法是先把CH_EN寄存器明确设为使能4个通道然后在示波器上确认DRDY有脉冲输出。若DRDY完全没有信号那多半是CLOCK配置里有个使能位没置1。ADS131M04有个PWR寄存器或者CLOCK寄存器里的PD掉电控制位如果芯片进入掉电模式是不会产生DRDY的。5.3 数据跳变、噪声大数据读回来会跳或者噪声明显偏大一般先怀疑参考电压和电源再怀疑布局。我用一个简单方法区分把ADC输入端短路到GND如果此时输出码的方差依然很大问题在电源或PCB布局如果输出码稳定那问题在输入信号路径。PCB布局上尽量让模拟地和数字地在芯片下方单点连接SPI数字线不要从ADC输入引脚旁边穿过。电源去耦我习惯在AVDD引脚放一个10μF钽电容并联0.1μF瓷片电容DVDD那边至少放一个0.1μF。加一个LDO给模拟电源供电而不是直接吃MCU板的3.3V开关噪声可以明显改善低频噪声。5.4 增益越大噪声越大的正确理解很多新手以为增益设大了噪声会变小。实际上PGA增益同时放大信号和噪声只是折算到输入端的噪声RTI会降低而输出端的等效噪声会增加。打个比方增益1时可能有1mV的噪声增益128时输出端噪声可能是128mV的等效噪声。所以选择增益必须看信号本身幅度小信号才适合高增益大信号用高增益反而会让ADC提前饱和。实际项目中我还遇到过增益切换后数据有一段跳变期这是PGA建立时间导致的。每次切换增益后最好丢弃前几十个采样点等内部RC稳定后再采集有效数据。这个细节在实时控制系统里尤其重要否则启动瞬间会出现明显的毛刺。写在最后ADS131这颗芯片写驱动并不难难的是理解它的时序框架和帧结构。我最初总想着快速把数据读出来结果浪费了很多时间在通信错误上。后来我养成一个习惯不管什么芯片先建立自检流程把ID读出来把寄存器回读确认再谈数据采集。这套方法帮我绕开了很多坑也让我换平台时更有底气。如果你刚开始调ADS131建议按照上面的步骤一步步来先从最简单的SPI自检开始再逐步增加功能往往最稳妥。本文还有配套的精品资源点击获取
返回列表