简介:基于Arduino的ESP32与ADS1256模数转换系统源码包,主要面向使用Arduino IDE开发ESP32的电子工程师、物联网开发者及嵌入式初学者,用于解决高精度模拟信号采集场景中的转换精度不足、寄存器配置复杂等痛点,可方便地连接各种传感器和信号源。压缩包共11个文件,大小约564KB,核心代码为2个cpp源文件与1个h头文件,实现ADS1256的驱动封装与SPI高速数据读取;同时包含3个readme和1个md说明文档,详述硬件接线、寄存器配置及安装步骤;2张jpg与1张png图片可辅助实物对照,platformio.ini便于多平台工程配置。通过学习本项目,可掌握ADS1256的SPI通信时序、工作模式选择与数据速率设定,并能将传感器电压值转换为数字量直接输出,便于快速集成到其他硬件项目中。目前已有83人学习下载,适合需要低成本实现高精度ADC功能或参考完整驱动实现的开发者使用。
1. 为什么是“ESP32 + ADS1256”而不是直接用内置 ADC
ESP32 自带的 ADC 是 12 位逐次逼近型,量程 0-3.3V,用来检测电池电压、光敏电阻这种低精度需求完全够用,但一旦要接称重传感器、热电偶、PT100 这类微弱信号,你会立刻撞上两个问题:一是分辨率不够,12 位在 3.3V 量程下每个 LSB 约 0.8mV,微小电压变化根本分辨不出来;二是 ESP32 内置 ADC 的线性度和温漂表现不稳定,同一输入电压在不同衰减挡位、不同温度下读出来的值会有肉眼可见的偏差,而且通道之间有串扰。ADS1256 是 TI 的 24 位 Δ-Σ ADC,8 通道(4 路差分或 8 路单端),内置可编程增益放大器(PGA),最高增益 64 倍,输出速率最高 30kSPS,通过 SPI 与主控通信。把它和 ESP32 组合起来,相当于用一个 12 位的内置外设换来了一个工业级数据采集前端,成本增加不多,但精度和灵活性完全是两个量级。这套组合特别适合做多通道传感器采集、工业仪表、实验室数据记录仪,以及任何需要 “uV 级分辨率” 的嵌入式项目。下面按硬件接线、寄存器配置、代码实现、数据读取、校准优化这条线,把这套基于 Arduino 框架(PlatformIO 工程)的源码拆开讲清楚,包括我会在实际项目中改哪些地方。
2. ADS1256 硬件基础与 ESP32 SPI 接线
ADS1256 虽然功能复杂,但引脚不算多。搞清楚每个引脚的作用和上电时序,是驱动它正常工作的前提。ESP32 这边用的是 VSPI(默认 SPI2),默认引脚为 SCK=18、MISO=19、MOSI=23、CS=5,如果你用默认引脚可以不接线,但为了稳定性我一般显式指定。
2.1 ADS1256 关键引脚与功能划分
ADS1256 的引脚分成三组:模拟输入、数字接口、电源与参考。
| 引脚 | 类型 | 功能说明 | 备注 |
|---|---|---|---|
| AVDD / AVSS | 电源 | 模拟电源 +5V / -5V | ADS1256 需要 ±2.5V 至 ±5V,单 5V 供电时 AVSS 接地 |
| DVDD | 电源 | 数字电源 +3.3V 或 +5V | 建议与 ESP32 一样用 3.3V,避免电平不匹配 |
| REFP / REFN | 输入 | 差分参考电压输入 | 推荐 2.5V 参考源,如 REF5025,精度直接影响 ADC |
| AIN0-AIN7 | 输入 | 模拟输入通道 | 可配置为单端或差分输入 |
| SCLK | 输入 | SPI 时钟 | 最高 1.92MHz(PGA=1 时) |
| DIN / DOUT | 输入/输出 | SPI 数据输入/输出 | DOUT 在 DRDY 拉低后可读取 |
| CS | 输入 | 片选,低有效 | 低电平使能 SPI 通信 |
| DRDY | 输出 | 数据就绪信号,低有效 | 每次转换完成后拉低,读完成后再拉高 |
| RESET | 输入 | 复位,低有效 | 至少 4 个时钟周期低电平 |
| SYNC | 输入 | 同步/唤醒 | 用于多片同步或从待机唤醒 |
| PDWN | 输入 | 掉电模式控制 | 拉低进入掉电模式 |
注意 DRDY 是中断信号,不是普通的握手信号。转换完成,DRDY 会从高变低。最好的做法是把它接到 ESP32 的一个 GPIO 上,用轮询或者中断的方式检测下降沿,而不是 sleep 或 delay 盲等。盲等会导致漏读或者读到不完整的数据,这是因为 ADS1256 的数据输出和下一个采样周期是重叠的,如果读得太慢,数据就会被新转换覆盖。
2.2 接线图和电平匹配说明
ADS1256 的数字部分建议接 3.3V,因为 ESP32 的 GPIO 是 3.3V 电平。ADS1256 的数字引脚内部虽然有保护二极管,但长期 5V 灌入会降低可靠性。参考电压 REFP 接 2.5V,REFN 接 GND。模拟电源部分如果手头只有单 5V,可将 AVDD 接 5V、AVSS 接 GND,但要注意这是单极供电,输入信号不能低于 GND。下面是标准接线表:
| ESP32 引脚(GPIO) | ADS1256 引脚 |
|---|---|
| 18 | SCLK |
| 19 | MISO (DOUT) |
| 23 | MOSI (DIN) |
| 5 | CS |
| 34 | DRDY(建议用支持中断的输入引脚) |
| 3.3V | DVDD |
| GND | DGND、AGND |
| 5V | AVDD |
| GND | AVSS(单电源模式接地) |
| 2.5V 参考源 | REFP |
| GND | REFN |
接好线后别急着写代码,先做一个最简单的 SPI 通信测试:通过 SPI 向 DRDY 引脚对应的 GPIO 发送一个高电平,读回 MISO 上的数据,如果全 0xFF 或全 0x00,说明 SPI 时钟极性、相位或电平转换有问题。ADS1256 的 SPI 模式是 CPOL=0,CPHA=1(SPI_MODE1),即时钟空闲为低电平,数据在第二个边沿采样,这一点必须和 ESP32 的 SPI 配置一致。
提示:DRDY 一定要接中断引脚,ESP32 的 GPIO 34-39 是纯输入引脚,不能输出但适合做中断检测,常见的板子如 NodeMCU-32S、ESP32 DevKitC 都可用。
2.3 电源与参考电压的坑
ADS1256 对模拟电源和参考电压的噪声非常敏感。参考电压的噪声会直接叠加到转换结果上:如果参考电压有 1mV 的纹波,即便 ADC 有 24 位分辨率,最终结果也会被这个纹波吃掉低几位。我通常会这样做:
- 模拟电源 AVDD 和数字电源 DVDD 分开走线,各自加 10uF + 0.1uF 去耦电容。
- 参考电压用独立的高精度基准源(如 REF5025),而不是直接从 ESP32 的 3.3V 分压。
- AVSS 与 AGND 单点连接,避免数字地噪声通过地平面串入模拟回路。
如果测量精度要求不高(0.1% 级别),也可以直接使用 5V 电源分压到 2.5V,但温度漂移会比较大。对于长期运行的采集设备,加一个基准源芯片是非常值得的投资。
3. 源码工程结构与 ADS1256 驱动实现
下载解压之后,src 目录下有三个文件:ADS1256.h、ADS1256.cpp、main.cpp,根目录有一个 platformio.ini。这不是 Arduino 传统的 .ino 单文件结构,而是 PlatformIO 工程组织方式。用 PlatformIO 的好处是库依赖和编译选项都写在配置文件中,多人协作或换电脑时不需要手动安装库。
3.1 platformio.ini 配置解析
[env:esp32dev] platform = espressif32 board = esp32dev framework = arduino monitor_speed = 115200 build_flags = -DCORE_DEBUG_LEVEL=3platform 指定乐鑫官方 espressif32 平台,framework=arduino 说明这个工程跑在 Arduino 框架下。这里没有指定 lib_deps,因为 ADS1256 驱动是直接写在 src 里的,不依赖第三方库。build_flags 里的 CORE_DEBUG_LEVEL=3 会打开 ESP32 的日志输出,调试 SPI 时序时很有用。
3.2 ADS1256.h 头文件与寄存器定义
// ADS1256.h #pragma once #include <Arduino.h> #include <SPI.h> #define ADS1256_CS 5 #define ADS1256_DRDY 34 // 寄存器地址 #define REG_STATUS 0x00 #define REG_MUX 0x01 #define REG_ADCON 0x02 #define REG_DRATE 0x03 #define REG_IO 0x04 #define REG_OFC0 0x05 #define REG_OFC1 0x06 #define REG_FSC0 0x07 #define REG_FSC1 0x08 #define REG_FSC2 0x09 // 命令字 #define CMD_WAKEUP 0x00 #define CMD_RDATA 0x01 #define CMD_RDATAC 0x03 #define CMD_SDATAC 0x0F #define CMD_RREG 0x10 #define CMD_WREG 0x50 #define CMD_SELFCAL 0xF0 #define CMD_SELFOCAL 0xF1 #define CMD_SELFGCAL 0xF2 #define CMD_SYNC 0xFC #define CMD_STANDBY 0xFD #define CMD_RESET 0xFE class ADS1256 { public: ADS1256(int csPin = ADS1256_CS, int drdyPin = ADS1256_DRDY); bool begin(); void reset(); void setChannel(uint8_t channel, bool differential = false); void setPGA(uint8_t pga); void setDataRate(uint8_t drate); float readVoltage(uint8_t channel); void selfCalibrate(); int32_t readRaw(); void waitDRDY(); private: void writeReg(uint8_t reg, uint8_t val); uint8_t readReg(uint8_t reg); uint8_t _csPin, _drdyPin; float _vref; };头文件里值得注意两点:一是命令字CMD_RREG 0x10和CMD_WREG 0x50不是完整命令,后面还要跟寄存器地址。比如读寄存器 0x02(ADCON)的命令是0x10 | 0x02,再跟一个字节表示连续读的寄存器个数减 1。二是 CHANNEL 的编号和 MUX 寄存器不是线性对应关系,实际映射要查表。
3.3 ADS1256.cpp 驱动实现与代码逻辑
看 main.cpp 和 ADS1256.cpp 的核心逻辑,重点是waitDRDY()、writeReg()和readRaw()这三个函数,它们决定了数据能否正确读出。
// ADS1256.cpp - 关键实现 void ADS1256::waitDRDY() { // 轮询 DRDY 引脚,低电平表示数据就绪 uint32_t timeout = millis() + 1000; while (digitalRead(_drdyPin) == HIGH) { if (millis() > timeout) { Serial.println("DRDY timeout"); break; } yield(); } } void ADS1256::writeReg(uint8_t reg, uint8_t val) { waitDRDY(); // 写寄存器前必须等待当前转换完成 SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); digitalWrite(_csPin, LOW); SPI.transfer(CMD_WREG | reg); // WREG 命令 + 寄存器地址 SPI.transfer(0x00); // 连续写个数 - 1,这里只写 1 个 SPI.transfer(val); digitalWrite(_csPin, HIGH); SPI.endTransaction(); } int32_t ADS1256::readRaw() { waitDRDY(); SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); digitalWrite(_csPin, LOW); SPI.transfer(CMD_RDATA); // 读取一次转换结果 uint32_t h = SPI.transfer(0); // 先读高位字节 uint32_t m = SPI.transfer(0); uint32_t l = SPI.transfer(0); digitalWrite(_csPin, HIGH); SPI.endTransaction(); // 24 位有符号整数扩展为 32 位 int32_t raw = (h << 16) | (m << 8) | l; if (raw & 0x800000) { raw |= 0xFF000000; // 符号扩展 } return raw; }关键逻辑说明:
- ADCON 寄存器的 Bits 7:6 是 CLKOUT 时钟输出使能,如果你没用 ADS1256 的 CLKOUT 去驱动外部器件,应该把它设为 00。
ADS1256_MUX通道配置只需要在设置后触发一次 SYNC 命令真正生效,需要调用writeReg(REG_MUX, (P_AIN0 | N_AINCOM))后,再发一个CMD_SYNC让新的通道切换立即生效,否则要等到当前转换周期自然结束,这在连续转换模式下会引入最长 1/DRATE 的延迟。SPISettings(1920000, MSBFIRST, SPI_MODE1)这行配置很重要。1920000 是时钟频率,ADS1256 最高支持到 1.92MHz(PGA=1 时),超过这个频率会导致通信错误;SPI_MODE1 表示 CPOL=0、CPHA=1,也就是时钟空闲低电平,第二个边沿采样,这是 ADS1256 要求的工作模式。
3.4 寄存器配置速查表
ADS1256 的寄存器不多,但每个寄存器的每个位都有意义。源码里最核心的配置是 STATUS、MUX、ADCON、DRATE 这四个:
| 寄存器 | 地址 | 功能 | 常用配置值 | 说明 |
|---|---|---|---|---|
| STATUS | 0x00 | 状态控制:缓冲、校准、复位等 | 0x00 | Bits 7:6 保留;Bit 5 禁止缓冲;Bits 3:2 校准状态 |
| MUX | 0x01 | 输入通道多路选择 | 见 3.5 节 | 高 4 位正输入,低 4 位负输入 |
| ADCON | 0x02 | 时钟输出、PGA、传感器检测 | 0x20(PGA=32) | Bits 7:6 时钟输出;Bits 5:3 PGA;Bits 2:0 传感器检测 |
| DRATE | 0x03 | 数据输出速率 | 0xF0(30000SPS) | 不同值对应不同采样率 |
DRATE 寄存器在高精度场景要按需选择,不要一味追求最高速率。ADS1256 有效位数 ENOB 与数据速率强相关:30kSPS 时有效位数约 16.5 位,而 10SPS 时可以达到 23 位以上。如果你采集的是温度这种缓变信号,10-100SPS 就足够了;只有振动、电流波形这类高频信号才需要拉高速率。
| DRATE 值 | 数据速率 | 有效分辨率(典型) |
|---|---|---|
| 0xF0 | 30000 SPS | 16.5 位 |
| 0xE0 | 15000 SPS | 18.5 位 |
| 0xD0 | 7500 SPS | 19.5 位 |
| 0xC0 | 3750 SPS | 20.5 位 |
| 0xA0 | 1000 SPS | 21.5 位 |
| 0x80 | 100 SPS | 23 位 |
| 0x72 | 50 SPS | 23 位 |
| 0x63 | 25 SPS | 23.5 位 |
| 0x53 | 10 SPS | 24 位 |
3.5 通道选择:单端与差分模式
MUX 寄存器的配置是新手最容易出错的地方。ADS1256 的 MUX 寄存器高 4 位选择正输入(PSEL),低 4 位选择负输入(NSEL)。AIN0-AIN7 分别对应 0000-0111。单端模式时负输入固定接 AINCOM(1111),差分模式时负输入接另一个通道。
// 单端模式:AIN0 对 AINCOM,ADC 数值 = (V_AIN0 - V_AINCOM) * PGA * 2^23 / 2.5 // 差分模式:AIN0 - AIN1,ADC 数值 = (V_AIN0 - V_AIN1) * PGA * 2^23 / 2.5 void ADS1256::setChannel(uint8_t channel, bool differential) { uint8_t mux; if (differential) { // 偶数通道对下一个奇数通道:CH0-CH1, CH2-CH3... mux = (channel << 4) | (channel + 1); } else { mux = (channel << 4) | 0x0F; // 0x0F = AINCOM } writeReg(REG_MUX, mux); sendCommand(CMD_SYNC); // 立即生效 sendCommand(CMD_WAKEUP); // 退出 SYNC 后的待机 }注意:差分模式下通道编号只能取 0、2、4、6,分别对应 AIN0-AIN1、AIN2-AIN3、AIN4-AIN5、AIN6-AIN7。如果传一个奇数通道进来,比如 setChannel(1, true),那么高 4 位是 1,低 4 位是 2,实际测量的是 AIN1 和 AIN2 之间的电压,这可能不是你想要的。
4. 数据采集实战:从读取原始值到电压换算
ADS1256 的转换结果是二进制补码格式,需要转成 int32_t 类型再用公式换算为实际电压。换算公式为:电压 = ADC 数值 / 2^23 * 参考电压 / PGA。这个公式中的 2^23 是 24 位满量程的一半(二进制补码从 -8388608 到 8388607)。
4.1 main.cpp 完整读取流程
// main.cpp #include <Arduino.h> #include <ADS1256.h> ADS1256 adc; void setup() { Serial.begin(115200); SPI.begin(18, 19, 23, 5); // SCK, MISO, MOSI, CS delay(500); if (!adc.begin()) { Serial.println("ADS1256 init failed"); while (1) { delay(100); } } adc.setPGA(0x02); // PGA = 4 adc.setDataRate(0x80); // 100 SPS adc.setChannel(0, false); // 单端 AIN0 adc.selfCalibrate(); // 上电自校准 } void loop() { int32_t raw = adc.readRaw(); float vref = 2.5; // 参考电压 2.5V int pga = 4; float voltage = raw * vref / 8388608.0f / pga; Serial.print("RAW="); Serial.print(raw); Serial.print(" VOLT="); Serial.print(voltage, 8); // 打印 8 位小数观察噪声 Serial.println(" V"); delay(10); }这个程序有一个细节:SPI.begin(18, 19, 23, 5)在 setup 中显式指定 SPI 引脚,可以避免不同的 ESP32 开发板默认引脚不同的兼容性问题。readRaw() 返回的是二进制补码格式的 int32_t,电压换算时除以 8388608.0f(即 2^23)得到归一化的小数,再乘参考电压和除以 PGA 增益。为什么是 2^23 而不是 2^24?因为 ADS1256 的输出范围是 -Vref 到 +Vref(比例于 PGA),对应 -8388608 到 8388607,所以换算分母是 2^23。
4.2 单端输入与 AINCOM 的接线注意事项
使用单端模式时,AINCOM 引脚需要直接接到模拟地。但很多信号源的 “地” 并不干净,可能带有共模噪声。如果测量对象和 ADS1256 的供电共地不可靠,串行噪声会很大,解决办法是:
- 如果信号源和 ADS1256 供电来自同一块板,AINCOM 直接连 AGND。
- 如果信号源是独立的(比如电池供电的传感器),先把信号源的 GND 和 ADS1256 的 AGND 接在一起,再接入 AINCOM,否则信号端的电压超出 ADC 的共模输入范围。
差分模式没有这个问题,但要求两个输入引脚的电压都在 GND 和 AVDD 之间。超出这个范围就需要做电平偏移或者加运放电路,这也是为什么 ADS1256 的前端通常会加一个一阶 RC 低通滤波器。
4.3 差分模式接线示例:称重传感器
称重传感器输出就是一个典型的差分信号,电桥输出电压通常只有 1-2mV/V,也就是说 5V 激励下满量程也只有 10mV。直接接单端模式会把地弹噪声和传感器的共模电压一起采进去,用差分模式加 PGA 增益才是正确做法。
接线:
| 称重传感器 | ADS1256 |
|---|---|
| 激励+ | 5V |
| 激励- | GND |
| 信号+ | AIN0 |
| 信号- | AIN1 |
代码改为:
adc.setPGA(0x05); // PGA = 32,放大微弱信号 adc.setDataRate(0x80); // 100 SPS adc.setChannel(0, true); // 差分模式 AIN0 - AIN1PGA=32 时满量程约 ±78mV,称重传感器的 10mV 输出能占到约 1/8 量程,有效分辨率大约是 20 位,对应到 100g 量程的传感器可以达到约 0.1mg 的理论分辨率,当然实际还要看机械结构的噪声。如果 PGA=1,则 10mV 信号只占满量程的 0.3%,有效分辨率直接降低 5 位左右。
4.4 关于 ADS1256 的 SPI 连续读模式
源码里用的是单次读(CMD_RDATA)方式,即每次转换完成后发一次读取命令,读三个字节。ADS1256 也支持CMD_RDATAC连续读模式,即一次性发送命令后,之后每次 DRDY 下降沿只需读三个字节即可,不需要再发命令。连续读模式在高速采集(>1000SPS)场景下能减少 SPI 命令开销:
// 连续读取模式示例 SPI.transfer(CMD_RDATAC); // 进入连续读模式 // 之后每次 DRDY 拉低后,直接读 3 个字节 int32_t continuousRead() { waitDRDY(); SPI.beginTransaction(SPISettings(1920000, MSBFIRST, SPI_MODE1)); digitalWrite(_csPin, LOW); uint32_t h = SPI.transfer(0); uint32_t m = SPI.transfer(0); uint32_t l = SPI.transfer(0); digitalWrite(_csPin, HIGH); SPI.endTransaction(); // 符号扩展后返回 return (h << 16) | (m << 8) | l; }注意连续读模式一旦进入,要切换通道或者读寄存器,必须先发CMD_SDATAC退出连续读状态。如果忘记退出,向寄存器写的任何命令都会被当作无效数据忽略,这是一个很隐蔽的 bug。实际上,我通常只在超出 1000SPS 采样时才用连续读模式,低速应用直接用 RDATA 更安全。
5. 标定、噪声排查与精度优化
这套系统如果只按默认配置跑,能出数但未必能出准数。在实际项目中,哪怕是同一批次的 ADS1256,偏移误差和增益误差也会有差异。要让 24 位的分辨率真正转化为精度,必须做标定和噪声优化。
5.1 自校准命令的原理调用时机
ADS1256 提供 SELFOCAL(自偏移校准)和 SELFGCAL(自增益校准)命令,分别校准偏移和增益。偏移校准是把输入短接到内部的一个参考点,测量出系统偏移并写入 OFC 寄存器;增益校准是把输入切换到参考电压,测量实际增益并写入 FSC 寄存器。源码包里的selfCalibrate()其实就是先发CMD_SELFOCAL再发CMD_SELFGCAL,但有一个关键点:校准前必须已经设置好 PGA 和 DATARATE,因为校准结果是存放在当前 PGA 和采样率下的。
准备经验:
- 每次改变 PGA 或数据速率后都要重新做一次自校准。ADS1256 的 OFC 和 FSC 寄存器是针对特定配置的,不重新校准就切换增益,读出的数据可能会偏差几个 LSB。
- 上电后等待 100ms 再做校准,因为模拟电源和参考电压上电瞬间会有短暂波动。
- 如果校准后读数仍有固定偏移,可以自己做一个“软件零点校准”:输入端接地,读 100 个值取平均,存这个偏移量,之后每次读数减去它。
// 软件零点校准示例 int32_t zero_offset = 0; const int N = 100; for (int i = 0; i < N; i++) { zero_offset += adc.readRaw(); delay(2); } zero_offset /= N; // 使用 int32_t corrected = adc.readRaw() - zero_offset;5.2 数据跳动的根源:是噪声还是时序问题
读数表现为最低几位跳动时,先别急着怀疑芯片质量。最常见的三类原因按概率排序:
第一,模拟前端的抗混叠滤波缺失。ADS1256 是 Δ-Σ ADC,对高频噪声有较好的抑制能力,但如果信号源本身带有较大的高频干扰(如 PWM 驱动、开关电源),这些噪声会被折叠到低频段,表现为读数在几个码值之间随机跳动。解决方法是在每个模拟输入端到 AGND 之间加一个 RC 低通滤波,比如 100Ω 电阻串联加 1uF 电容到地,截止频率约 1.6kHz,可以有效滤除大部分高频干扰。
第二,SPI 时序不满足保持时间。ESP32 的 SPI 主模式默认时序是稳定的,但如果接了很多跳线、线缆较长(超过 20cm),SCLK 边沿和数据线上的信号会产生偏差,导致读回的数据偶尔错一个位,表现为读数周期性跳变。这时可以把 SPI 时钟降到 400kHz 试试看问题是否消失,如果消失,说明是信号完整性问题而不是 ADC 问题。
第三,参考电压噪声。前面说过,参考电压的纹波直接进到转换结果里。用示波器测 REFP 与 AGND 之间的纹波,如果超过 1mV,就要增加去耦电容或换更好的基准源。
// 数字滤波:移动平均(滑动窗口) #define WINDOW_SIZE 20 int32_t samples[WINDOW_SIZE]; int index = 0; int32_t filteredRead() { samples[index] = adc.readRaw(); index = (index + 1) % WINDOW_SIZE; int64_t sum = 0; for (int i = 0; i < WINDOW_SIZE; i++) { sum += samples[i]; } return (int32_t)(sum / WINDOW_SIZE); }移动平均是牺牲时间分辨率换取幅度分辨率的最简单手段:把采样率提高到 N 倍,然后 N 点取平均,等效于将白噪声降低约 sqrt(N) 倍。20 点平均大约能把随机噪声降低 4.5 倍,但响应时间也慢了 20 倍,所以要根据信号的带宽要求来选择窗口大小。
5.3 量程压缩:PGA、参考电压与信号调理的匹配
很多初学者拿到 24 位 ADC 就以为多小的信号都能直接测,忽略了 PGA 的放大能力和信号源的输出阻抗。ADS1256 的 PGA 虽然增益最高 64 倍,但输入等效阻抗会随 PGA 增益变化而改变,高增益时输入阻抗下降(几十 kΩ),如果信号源是高阻输出(比如某些电化学传感器),直接接入会因阻抗不匹配导致明显的增益误差和失真。
解决方法是加一级运放缓冲器(如 OPA2277、AD8628 等),把信号源的高阻输出转换成低阻输出,再接 ADS1256。增益分配原则是:传感器输出信号幅度经过运放后放大到 ADS1256 满量程的 50%-80% 范围内,避免过大削波,过小浪费分辨率。比如热电偶在正常测量范围内输出约 0-40mV,先用运放放大 50 倍到 0-2V,然后 ADS1256 设置 PGA=4,满量程为 ±10.4V(PGA 会缩放),这样整个链路的有效分辨率最高,ADC 自身也不会饱和。
5.4 使用 Wokwi 仿真平台预验证硬件代码
在线仿真平台如 Wokwi 现在已经支持 ESP32 和 ADS1256,在你还没有硬件的情况下,用它来验证 SPI 时序、寄存器配置逻辑是完全可行的。Wokwi 结合 Arduino 或 PlatformIO 项目可以直接跑通源码。
在 Wokwi 里对这套工程有两个必须注意的地方:
- ADS1256 仿真模型不支持所有寄存器组合,比如某些写寄存器操作会被静默忽略,但 SPI 波形能帮助你判断命令字是否正确。
- 仿真时没有模拟噪声,读出的值可能是完全恒定的,所以不能用仿真结果来评价真实噪声水平。
用仿真平台做前期逻辑验证,把精力放在代码流程、状态机和命令时序上,等真实硬件到手后再做精度调优,是更高效的路径。
5.5 附:用示波器快速检查 DRDY 与 SCLK 时序
如果手头有示波器,可以快速检查 ADS1256 是否正常工作的三处关键波形:
- 上电后 DRDY 引脚应该出现周期性的下降沿脉冲,频率接近当前 DRATE 值。如果没有脉冲,说明芯片没有启动转换,检查电源和复位引脚。
- 发送命令期间 CS 应为低电平,SCLK 应为规则的方波信号。如果 CS 和 SCLK 方向不对,代码中 CS 引脚配置可能有误,应该检查 pinMode 和 digitalWrite 的电平逻辑。
- 读取时 MISO(DOUT)上应能看到三个字节的数据。如果 MISO 是恒定高电平或恒定低电平,说明 DOUT 引脚虚焊或 SPI 模式设置错误。
这一步排查完成后,再回到代码里做数据标定循环,系统才真正算调通。
本文还有配套的精品资源,点击获取