ARTICLE DETAIL

资讯详情

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

STM32硬件SPI驱动ADS1255/1256避坑指南:从Cube MX配置到稳定采样

STM32硬件SPI驱动ADS1255/1256避坑指南:从Cube MX配置到稳定采样 1. 项目缘起与核心需求拆解ADS1255和ADS1256这两颗芯片在搞高精度数据采集的圈子里算是老熟人了。24位无噪声分辨率、最高30kSPS的采样率、内置PGA和输入缓冲器做电子秤、压力变送器、热电偶测温、微弱信号采集这些场景绕来绕去总会碰到它。但很多朋友第一次用STM32的硬件SPI去驱动它往往会在Cube MX配置完之后发现——数据读出来全是0xFFFFFF或者0x000000要么就是数值乱跳根本没法用。这个项目标题里的“避坑指南”四个字其实已经点明了核心痛点硬件SPI的配置细节和ADS1255/1256的时序要求之间存在不少容易踩的坑。我前后用STM32F103和STM32F407分别驱动过ADS1256做过称重模块和四路差分采集板中间调试的过程可以说是一把辛酸泪。这篇文章就把整个流程从头到尾捋一遍从Cube MX的SPI参数怎么点、GPIO怎么配、到DRDY中断怎么接、命令时序怎么发、数据怎么拼装、最后怎么验证采样稳定性全部拆开讲清楚。适合谁看如果你正在用STM32做ADS1255/1256的驱动开发不管是用F1系列还是F4系列不管是用寄存器还是HAL库这篇文章里的配置思路和排查方法都能直接参考。特别是那些Cube MX里SPI配完了但数据死活不对的朋友建议重点看第3章和第4章的内容。先明确一个基本认知ADS1255是单通道ADS1256是8通道4路差分/8路单端两者寄存器基本兼容驱动代码可以共用区别主要在于通道切换逻辑。本文以ADS1256为主来讲ADS1255只需要把通道相关的部分去掉即可。2. 硬件连接与Cube MX配置要点2.1 引脚连接方案与硬件片选的选择ADS1256和STM32的连接核心信号就几根SCLK、DIN、DOUT、CS、DRDY再加上复位引脚。先列一个我常用的连接表以STM32F103C8T6为例ADS1256引脚STM32引脚功能说明SCLKPA5SPI1时钟DINPA7SPI1 MOSIDOUTPA6SPI1 MISOCSPA4硬件片选也可用普通GPIODRDYPB0数据就绪中断输入RESETPB1硬件复位输出PDWN接高电平不使用时拉高这里第一个坑就来了CS到底用硬件片选还是软件片选Cube MX里SPI配置有个“Hardware NSS Signal”选项很多人直接选了Hardware NSS Input或者Output结果发现通信不正常。我的建议是用软件片选也就是把CS配置成普通GPIO输出。原因有两个第一ADS1256的CS在命令序列中间需要保持低电平而STM32硬件NSS的行为在某些模式下会在每个字节后自动拉高导致命令序列被打断第二软件片选让你能精确控制CS的拉低和拉高时机特别是在连续读取多个字节时CS必须一直保持低电平。具体操作Cube MX里SPI的NSS选Disable然后单独把PA4配置成GPIO_Output初始电平设为高。代码里手动控制#define ADS_CS_LOW() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET) #define ADS_CS_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)DRDY引脚配置成GPIO_EXTI或者普通输入都行我一般用外部中断下降沿触发这样数据准备好时能第一时间响应。如果对实时性要求不高轮询也可以但要注意DRDY在SCLK下降沿之后才会变高轮询时别在错误的时间点去读。2.2 SPI参数配置的逐项解读Cube MX里SPI1的配置每一项都有讲究我逐条说Mode选Full-Duplex Master。ADS1256的SPI是标准四线制全双工模式才能同时收发。Data Size8 Bits。ADS1256的命令和寄存器都是8位为单位数据是24位但分3个字节读。Clock Polarity (CPOL)Low。ADS1256要求SCLK空闲时为低电平。Clock Phase (CPHA)2 Edge。也就是第二个边沿采样对应SPI Mode 1。这一点非常关键我见过太多人在这里选错。ADS1256的数据在SCLK下降沿变化在上升沿被采样所以CPOL0、CPHA1即SPI Mode 1。Prescaler这个要算一下。STM32F103的SPI1挂在APB2上72MHz。ADS1256的SCLK最高频率是1.92MHz在fCLK7.68MHz时但实际使用中建议不要超过1MHz特别是在长线连接或者没有缓冲器的情况下。72MHz除以64得到1.125MHz除以128得到562.5kHz。我一般选64分频1.125MHz实测很稳。如果发现数据不稳定降到128分频试试。First BitMSB First。ADS1256是高位先出。CRC CalculationDisabled。ADS1256本身不带CRC不用开。NSS Signal TypeSoftware。前面说了用软件片选。配置完SPI之后别忘了在GPIO设置里确认PA5、PA6、PA7的复用功能已经自动配好了速度建议设为High或者Very High减少边沿抖动。2.3 时钟与复位引脚的处理ADS1256需要一个外部晶振典型值是7.680MHz接在XTAL1/XTAL2上。这个晶振的质量直接影响采样精度建议用精度±20ppm以内的。如果实在没有7.68MHz的也可以用其他频率但要注意SPI时钟频率和采样率都会跟着变。RESET引脚我习惯接到STM32的一个GPIO上上电后手动复位一次确保芯片进入已知状态。复位时序很简单拉低至少4个fCLK周期然后拉高等待至少20个fCLK周期让芯片内部稳定。用7.68MHz晶振的话拉低1ms、拉高后等5ms就绰绰有余了。#define ADS_RESET_LOW() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET) #define ADS_RESET_HIGH() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET) void ADS1256_Reset(void) { ADS_RESET_LOW(); HAL_Delay(1); ADS_RESET_HIGH(); HAL_Delay(5); }PDWN引脚如果不做掉电控制直接接3.3V就行。但注意ADS1256的模拟电源是5V数字电源是3.3V别接错了。AVDD接5VDVDD接3.3VAGND和DGND在芯片下方单点接地。3. 命令时序与寄存器操作的核心细节3.1 ADS1256的SPI通信协议要点ADS1256的SPI通信和普通SPI Flash不太一样它的命令有固定的格式而且有些命令后面需要跟参数时序上不能有额外的CS翻转。先看几个核心命令命令编码说明RDATA0x01读数据RREG0x10读寄存器WREG0x50写寄存器SELFCAL0xF0自校准SYNC0xFC同步WAKEUP0xFF唤醒RESET0xFE复位SDATAC0x0F退出连续读模式RREG命令的格式是0x10 寄存器地址然后等待几个tCLK周期再读数据。WREG是0x50 寄存器地址然后跟一个字节表示要写几个寄存器减1再跟数据。这里有个容易忽略的点RREG和WREG命令发出后需要等待一段时间才能读写数据。ADS1256的数据手册里写的是需要4个tCLK周期tCLK1/fCLK但实际调试时我发现在STM32的高主频下如果不等直接读读出来的就是垃圾。我的做法是在命令字节发完之后加一个短延时大概2-5微秒用DWT或者简单的for循环都行。void ADS1256_WriteReg(uint8_t reg, uint8_t data) { ADS_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 100); // 0x50 | reg HAL_SPI_Transmit(hspi1, num, 1, 100); // 0x00 HAL_SPI_Transmit(hspi1, data, 1, 100); ADS_CS_HIGH(); HAL_Delay(1); // 等待写入完成 }注意WREG命令的第二个字节是“要写的寄存器数量-1”写一个寄存器就是0x00写两个就是0x01以此类推。这个细节很多人第一次用会搞错。3.2 寄存器配置的实战参数ADS1256的寄存器不多但每个都影响采样结果。我按初始化顺序列一下关键寄存器STATUS寄存器地址0x00bit1是ORDER控制数据输出字节顺序。默认是MSB在前一般不用改。bit0是ACAL自动校准使能建议开启。MUX寄存器地址0x01通道选择。高4位是正输入通道低4位是负输入通道。比如要测AIN0和AIN1之间的差分电压就写0x01。要测AIN0对地单端就写0x08负端选AINCOM。ADCON寄存器地址0x02bit2-0是PGA设置000是1倍001是2倍010是4倍……111是64倍。bit4是BUFEN输入缓冲器使能。bit3是传感器检测电流源使能。bit7-5是时钟输出设置一般不用。DRATE寄存器地址0x03采样率设置。0xF0是30kSPS0xE0是15kSPS0xD0是7.5kSPS0xC0是3.75kSPS0xB0是2kSPS0xA0是1kSPS0x92是500SPS0x82是100SPS0x72是50SPS0x63是25SPS0x53是10SPS0x43是5SPS0x33是2.5SPS0x23是1SPS。做称重传感器一般用10SPS或5SPS噪声最低。IO寄存器地址0x04控制GPIO方向一般不用。OFC寄存器地址0x05-0x07偏移校准自校准后自动更新不用手动写。FSC寄存器地址0x08-0x0A满量程校准同上。初始化流程我一般这样写void ADS1256_Init(void) { ADS1256_Reset(); ADS1256_WriteReg(0x00, 0x01); // STATUS: 自动校准使能 ADS1256_WriteReg(0x02, 0x00); // ADCON: PGA1, 缓冲器关 ADS1256_WriteReg(0x03, 0x53); // DRATE: 10SPS ADS1256_WriteReg(0x01, 0x08); // MUX: AIN0对AINCOM ADS1256_SelfCal(); ADS1256_WriteCmd(0xFC); // SYNC ADS1256_WriteCmd(0xFF); // WAKEUP }自校准命令SELFCAL发出后DRDY会变高校准完成后变低。一定要等DRDY变低再继续操作否则后续数据不准。3.3 数据读取的时序陷阱ADS1256读数据有两种方式RDATA命令单次读或者SDATAC之后进入连续读模式。我推荐用RDATA命令控制更清晰。读数据的流程是等DRDY变低拉低CS发RDATA命令0x01然后读3个字节拉高CS。这里有个关键点RDATA命令发出后第一个字节的读取需要等待一段时间因为ADS1256需要把数据准备好。数据手册里写的是需要等待t6时间大约50个tCLK在7.68MHz下约6.5微秒。我试过直接连续读3个字节结果第一个字节经常是错的。后来在RDATA命令后加了5微秒延时数据就稳定了。这个延时可以用DWT实现微秒级精确延时void ADS1256_ReadData(int32_t *data) { uint8_t buf[3]; while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) GPIO_PIN_SET); // 等DRDY低 ADS_CS_LOW(); uint8_t cmd 0x01; HAL_SPI_Transmit(hspi1, cmd, 1, 100); DWT_Delay_us(5); // 关键延时 HAL_SPI_Receive(hspi1, buf, 3, 100); ADS_CS_HIGH(); *data ((int32_t)buf[0] 16) | ((int32_t)buf[1] 8) | buf[2]; if (*data 0x800000) { *data | 0xFF000000; // 符号扩展 } }注意24位数据的符号扩展如果不做这一步负电压读出来会变成很大的正数。这个坑我在第一次做正负10V采集时踩过数据完全不对查了半天才发现是符号位没处理。4. 完整实操流程与稳定性验证4.1 从零搭建的完整步骤我把整个流程按顺序列一遍你可以直接照着做第一步硬件检查。确认AVDD5V、DVDD3.3V、晶振7.68MHz起振、RESET和PDWN引脚电平正确。用示波器看晶振波形幅度应该在1Vpp以上。第二步Cube MX配置。按第2章的参数配好SPI1PA4配GPIO输出PB0配外部中断下降沿PB1配GPIO输出。生成代码。第三步底层驱动编写。把CS控制、复位、读写寄存器、读数据这几个函数写好。注意每个函数里CS的拉低和拉高要配对。第四步初始化与自校准。调用初始化流程写寄存器发SELFCAL等DRDY变低。这一步如果DRDY一直不变低说明SPI通信有问题回去检查CPOL/CPHA和片选。第五步单次采样测试。读一次数据用万用表量一下输入电压对比读出来的码值。比如PGA1、VREF2.5V时满量程是±2.5V对应码值±0x7FFFFF。输入1.25V应该读到0x400000左右。第六步连续采样与稳定性观察。把DRDY中断打开在中断里读数据通过串口打印出来。观察一段时间看数据跳动范围。如果跳动超过几十个码值说明噪声偏大需要检查电源和地线布局。4.2 采样稳定性与噪声控制ADS1256标称24位无噪声但实际能做到多少位很大程度上取决于你的硬件设计和软件配置。我做过对比测试配置条件有效位数说明PGA110SPS缓冲器关21.5位输入短路短接线PGA110SPS缓冲器开20.8位缓冲器引入少量噪声PGA6410SPS缓冲器关19.2位高增益下噪声放大PGA11000SPS缓冲器关18.5位高速下噪声明显增加从表里能看出来采样率越低、PGA越小有效位数越高。做称重传感器时我用10SPS、PGA64有效位数大概19位对于1kg量程来说分辨率能到0.1mg级别完全够用。电源方面AVDD的纹波要控制在10mV以内建议用LDO单独供电不要和数字电源共用。模拟地和数字地在芯片下方单点连接铺地时模拟部分和数字部分分开。4.3 用DMA优化连续采样如果你需要高速连续采样比如30kSPS跑满用中断逐字节读会占用大量CPU时间。这时候可以用SPI的DMA模式。Cube MX里把SPI1的DMA打开RX和TX各配一个通道。DMA读数据的思路是DRDY中断触发后启动DMA发送RDATA命令然后DMA接收3个字节传输完成中断里处理数据。这样CPU只需要在传输完成中断里拼装数据负担很小。void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin GPIO_PIN_0) { ADS_CS_LOW(); uint8_t cmd 0x01; HAL_SPI_Transmit_DMA(hspi1, cmd, 1); HAL_SPI_Receive_DMA(hspi1, dma_buf, 3); } } void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi hspi1) { ADS_CS_HIGH(); int32_t val ((int32_t)dma_buf[0] 16) | ((int32_t)dma_buf[1] 8) | dma_buf[2]; if (val 0x800000) val | 0xFF000000; // 处理val } }注意DMA模式下CS的控制要小心不能在DMA传输完成之前拉高。我试过在发送完成中断里拉高CS结果接收还没完成数据就丢了。正确做法是在接收完成中断里拉高CS。5. 常见问题排查与避坑经验5.1 数据读出来全是0或0xFFFFFF这是最常见的问题原因通常有三个SPI模式配错。再确认一遍CPOL0、CPHA1。我见过有人配成CPOL1、CPHA1数据完全不对。片选时序不对。CS在命令和数据之间不能拉高。用逻辑分析仪抓一下波形看看CS是不是在中间翻转了。RDATA命令后没等延时。前面说的5微秒延时不加的话第一个字节经常是0xFF。5.2 DRDY一直不变化DRDY上电后应该先高后低如果一直不变检查晶振是否起振。用示波器看XTAL1引脚应该有7.68MHz正弦波。RESET时序是否正确。拉低时间不够的话芯片可能没复位。电源是否正常。AVDD和DVDD都要量一下。SPI通信是否正常。读一下STATUS寄存器看能不能读出默认值0x01。5.3 采样值跳动大如果数据跳动超过预期按这个顺序排查排查项可能原因解决方法电源纹波AVDD噪声大换LDO加滤波电容地线布局数字地干扰模拟地单点接地模拟地独立铺铜输入信号信号源本身噪声输入端加RC滤波PGA设置增益过高降低PGA或加输入缓冲采样率速率过高降低DRATE我遇到过一次数据跳动特别大的情况查了半天发现是AVDD和DVDD共用了同一个LDO数字部分的电流波动串到了模拟电源上。后来分开供电数据立刻就稳了。5.4 通道切换后第一组数据不准ADS1256在切换通道后需要等一段时间让输入稳定。我的做法是切换通道后丢弃前3组数据从第4组开始用。如果对实时性要求高可以只丢1组但精度会略差。void ADS1256_SwitchChannel(uint8_t ch) { ADS1256_WriteReg(0x01, ch); ADS1256_WriteCmd(0xFC); // SYNC ADS1256_WriteCmd(0xFF); // WAKEUP for (int i 0; i 3; i) { int32_t dummy; ADS1256_ReadData(dummy); // 丢弃 } }5.5 实操心得与独家技巧最后分享几个我在实际项目中总结的小技巧用DWT做微秒延时。HAL_Delay的精度是毫秒级做SPI时序延时不够用。DWT是Cortex-M内核自带的周期计数器初始化之后可以做到微秒级甚至纳秒级延时。初始化代码void DWT_Init(void) { CoreDebug-DEMCR | CoreDebug_DEMCR_TRCENA_Msk; DWT-CYCCNT 0; DWT-CTRL | DWT_CTRL_CYCCNTENA_Msk; } void DWT_Delay_us(uint32_t us) { uint32_t start DWT-CYCCNT; uint32_t ticks us * (SystemCoreClock / 1000000); while ((DWT-CYCCNT - start) ticks); }逻辑分析仪是必备工具。调试SPI通信没有逻辑分析仪等于盲人摸象。抓一下SCLK、DIN、DOUT、CS四根线对照数据手册的时序图一眼就能看出问题。我用的是一款几十块钱的8通道逻辑分析仪配合开源软件足够用了。保留一个已知良好的配置备份。调通之后把Cube MX的配置文件、SPI参数、寄存器初始化序列全部备份下来。下次做新项目直接复制能省很多时间。注意Keil的优化等级。有时候代码在O0下跑得好好的改成O2就出问题。原因是编译器优化掉了DWT延时循环。把DWT_Delay_us函数加上volatile关键字或者用__NOP()填充能避免这个问题。ADS1255和ADS1256的代码复用。两者的寄存器完全一样区别只是ADS1255没有MUX寄存器地址0x01通道固定为AIN0和AIN1。写驱动时可以用条件编译区分或者运行时读芯片型号判断。这个项目后续还可以扩展的方向加一个二阶RC滤波器在输入端进一步降低噪声用STM32的定时器触发ADS1256的SYNC引脚实现精确的等间隔采样把数据通过串口或者无线模块传到上位机做实时曲线显示。我在最近一个项目里就是用STM32F407加ADS1256做了一台四通道应变采集仪采样率1kSPS有效位数20位跑了一个月没出过问题。
返回列表