ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

STM32通过SPI读取ADS1220 24位ADC完整方案

STM32通过SPI读取ADS1220 24位ADC完整方案 简介本资源是一套面向嵌入式开发工程师与STM32进阶学习者的ADS1220高精度ADC驱动实战代码聚焦STM32通过硬件SPI接口完成对TI 24位低噪声ADC芯片的可靠采样与配置。资源解决了工业传感场景中如温度、压力等慢变信号高分辨率数据采集的软硬件协同难点涵盖寄存器级配置、时序控制、PGA增益设置及连续/单次转换模式实现。压缩包含1050个文件主体为593个C源码与285个头文件实现SPI初始化、ADS1220命令封装、数据解析与校准逻辑辅以78个汇编启动文件、46个IAR工程配置及多个ARM Cortex-M4数学库静态链接文件如libarm_cortexM4lf_math.a整体30.18MB结构完整适配主流开发环境。已有729人下载学习提供可直接移植的HAL库裸机双模式参考、详尽寄存器操作注释及典型错误处理机制显著降低高精度ADC接入门槛。 做24位ADC采样的时候我第一个想到的就是ADS1220。这颗芯片在工业称重、温度采集、电桥信号处理这些场景里出现频率极高24位分辨率、内置PGA和基准源、一路SPI就能搞定通信集成度相当高。之前有朋友问我为什么放着STM32自带的ADC不用非要外挂它这其实是个很现实的问题——很多模拟小信号的精度要求内置12位ADC根本接不住。这篇博文就围绕STM32通过SPI读取ADS1220的完整方案来写从硬件接线、寄存器配置、采样点计算到可直接抄的源代码再附上我实际调试中踩过的一些坑给正在做类似项目的朋友一个参考。1. 项目整体思路为什么选ADS1220而不是直接怼ADC1.1 内置ADC的局限性在哪里STM32的ADC默认是12位分辨率虽然F3系列有16位但对于真正需要高精度的场景还是不够看。比如称重传感器输出只有2mV/V左右加载满量程时差分信号也不过10mV上下12位ADC配合外部基准源在5V供电下每个LSB大约1.2mV做10mV量程的测量有效分辨率只有不到4位误差不可接受。ADS1220解决的是这类典型问题24位分辨率把LSB直接拉到微伏级内部PGA最高支持128倍增益可以把毫伏级别的传感器信号放大到ADC满量程附近再量化。芯片还内置了可编程电流源可以直接给RTD电阻测温提供激励省掉外围恒流源电路这在板子空间紧张的场合非常有用。1.2 ADS1220的核心参数和选型对比先列一下ADS1220的主要特点方便新手理解这块芯片到底能干什么参数ADS1220分辨率24位无失码通道数4路复用输入可配差分或单端PGA增益1, 2, 4, 8, 16, 32, 64, 128采样率20 SPS 到 2000 SPS 可编程内部基准2.048V精度0.05%通信接口SPI支持3线或4线供电范围模拟2.3V~5.5V数字1.8V~3.6V电流源2路可编程IDAC100uA~1500uA同类24位ADC里ADS1256和AD7190也是常被拿来比较的型号但ADS1220的优势在于封装小TSSOP-16、功耗低、内置基准和电流源外围电路极其简单。对于一般两路电桥或三线RTD测温需求一片ADS1220全搞定。1.3 适用场景判断我个人的经验是遇到以下情况优先考虑ADS1220:传感器输出信号幅度在几毫伏到几十毫伏之间需要放大器信号源是差分的比如电桥、称重传感器、热电偶板子空间小不想额外搭仪表放大器和基准源系统里有干扰需要内置50Hz/60Hz数字滤波如果是采集高速动态信号比如振动、音频、电机相电流这种带宽高、采样率要求几十kHz的场合ADS1220就不合适了2000SPS上限注定它只能用于直流或低频慢变信号。选型之前先把自己信号的带宽需求想清楚别拿着24位就当万能药。2. 硬件连接与SPI通信设计2.1 引脚定义与接线表ADS1220引脚不多典型接法如下以STM32F103C8T6为例STM32引脚ADS1220引脚说明PA5SPI1_SCKSCLKSPI时钟PA7SPI1_MOSIDIN数据输入PA6SPI1_MISODOUT数据输出PA4SPI1_NSSCS片选软件IO控制也可任意GPIODRDY数据就绪标志查询或外部中断3.3VDVDD数字供电3.3V或5VAVDD模拟供电按传感器供电需求GNDGND公共地2.048V内部基准REFP0/REFN0或使用外部基准建议DRDY一定接出来不能省。连续转换模式下DRDY拉低表示新数据已经准备好此时才能读数据寄存器。如果跳过DRDY只靠延时去读数据可能被读了一半而下一组已经覆盖或者读到不完整的24位。2.2 SPI模式选择为什么是Mode 3ADS1220的SPI时序要求SCLK空闲时为高电平数据在SCLK下降沿从DOUT输出在上升沿被DIN锁存。这个时序对应SPI Mode 3也就是CPOL1、CPHA1。STM32 HAL库初始化时这样配置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;这里必须注意网上有些ADS1220例程用的Mode 1也能跑那是因为某些STM32的SPI相位配置配合时序刚好误打误撞能读到数据但数据稳定性很差尤其是时钟频率提高之后。正规做法就是按数据手册时序来Mode 3一次传8位。2.3 硬件片选和软件片选的取舍ADS1220的CS引脚其实有两个用处一是作为SPI片选二是配合START引脚实现同步转换。如果系统中只有一个SPI从机完全可以把CS接地省一路GPIO操作时不用管片选逻辑。这就是所谓“硬拉低”的玩法。我的建议是如果SPI总线上只有ADS1220直接CS接地最省心。如果有多个SPI设备就分配一个GPIO做软件片选注意GPIO速度选High否则片选拉低信号边缘太缓可能被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)2.4 电源和基准电压的坑ADS1220的模拟供电AVDD和数字供电DVDD我建议分开走线尤其AVDD要加10uF和0.1uF去耦电容尽量靠近VDD引脚。如果传感器信号很小电源纹波会对有效分辨率影响非常大。参考电压可以用内部2.048V这种情况下不太需要关注REF引脚外围。但如果追求更高的温度稳定性可以外接精密基准比如REF5025这种参考电压的噪声直接决定了ADC的噪声下限这点钱不能省。ADS1220内部基准0.05%精度对大多数场景够用但是温漂指标一般做高精度产品还是外接稳妥。3. ADS1220寄存器配置与采样参数计算3.1 四个配置寄存器逐位拆解ADS1220的寄存器配置是整个驱动最核心的部分。它有四个8位配置寄存器地址00h到03h后面还有校准寄存器但基础采样只需要配好前四个。配置寄存器0地址00h这个寄存器负责增益和输入多路复用器设置bit7DONDIN写1表示使用START引脚和DRDY引脚做外部同步/就绪控制写0表示内部连续转换模式bit6~bit4GAIN[2:0]PGA增益设置bit3~bit1MUX[2:0]输入通道选择bit0DON写0表示禁止内部电流源下表是不同增益和MUX配置对应的编码实际使用对照写入就行GAIN[2:0]增益MUX[2:0]输入通道0001000AINPAIN0, AINNAIN10012001AINPAIN0, AINNAIN20104010AINPAIN0, AINNAIN30118100AINPAIN1, AINNAIN210016101AINPAIN1, AINNAIN310132110AINPAIN2, AINNAIN311064111内部短路测试111128011保留举例说明如果接的是AIN0和AIN1差分输入PGA设置为8倍增益配置寄存器0的值就是0b00100000 0b000 0x20。配置寄存器1地址01h这个寄存器负责采样率、工作模式以及IDAC电流源配置bit7~bit5DR[2:0]数据输出速率bit4MODE1表示节能模式0表示高分辨率模式一般选0bit3~bit2IDAC[1:0]电流源开启控制bit1~bit0IMAG[1:0]电流源幅度配置寄存器2地址02h主要控制基准源和电流源输出引脚bit7VREF0表示内部基准1表示外部基准bit6VREFCON基准检测和关断控制bit5~bit4PSW电源切换bit3~bit2IDAC1[1:0]第一路电流源输出引脚bit1~bit0IDAC0[1:0]第二路电流源输出引脚配置寄存器3地址03h控制DRDY输出模式、温度传感器和偏移校准bit7DOUTMODE1表示DRDY为高电平脉冲bit6TS温度传感器使能bit5~bit4R11, R10内部电阻设置bit3FLIP输入极性翻转bit2~bit0偏移校准相关对于最常用的双通道差分输入、内部基准、PGA8、采样率20SPS的配置初始化寄存器可以写成uint8_t ads1220_config[4]; ads1220_config[0] 0x20; // GAIN8, AIN0-AIN1差分, 内部电流源关 ads1220_config[1] 0x00; // DR20SPS, 高分辨率模式, IDAC关 ads1220_config[2] 0x00; // 内部2.048V基准电流源输出全部关闭 ads1220_config[3] 0x00; // 标准DRDY模式温度传感器关3.2 采样率设置与采样点计算公式ADS1220的DR[2:0]位直接决定了每秒钟能出多少个转换结果采样点时间间隔就是SPS的倒数即Δt 1 / SPS。工程里经常需要用这个公式推算多久能拿到一个新数据或者反过来根据系统实时性要求选择合适的SPS档位。DR[2:0]采样率(SPS)单点采样周期适用场景0002050ms称重、温度等慢信号0014522.2ms一般工业采集0109011.1ms电桥信号0111755.7ms动态响应要求较高的场合1003303.0ms较快动态信号1016001.67ms数据记录11010001ms实时监控11120000.5ms快速采样但噪声较大这里有个容易忽略的重点SPS越高有效分辨率越低。ADS1220在20SPS时的有效分辨率能到21位左右而2000SPS时有效分辨率可能掉到17位上下。这不是芯片不行是ΔΣ ADC固有的转换噪声与带宽折中。做高精度测量时我通常选20SPS到90SPS然后靠软件平均值滤波把噪声再压一档。3.3 码值到电压的换算关系ADS1220输出的是24位二进制补码正满量程对应0x7FFFFF负满量程对应0x800000。换算成电压的公式关键在于明确满量程输入范围对差分输入而言满量程是±VREF/Gain。换算公式[ 电压 \frac{码值带符号整数 \times VREF}{Gain \times 2^{23}} ]其中码值是int32类型不是uint32因为ADS1220的输出有符号。举例VREF2.048VGain8码值0x200000即2097152那么电压2097152×2.048/(8×8388608)64mV。如果使用STM32内部代码这个换算我会放在一个独立函数里避免每次都在主循环里重算浮点数。不过需要注意码值读回来是先左移8位再按有符号数处理否则负数符号位会丢失。4. 实用源代码实现从SPI初始化到连续采样4.1 SPI底层驱动HAL库版本我用STM32CubeMX配置好SPI1和PA5、PA6、PA7引脚后HAL库的初始化代码基本不用改。关键是SPI时钟分频——STM32F103的SPI1挂载在APB2总线上时钟72MHz如果分频系数选4SPI时钟18MHz已经超过ADS1220的SCLK最高限制。ADS1220的数据手册规定SCLK最高是4MHz。所以分频系数必须保证SPI时钟不超过4MHz72MHz主频下选64分频即1.125MHz留足余量。如果用的F4系列主频更高分频系数还要相应加大。别小看这一步我刚开始调试时就是用了8分频SPI时钟9MHz结果读出来的数据偶尔错位排查了很久才发现是时钟超限。4.2 ADS1220寄存器读写函数先写两个基础函数写寄存器和读寄存器。ADS1220的SPI命令格式是第一个字节高四位是命令码低四位是寄存器地址。写命令码是0x40读命令码是0x20。写多个寄存器时第一个字节带上起始地址和数量后续字节就是寄存器内容。void ADS1220_WriteReg(uint8_t regAddr, uint8_t *buf, uint8_t len) { uint8_t cmd 0x40 | (regAddr 2) | (len - 1); ADS1220_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); HAL_SPI_Transmit(hspi1, buf, len, 10); ADS1220_CS_HIGH(); } void ADS1220_ReadReg(uint8_t regAddr, uint8_t *buf, uint8_t len) { uint8_t cmd 0x20 | (regAddr 2) | (len - 1); ADS1220_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); HAL_SPI_Receive(hspi1, buf, len, 10); ADS1220_CS_HIGH(); }注意命令码和地址的位操作关系。ADS1220的数据手册里读寄存器命令的格式是0010 AABB其中AAA是地址高3位BB表示读几个寄存器减1。所以地址左移2位数量减1放低2位这是新手最容易写错的地方。4.3 初始化函数一次性把所有寄存器写好写一个批量配置函数把配置寄存器0~3一次性写入void ADS1220_Init(void) { uint8_t cfg[4]; // 复位芯片确保寄存器处于默认状态 ADS1220_Reset(); HAL_Delay(10); // PGA8AIN0-AIN1差分 cfg[0] 0x20; // 20SPS高分辨率模式 cfg[1] 0x00; // 内部基准2.048V cfg[2] 0x00; // 标准模式 cfg[3] 0x00; ADS1220_WriteReg(0x00, cfg, 4); HAL_Delay(5); }复位函数可以直接用SPI命令0x06实现软复位也可以拉高复位引脚如果有接。我习惯先发复位命令确保芯片从干净状态开始配置避免上一次配置残留导致误动作。4.4 读取24位转换数据读取转换值要等DRDY引脚拉低。标准流程是查询DRDY引脚电平低电平后给ADS1220发送一个字节的读数据命令0x10然后读回3个字节拼成一个24位有符号数。int32_t ADS1220_ReadData(void) { uint8_t cmd 0x10; // RDATA命令 uint8_t buf[3]; int32_t raw; // 等待DRDY拉低最多等1ms uint16_t timeout 1000; while (HAL_GPIO_ReadPin(DRDY_GPIO_Port, DRDY_Pin) GPIO_PIN_SET) { if (--timeout 0) return 0x7FFFFFFF; // 超时返回异常值 } ADS1220_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); HAL_SPI_Receive(hspi1, buf, 3, 10); ADS1220_CS_HIGH(); // 拼24位整数MSB先出 raw ((int32_t)buf[0] 16) | ((int32_t)buf[1] 8) | buf[2]; // 处理符号位如果最高位是1转为负数 if (raw 0x800000) { raw | 0xFF000000; // 符号扩展 } return raw; }这个函数里有几个关键点。第一DRDY等待超时必须处理否则SPI卡死都不知道原因。第二符号扩展非常重要buf[0]是负数时的最高字节如果不做符号扩展int32_t的高8位全是0负数会被当成正数处理电压值直接错乱。第三读数据命令0x10和之前的寄存器读命令格式不同0x10是固定的RDATA命令不是0x20那种格式。4.5 电压换算与平均滤波裸码值不能直接用要换算成实际电压。同时为了降低随机噪声我在每次读取后做10次平均float ADS1220_GetVoltage(void) { int32_t sum 0; int32_t raw; float voltage; uint8_t i; for (i 0; i 10; i) { raw ADS1220_ReadData(); if (raw 0x7FFFFFFF) continue; // 读取超时跳过 sum raw; } sum / 10; // VREF2.048V, Gain8, 2^238388608 voltage (float)sum * 2.048f / (8.0f * 8388608.0f); return voltage; }实测下来10次平均能把码值跳动从±50码压到±10码以内对0.1mV级别的小信号识别很有帮助。如果系统对实时响应有要求可以改成滑动平均避免每次都要等500ms20SPS×10次。4.6 连续转换模式与单次转换模式ADS1220支持两种基本工作方式连续转换模式和单次转换模式。连续转换模式下配置完寄存器后芯片自己按设定SPS不断转换DRDY引脚周期性拉低CPU只需要等DRDY去读数据。单次转换模式需要每次发送START命令或拉高START引脚触发一次转换转换完进入休眠。低功耗应用优先考虑单次模式普通采集用连续模式更省事。我上面的代码默认是连续模式因为配置寄存器0的bit7写的是0。如果要用单次模式需要在读取完数据后发送0x08命令void ADS1220_StartSingle(void) { uint8_t cmd 0x08; // START命令 ADS1220_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); ADS1220_CS_HIGH(); }4.7 进阶SPI DMA与中断采集当采样率提升到1000SPS以上时主循环查询DRDY的CPU开销已经不小。此时可以走SPI DMA DRDY外部中断的方案。大致思路是DRDY下降沿触发外部中断在中断服务函数里启动SPI DMA传输读取3字节数据DMA完成中断里做拼数和换算把结果放到一个环形缓冲区主循环只消费数据。这个方案对STM32来说非常成熟。SPI DMA配置时要注意由于ADS1220是半双工模式下先发命令再收数据用HAL_SPI_TransmitReceive_DMA时发送的命令缓冲区和接收缓冲区要同时准备好不能让DMA只收不发否则SCLK没有时钟数据根本出不来。具体实现代码比较长本篇文章先把基础轮询版本讲透DMA版本后续单独开一篇。5. 实际调试中踩过的坑和排查技巧5.1 DRDY引脚没有拉低这个问题最常见的原因是配置寄存器0的bit7位被误设成了1。bit71时ADS1220处于外部START/DRDY同步模式DRDY引脚的行为变成由START信号控制不会自动在数据就绪时拉低。我用逻辑分析仪抓了好几轮才发现是这个位的问题参考手册里这个位的默认值是0但一些网上复制来的初始化代码里把这个位写成1造成数据怎么也读不出来。排查方法很简单DRDY引脚通过万用表量电压正常连续转换模式下DRDY应该周期性拉低20SPS下大约每50ms有一个低脉冲。如果一直是高电平大概率寄存器配置不对。5.2 SPI读回来的数据有时是3字节有时是2字节这种问题多半是SPI时钟太快导致位信号变形或者CS拉低后没有等足够的时间就发命令。ADS1220对CS下降沿到SCLK第一个上升沿之间有建立时间要求虽然手册上写的是纳秒级但实际调试时我遇到MCU的SPI外设和ADS1220之间配合不默契最后靠CS拉低后加一个空操作的延时解决。还有一种情况是把CS直接接地后SDO上没有数据返回这时候检查SPI数据方向配置是否正确。ADS1220的DOUT是三态输出只有CS有效时才输出数据CS接地后三态控制失效数据输出应该一直可用。5.3 数据总是偏大或带有固定直流偏置这是差分输入共模电压没处理好的表现。ADS1220的差分输入要求共模电压在一定的范围内如果传感器输出的共模电压超过了规定范围ADC输出就会带有明显偏置。单片机的GND和传感器供电的GND存在电位差也会造成这种情况。解决思路把传感器供电的参考地跟ADS1220的AGND接到同一点采用星形接地如果传感器输出有偏置用分压电阻给AINN一个合适的共模电压。另外ADS1220内置了系统校准寄存器可以发送校准命令抵消固定偏置0x60是自校准0x64是系统零点校准0x68是系统增益校准。5.4 有效分辨率达不到数据手册标称值这是最容易被忽略的点。数据手册上标称的分辨率是在理想电源、理想基准、低噪声环境下测得的实际项目里如果电源纹波大、PCB走线长、SPI时钟线紧贴模拟信号线有效分辨率掉个三四位很正常。我实际测过一组数据在开关电源直接供电、没有做LDO滤波的情况下20SPS采样率码值跳动约±80码加上一片AMS1117和RC滤波后跳动降到±15码再使用单独的AGND走线最终稳定在±5码左右。所以做高精度采集电源和地线设计比代码重要得多。5.5 使用内部基准时读数有轻微温漂ADS1220内部基准电压精度够但温漂指标一般。我的做法是如果产品工作环境温度波动超过20度必须外接REF5025或ADR4525这类精密基准。同时配置寄存器2的bit7写1把基准源切换到外部。外接基准的PCB布局同样要注意基准输出引脚附近尽量干净不要把数字信号从它旁边拉过去。5.6 SPI通信偶尔死锁这个现象在EMC干扰强的环境会出现。ADS1220如果接收到错误的SPI命令状态机可能进入未定义状态DRDY信号不再正常。解决办法是每次读数据前判断DRDY超时超时后先拉高CS复位SPI状态再发送0x06复位命令等5ms后重新配置寄存器。必要时在SPI通信帧里加CRC检验但大多数场景下复位机制就够用了。6. 常见问题速查表把上面调试经验整理成一张速查表平时遇到问题直接查现象可能原因解决办法DRDY一直高电平配置寄存器0 bit71改回0使用连续转换模式读到的数据都是0x00CS状态不对或SPI时钟不匹配检查CS拉低时序确认Mode 3数据偶发错位SPI时钟超4MHz分频系数加大确保不超上限码值跳动范围大电源纹波大基准不稳增加LDO、RC滤波AGND星形接地电压偏置明显共模电压超出范围调整输入共模电压做系统校准温度升高读数漂移内部基准温漂换外部精密基准配置VREF1主循环卡死SPI等待DRDY死循环加超时计数超时返回异常值7. 项目实测效果和扩展方向最后分享一下我实际项目里的数据。一个双路称重采集板用STM32F103C8T6 ADS1220PGA增益128内部基准20SPS采样10次平均后码值波动约±2码换算成电压约0.048uV。配合满量程10mV的称重传感器做满量程标定后重复精度能到1/10000这个指标对于工业现场设备已经够用了。后续如果要扩展可以考虑几个方向第一把连续转换改成单次转换再配合STM32的低功耗模式实现电池供电的无线传感器节点第二ADS1220支持双路IDAC可以直接驱动三线RTD测温把温度采集的激励源和ADC整合到一颗芯片上第三如果有多路传感器需要同步采样可以挂多片ADS1220到同一条SPI总线上每片用独立CS控制注意每片的初始化配置要单独下发。回到标题本身STM32对ADS1220的SPI采集方案核心难点其实不在代码量而在于对寄存器位定义的理解、SPI时序的匹配、以及硬件电路对模拟信号的影响。把这几块吃透了后面换其他24位ADC芯片也是差不多的套路。我一开始也是被一堆寄存器配置吓到了真调起来发现只要按顺序来一步一步验证很快就能稳定读出数据。希望这篇博文能帮你少走一些弯路。本文还有配套的精品资源点击获取
返回列表