简介面向STM32F103xx开发者的ADS1220软件模拟驱动包以纯软件方式模拟SPI时序完成高精度ADC的初始化、工作模式配置、数据转换与结果读取解决无硬件SPI或低成本采集场景下的模拟量输入需求。ADS1220为24位低功耗模数转换器常用于温度、压力、应变等小信号测量与STM32F103xx配合后适用于工业测量、仪器仪表等嵌入式项目。包内共172个文件压缩包大小5.22MB以Keil MDK工程为核心包含35个C源文件、34个头文件、工程配置uvprojx/uvoptx、分散加载描述sct以及编译生成的hex、axf、lst文件其中d、o、crf等属于编译中间产物不影响源码阅读与重新编译。资源已有794人学习/下载适合正在调试ADS1220或希望参考软件模拟SPI驱动框架的开发者。通过它可拿到完整工程源码、头文件、Keil工程配置与烧写固件能直接对照寄存器配置和时序控制代码理解模拟SPI的实现思路代码基于STM32标准外设库便于移植到其他型号也可自行扩展连续转换、单次转换、PGA增益配置等功能整体结构清晰是高精度ADC驱动开发的一份实用参考。1. 软件模拟 ADS1220 驱动硬件 SPI 不够用时的 24 位采样方案接手过一个称重采集项目MCU 的硬件 SPI 被 Flash 和显示屏占满剩下的引脚只剩几个普通 GPIO可偏偏要外挂一片 24 位 ADC 做称重传感器的信号采集。当时第一反应是换 MCU但结构、成本都定死了只能硬着头皮用 GPIO 手搓 SPI 时序去驱动这颗 ADS1220。做完之后发现软件模拟 ADS1220 并没有想象中那么玄学这颗芯片的 SCLK 最高只有 2MHzGPIO 翻转完全跟得上真正容易翻车的反而是时序细节、寄存器配置和 DRDY 引脚的状态判断。这篇笔记就是把这条路走通的血泪经验整理出来给那些遇到硬件 SPI 被占用、引脚复用冲突、或者想省一个 SPI 外设的开发者一个可以直接照着做的方案。新手能跟着把驱动跑起来熟手可以直接跳过基础部分看参数配置和避坑清单。2. ADS1220 寄存器与 SPI 时序写驱动前必须读懂的 4 个配置字节2.1 寄存器概览CONFIG0 到 CONFIG3 各管什么ADS1220 是一颗 24 位、4 通道、带 PGA 增益的低功耗 ADC控制逻辑比想象中简单所有功能都浓缩在 4 个 8 位配置寄存器里地址从 0x00 到 0x03。驱动工作的本质就是把这 4 个寄存器写对、再按时序把 24 位转换结果读回来没有复杂的命令状态机。CONFIG0 寄存器管输入多路开关和增益。高 4 位选择通道和输入模式比如差分输入 AIN0/AIN1、AIN2/AIN3或者单端输入低 4 位配置 PGA 增益增益值从 1 到 128 共 8 档。用表格看更清晰位名称作用7:4MUX输入通道选择差分/单端3:0PGA增益配置00001、00012…0111128CONFIG1 寄存器管数据速率、滤波器模式和转换模式。数据速率从 20SPS 到 2000SPS 可调还能选择 50Hz/60Hz 工频抑制滤波器这些参数直接影响采样噪声表现。转换模式支持单次转换和连续转换单次转换适合电池供电的低功耗场景连续转换则省去每次发送 START 命令的麻烦。CONFIG2 寄存器管参考源和传感器激励。可以选择内部 2.048V 参考电压也可以选择外部参考引脚还能开启两个恒流源用于 PT100、热敏电阻这类需要激励电流的传感器测量。CONFIG3 寄存器则负责空闲输入通道的固定偏置、基准电压检测和复位功能。驱动初始化时把这 4 个寄存器一次写完芯片就进入用户配置好的工作状态。2.2 SPI 时序与命令字节为什么软件模拟完全可行ADS1220 的 SPI 时序对于 GPIO 模拟来说非常友好。它的 SCLK 最高 2MHz这个频率下普通 MCU 的 GPIO 翻转延时完全可以满足命令格式也清晰无非是读寄存器、写寄存器、读转换数据、复位、同步这几种。通信时 CS 拉低SCLK 空闲为低电平数据在时钟边沿被采样常见的实现配置是 SPI 模式 1也就是 CPOL0、CPHA1。我一般会先在逻辑分析仪上确认一下芯片手册里的时序图再写代码毕竟不同批次的手册细节可能有差异但绝大多数情况模式 1 是正确的。命令字节的定义是驱动里最容易写错的地方。查询数据手册的命令定义部分几个关键命令如下复位命令发送 0x06启动转换发送 0x04读数据命令发送 0x10 然后读取 24 位寄存器读命令 RREG 的基础字节是 0x20寄存器写命令 WREG 的基础字节是 0x40。读或写多字节寄存器时需要用「起始地址」和「寄存器个数减一」两个字节来组织命令。#define ADS1220_CMD_RESET 0x06 #define ADS1220_CMD_START 0x04 #define ADS1220_CMD_RDATA 0x10 #define ADS1220_CMD_RREG 0x20 #define ADS1220_CMD_WREG 0x40 #define ADS1220_REG_CONFIG0 0x00 #define ADS1220_REG_CONFIG1 0x01 #define ADS1220_REG_CONFIG2 0x02 #define ADS1220_REG_CONFIG3 0x03上面的宏定义里命令基础字节和寄存器地址分开定义组命令时用按位或合成最终字节。比如写 CONFIG0 就发送ADS1220_CMD_WREG | 0x00即 0x40读 CONFIG2 就发送ADS1220_CMD_RREG | 0x02即 0x22。有些驱动会把 WREG 写成固定的 0x40 然后第二个字节放寄存器数减一这种写法与宏定义不冲突只是可读性差一些。建议把命令定义和寄存器地址分开方便后续维护。还有一点容易被忽略的是寄存器写入个数字节。ADS1220 的 WREG 命令序列是第一个字节写命令加起始地址第二个字节写「要写的寄存器数量减一」之后才是实际数据。如果只写一个寄存器第二字节就是 0x00。读命令同理发送完命令和数量字节后依次读取数据。这个「数量减一」的格式和很多其他 SPI 芯片不同容易踩坑。3. GPIO 手搓 SPI 读写 ADS1220核心驱动函数的逐段实现3.1 底层字节收发GPIO 初始化与位翻转软件模拟 SPI 的第一步是把 SCLK、MOSI、MISO、CS 四根线对应的 GPIO 配置好。SCLK、MOSI、CS 是输出模式MISO 是输入模式。需要注意的是 GPIO 输出速度不能配得太低否则边沿太缓ADS1220 在 2MHz 下可能采样出错一般配到最高速度档位同时开启内部上拉或下拉保持默认电平。void ads1220_gpio_init(void) { GPIO_InitTypeDef gpio; gpio.Mode GPIO_MODE_OUTPUT_PP; // SCLK/MOSI/CS 推挽输出 gpio.Pull GPIO_PULLDOWN; // 默认拉低SCLK 空闲低电平 gpio.Speed GPIO_SPEED_FREQ_HIGH; // 高速翻转保证时序边沿 gpio.Pin ADS1220_PIN_SCLK; HAL_GPIO_Init(ADS1220_SCLK_PORT, gpio); gpio.Pin ADS1220_PIN_MOSI; HAL_GPIO_Init(ADS1220_MOSI_PORT, gpio); gpio.Pin ADS1220_PIN_CS; HAL_GPIO_Init(ADS1220_CS_PORT, gpio); gpio.Mode GPIO_MODE_INPUT; // MISO 输入 gpio.Pull GPIO_PULLUP; gpio.Pin ADS1220_PIN_MISO; HAL_GPIO_Init(ADS1220_MISO_PORT, gpio); }上述代码里SCLK 默认拉低这是配合模式 1 时序的必要条件。GPIO 速度配 HIGH 级别是为了让 SCLK 在 2MHz 时边沿足够陡。如果你用的 MCU 不支持 HAL 库换成寄存器操作也是同样的思路方向寄存器设输出速度寄存器设最高档。基础的字节收发是整个软件模拟 SPI 的心脏。发送一个字节时从最高位开始逐位输出接收一个字节时从最高位开始逐位读取 MISO。注意 ADS1220 是 24 位 ADC所以数据传输经常是 3 字节但底层字节函数必须保证每一位都在正确的时钟边沿被采样。void ads1220_spi_write_byte(uint8_t data) { for (int i 7; i 0; i--) { ADS1220_SCLK_LOW(); if (data (1 i)) ADS1220_MOSI_HIGH(); else ADS1220_MOSI_LOW(); ADS1220_SCLK_HIGH(); // 上升沿主机发送数据 } ADS1220_SCLK_LOW(); } uint8_t ads1220_spi_read_byte(void) { uint8_t value 0; for (int i 7; i 0; i--) { ADS1220_SCLK_LOW(); ADS1220_SCLK_HIGH(); // 下降沿主机采样 MISO if (ADS1220_MISO_READ()) value | (1 i); } ADS1220_SCLK_LOW(); return value; }这里最关键的是时钟边沿的方向。写字节时先把 SCLK 拉低设置 MOSI 电平再拉高 SCLK这样数据在上升沿被芯片采样读字节时拉低 SCLK然后拉高芯片在下降沿把数据放到 MISO 上主机在 SCLK 低电平阶段读取。两个函数的节奏配合必须一致否则数据会整体错位。另外有些驱动会在每发完一字节后加几个空循环延时对于 2MHz 以内的速率现代 MCU 的 GPIO 翻转速度足够不需要额外延时但如果你移植到很慢的 MCU 或者用 GPIO 模拟时钟频率超过 1MHz适当插入空循环会更稳。CS 的控制比较简单通信开始前拉低通信结束后拉高。实际项目中连续读寄存器或读转换数据时CS 可以一直保持低电平不需要每字节都重新拉一次。void ads1220_cs_low(void) { ADS1220_CS_LOW(); } void ads1220_cs_high(void) { ADS1220_CS_HIGH(); }3.2 寄存器读写命令封装地址、数量、数据三段式有了底层字节函数寄存器读写命令就顺理成章了。写寄存器时先拉低 CS发送命令字节和数量字节再发送配置数据最后拉高 CS。这个过程的时序要求是 CS 低电平期间 SCLK 连续翻转中间不能有太长的停顿否则芯片可能判定通信结束。void ads1220_write_reg(uint8_t reg_addr, uint8_t data) { ads1220_cs_low(); ads1220_spi_write_byte(ADS1220_CMD_WREG | reg_addr); ads1220_spi_write_byte(0x00); // 写 1 个寄存器数量减一等于 0 ads1220_spi_write_byte(data); ads1220_cs_high(); }第二个字节固定写 0x00表示只写一个寄存器。如果要连续写多个寄存器就需要把数量减一的值写在这里然后依次发送各个寄存器数据。写完后可以立刻进入下一步操作但如果是复位之后第一次配置建议等一下 DRDY 引脚状态或者加一个小延时让芯片内部完成初始化。读寄存器是对称的操作发送读命令和数量字节然后读取一个或连续多个字节。注意读的时候MOSI 上不需要继续输出数据但 SCLK 必须继续翻转否则芯片不会输出。uint8_t ads1220_read_reg(uint8_t reg_addr) { uint8_t value 0; ads1220_cs_low(); ads1220_spi_write_byte(ADS1220_CMD_RREG | reg_addr); ads1220_spi_write_byte(0x00); // 读 1 个寄存器 value ads1220_spi_read_byte(); ads1220_cs_high(); return value; }这段代码在调试阶段非常重要。写完配置寄存器后建议立刻把它们读回来比对确认写入值和读回值一致。很多时候驱动「看起来在跑」但采样数据完全不对其实是寄存器没写进去原因可能是 CS 时序不对、命令字节拼错或者 GPIO 方向配置错误。读回校验是最快的排错手段。3.3 读转换结果DRDY 轮询与 24 位数据拼接ADS1220 完成一次转换后会把 DRDY 引脚拉低。软件模拟驱动最常用的方式是轮询 DRDY检测到低电平后通过 SPI 读取 24 位数据DRDY 会自动恢复高电平。单次转换模式下每次读完数据后芯片进入空闲状态要再次启动转换需要发送 START 命令。int32_t ads1220_read_data(uint32_t timeout_ms) { uint32_t start_tick HAL_GetTick(); // 等待 DRDY 拉低表示转换完成 while (ADS1220_DRDY_READ() ! 0) { if (HAL_GetTick() - start_tick timeout_ms) return ADS1220_TIMEOUT; // 超时保护返回错误码 } uint32_t raw 0; ads1220_cs_low(); ads1220_spi_write_byte(ADS1220_CMD_RDATA); // 发送读数据命令 raw (uint32_t)ads1220_spi_read_byte() 16; raw | (uint32_t)ads1220_spi_read_byte() 8; raw | (uint32_t)ads1220_spi_read_byte(); ads1220_cs_high(); // 24 位有符号数转 int32 int32_t value (int32_t)(raw 8) 8; return value; }这里最容易翻车的是 24 位有符号数的类型转换。raw是 uint32_t如果直接强转 int32_t符号位不会自动扩展正的 0x7FFFFF 没问题但负的 0x800000 会被当成 8388608而不是 -8388608。正确做法是先把 raw 左移 8 位把符号位顶到 int32 的最高位再算术右移 8 位恢复原始数值。这一行写错整个 ADC 的读数就会在零附近来回乱跳而且数值越大误差越离谱。DRDY 轮询的超时值也要合理设置。假设数据速率配置为 20SPS一次转换最长需要约 50ms超时设 200ms 是比较安全的如果配置为 2000SPS转换只要 0.5ms超时设 100ms 足够。超时返回值建议用一个明显异常的数比如0x7FFFFFFF或自定义的宏避免和正常 ADC 数值混淆。4. 采样参数配置实战增益、速率与滤波器怎么搭配4.1 PGA 增益与满量程选错档位会让有效位数白白丢失ADS1220 内置 PGA 可编程增益放大器增益范围为 1 到 128共 8 档。增益的作用是把小幅度的传感器信号放大到接近满量程从而充分利用 24 位分辨率。以内部 2.048V 参考电压为例不同增益下对应的满量程输入范围如下PGA 增益满量程输入范围1 LSB 对应电压1±2.048V0.244µV8±0.256V0.031µV16±0.128V0.015µV64±0.032V0.004µV128±0.016V0.002µV选增益的核心原则是让目标信号尽量占满量程的 50% 到 90%。比如称重传感器的满量程输出通常是 2mV/V5V 激励下满量程 10mV选增益 128 后信号被放大到 1.28V接近但不超过满量程这样 24 位分辨率发挥得最充分。如果信号太小却选了低增益读数会在低位大幅跳动有效位数可能只有 12 位白白浪费了这颗 ADC 的精度。我用表格来说明一个常见选型场景PT100 在 0℃ 时电阻 100Ω1mA 激励下电压 0.1V温度到 100℃ 时电阻约 138.5Ω电压 0.1385V。差分信号变化只有约 38.5mV选增益 32 或 64 比较合适。反过来说如果你直接测一个 0 到 5V 的信号就必须用外部参考或分压增益只能选 1。4.2 数据速率与滤波器20SPS 到 2000SPS 怎么权衡数据速率配置在 CONFIG1 寄存器中直接影响噪声表现和 50Hz/60Hz 工频抑制能力。ADS1220 的可选速率从 20SPS 到 2000SPS配合内置数字滤波器不同速率下的噪声指标差异很大。速率越低数字滤波器平均的点数越多噪声越小速率越高响应越快但噪声也会明显增大。数据速率典型有效分辨率工频抑制20SPS接近 24 位同时抑制 50Hz 和 60Hz45SPS约 22 位抑制 50Hz 较好90SPS约 21 位抑制 60Hz 较好340SPS 以上约 19 位以下基本无工频抑制能力实际项目中采集称重、温度这类缓变信号我一般选 20SPS 或 45SPS噪声表现最好而且 50Hz 工频干扰能被有效滤除。如果你要采集的是振动信号或者需要快速响应的场景那就得被迫接受高速率下的噪声这时候可以配合外部硬件滤波来弥补。这个取舍必须在项目需求阶段就确认清楚否则等硬件做好了再改速率配置往往发现噪声压不下去。滤波器的选择还涉及一个容易忽略的点ADS1220 的数据速率改变后第一次转换结果需要等待滤波器稳定通常要丢几个点。驱动初始化后不要立刻采数据先空读几次或丢弃前几个转换值否则初始数据会偏大。4.3 参考电压选择内部参考省事外部参考更稳CONFIG2 寄存器里可以选择内部 2.048V 参考或外部参考输入。内部参考的好处是省外围器件、上电即可用适合精度要求不特别高的场景缺点是温漂和初始精度有限而且它是从 AVDD 分压得到的如果 AVDD 纹波大参考电压也会跟着抖。外部参考一般用专用的参考芯片温漂可以做到很低适合称重、高精度温度测量这类需要长期稳定性的场景。驱动代码里配置参考源的位域就在 CONFIG2 寄存器的 bit7 和 bit6。内部参考对应二进制01外部参考对应10或11。选择外部参考时REF 引脚上的电压必须稳定去耦电容要靠近引脚放置至少 10µF 电解电容并联 100nF 陶瓷电容。我用过一个项目用了内部参考后数据在 1 小时内漂了十几个码换成外部参考后漂移立刻小了一个数量级这个代价是值得的。4.4 一次完整的 4 字节初始化配置把以上三个小节综合成一个实际配置单次转换模式、差分输入 AIN0/AIN1、增益 32、数据速率 20SPS、内部参考。对应 CONFIG0 到 CONFIG3 的字节如下uint8_t config0 0x00; // MUX0000, AIN0/AIN1 差分输入 config0 | (4 0); // PGA0100, 增益 32 uint8_t config1 0x04; // DR010, 20SPS, 单次转换模式 config1 | (0 4); // 不使能 50/60Hz 同时抑制吗? 见下方说明 uint8_t config2 0x10; // 内部参考 2.048V, 关闭电流源 uint8_t config3 0x00; // 默认, 空闲通道接 AVDD // 写入寄存器, 注意从 CONFIG0 开始连续写 4 个 ads1220_cs_low(); ads1220_spi_write_byte(ADS1220_CMD_WREG | 0x00); ads1220_spi_write_byte(0x03); // 连续写 4 个寄存器: 4-13 ads1220_spi_write_byte(config0); ads1220_spi_write_byte(config1); ads1220_spi_write_byte(config2); ads1220_spi_write_byte(config3); ads1220_cs_high();上面代码里的config1位域实际含义要翻手册确认数据速率位通常占 bit7:5增益位占 CONFIG0 的 bit3:0。不同的参考手册可能把位域顺序画得不一样写代码前必须对着数据手册的寄存器表逐位核对不要凭记忆填值。这里给的字节值是常见配置但如果你手里的芯片是 B 版本或更晚的硅片个别位域定义可能有增补务必以手册为准。写完后立刻把 CONFIG0 到 CONFIG3 读回来和写入值逐位比对确认无误后再启动转换。5. ADS1220 驱动避坑指南DRDY 异常与数据漂移的 5 个排查点5.1 DRDY 引脚永不拉低转换根本没启动现象程序烧进去后轮询 DRDY 一直超时要么卡死要么反复返回超时错误码。用示波器量 DRDY 引脚发现始终保持高电平没有任何脉冲。原因最常见的是复位后没有发送 START 命令。ADS1220 上电后处于空闲状态不会自动开始转换即使配置了寄存器也必须发送 START 命令0x04才能触发第一次转换。另一个原因是复位命令或配置命令本身没被正确接收芯片仍在默认状态。解决上电后先延时 100ms 等待芯片内部振荡器稳定然后发送复位命令 0x06再等待 100ms写入配置寄存器最后发送 START 命令。每次写完寄存器后建议把 DRDY 引脚重新确认一下看是否有下降沿。如果还是没有用逻辑分析仪抓 SPI 波形重点看 CS 低电平期间命令字节是否发送完整。5.2 读回来的 24 位数据整体乱跳符号位没处理现象输入悬空或接零信号时读回的数据在正负大数之间随机跳动比如 0x7FFFFF 和 0x800000 之间来回切换完全没有稳定数值。原因这是把 24 位无符号拼接结果直接强转 int32 导致的。24 位 ADC 的转换结果最高位是符号位但uint32_t类型里它只是普通数据位直接赋值后CPU 不会自动把它扩展成 int32 的符号位。结果就是正的极限值 0x7FFFFF 和负的极限值 0x800000 在无符号视角下只差 1但转成有符号后一个巨大正值一个巨大负值。解决使用 3.3 小节提到的左移 8 位再右移 8 位的方法先让符号位占据 int32 的最高位再算术右移恢复。代码如下int32_t value (int32_t)(raw 8) 8;这一行代码写错的人不少排查最快的方法是打印 raw 原始值和转换后的 value对比零输入时是否都接近 0。如果原始值接近 0x800000 而转换值接近 -8388608那基本就是符号扩展的锅修复后立刻恢复正常。5.3 低增益下读数稳定高增益下低位狂跳现象配置为增益 1 时读数平稳但切到增益 64 或 128 后数据低 4 位甚至低 8 位持续无规律跳动完全无法用。原因高增益会把噪声同步放大。电源纹波、参考电压噪声、PCB 走线耦合进来的干扰在增益 128 下会被放大一百多倍24 位 ADC 的低位自然被噪声淹没。另一个常见原因是传感器的激励电流源配置不合理导致传感器信号叠加了共模噪声。解决先检查模拟电源和参考电压的纹波AVDD 上用磁珠加 10µF 电容滤波参考电压引脚并 100nF 和 1µF 电容。PCB 布局上模拟信号走线尽量短且远离数字信号线MISO/MOSI/SCLK 这类 SPI 线不要与模拟输入平行走线。软件上降低数据速率到 20SPS开启内置滤波器能明显改善低位跳动。如果还不行检查传感器激励电流是否匹配传感器阻值范围电流过大会导致传感器自热和噪声放大。5.4 软件模拟数据偶尔错位SCLK 边沿采反了现象大部分时间读数正常但偶尔读回来的值是上一次的旧值或者数据整体像被移动了 8 位。用逻辑分析仪抓波形发现 SCLK 和数据关系看起来是对的但芯片手册和实际代码对不上。原因SPI 模式配置错误。ADS1220 常见的用法是模式 1也就是 CPOL0、CPHA1数据在 SCLK 下降沿变化、上升沿被采样。如果你的代码写成模式 0 或模式 3大部分位还能猜对但边界处的移位就会偶发错位。还有一个隐藏坑GPIO 模拟时读字节函数里 SCLK 拉高后立刻读 MISO有些 MCU 的 GPIO 输入在边沿附近采样不稳定需要加 1-2 个 NOP 延时再读。解决用逻辑分析仪对比芯片手册的时序图确认 SCLK 空闲电平是低还是高、数据在哪个边沿有效。读字节函数里在 SCLK 拉高后加两个空指令再读取 MISO给 GPIO 输入电路一点稳定时间。如果改用硬件 SPI 做对照测试确认驱动逻辑本身没问题再回头优化软件模拟的时序。5.5 多次重启后配置丢失复位时序与寄存器写入顺序不对现象程序下载后第一次运行正常但按复位键后读数全乱或 ADC 不工作有时是写入的配置要反复写两遍才生效。原因复位后 ADS1220 还没准备好就收到了配置命令。芯片上电或复位后需要一个短暂的内部初始化时间通常要几毫秒到几十毫秒如果在这期间发送 SPI 命令芯片会忽略或部分接收。另一个可能是 CS 信号在复位期间被噪声拉低芯片误收到随机数据进入异常状态。解决驱动初始化代码里上电后先延时 100ms再发送复位命令再延时 50ms之后才写配置寄存器。如果复位引脚由 MCU 控制确保复位释放后也延时。日常运行时如果不需要复位芯片建议只在初始化时发一次复位命令每次写寄存器前可以先发一个 NOP 或读一次寄存器确认芯片在线避免因为通信异常导致配置静默失败。最后检查 CS 引脚的默认电平不要在芯片未选中时被干扰信号拉低。6. 把软件模拟驱动跑得更稳校验、回调与性能边界驱动基本跑通后下一步就是围绕稳定性和响应速度做工程化收尾。我自己的习惯是任何寄存器的写入之后紧跟着读回并比对发现不一致就重试三次三次都失败就上报错误绝不带着错误配置继续采样。这个习惯救过我很多次尤其是在现场调试时硬件连接松动导致 SPI 通信间歇性异常如果配置静默失败后面所有的数据都是垃圾而你却以为系统在正常工作。中断设计上软件模拟 SPI 和硬件 SPI 的差别在于模拟方式不能进入中断回调去读寄存器因为 GPIO 翻转时序不能被打断。常见做法是 DRDY 引脚接 MCU 的外部中断中断里只置一个标志位主循环检测到标志后执行完整的 SPI 读取流程。这样既保证了时序不被中断破坏又避免了主循环空转等待 DRDY 浪费 CPU。对于 20SPS 的低速率场景CPU 占用率不到 1% 完全够用。还有一个实际项目中踩过的坑软件模拟 SPI 的时钟频率完全由 MCU 主频和代码效率决定不同编译优化等级下同一份代码的 GPIO 翻转速度可能差 30% 以上。所以在性能边界上不要只看 2MHz 的标称值实际 SCLK 频率要在示波器上测一下确保低于芯片上限。如果移植到更低主频的 MCU适当在每字节后加延时把 SCLK 压到 500kHz 到 1MHz稳定性和兼容性反而更好。最后建议保留一个「配置快照」功能把 4 个配置寄存器当前值保存到非易失存储里每次上电读取并和实际写入值比对。ADS1220 的寄存器是易失的掉电即失但通过比对可以发现 MCU 启动时序差异、外部干扰导致的寄存器意外改写。某次排查一个数据随机跳变的问题时就是通过这个快照功能发现稳定的问题——不是 ADC 本身的问题而是某个 GPIO 在这组配置下被复用成了片上外设功能导致 CS 信号被周期性干扰。排查最后靠的是一根逻辑分析仪结论很简单但过程让我养成了新板子调试先读回校验、再跑数据流的习惯。希望帮到你。本文还有配套的精品资源点击获取