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

资讯详情

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

STM32驱动ADS1220高精度ADC:SPI配置与数据采集实战

STM32驱动ADS1220高精度ADC:SPI配置与数据采集实战 简介本资源是一套面向嵌入式开发工程师与STM32进阶学习者的ADS1220高精度ADC驱动实战代码聚焦SPI通信下的24位模拟信号采集场景适用于工业传感器如温度、压力、精密测量仪器等对分辨率与低噪声有严苛要求的应用。资源包共1050个文件涵盖593个C源文件、285个头文件h、78个汇编文件s及IAR/Keil工程配置文件icf/uvprojx含CMSIS-DSP数学库静态链接库如libarm_cortexM4lf_math.a与硬件抽象层适配代码整体压缩后达30.18MB结构完整、模块清晰便于移植与调试。已有729人下载学习提供从SPI外设初始化、ADS1220寄存器配置PGA增益、数据速率、工作模式、连续/单次采样控制到原始数据解析的全流程实现附带多平台工程模板与关键时序注释显著降低高精度ADC集成门槛。1. 项目概述与方案选型1.1 为什么在STM32项目中偏偏选中ADS1220在工业测量、仪器仪表、称重变送器这类场景里ADC选型往往决定整个项目的精度天花板。STM32F103这类MCU内置的12位ADC日常够用但在做高精度数据采集时就显得心有余而力不足——12位分辨率理论上只有4096个码值即便参考电压3.3V每个码值对应的电压步进也有0.8mV左右测个电池电压、做个小功率电流采样还行真要测毫伏级信号噪声和量化误差就把信号淹没了。ADS1220是一款24位Delta-Sigma型ADC内部自带可编程增益放大器PGA、低温漂基准电压源、两个可编程激励电流源、温度传感器以及四个输入通道。它支持的增益范围从1到128倍输入多路复用器可以配置成差分或单端模式最关键的是它的有效分辨率很高——在20SPS数据速率、PGA等于128的配置下即使在STM32上做最简单的均值滤波也能轻松分辨微伏级别的电压变化。这用来做称重传感器、热电偶测温、电桥信号采集这类微弱信号项目效果完全不在一个量级上。另一个选它而不是ADS1232或HX711的原因在于ADS1220的配置灵活性。HX711是固定增益、固定速率的称重专用芯片功能单一ADS1232虽然也不错但是只有两个差分通道。ADS1220通过SPI寄存器可以实现通道切换、增益调整、数据速率调整、基准源选择、电流源开关甚至还能配置成IDAC恒流源给RTD铂电阻供电做二线制温度测量非常方便。换句话说一颗ADS1220可以覆盖多种传感器前端应用备货和通信调试成本都下来了。1.2 项目整体架构与数据流走向这个项目的整体结构用一个图表示比较直观但博主在实际开发中一般就是三个块传感器信号进来ADS1220负责把模拟信号转成24位数字码STM32通过SPI把码值读走最后在MCU内部做工程量换算和滤波输出。具体到数据链路是这样的ADS1220的AIN0和AIN1引脚接收来自传感器的差分电压信号内部PGA先把信号放大到ADC内核的能量程范围调制器把模拟量变成数字脉冲流数字滤波器累加出24位转换结果写入输出寄存器。这个过程中DRDY引脚是关键——只有当DRDY变为低电平才表示新的转换数据已经准备好此时STM32通过SPI指令读取三个字节的转换结果。STM32F103C8T6作为主控负责初始化SPI外设、配置ADS1220寄存器、管理DRDY中断或查询、处理读回的数据并换算成实际电压或物理量。如果项目有显示需求就用I2C接OLED有通讯需求就用串口上报到上位机。整个软件框架的耦合度很低主循环只管取数据、做滤波、跑业务逻辑SPI的收发全部放在中断或事务回调里处理。这种设计的好处是数据流清晰排查问题非常方便先用逻辑分析仪抓到SPI总线上的波形再用示波器看DRDY引脚拉低时序问题大多很快定位。2. 硬件连接与SPI通信深挖2.1 引脚分配与硬件连接要点先列出ST官网NUCLEO或自制板常见的接线方式各位按自己的板子对号入座。这里以STM32F103C8T6蓝丸小板为例信号STM32引脚说明SCLKPA5SPI1的SCKMOSIDINPA7主出从入MISODOUTPA6主入从出CS片选PA4软件或硬件片选均可DRDYPA3外部中断输入判断数据就绪VCC3.3V数字和模拟电源统一3.3VAVDD3.3V或5V模拟电源建议单独走线DVDD3.3V数字电源接线时有一点必须注意ADS1220的模拟电源AVDD可以不等于数字电源DVDD但如果用5V给AVDD供电那么SPI引脚的逻辑电平也要相应匹配。很多新手直接拿5V供电然后STM32的3.3V引脚去连SPI结果数据总是不对就是因为电平不匹配。稳妥的做法是AVDD和DVDD统一接3.3V让所有逻辑电平均为3.3V省去电平转换的麻烦。此外AVDD引脚和DVDD引脚虽然是分开的但在PCB或面包板上要注意退耦电容的摆放。AVDD附近放一个10uF钽电容或电解电容并联100nF陶瓷电容DVDD引脚附近放100nF电容即可。模拟输入引脚AIN0/AIN1如果接的是电桥输出最好在传感器和芯片之间串一个RC低通滤波器例如1k电阻加10nF电容能有效滤除高频共模噪声这对提高有效分辨率帮助极其明显。2.2 SPI模式选择的原理解释ADS1220的数据手册关于SPI时序有明确规定数据在SCLK的下降沿变化在SCLK的上升沿被主控采样时钟空闲状态为低电平。对照STM32的SPI模式定义CPOL0空闲低电平、CPHA1第二个边沿采样正好匹配也就是所谓的SPI Mode 1。这里展开说一下原因。SPI通信有四种模式组合CPOL决定时钟空闲电平CPHA决定数据采样沿很多芯片用Mode 0CPOL0CPHA0也可以工作但ADS1220的时序要求是SCLK下降沿输出数据、上升沿锁存数据也就是说主控必须在SCLK上升沿读取MISO上的数据。如果CPHA配错了读回来的数据会错开半个位周期经常出现LOW位和HIGH位反了或者整体错位移最终表现是读回的寄存器值对不上数据完全乱掉。遇到过不少朋友在F103的标准库初始化里把CPOL配成0、CPHA配成0读寄存器时偶尔数据还突然正常其实那都是侥幸遇到快速连续读取或高温环境就出问题。再看时钟频率。ADS1220数据手册给出的最大SCLK频率通常是4MHz左右超了会导致通信不稳定。STM32的SPI1挂在APB2总线上APB2为72MHz经过分频之后可选的最小分频是2分频也就是36MHz这显然太高4分频是18MHz依然超限最后选16分频得到4.5MHz略微超标但实测可用更稳妥的选法是32分频得到2.25MHz。博主个人习惯直接选8分频9MHz也会报错吗实测部分ADS1220样品在高温下可能出现偶发数据错误所以最终建议直接用16分频或32分频。一次读24位数据在2MHz的时钟下仅需12微秒完全不影响采样速率没必要为了追求高时钟去冒不稳定风险。2.3 片选信号软件片选和硬件片选怎么选STM32的SPI外设支持硬件片选NSS硬件管理和软件片选SSM1 SSI控制两种模式。ADS1220项目里强烈建议用普通GPIO软件片选原因有两个。第一硬件片选模式下NSS引脚由SPI外设自动控制但时序上有时会提前拉高或滞后拉高ADS1220这种对CS时序有明确要求的从机容易出问题。第二ADC的SPI通信不是连续的每次通信都需要启停用GPIO控制CS非常直接——通信前拉低通信结束拉高逻辑完全透明排查问题方便。软件片选的配置方法很简单把NSS引脚配置为普通推挽输出GPIOSPI配置时设置SSM1、SSI1忽略硬件NSS然后在每次通信前手动把CS拉低通信完拉高。注意拉高前必须确保SCK停止在空闲电平即低电平否则CS拉高的瞬间SCK的跳变会被从机误认为时钟导致状态机错乱。博主在STM32CubeMX里配置SPI时额外把CS引脚单独定义一个宏这样做的好处是代码可读性更好未来更换SPI引脚时也不用改SPI外设配置。3. 寄存器配置与代码实现详解3.1 ADS1220寄存器逐个击破ADS1220有四个配置寄存器每个寄存器8位通过SPI发送命令字节来访问。很多朋友第一次接触时容易被命令机制绕晕这里掰开揉碎讲。先说命令机制。当前设计下写入寄存器用WREG命令命令字节的格式是0010_rrrr_xx其中rrrr为目标寄存器号最后一个字节是要写入的数据00是命令编码。读取寄存器用RREG命令格式是0010_rrrr_xx注意这里读和写的高4位不同实际是0010写、0010读这里需要区分ADS1220的命令字节最高位bit7和bit6分别是1和0表示写0和1表示读不同芯片命令编码有区别务必以手册为准。博主的记忆技巧是直接查手册表格而不是硬背防止混淆。四个寄存器分别是寄存器功能概括常见配置位配置寄存器0输入多路复用器、增益MUX[3:0]选择通道PGA[2:0]选择增益配置寄存器1数据速率、工作模式DR[3:0]控制速率MODE选择连续转换或单次配置寄存器2基准源、PGA旁路、滤波器VREF选择内部基准或外部BYPASS是否旁路PGA配置寄存器3激励电流源、传感器检测IDAC[1:0]PSWTS温度传感器开关以电流采样项目为例如果输入信号来自采样电阻两端电压可能只有几十毫伏需要开启PGA一般PGA设为32或64。若传感器是电桥输出差分信号范围大概在0-100mVPGA32时满量程输入约100mV正好匹配。通道选择方面如果接的是AIN0和AIN1MUX配置为0000即差分AIN0-AIN1。数据速率设为20SPS或者40SPS比较合适这经过50Hz工频干扰抑制20SPS的FIR滤波器对工频及谐波有-100dB以上的抑制能力实测效果明显优于100SPS。3.2 初始化SPI和ADS1220的完整代码下面直接给出用户在STM32上可用的完整初始化代码这里以STM32 HAL库为例标准库也类似只需要替换底层寄存器操作即可。首先是GPIO和SPI初始化CubeMX里直接配置这里贴出关键配置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_2EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_16; // 4.5MHz够用 hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE; HAL_SPI_Init(hspi1);注意CLKPolarity和CLKPhase的取值。上面配置对应CPOL0、CPHA1也就是SPI Mode 1这一点绝对不要配错。然后是ADS1220写寄存器和读寄存器的基础函数#define ADS1220_CS_LOW() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET) #define ADS1220_CS_HIGH() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET) void ADS1220_WriteRegister(uint8_t reg, uint8_t value) { uint8_t cmd 0x40 | (reg 2); // WREG命令0010 rrrr 00 uint8_t data[2] {cmd, value}; ADS1220_CS_LOW(); HAL_SPI_Transmit(hspi1, data, 2, 10); ADS1220_CS_HIGH(); } uint8_t ADS1220_ReadRegister(uint8_t reg) { uint8_t cmd 0x20 | (reg 2); // RREG命令0010 rrrr 00此处为示意请核对手册 uint8_t recv 0; ADS1220_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); HAL_SPI_Receive(hspi1, recv, 1, 10); ADS1220_CS_HIGH(); return recv; }寄存器配置函数这里推荐一套适合普通电压采集的通用配置应用可根据实际场景修改void ADS1220_Init(void) { ADS1220_WriteRegister(0x00, 0x00); // MUXAIN0-AIN1差分PGA1看需求改 ADS1220_WriteRegister(0x01, 0x04); // 连续转换模式20SPS ADS1220_WriteRegister(0x02, 0x10); // 内部基准开启PGA不旁路FIR滤波 ADS1220_WriteRegister(0x03, 0x00); // 不用电流源和温度传感器 // 等待内部上电稳定 HAL_Delay(10); }3.3 数据读取与采样点公式换算ADS1220正常工作后一次数据读取时序是这样的启动连续转换发送START命令0x08等待DRDY引脚拉低然后CS拉低发送RREG命令读取输出寄存器三字节或者通过NOP命令读数据。在连续转换模式中推荐在DRDY拉低后发送RDATA命令0x10读取三个字节这是数据手册推荐的读取方式。实际代码可以这样写uint32_t ADS1220_ReadADC(void) { uint8_t rx[3] {0, 0, 0}; uint8_t cmd 0x10; // RDATA命令 uint32_t raw 0; // 等待DRDY拉低 while (HAL_GPIO_ReadPin(DRDY_GPIO_Port, DRDY_Pin) ! GPIO_PIN_RESET); ADS1220_CS_LOW(); HAL_SPI_TransmitReceive(hspi1, cmd, rx, 3, 10); // 发送命令同时接收数据 ADS1220_CS_HIGH(); raw ((uint32_t)rx[0] 16) | ((uint32_t)rx[1] 8) | rx[2]; // 24位有符号数转int32 if (raw 0x800000) { raw | 0xFF000000; } int32_t adc_val (int32_t)raw; return adc_val; }采样数据的换算公式是项目里的核心点很多朋友读回原始码值之后不知道如何变成电压。ADS1220输出是二进制补码格式转换结果和输入电压的关系如下对于增益为PGA、输入范围从0到VREF的情况下ADC码值和电压的关系差分输入时满量程对应-0.5VREF到0.5VREF码值LSB电压 VREF / (PGA * 2^23)实际差分电压 (ADC_Code / 2^23) * (VREF / PGA)举一个实际例子。假设VREF2.048V内部基准PGA64读回ADC码值为838860大约满量程的十分之一实际差分电压 (838860 / 8388608) * (2.048 / 64) ≈ 0.0032V 3.2mV。如果传感器是采样电阻再除上电阻值就能得到电流值。注意ADS1220内部基准默认是2.048VREFOUT引脚和基准配置相关若配置了外部基准要用外部基准的实际电压和分压网络计算。3.4 连续转换模式与单次转换模式的取舍ADS1220支持连续转换和单次转换两种工作模式。配置寄存器1的MODE位决定模式MODE0为连续转换MODE1为单次转换。连续转换模式的好处是芯片始终在跑ADCDRDY按固定数据速率拉低STM32只需要在中断里等待DRDY事件读取即可。这个模式适合需要持续监测场景比如实时电流监控、连续温度采集。缺点是功耗偏高在电池供电设备里不友好。单次转换模式的流程是写入START命令0x08等待DRDY拉低读取结果然后芯片自动进入低功耗待机状态。这种模式的功耗非常低适合长时间无人值守的传感节点。博主自己的经验是对于需要用相同增益和通道配置做持续监测的场景连续转换模式配合DRDY中断更省事如果项目对功耗敏感或者通道需要频繁切换单次转换模式更好控制。4. 高级优化与工程实践4.1 用DRDY外部中断替代轮询读取最简单的读法是在主循环里while等DRDY代码写起来方便但有个致命问题如果主循环里有其他耗时操作比如串口打印、Flash写操作、按键扫描等待过程中很容易把DRDY的新周期错过——虽然数据不会丢但数据读取的时间点变得不可控尤其是需要精确采样时间戳的场合误差会直接影响测量结果。推荐的做法是把DRDY引脚配置为下降沿触发的外部中断。当ADS1220完成一次转换并将新数据锁存到输出寄存器时DRDY引脚拉低中断服务函数被触发在ISR里直接读出三字节数据。这样可以做到转换完成立即读取时间抖动可以控制在几个微秒以内。示例代码void EXTI3_IRQHandler(void) { if (__HAL_GPIO_EXTI_GET_IT(DRDY_Pin) ! RESET) { __HAL_GPIO_EXTI_CLEAR_IT(DRDY_Pin); adc_raw ADS1220_ReadADC(); adc_flag 1; } }主循环里判断adc_flag置位后处理数据并做滤波。这样主循环的耗时操作不会影响采样时序。4.2 数据滤波去极值均值与一阶低通ADS1220本身带有数字滤波器在20SPS和40SPS速率下对工频干扰抑制效果很好但在工业现场传感器本体振动、引线耦合的电磁干扰依然会带来随机噪声。此时软件滤波非常关键。博主在线性传感项目里常用的组合是“去极值均值”加“一阶低通”。去极值均值是把连续采到的12个样本去掉最大值和最小值剩余10个取平均能有效消除毛刺。如果感觉平均后依然有缓慢漂移则再叠加一阶低通filtered_value alpha * (float)raw_value (1.0f - alpha) * filtered_value;alpha的值取0.2到0.4之间效果比较合适。alpha太大则滤波效果弱太小则响应滞后明显。对于慢速温度或液位测量alpha取0.1对于动态电流显示alpha取0.3比较平衡。4.3 工程上要避开的坑第一点不要在等待DRDY的过程中修改ADS1220的寄存器配置。因为芯片在转换过程中接收配置命令会导致当前转换结果作废或者芯片内部状态机错乱后面连续读出的数据全是错的。稳妥的顺序是停止转换发送STOP命令0x0A等待当前转换周期结束再写寄存器然后重新启动转换。第二点SPI收发函数不能放在中断和主循环同时调用否则可能出现数据竞争。博主一般是在DRDY中断里只置标志位在主循环里读数据、写寄存器这样彻底避开多线程问题。第三点ADS1220的上电稳定时间不能省。上电之后至少要延时50ms再开始配置寄存器部分精度要求高的设计甚至加500ms预热确保内部基准源稳定。如果上电后立刻读数据前几个值往往是漂移的。5. 常见问题与排查技巧5.1 SPI通信完全无响应的检查思路频繁遇到的现象是DRDY引脚从未拉低或者读寄存器返回全是0xFF这说明SPI通信链路不通排查步骤按优先级排序检查项方法常见误区供电万用表测量AVDD、DVDD均为3.3VAVDD悬空或压差过大SPI模式示波器抓SCK和MOSI波形确认CLKPolarity和CLKPhaseCPOL/CPHA配反CS时序示波器确认每次通信前CS能拉低CS被配置成了复用功能导致一直拉低引脚连接万用表通断档查SCK、MISO、MOSI连线抄板时搞混MISO和MOSI电平匹配确认从机供电与主控IO电压一致5V供电时SPI引脚与3.3V主控不匹配5.2 读回来的数据永远是0x7FFFFF或0x800000这个现象通常是PGA配置不当导致信号饱和。如果输入信号超过量程ADC就会输出接近满码或者负满码的值呈现0x7FFFFF或0x800000。解决办法是降低PGA增益或者调整传感器激励电压减小输出范围。另外还有一种原因通道选择错误。比如实际信号接在AIN2和AIN3上但寄存器0里的MUX配置仍然是AIN0-AIN1那么读回的数据自然不是期望信号也可能因为该通道悬空导致码值漂移到满量程附近。用万用表量一下对应引脚是否有信号很重要。5.3 数据抖动过大如何解决如果读回的数据在低位跳动幅度超过几十个码值首先检查参考电压的稳定性。内部基准虽然温漂低但是电源纹波会直接耦合到基准上。在AVDD引脚加高品质的100nF陶瓷电容最好再并联一个10uF的钽电容同时保证GND走线短而粗。其次检查传感器引线是否和电源线平行走线这是串扰的主要来源。对于PGA开启的高增益场景参考电压必须使用低噪声源。如果使用外部基准REF5025这类芯片需要在基准输出引脚加10uF的储能电容并且尽可能靠近ADS1220的VREF引脚放置。5.4 关于采样点计算公式再补充一点热词里有“采样点计算公式”在这个项目里它指的是ADC码值到物理量的换算。再回顾一下完整步骤读取24位ADC码值转成int32类型如果是差分输入用公式计算电压电压再乘传感器灵敏度系数得到物理量例如用0.01欧姆采样电阻测电流PGA64VREF2.048V采集到码值code则电流值I (code / 8388608) * (2.048 / 64) / 0.01。这里单位要注意0.01欧姆电阻两端电压差在1A电流时只有10mVPGA64放大后是0.64V完全在量程内。如果PGA配低了电压差太小有效分辨率不够用电流测量误差就会很大。6. 个人经验总结与扩展想法这个ADS1220采样项目我从最初在面包板上搭电路到后来画进正式产品PCB前前后后改了好几版。第一版用的是标准库直接操作寄存器代码虽然精简但可读性差换人维护困难第二版改用HAL库代码清晰但HAL的SPI收发函数需要小心超时参数在中断里使用必须把超时设得短一些否则卡住整个系统。最终定版的架构是DRDY外部中断加主循环处理数据HAL库的SPI只用于主循环上下文不涉及中断重入稳定性实测非常好。关于扩展方向ADS1220内部那两个可编程电流源IDAC可以利用起来驱动PT100热电阻实现两线制温度测量一路电流源给PT100供电另一路给高精度电阻供电做参考四线制补偿后测温精度非常高。这块博主最近正在验证后续有结果再单独记录。另外如果你的板子需要同时采样多个模拟通道可以用STM32的内部定时器触发SPI配合DMA在中断里不耗CPU完成三字节读取——不过对ADS1220来说DRDY中断读三字节本身只用十几微秒一般场景没必要上DMA如果项目里已经存在CAN或以太网等高负载中断再考虑用DMA把CPU释放出来。最后分享一个小细节ADS1220的SPI命令中有一个NOP指令0x00它不会改变芯片状态只产生时钟。调试的时候如果怀疑SPI时序有问题可以发一条NOP命令然后用示波器观察DOUT引脚上的时钟对齐情况能非常直观地验证硬件连接是否正确。这个小技巧救过我不少次节省了大量排查时间。本文还有配套的精品资源点击获取
返回列表