资讯动态

ADS1118源代码实战:SPI时序与配置字全解析

发布时间:2026/10/15 11:56:13 来源:尧图企业网站定制
简介针对TI公司16位高精度ADC芯片ADS1118这份基于51单片机的驱动源代码包提供了完整可用的采集方案。资源面向嵌入式初学者及需要做电压精确测量的开发者代码注释详尽结构清晰涵盖初始化、配置寄存器、读取转换结果及通过串口将电压值发送到PC端显示等环节便于理解高精度ADC与单片机通信的典型流程。压缩包共17个文件体积仅41KB以C语言源文件.c和头文件.h为核心另含Keil工程文件.uvproj、编译生成的HEX/OBJ/LST/M51文件及备份文件.bak既可直接阅读源码也可辅助验证编译与烧录流程。目前已有851人学习下载。通过这份代码可掌握ADS1118的寄存器配置、串行接口读写时序以及51单片机经UART向PC发送数据的方法工程备份文件还能帮助对照修改记录快速用于课程设计或低功耗高精度采集项目。1. 做热电偶、电池巡检都会翻到的它ADS1118和“源代码”到底指什么做工业采集或者仪器仪表的工程师大概率在某个项目里翻过这颗芯片的手册16位ADC、内置PGA、自带温度传感器、SPI接口ADS1118几乎是为低速高精度场景准备的。网上搜“ads1118源代码”搜出来的东西很杂有寄存器配置片段有基于某款开发板的完整驱动也有写着“实测可用”但注释全无的压缩包。问题是很多代码抄过来能编译接上芯片却读不出正确数值或者数据跳得离谱。这颗芯片的坑不在“配置”而在读数的时序和参考电压的理解。ADS1118是ΔΣ型ADC不是逐次逼近型它的分辨率是16位但在2V满量程下LSB只有约31.25μV实际有效位数受噪声影响通常在14到15位之间。如果项目是测热电偶毫伏信号、电池单体电压、桥式应变片这颗芯片的资源和价格是够用的如果是在高速采样或者动态信号分析场景直接换更高带宽的ADC更合适。这篇文章用一套可直接复制的源代码为主线把芯片配置字、SPI时序、自校准、温度传感器读数、PCB布线全部串起来。看完你能直接把代码搬进自己的工程里。需要说明的是文中代码不依赖特定厂商的库函数用最基础的GPIO模拟SPI实现方便移植到任何MCU上。2. ADS1118的配置字与读出时序先把寄存器字节掰开揉碎2.1 配置寄存器的16位布局哪个位写错会读回全零ADS1118的所有设置都通过一次16位写操作完成芯片没有单独的寄存器地址概念SPI传输一次就是完整的一个配置字。这16位里最高位是开始转换位其余部分分别是MUX通道选择、PGA增益、操作模式、数据速率、比较器模式等。网上所谓“源代码”如果只是复制了一个十六进制数比如0x8B0A那换一个场景直接翻车因为你不知道这串值为什么是这样。先看配置位的完整布局这是任何操作的前提Bit位置名称含义15开始转换/读状态写1启动单次转换读数据时此位为转换状态14:12MUX输入多路复用选择000为AINP-AINN差分100为AIN0对地等11:9PGA增益设置000±6.144V001±4.096V010±2.048V011±1.024V100±0.512V101±0.256V8操作模式0连续转换1单次模式7:5数据速率0008SPS00116SPS01032SPS01164SPS100128SPS101250SPS110475SPS111860SPS4比较器模式通常置1传统比较器或者窗口模式见具体应用3比较器极性0低电平有效1高电平有效2锁存比较器0不锁存1锁存1:0比较器阈值一般置00PGA增益和参考电压的关系是新手最容易搞混的地方。ADS1118内部分为两个参考源默认是内部4.096V参考电压但增益设置并不是改变参考电压而是改变输入信号在进入ADC前的衰减倍数。例如PGA010±2.048V实际等效满量程是±2.048V对应内部参考4.096V的满量程。如果你配置PGA000±6.144V输入噪声会明显变大因为信号被衰减了。同理测热电偶时一般选±0.256V或±0.512V档这样才能把毫伏级信号放大到ADC的满量程范围内。配置字的发送顺序决定了后面读书数的格式这一点在几乎所有搜索到的代码里都被轻轻带过。发送配置字后如果是单次模式必须等待转换完成而转换完成的信号就是DOUT引脚在配置字发送完成后拉低。因此SPI时序里先写配置字再等待DOUT变低然后启动下一次SPI传输读取16位结果读回来的第一个字节的最高位就是转换状态的标志位。这个标志位在单次模式下读完一次就自动清除所以你每次读之前都要重新写一遍配置字。2.2 SPI读时序里的“隐藏字节”和第16个SCLK很多复制过来的ADS1118代码读回来的数值一直是65535或者0原因在于SPI时序理解错了。ADS1118的DOUT引脚是双向的发送配置字时它作为输入读取结果时它作为输出同时它还兼任转换完成中断线。这个复用逻辑让不少工程师在这个芯片上卡了一周。具体时序是这样的CS拉低后第一个16位时钟发送配置字此时芯片处于接收状态。配置字的第15位置1表示启动转换发送完成后CS拉高或者保持低都可以但关键在于转换结束后DOUT会被芯片拉低。如果是连续模式DOUT在每个转换结束后拉低直到数据被读出。读取数据的操作是再次拉低CS发送16个SCLK芯片在这16个时钟内把上次转换结果按“高8位先出”的顺序送出来。真正被人忽视的是第16个SCLK。在读取数据时ADS1118会在第16个SCLK的下降沿把内部移位寄存器重置也就是说如果你在第16个时钟之前就结束了SPI传输数据可能不完整。正确的做法是SPI通信函数必须完整传输16个时钟不多不少并且时钟极性CPOL和相位CPHA必须严格按照手册配置。ADS1118要求SPI模式为CPOL0、CPHA1也就是空闲时钟为低电平数据在第二个边沿采样。如果你的项目的SPI配置是模式0CPOL0CPHA0读回来的数据多半是错的这也解释了为什么有人用GPIO模拟SPI反而能正常工作而用硬件SPI翻车的案例比比皆是。// 读取ADS1118转换结果的16位数据假设配置字已经发送完成并且转换结束 uint16_t ads1118_read_raw(void) { uint16_t raw 0; // 拉低CS开始传输 CS_LOW(); // 发送16个SCLK读取DOUT数据MSB在前 for (int i 0; i 16; i) { uint8_t bit 0; SCLK_LOW(); // 延时半个时钟周期 delay_us(1); // 在SCLK上升沿之前采样DOUT因为CPHA1数据在第二个边沿有效 if (DOUT_READ()) { bit 1; } SCLK_HIGH(); raw (raw 1) | bit; delay_us(1); } CS_HIGH(); return raw; }这段代码用翻转时钟的方式避免了硬件SPI模式配置的麻烦。逻辑上每拉高一次SCLK就采一个DOUT位16次之后拿到完整的16位数据。如果你使用硬件SPI必须确认主控的SPI外设支持模式1CPOL0CPHA1而且通信速率不要超过芯片的极限值。手册给出的SCLK最大频率是4MHz实际用2MHz左右比较稳尤其当你的PCB走线比较长时降速可以显著减少因信号抖动造成的误码。单次转换模式的完整读流程必须是拉低CS → 发送16位配置字启动转换→ 等待DOUT变低 → 再次拉低CS → 发送16个SCLK读取结果 → 拉高CS。如果在等待DOUT变低的过程中加了超时处理这个流程就是可上生产线的代码。别试图在连续模式下省略等待DOUT拉低是硬件状态不是软件标志位你无法通过查询寄存器判断转换是否完成只能靠这根线的电平这就是这颗芯片最容易被骂“玄学”的地方。3. 从零写ADS1118 SPI驱动最小文件、关键函数与参数选择3.1 一套不依赖厂商库的GPIO模拟SPI驱动去掉所有黑匣子在工程中我一般建议先用GPIO模拟SPI跑通芯片再去优化成硬件SPI。这样做的好处是遇到问题你能用逻辑分析仪抓每一根线的时序不会被芯片外设的FIFO或者DMA配置干扰判断。ADS1118这颗芯片本身速度不高模拟SPI完全够用即使吞掉几百微秒的IO翻转时间也不会影响采集周期。下面这个驱动文件是完整可编译的最小实现没有使用任何厂商SDK的抽象层仅有基础GPIO操作函数以及延时函数。你需要根据自己的MCU头文件替换引脚定义和延时实现。// ads1118_driver.h #ifndef ADS1118_DRIVER_H #define ADS1118_DRIVER_H #include stdint.h // 定义引脚操作把这些宏换成你的MCU寄存器操作 #define ADS1118_CS_LOW() GPIO_WriteLow(GPIOB, GPIO_PIN_0) #define ADS1118_CS_HIGH() GPIO_WriteHigh(GPIOB, GPIO_PIN_0) #define ADS1118_SCLK_LOW() GPIO_WriteLow(GPIOB, GPIO_PIN_1) #define ADS1118_SCLK_HIGH() GPIO_WriteHigh(GPIOB, GPIO_PIN_1) #define ADS1118_DIN_HIGH() GPIO_WriteHigh(GPIOB, GPIO_PIN_2) #define ADS1118_DIN_LOW() GPIO_WriteLow(GPIOB, GPIO_PIN_2) #define ADS1118_DOUT_READ() GPIO_ReadInputDataBit(GPIOB, GPIO_PIN_3) // 配置字宏定义组合你需要的参数 #define ADS1118_CFG_START (1 15) #define ADS1118_CFG_MUX_DIFF0 (0x00 12) // AIN0-AIN1差分 #define ADS1118_CFG_MUX_DIFF1 (0x01 12) // AIN2-AIN3差分 #define ADS1118_CFG_MUX_AIN0 (0x04 12) // AIN0对GND #define ADS1118_CFG_PGA_6144 (0x00 9) #define ADS1118_CFG_PGA_256 (0x05 9) // ±0.256V适合热电偶 #define ADS1118_CFG_MODE_SINGLE (1 8) #define ADS1118_CFG_MODE_CONT (0 8) #define ADS1118_CFG_DR_8SPS (0x00 5) #define ADS1118_CFG_DR_128SPS (0x04 5) #define ADS1118_CFG_DR_860SPS (0x07 5) uint16_t ads1118_read_raw_single(uint16_t config_word); float ads1118_to_voltage(uint16_t raw, float full_scale_voltage); int16_t ads1118_raw_to_signed(uint16_t raw); #endif对应实现文件中的核心函数是发送配置字并读取结果。这里要特别注意读取结果时的SPI时钟必须完整16个并且发送和接收共用同一套GPIO翻转逻辑。下面是实现// ads1118_driver.c #include ads1118_driver.h static uint16_t ads1118_spi_transfer16(uint16_t data) { uint16_t received 0; ADS1118_CS_LOW(); // 发送16位数据MSB在前 for (int i 15; i 0; i--) { ADS1118_SCLK_LOW(); if (data (1 i)) { ADS1118_DIN_HIGH(); } else { ADS1118_DIN_LOW(); } // 数据建立时间根据你的MCU主频调整 for (volatile int d 0; d 10; d); ADS1118_SCLK_HIGH(); // 在SCLK上升沿后的高电平期间读取DOUT received 1; if (ADS1118_DOUT_READ()) { received | 1; } for (volatile int d 0; d 10; d); } ADS1118_SCLK_LOW(); ADS1118_CS_HIGH(); return received; } uint16_t ads1118_read_raw_single(uint16_t config_word) { config_word | ADS1118_CFG_START; // 确保启动位被置1 ads1118_spi_transfer16(config_word); // 等待DOUT变低表示转换完成超时保护防止死循环 uint32_t timeout 100000; while (ADS1118_DOUT_READ() timeout--) { // 空等DOUT拉低表示转换完成 } if (timeout 0) return 0xFFFF; // 此时DOUT为低读取16位转换结果此值同时作为下一次通信的数据但会被忽略 uint16_t raw ads1118_spi_transfer16(0x0000); return raw; } int16_t ads1118_raw_to_signed(uint16_t raw) { // 数据用二进制补码表示直接转换为int16_t return (int16_t)raw; } float ads1118_to_voltage(uint16_t raw, float full_scale_voltage) { int16_t signed_raw (int16_t)raw; // 满量程对应的是正负满量程值所以分母是32768 return (float)signed_raw * full_scale_voltage / 32768.0f; }这段代码里的ads1118_spi_transfer16函数同时完成发送和接收。发送配置字时读回的数据无意义读取数据时发送的数据应设为0但芯片不关心你的发送内容。延时循环的周期数取决于MCU的运行频率如果跑在72MHz以上10个空循环约等于零点几微秒可以满足时钟要求。如果你用的是低速MCU比如8位单片机跑1MHz延时循环反而会太慢但不会造成错误只是单次转换耗时变长。注意单次模式下整个转换周期包括配置、等待、读取三个部分最大耗时取决于数据速率8SPS时一次转换约125ms860SPS时约1.2ms。参数设置是这套代码里唯一需要动脑的地方。MUX选择差分还是单端决定了你可测信号的范围和精度。测热电偶我通常选AIN0-AIN1差分PGA设±0.256V速率设128SPS这样既能滤掉一部分工频干扰又不会让数据速率太低导致响应慢。测电池电压换PGA±4.096V单端接法。这个选择不是拍脑袋而是信号幅度经过计算后必须落在PGA档的线性范围内。热电偶的K型在1000°C时输出电压约41mV用±0.256V档时信号只占满量程的16%有效分辨率最高如果你误用±6.144V档同样的信号只占0.67%量化噪声占主导16位分辨率实际能用的不到10位这就是同一个芯片在不同人手上有天壤之别的原因。3.2 连续模式与单次模式什么时候不能图省事用连续大部分网上搜到的源码默认用连续转换模式因为代码写起来少一步等待主循环里读一次数据就返回。但连续模式有一个隐藏的坑如果你在开机后立刻读数据读回来的可能是上一次配置的参数下转换的旧值甚至包含噪声未稳定的数据。另外连续模式下如果你改变了MUX或者PGA配置芯片需要几个转换周期才能稳定输出这在高速采集切换通道的场景下尤其致命。单次模式看似多了一次写配置字的操作但它有一个硬件上的天然优势每次读取结果都严格对应一次配置参数不会串数据。而且单次模式天然省电因为大部分时间芯片处于空闲状态。低功耗设备比如电池供电的采集器单次模式是唯一正确的选择。连续模式适合用在固定通道、固定增益、程序启动后不再改变参数的场景比如长期记录环境温度。ADS1118的连续模式下还有一个“转换结果更新”机制每次读取操作返回的是上一次转换的结果而且读取动作本身不会触发新转换新转换是在上一次转换完成后自动开始的。这意味着你读到的数据永远是“上一次完成的转换”的值如果你在主循环里循环读数据实际看到的数据会滞后一个转换周期。对于温度这类缓慢变化的信号这无所谓但对于快速变化的电压你会感觉数据“延迟”了其实不是芯片慢而是模式选择导致的固有行为。// 连续模式初始化与读取示例 void ads1118_continuous_start(uint16_t config_no_start) { // 注意连续模式配置字中不置START位位15为0 ads1118_spi_transfer16(config_no_start); } // 连续模式下读数据读回的是上一次转换的结果 uint16_t ads1118_continuous_read(void) { // 清零命令会被忽略芯片自动开始下一次转换 return ads1118_spi_transfer16(0x0000); }这段代码揭示了连续模式的真实操作你只发送一次配置字启动连续转换之后每次读结果只需发起一次SPI读取。DOUT线在每次转换完成时拉低但这根线在连续模式下会反复变化软件上无法直接用它作为“新数据就绪”的提示除非你用DOUT的下降沿触发外部中断。更常见的方法是定时读比如每100ms读一次不管DOUT电平如何。如果DOUT高电平持续超过一个转换周期的时间说明芯片可能进入了某种异常状态此时需要重新发送配置字复位。我在某项目里遇到过多次连续模式跑几个小时之后数据变成0的问题用逻辑分析仪抓线发现SCLK上多了毛刺芯片内部状态机错乱之后统一改单次模式再没出过这个故障。4. 自校准、温度传感器与数据率让16位分辨率真的可用的三个调节点4.1 内置温度传感器不用白不用的片上校准源ADS1118内部集成一个温度传感器测量范围是-40°C到125°C精度在手册上标称±0.5°C典型值。这个温度传感器不是用来做环境温湿度监控的摆设它有一个特别实用的价值当你用热电偶测量温度时冷端补偿是必须的。热电偶的输出电压是热端和冷端的温差函数而冷端通常就位于接线端子附近温度等于电路板局部温度。ADS1118内置温度传感器正好可以用来测量这个冷端温度。读取内部温度传感器的操作并不复杂MUX设为AINPAINNGND的组合之外的一个特定编码手册上写的是MUX101。配置完这个MUX后启动转换读回的数据经过换算就是芯片结温。换算公式是温度°Craw × 0.03125°C/LSB偏移量需要查手册的校准值。不同批次芯片的偏移会有差异建议在产线做一次单点校准记录常温下的误差并写进固件里。// 读取芯片温度 float ads1118_read_temperature(void) { // MUX101PGA±4.096V单次模式速率默认 uint16_t cfg ADS1118_CFG_START | (0x05 12) | ADS1118_CFG_PGA_6144 | ADS1118_CFG_MODE_SINGLE | ADS1118_CFG_DR_128SPS; uint16_t raw ads1118_read_raw_single(cfg); // 温度传感器输出为14位数据左对齐在16位中 int16_t signed_raw (int16_t)(raw 2); // 每LSB对应0.03125°C return (float)signed_raw * 0.03125f; }温度传感器的数据格式是14位也就是16位结果的低14位有效读回后的原始值需要右移2位再当作有符号整数处理。这里有个容易翻车的点如果你直接拿完整16位算温度误差会放大4倍。另外温度传感器的线性度在全范围不是完美的靠近-40°C和125°C两端会有一些偏差但用在0到60°C的电路板环境里完全够。热电偶冷端补偿的精度直接取决于这个温度读数的精度所以如果你要求最终测温误差在±1°C内最好在固件里对温度传感器做两点校准而不是直接用手册公式。4.2 数据速率与噪声为什么860SPS不如128SPS“准”ADS1118的数据速率从8SPS到860SPS可调每档对应的有效分辨率不同。这是ΔΣ型ADC工作方式决定的内部调制器以固定频率采样数字滤波器对采样结果做抽取滤波。数据速率越低抽取倍数越高滤波越强输出数据的噪声越小。查看手册上的噪声表格会发现PGA±0.256V、8SPS时的有效分辨率接近15.5位而同样的PGA在860SPS下有效分辨率只有约12位。这3.5位的差距意味着同样一根热电偶低速档读数稳定高速档读数在几个μV到几十μV之间跳动。如果你搞不清楚该选哪档记住一个经验值测量温度或缓慢变化的电压选8SPS到32SPS测量有一定动态要求的信号比如电池放电曲线选64SPS到128SPS只有对时间敏感且信号本身噪声很大的场景比如判断继电器是否吸合才用250SPS以上。在高速档下后级不加滤波的话数据会看起来像随机噪声那不是芯片坏了是物理规律。数据速率还会影响输入信号的建立时间。ADS1118内部有一个输入多路复用器和可编程增益放大器当你切换通道或者改变PGA增益时内部RC滤波器需要时间充电到新的电平。手册建议在通道切换后丢弃前几个转换结果。实际测试中从±0.256V档切到±4.096V档如果用860SPS读数据前两个数据点会明显偏离目标值。这在多通道巡检系统里特别值得注意你会看到同一通道的数据在第一轮巡检时不准第二轮就对了解决方法是每个通道固定用同样的PGA不动态切换或者每次切换后多读几次丢弃。4.3 自校准CA位到底校了什么什么情况下必须置1ADS1118配置字中有一个自校准位Bit 4CA位手册描述是当置1时芯片会在每次转换前执行一次自校准。这个自校准的目的是消除输入失调误差和增益误差。问题在于自校准不是免费的开启后每次转换需要额外的约200μs时间典型值这对低速模式没有明显影响但在高速模式下会压缩有效转换时间窗口。什么时候必须开自校准当环境温度变化超过一定范围时ADC的失调电压会随温度漂移。如果你的仪器需要在不同温度环境下达到高精度每次都开启自校准能保证测量的一致性。代价是转换周期变长。对于热电偶应用我一般建议配置CA1因为热电偶信号本身就很小失调电压哪怕几个LSB也会被当作真实信号。另一种做法是不开自校准而是在固件里做零位校准每次开机时短路输入端读取偏差值然后在数据处理时减去。这个方案的开销比每次都硬件自校准要小。值得说明的是自校准并不影响外部信号它校准的是芯片内部的模拟前端。如果外部电路有偏置电流或者串扰自校准对此无能为力。网上有一类“源代码”里强行把CA位置1但是读出的数据并没有变准那是因为PCB上参考电压纹波太大ADC的测量结果包含的是参考电压的噪声这不是内部自校准能解决的。校准算法再完美也救不了一个电源设计不合格的板子。// 开启自校准的配置字示例注意Bit4置1 uint16_t cfg ADS1118_CFG_START | ADS1118_CFG_MUX_DIFF0 | ADS1118_CFG_PGA_256 | ADS1118_CFG_MODE_SINGLE | ADS1118_CFG_DR_8SPS | (1 4);配置好这个字之后每次转换都会先完成内部校准再进入信号采样所以总耗时比不校准长一点点。实测在8SPS下转换时间从约125ms增加到约130ms这个差异可以接受。如果你用连续模式并且开了CA位每个转换周期都会被拉长导致实际数据速率下降因此连续模式和高数据速率下我建议关掉CA位改用软件校准。5. ADS1118驱动与布线的4个常见坑从“读数乱跳”到“序列错位”5.1 读到的数值总是满量程或者0检查配置字发送顺序和DOUT复用现象按网上源码接线程序跑起来读回的原始数据不是32767就是-32768或者固定为0。原因配置字的16位发送顺序反了或者DOUT/DRDY复用逻辑没搞清楚。这个芯片的DOUT引脚在发送配置字阶段是数据输入写在读取阶段是数据输出读如果你用硬件SPI主控的MOSI和MISO必须分别连到芯片的DIN和DOUT不能把DIN和DOUT短接在一起。解决方法用GPIO模拟SPI逐位操作先发送配置字然后等DOUT变低再做一次SPI读传输。如果读回来的值还是异常用示波器抓DOUT在发送配置字后的电平变化正常会在转换结束后产生一个由高到低的跳变。没有这个跳变说明配置字没被正确写入问题大概率出在SCLK极性或建立时间上。5.2 数据跳变几个LSB怎么查都是软件问题看参考电压去耦电容现象芯片读数在小范围内反复抖动幅值大概在±3到±5个LSB频率看起来没有规律。软件里加了均值滤波也没有明显改善。原因ADS1118内部参考电压是带隙基准它对外部电源纹波的反应不敏感但对外部参考引脚上的噪声敏感。如果你的电路把模拟电源和数字电源混在一起或者去耦电容容量小于0.1μF参考电压会被高频噪声污染。这个问题在热电偶应用里比较隐蔽因为信号本身就是微伏级参考电压上几毫伏的噪声直接等效成同量级的输入误差。解决方法是VDD引脚和REFP引脚附近各放一个1μF陶瓷电容加一个0.1μF高频电容并且尽量靠近引脚布置。必要时在模拟地和数字地之间加磁珠。这个坑基本所有网上源码都不会提因为它看起来是硬件问题但实际是系统集成问题。5.3 连续模式下数据“串位”通道A的数据出现在通道B的读数里现象多通道轮流采集时明明配置的是通道B读回来的数据明显是前一个通道A的数值。原因连续模式不会在切换通道后立刻清空内部数字滤波器所以滤波器的输出会混合两个通道的信号。ADS1118的ΔΣ调制器和数字滤波器在通道切换后需要若干转换周期才能完全更新。解决方法每次切换通道后先读取两次并丢弃第一次数据。如果是在单次模式下不存在这个问题因为单次模式的启动转换指令会重置滤波器。这也是为什么我在多通道项目中坚持用单次模式的原因能省掉很多软件层的“魔法数字”。5.4 SPI通信报错或者数据偶尔为0SCLK速率过高或者布线太长现象程序运行过程中偶发读回0值或者数据明显错误。用逻辑分析仪抓波形发现SCLK个别的时钟周期上有明显的回沟。原因SCLK速率太高信号在长走线上反射芯片端接收到的时钟边沿发生畸变。ADS1118的最高SCLK是4MHz但这只是一个理想值实际板子上如果SPI走线超过3厘米或者中间有过孔4MHz很容易出问题。解决方法是把SCLK降到1MHz左右同时确保DOUT线没有容性负载过重。如果你的MCU频率较高模拟SPI延时的空循环也要相应调整否则SCLK高电平持续时间过短芯片内部采样不满足时序要求。这条坑在快速调通阶段经常遇到降速是最快的解法不要试图通过优化代码来弥补硬件信号的缺陷。6. 一个让ADS1118读数稳定下来的实测小技巧滑动窗口与标定校正ADS1118的原始数据经过前面章节的处理后已经能稳定输出但要真正用在产品里还需要在软件层面做两道工序一是滑动窗口滤波二是线性标定校正。滑动窗口滤波不是简单的取平均它必须和数据速率匹配。我惯用的方案是8SPS下取4个点滑动平均等效输出2SPS噪声峰峰值能再压低一半以上128SPS下取16个点滑动平均等效8SPS适合热电偶测温的响应速度。注意不要对所有数据无限平均温度变化时响应会变得迟钝。// 滑动窗口滤波窗口大小为16点 #define FILTER_WINDOW_SIZE 16 float filter_buffer[FILTER_WINDOW_SIZE]; uint8_t filter_index 0; float filter_sum 0.0f; float ads1118_filtered_read(uint16_t config_word, float full_scale) { // 读一次原始数据并转为电压 uint16_t raw ads1118_read_raw_single(config_word); float voltage ads1118_to_voltage(raw, full_scale); // 减去最旧的数据加上最新的数据 filter_sum - filter_buffer[filter_index]; filter_sum voltage; filter_buffer[filter_index] voltage; filter_index (filter_index 1) % FILTER_WINDOW_SIZE; return filter_sum / FILTER_WINDOW_SIZE; }滑动窗口滤波在代码层面看起来简单但它在嵌入式环境里有个隐性问题初始阶段窗口未填满时平均值偏差很大。解决方法是先填充同一个值再启用滤波器或者在前N次读取时直接返回当前值而不是平均值。你可以在初始化函数里把filter_buffer所有元素设为第一个读取的电压值即可避免启动跳变。标定校正环节针对热电偶应用我实测的做法是在固件里存三个标定点零电压偏移输入短路时的读数、50mV标准源读数、100mV标准源读数。两点确定增益和偏移线性插值补偿。ADS1118的积分非线性误差典型值在0.01%FSR以内两点校准后残余误差主要来自温度漂移所以标定点越多不一定越好反而可能把传感器本身的非线性和噪声引入校正曲线。实际操作里还有一个容易被忽略的细节标定用的标准源精度至少要高于ADS1118的标称精度。用万用表当标准源不可靠因为普通手持万用表在毫伏档的分辨率只有0.1mV比ADS1118本身的LSB31.25μV还粗。我一般用有证书的电压校准源或者用精密电阻分压加上高精度参考源临时搭一个。标定后要在软件里记录标定系数对应的温度条件如果产品需要跨温度范围工作还需要在不同温度下重复标定。这个工作量不小但一旦做完整机的精度就从“能看”变成“能交付”。ADS1118这颗芯片上限在那里下限靠你的硬件设计和代码习惯托着。我用它做过某项目里6通道热电偶采集模块最后标定精度做到±0.3°C靠的就是把每个细节都弄清楚。希望这篇笔记里的代码和坑位清单能让你少走我走过的弯路。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑