资讯详情

资讯详情

STM32F103驱动ADS1220高精度电压采集实战

简介此工程包面向STM32开发者与高精度模拟量采集项目演示STM32F103通过SPI接口读取ADS1220高精度24位Σ-Δ型ADC芯片实现多路电压数据的采集、换算与输出。整个压缩包共308个文件大小约5.12MB文件类型覆盖87个C源文件、84个头文件、32个编译中间文件与31个crf列表文件最终生成hex/axf可执行镜像此外还包括ADS1220驱动目录、STM32标准外设库CMSIS与FWlib、Keil工程配置uvprojx/uvoptx以及用于清理编译中间文件的批处理工具整体归类清晰、便于移植。已有2237人学习下载。资源内按02_DOC文档、04_DRIVE驱动、05_FWlib固件库、07_Output输出等模块划分从硬件接口连接、SPI寄存器配置到二进制ADC码值向实际电压的换算形成完整闭环无论是初次接触高精度ADC的嵌入式入门者还是需要快速搭建多通道电压采集方案的工程师都可以直接参考甚至复用底层驱动与主程序代码。 做过不少采集类的项目我越来越觉得“高精度电压测量”是很多嵌入式方案的隐形瓶颈。普通单片机自带的12位ADC配上3.3V参考电压理想分辨率也就0.8mV左右真要去测称重传感器、热电偶、流量计输出的微弱信号根本不够用。ST的STM32F103和TI的ADS1220这个组合算是我个人在低成本、高精度电压采集项目里用得最顺手的一套方案一颗24位低功耗Delta-Sigma ADC通过SPI挂在STM32上既能满足微伏级分辨率的需求又不用把成本抬高到工业采集卡的级别。这篇文章就把我在STM32F103上驱动ADS1220做高精度电压采集的完整过程写出来包括硬件连接、寄存器配置、SPI时序、电压换算公式和实测踩坑记录给正在做传感器采集或者毕业设计的朋友一份可以直接抄作业的参考。1. 为什么用STM32F103加ADS1220做电压采集1.1 普通MCU内部ADC不够用在哪在解释选型之前得先说清楚一个常被新手忽略的问题单片机的内置ADC到底差在哪里以STM32F103为例它的ADC是12位分辨率参考电压一般直接接VDDA也就是3.3V。那么理论上每个LSB代表的分辨率是3.3V除以4096算下来约等于0.8mV。这还只是理论值实际使用中受电源纹波、参考电压噪声、PCB布局等因素影响有效位数往往到不了12位能有10位就不错了。如果测量的信号是0到3.3V的电压0.8mV的分辨率勉强够用但如果是称重传感器满量程输出只有几毫伏或者热电偶在常温附近的信号变化只有几十微伏那MCU内部ADC基本什么都看不出来。一个典型例子称重传感器满量程2mV/V5V激励下满量程输出是10mV如果要求测出千分之一的重量变化对应就是10微伏的电压变化12位ADC完全做不到。这时候就需要外挂高分辨率ADC把微弱信号先放大、再数字化。1.2 ADS1220的关键特性与选型理由ADS1220是TI推出的一款24位、低功耗、Delta-Sigma型ADC专门为传感器测量场景设计。它最核心的几个特点一是分辨率24位输出码率最高到2000SPS二是内部集成可编程增益放大器PGA增益范围从1倍到128倍可选可以放大微弱信号三是内置2.048V低漂移参考电压省掉了外部基准源四是有4路模拟输入可以配置成差分输入或者单端输入五是SPI接口跟单片机连接非常方便。选型的时候我对比过ADS1220和ADS1256。ADS1256是24位8通道性能更好但价格贵不少而且本身不带PGA和内部参考用起来要外接一堆器件。ADS1220价格便宜自带PGA和参考对于单通道或双通道差分测量性价比特别高。如果你的项目只需要测一两路信号ADS1220是更务实的方案。网上还有人拿它和国产的HX711对比HX711主要针对称重类应用24位分辨率但内部没参考源、PGA增益档位少通用性不如ADS1220配STM32做通用电压采集的话ADS1220更合适。1.3 硬件连接与PCB布板要点ADS1220的封装有TSSOP-16和QFN-16建议用TSSOP封装手工焊接更方便。和STM32之间的连接线有6根CS、SCLK、DIN、DOUT、DRDY、以及电源和地。SPI通信是标准的4线制主从设备连接方式如下表ADS1220引脚功能连接目标SCLKSPI时钟输入STM32 SPI1_SCK (PA5)DINSPI数据输入单片机写STM32 SPI1_MOSI (PA7)DOUTSPI数据输出单片机读STM32 SPI1_MISO (PA6)CS片选低有效STM32任意GPIO建议PA4DRDY数据就绪低电平有效STM32任意GPIO建议PA8电源方面ADS1220支持模拟电源AVDD、数字电源DVDD分开供电也可以直接连到一起我一般AVDD和DVDD都接3.3V。关键点在于去耦电容要贴近电源引脚放置模拟电源和数字电源之间加磁珠或0欧电阻模拟地和数字地单点连接。参考电压如果使用内部2.048VAVDD必须足够干净否则噪声会直接串进参考源影响转换精度。PCB布板时模拟输入信号线尽量远离SPI时钟线减少串扰如果信号源阻抗很高建议在输入端并联一个0.1uF电容到地配合ADC内部的输入采样电容提升稳定性。2. ADS1220寄存器配置与初始化流程2.1 四个配置寄存器怎么设置ADS1220内部有4个可配置寄存器寄存器地址从0x00到0x03每个8位。这4个寄存器控制着输入通道、PGA增益、数据速率、参考电压源、转换模式和电流源等参数配置错误会导致测量完全不对。我逐个说明一下常用位的含义。配置寄存器0地址0x00主要设置输入多路复用器和PGA增益。其中bit7到bit5决定输入通道选择000表示AIN0和AIN1组成差分输入001表示AIN2和AIN3组成差分输入这两档最常用。bit4到bit1设置PGA增益0000是1倍0001是2倍0010是4倍0011是8倍以此类推到128倍。需要注意PGA增益越高输入范围越小内部2.048V参考下PGA1时满量程是±2.048VPGA4时满量程只有±0.512V小信号放大能力虽然强了但大信号会直接超量程。配置寄存器1地址0x01的bit7和bit6设置工作模式00为正常模式01为涡轮模式10为低功耗模式。bit5到bit0设置数据速率不同模式对应不同的速率表正常模式下2对应90SPS4对应330SPS6对应1000SPS。数据速率越低数字滤波器对噪声的抑制越好测量越稳定速率越高响应越快但噪声越大。做电压采集我一般选90SPS噪声和速度比较均衡。配置寄存器2地址0x02的bit7和bit6选择参考电压源00为内部2.048V参考01为外部REFP和REFN引脚输入10为直接使用AVDD和AVSS。bit5使能内部温度传感器一般不用保持0。bit4选择转换模式0为单次转换模式1为连续转换模式。bit3和bit2配置电流源。温度传感器和电流源平时用不到所以参考源选内部2.048V时寄存器的值通常写0x00。配置寄存器3地址0x03主要是IDAC电流源输出路由和内部比较器配置常规电压采集全部写0x00即可。如果需要驱动RTD等传感器才需要关注这个寄存器。2.2 完整初始化与单次转换流程ADS1220的SPI通信是单向命令式的主机向DIN写入命令字节从DOUT读取数据。整个初始化流程分成四步复位、配置寄存器、发送启动转换命令、等待DRDY变低最后读取转换结果。复位操作很简单片选拉低发送0x06命令字节然后拉高片选。复位之后等待至少10ms让ADC内部完成上电复位。配置寄存器时使用写寄存器命令0x40加上寄存器地址左移一位。例如要写配置寄存器0命令字节就是0x40 | (0x00 1) 0x40写配置寄存器1就是0x42。写法固定的套路是先发命令字节再发送一个字节表示要写入的寄存器个数减1和TI其他ADC一致0表示写1个寄存器最后发送寄存器数据。启动单次转换命令字节是0x08。发送完启动命令后ADS1220开始采样转换转换完成时DRDY引脚自动拉低。这里关键点在于必须等DRDY变低才能读数据不能靠延时盲读否则可能读到未完成的转换数据或者旧数据。读取转换结果的命令是0x10发送这个命令之后连续读取3个字节分别是24位数据的最高字节、中间字节、最低字节。读完数据后DRDY自动拉高等待下一次转换完成。2.3 一个实用的寄存器配置实例以典型的高精度电压采集场景为例我常用的配置是AIN0和AIN1差分输入、PGA增益4倍、正常模式90SPS、内部2.048V参考、单次转换模式。寄存器值如下寄存器地址写入值含义配置寄存器00x000x20差分输入AIN0-AIN1PGA4配置寄存器10x010x02正常模式90SPS配置寄存器20x020x00内部2.048V参考单次转换配置寄存器30x030x00关闭IDAC电流源as 0x20的二进制是0010 0000其中bit5对应PGA增益为4倍。这时候满量程输入范围是±512mV适合测量桥式传感器、热电偶这类小信号。如果测量对象是0到2V的电压信号需要把PGA改为1倍配置寄存器0改成0x00这样满量程就是±2.048V。这一点必须根据实际信号范围提前算好不然很容易出现超量程削顶的问题。3. STM32F103的SPI驱动与电压换算3.1 SPI初始化配置与参数计算ADS1220的SPI通信有明确要求CPOL为低电平CPHA为第二个边沿采样也就是SPI mode 1。数据位宽8位MSB先传输最大SPI时钟频率4MHz。在STM32F103上SPI1挂在APB2总线上系统时钟72MHz时SPI1外设时钟也是72MHz。为了留足余量我选择64分频SPI时钟为72MHz除以64等于1.125MHz低于4MHz上限通信稳定。另一个容易踩坑的地方是NSS引脚。ADS1220的CS由普通GPIO控制不依赖STM32的硬件NSS所以SPI配置要设置为主模式、软件NSS管理。初始化代码基于STM32CubeMX生成的HAL库修改核心配置如下SPI_HandleTypeDef hspi1; void MX_SPI1_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_2EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_64; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; HAL_SPI_Init(hspi1); }CLKPolarity和CLKPhase这两个参数写错的话读回来的数据就是0xFF或者错位数据这是和ADS1220通信最容易出的问题之一。我建议第一次调试时先用逻辑分析仪抓一下SCLK和DOUT的波形确认时序是否符合预期再继续往下调。3.2 寄存器读写和转换数据读取代码SPI单字节收发函数我直接用HAL库的TransmitReceive一次同时完成发送和接收。对于ADS1220这种半双工性质的通信主机发命令字节的同时需要读回数据所以用TransmitReceive是最省事的uint8_t ADS1220_SpiTransfer(uint8_t byte) { uint8_t rx 0xFF; HAL_SPI_TransmitReceive(hspi1, byte, rx, 1, 100); return rx; }写寄存器函数遵循ADS1220的WREG命令格式void ADS1220_WriteReg(uint8_t regAddr, uint8_t regValue) { ADS1220_CS_LOW(); ADS1220_SpiTransfer(0x40 | (regAddr 1)); // WREG命令 ADS1220_SpiTransfer(0x00); // 只写1个寄存器 ADS1220_SpiTransfer(regValue); ADS1220_CS_HIGH(); }启动转换并等待DRDY变低的函数void ADS1220_Start(void) { ADS1220_CS_LOW(); ADS1220_SpiTransfer(0x08); // START命令 ADS1220_CS_HIGH(); } uint8_t ADS1220_WaitDRDY(uint32_t timeout) { while (HAL_GPIO_ReadPin(DRDY_GPIO_Port, DRDY_Pin) ! GPIO_PIN_RESET) { if (--timeout 0) return 1; // 超时 Delay_us(10); } return 0; }读取24位转换结果的函数要特别注意符号扩展。ADS1220输出的是二进制补码格式最高位是符号位。存到int32_t时如果24位数据的最高位为1必须手动把高8位填充为0xFF否则负数会被当成正数处理int32_t ADS1220_ReadConversion(void) { uint8_t buf[3]; int32_t raw; ADS1220_CS_LOW(); ADS1220_SpiTransfer(0x10); // RDATA命令 buf[0] ADS1220_SpiTransfer(0xFF); buf[1] ADS1220_SpiTransfer(0xFF); buf[2] ADS1220_SpiTransfer(0xFF); ADS1220_CS_HIGH(); raw ((int32_t)buf[0] 16) | ((int32_t)buf[1] 8) | buf[2]; if (raw 0x800000) raw | 0xFF000000; // 24位转32位补码符号扩展 return raw; }主循环里的读取逻辑是发送START等待DRDY拉低读取转换结果做电压换算。如果想连续测量可以把配置寄存器2的bit4改为1配置成连续转换模式这样就不需要每次都发START命令DRDY每次数据就绪都会拉低一次读数据更简单。3.3 原始码换算实际电压的公式与滤波处理拿到24位原始码之后最核心的一步就是换算成实际电压。ADS1220是双极性ADC采集范围是正负满量程对应到24位补码就是从-8388608到8388607。实际电压换算公式如下Vin (raw / 2^23) × (VREF / PGA)其中VREF是参考电压PGA是增益倍数。如果使用内部参考VREF 2.048V增益PGA 4满量程就是±0.512V。举个例子读取到的原始码是0x320000十进制是3276800代入公式Vin 3276800 / 8388608 × (2.048 / 4) 0.2V这个0.2V就是实际的输入电压。如果使用外部参考电压把VREF替换成外部基准的实际电压值即可。我用这个公式验证过多次换算结果和精密万用表读数基本吻合。软件滤波方面单次采样结果会有一定的随机噪声尤其是PGA增益高或者数据速率高的时候。我常用的处理方式是先做中值滤波再去均值连续采11个点去掉最大值和最小值剩下9个取平均。这样既能抑制脉冲干扰又能平滑随机噪声实测下来比单纯滑动平均效果好不少。滤波后的数据再代入上面的公式换算电压显示出来的数值非常稳定。需要注意滤波必须针对原始码做不要先换算成电压再滤波否则浮点运算会引入额外的舍入误差。4. 实测数据与高频踩坑记录4.1 实测数据短路噪声和基准源测试硬件焊接完成之后第一件事不是接传感器而是把所有配置跑通之后做两个基础测试输入短路噪声测试和基准电压测试。把AIN0和AIN1直接短接此时输入信号为零。理论上转换结果应该一直是0实际会有少量噪声。我实测在PGA 4、90SPS配置下原始码在±3 LSB左右跳动按照公式计算每个LSB对应约0.122μV也就是说峰峰值噪声不到0.8μV这个水平对大部分传感器应用已经非常理想。如果噪声明显偏大比如几十个LSB跳动优先检查电源去耦和地线布局。基准电压测试我用了一个精密可调电压源输出1.0000V接到AIN0和AIN1之间。不过注意PGA 4时满量程只有±0.512V1V会超量程所以这个测试要先配置成PGA 1。实测原始码在0x3E8000附近跳动对应电压约1.0000V和设定值的误差在0.01%左右。这个误差主要来自内部参考电压的初始精度如果要求更高精度可以使用外部基准源做软件校准或硬件换参考芯片。4.2 常见问题排查速查表和ADS1220打交道的这几次项目里我归纳了几个出现频率特别高的问题列成一张排查表遇到问题可以对照检查问题现象可能原因解决方法SPI读回数据全是0xFFCPOL/CPHA配置错误CS没有正确拉低焊接虚焊检查SPI时钟极性相位设置为mode 1用示波器量CS和SCLK波形补焊ADS1220引脚DRDY一直为高不拉低没有发送START命令之前的通信配置有误芯片处于复位状态确认发送过0x08命令重新上电后先复位再配置寄存器检查AVDD供电是否正常读数跳动很大电源纹波大参考电压不稳模拟输入线过长电源引脚加100nF10uF去耦电容模拟输入用屏蔽线或加RC滤波降低数据速率读到的数值始终为固定值输入通道配置错误CS时序不对导致配置没写进去核对配置寄存器0的MUX位是否选择了正确的输入通道读寄存器验证写入值电压换算结果与实际严重不符满量程计算错误PGA增益设置和换算公式不一致确认PGA值代入公式参考电压和增益必须与配置一致检查VREF数值填的是2.048还是外部基准4.3 几个容易被忽略的稳定性和精度细节有一个细节我一开始没注意导致数据稳定性一直上不去STM32和ADS1220共地的问题。如果ADS1220的模拟地和STM32的数字地之间阻抗较大数字信号回流会穿过模拟区域在ADC的参考地和信号地之间产生压差最终表现为转换结果上的随机噪声。后来我把模拟地和数字地改成了单点连接并且让所有模拟信号回路尽量短问题立刻改善了很多。还有一个是DRDY引脚的读取方式。很多教程用延时等待转换完成再读数据我强烈不建议这么做。温度和电压的变化会影响ADC实际的转换时间固定延时可能偏短或偏长偏短会读到上一帧数据偏长会降低采样率。DRDY引脚就是干这个用的一定要用阻塞查询或者外部中断来触发读取这是保证数据时序准确的最可靠方式。电源方面也提一句ADS1220内部参考源的噪声和AVDD质量强相关。如果板子上有继电器、电机这类大电流负载AVDD一定要单独用LDO供电不能直接和数字3.3V共用否则转换结果会周期性出现尖刺噪声。我的经验是ADS1220的供电越干净后期软件滤波的负担就越小。最后再分享一个调试的小技巧。ADS1220支持回读寄存器内容调试的时候写一个简单的读寄存器函数确认写入值和读出值一致能快速排除SPI通信和寄存器配置的问题。我遇到过两三次读数不对的故障排查到最后都是通信时序问题回读寄存器功能让整个调试过程省了很多时间。先用回读确认寄存器没问题再接传感器处理问题的思路就清晰多了。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →