ADS1232称重底板程序开发:时序、滤波与标定
2026/9/15 4:33:26 网站建设 项目流程

简介:针对ADS1232高精度24位ADC与STC15F32S2微控制器的底板程序V1.1压缩包,主要面向从事高精度数据采集、工业测量及嵌入式系统开发的工程师,解决两者间可靠通信与快速开发的问题。程序包基于STC15F系列单片机制作,通过RS-232/RS-485等串行接口实现与ADS1232的交互,包含主程序、外设驱动、功能配置等完整工程源码,便于直接移植或二次开发。包内共70个文件,以C语言源文件(.c)、头文件(.h)和链接列表(.lst)为核心,配合编译生成的.obj、.hex、.m51、.uvproj等工程与调试文件,以及启动文件STARTUP.A51和构建日志,整体体积仅236KB,结构紧凑,足以支撑Keil环境下从编译、烧录到在线调试的完整流程。资源已累计被163人学习下载,适合具备一定单片机基础、需要快速搭建ADS1232采集系统的开发者参考。通过阅读程序更改记录与RS485控制模块,还可复用轮询、外部中断、定时器等多任务处理思路,显著缩短硬件验证周期。

1. 管好ADS1232底板程序,比写好一次采样函数更费心

ADS1232、STC15F32S2、底板程序,这三个词组合在一起,通常是一块称重或应变采集底板的固件包。这类板子的主控是STC的8位单片机,模拟前端是ADS1232这颗24位ADC,程序量不大,核心逻辑就是按时序读码、滤波、标定、输出。真正花时间的不是“把码读回来”,而是把整块底板的上电时序、电源纹波、通道切换和标定参数放在一起考虑,稍有不慎读数就在零点附近来回跳。V1.1这类底板程序之所以值得解压后逐行核对,是因为里面往往不只是ADC驱动,还带着整机批量生产时定下的校准约定。适合正在做称重仪表、拉力计、压力变送器或应变底板的工程师,也适合接手旧项目、需要快速弄清固件边界的人。

2. ADS1232采样时序与STC15F32S2的IO配合

2.1 ADS1232的类SPI时序:DOUT是状态线,SCLK是移位时钟

ADS1232内部是Δ-Σ架构,和常规SAR型ADC不同,它把高速过采样和数字滤波做在内部,输出的是已经处理过的24位码。这颗芯片常用于桥式传感器,因为内部集成了可编程增益放大器,差分输入范围很小,能直接和惠斯通电桥对接。通信口只有两根线:DOUT和SCLK,外观上很像SPI,但没有片选,也没有严格意义的时钟极性和相位约定,所以大多数工程师习惯用普通IO模拟。

工作时序的关键一条:ADC完成一次转换后,DOUT引脚由高变低,表示数据就绪;单片机检测到这个状态后,在SCLK上加脉冲,每来一个脉冲,DOUT在SCLK下降沿之后输出下一位,高位在前;连续输出24位后,需要再补一个SCLK脉冲,DOUT才恢复为高电平。也就是说,DOUT既是状态线又是数据线,第25个脉冲不是多发的,而是让总线回到空闲状态。若漏掉这个脉冲,下一次循环去查DOUT时就可能读到错误状态。

2.2 为什么不用STC15F32S2的硬件SPI

STC15F32S2内部有硬件SPI模块,但用它驱动ADS1232并不省事。首要原因是ADS1232没有片选信号,硬件SPI的NSS引脚反而要额外处理。其次是时序形状不匹配:硬件SPI把24位数据一次性移完后,不会自动补第25个脉冲,MCU还得手工控制SCLK补一下,同步逻辑被拆成两段,代码读起来更绕。第三是ADS1232数据率本身不高,10SPS模式下每次转换周期100ms,读24位码只占用几十微秒,普通IO模拟完全追得上。实际写底板驱动时,我一般直接用sbit定义引脚,靠几条语句控制SCLK翻转,代码直观且容易排查。

2.3 底板级连接与引脚分配

底板上的连接顺序往往是:传感器差分输出接ADS1232的通道输入,ADS1232的DOUT、SCLK、PDWN接STC15F32S2的普通IO,电源把模拟供电和数字供电分开走线。一个典型的引脚分配如下。

ADS1232引脚信号方向接到STC15F32S2说明
DOUT输出P1.0转换完成标志与数据输出
SCLK输入P1.1移位时钟,空闲保持低
PDWN输入P1.2高电平正常工作
A0 / A1输入GND / GND固定选择通道1
GAIN0 / GAIN1输入按需配置选择PGA增益
SPEED输入按需配置10SPS或80SPS

模拟地和数字地在ADS1232附近单点汇合,传感器屏蔽层接到底板模拟地,不要直接接到STC的GND引脚,否则数字噪声会从地平面耦合进转换结果。STC15F32S2的IO复位期间默认是高阻输入,上电后第一步要把SCLK拉低、PDWN先拉低再拉高,不能让复位瞬间的SCLK毛刺当成有效时钟进入ADS1232。

提示:STC15F32S2使用5V供电、ADS1232也工作在5V时可以直接连IO;若底板是3.3V系统,则AVDD和DVDD都要统一到3.3V,不要出现5V模拟电源配3.3V数字IO的混接。

3. 底板最小驱动:ADS1232读码、滤波与串口输出

3.1 引脚宏与初始化流程

拿到一块陌生底板时,我习惯先把引脚定义整理成一个文件头部,方便核对硬件连接。下面的代码以Keil C51为目标环境,适配STC15F32S2。

#include "stc15.h" #include <intrins.h> sbit ADS_DOUT = P1^0; // ADS1232 DOUT,输入状态 sbit ADS_SCLK = P1^1; // ADS1232 SCLK,输出时钟 sbit ADS_PDWN = P1^2; // ADS1232 PDWN,掉电控制 void delay_ms(unsigned int ms) { unsigned int i; while (ms--) { for (i = 0; i < 12000; i++); } } void ADS1232_Init(void) { ADS_PDWN = 0; // 先进入掉电模式 ADS_SCLK = 0; // 时钟空闲为低 delay_ms(10); // 等电源稳定 ADS_PDWN = 1; // 拉高进入正常转换 delay_ms(100); // 等待第一次转换完成 }

PDWN先拉低不是多余动作。很多复位电路在电压上升过程中会让IO先出现随机电平,若PDWN和SCLK同时处于不确定状态,ADS1232可能收到半截时钟序列。先进入掉电模式,等电源稳定后再拉高,能保证第一次转换从干净状态开始。100ms的等待时间按10SPS算足够,若用80SPS,这个延时可以缩短到30ms左右。

3.2 读24位原始码:SCLK高电平期间取样

读码函数的写法直接影响稳定性。我采用的顺序是:等待DOUT变低,然后在SCLK高电平期间读取DOUT,拉低SCLK让ADS1232准备下一位,循环24次,最后补一个时钟脉冲。

unsigned long ADS1232_ReadRaw(void) { unsigned char i; unsigned long value = 0; while (ADS_DOUT); // DOUT为低表示数据就绪 for (i = 0; i < 24; i++) { ADS_SCLK = 1; _nop_(); _nop_(); value <<= 1; if (ADS_DOUT) { value |= 0x01; } ADS_SCLK = 0; _nop_(); _nop_(); } ADS_SCLK = 1; // 第25个脉冲,让DOUT恢复为高 _nop_(); _nop_(); ADS_SCLK = 0; return value; }

这段代码有三个关键参数要说明。第一,while (ADS_DOUT)是死等,若SCLK干扰或PDWN异常导致DOUT一直为高,程序会卡死在这里,所以生产版固件里这一步要加超时退出,比如设置一个计数器,超过几百微秒就返回上次有效值。第二,_nop_()是为了保证SCLK高电平持续时间足够,STC15F32S2运行在22.1184MHz时,两条_nop_()大约产生0.1us,已超过ADS1232对SCLK高电平的最小时序要求。第三,读回的24位是二进制补码,bit23是符号位,若传感器输出反向或接成拉压两用,需要把它转成有符号long后再做后续计算。

3.3 去极值平均与通道切换稳定性

ADS1232原始码在10SPS下仍会有几个码的噪声,直接显示重量会看到最末位跳动。常见做法是连续读多次、丢掉一个最大值和一个最小值,再对中间值求平均。下面的函数把排序和截尾平均合在一起。

unsigned long ADS1232_ReadStable(unsigned char times) { unsigned long buf[8]; unsigned long t, sum = 0; unsigned char i, j; if (times > 8) times = 8; for (i = 0; i < times; i++) { buf[i] = ADS1232_ReadRaw(); } for (i = 0; i < times; i++) { for (j = i + 1; j < times; j++) { if (buf[j] < buf[i]) { t = buf[j]; buf[j] = buf[i]; buf[i] = t; } } } for (i = 1; i < times - 1; i++) { sum += buf[i]; } return sum / (times - 2); }

这个滤波方案适合传感器响应比较慢的场景,代价是引入延迟。10SPS下单次转换已经要100ms,读6次再滤波,一次稳定读数接近700ms,对需要快速响应的拉力计不太合适。另一种做法是滑动平均:保留最近N次读数,每次只加新值、减旧值,响应更快,但异常脉冲会在窗口里停留一段时间。底板程序里我更常用滑动平均加限幅,限幅判据是两次相邻读数的最大变化量,超限就丢弃新值。

3.4 用串口把码值变成调试日志

底板调试阶段,建议先不接上位机协议,直接用UART1打印原始码和滤波值,确认信号通路正常后再加协议。STC15F32S2的UART1波特率由定时器2生成,初始化代码比STC89C52简单。

void UART1_Init(void) { SCON = 0x50; // 模式1,8位UART,允许接收 AUXR |= 0x04; // T2做波特率发生器 AUXR &= 0xFE; // T2不分频 T2L = 0xE8; // 115200bps,22.1184MHz T2H = 0xFF; AUXR |= 0x01; // 启动T2 } void UART1_SendString(char *str) { while (*str) { SBUF = *str++; while (!TI); TI = 0; } }

波特率参数按22.1184MHz晶振算,若STC-ISP烧录时把IRC频率设成了24MHz或11.0592MHz,串口会乱码。这是解压旧工程最容易踩的坑:板子贴片晶振没装,用的是内部IRC,但工程里的波特率初值还是以前24MHz算的。调试日志建议只打印滤波后的码值和一个简单头标志,例如"RAW=1234567\r\n",先用串口助手确认没有乱码,再开始分析读数稳定性。

注意:ADS1232通道切换后,内部数字滤波器需要几个转换周期才能稳定。若底板双通道轮流采样,切换A0/A1后必须丢弃前2~3次读码,否则每次切换都会在结果里看到一个台阶跳变。

4. ADS1232参数调优与排错:从读数跳到稳定显示

4.1 增益、数据率与满量程:先算再采样

ADS1232的PGA增益由GAIN0、GAIN1引脚的电平组合决定,SPEED引脚选择10SPS或80SPS。这个选择不是随便定的,它决定传感器输出范围和分辨率之间的匹配关系。满量程差分输入按公式估算:满量程 = ±VREF / Gain。例如VREF取AVDD=5V,增益128倍时,满量程约±39mV。一颗灵敏度2mV/V的称重传感器,在5V激励下满量程输出10mV,只用了ADC量程的四分之一左右,余量用来覆盖超载和零点偏移。

10SPS和80SPS的取舍比较直接:低速时内部数字滤波对50Hz工频有更好的抑制,读数更稳;80SPS适合需要快速跟踪应力变化的场合,但噪声明显变大,需要加大软件平均。我一般在称重底板上固定用10SPS,在应变监测底板上才开80SPS并配合滑动平均。

SPEED配置输出速率单次周期适用场景
低电平10SPS100ms称重、料斗秤、需要稳定显示的场合
高电平80SPS12.5ms应变快速采样、峰值捕捉

选择增益时还要看传感器的桥臂阻抗。传感器灵敏度低、输出信号小,就选大增益;传感器输出信号接近满量程,增益太大会让放大器饱和,码值顶到0x7FFFFF后不再变化。底板程序里大多数固定配置为128倍增益,若实测小量程传感器读数变化幅度太小,再考虑降到64倍并提高激励电压。

4.2 两点标定的嵌入式写法

底板程序的核心不只是读码,而是把码值换算成重量。工程上最常用的做法是两点标定:空载记录零点码,加载已知重量的标准砝码记录满码,然后用线性比例换算。

long zero_offset = 0; unsigned long load_code = 0; float code_per_gram = 1.0f; void ADS1232_Calibrate(unsigned int load_gram) { zero_offset = (long)ADS1232_ReadStable(6); delay_ms(200); load_code = ADS1232_ReadStable(6); code_per_gram = (float)(load_code - zero_offset) / (float)load_gram; } long ADS1232_GetGram(void) { long raw = (long)ADS1232_ReadStable(6); return (long)((raw - zero_offset) / code_per_gram); }

两点标定有两个容易忽略的前提。第一,零点码和满码必须在同一个增益和速率档下读取,标定完再改SPEED或增益,换算比例就失效了。第二,zero_offset要用有符号long保存,否则空载码小于零点时减法会出错。标定结果在掉电后需要保存,这就是下一章讲EEPROM的原因。若传感器本身非线性明显,两点标定不够,可以在三点之间做分段线性,每段的code_per_gram单独存。

4.3 从现象反推配置:一张排查表

底板调不好,最常看到的就是读数乱跳、满量程不对、数据偶发错误。下面这张表按我自己的排查习惯整理,现象和原因不一定一一对应,但按行检查通常能把问题定位到某个具体环节。

现象可能原因处理建议
DOUT一直为高,程序卡在读码PDWN未拉高,或SCLK空闲电平不对用万用表测PDWN电压,确认SCLK空闲为低
读码始终接近0x7FFFFF增益过大,输入超满量程检查传感器输出幅度,降低PGA增益
读数跳动超过几十个码供电纹波大,或采样速率选在80SPS改用10SPS,AVDD与DVDD间加RC滤波
码值稳定但重量显示漂移传感器蠕变或温度漂移缩短滤波窗口,加入温度补偿
串口打印数据偶发错位波特率初值与IRC频率不匹配核对STC-ISP下载时设置的IRC频率
双通道切换后第一个值异常数字滤波器未稳定丢弃切换后的前3次读码

排查时优先从电源入手。ADS1232的AVDD如果直接取自开关电源,纹波会在末位码上体现出来。常见做法是在AVDD引脚前串联10Ω电阻,再加10uF和0.1uF电容到模拟地。另一个高频坑是DOUT和SCLK走线过长且没包地,单片机侧加上拉电阻后,SCLK边沿反射会造成误读,把SCLK串联33Ω电阻可以有效缓解。

5. 进阶技巧:把ADS1232标定参数写进STC15F32S2内部EEPROM

5.1 在STC-ISP里预留EEPROM区域

STC15F32S2的程序Flash之外有一块Data Flash区域,可以在STC-ISP下载程序时勾选“EEPROM大小”来预留。下载器会把这部分空间独立出来,用IAP方式读写,不占用程序运行时的RAM。底板程序里可以在工程开头定义一个结构体,把零点、比例因子、通道配置和校验和放在一起。

5.2 IAP读写与批量下发

标定参数写EEPROM前,先规划好地址布局。以STC15系列常见的1KB EEPROM为例,起始地址0x0000放版本号,0x0010放零点码,0x0014放比例因子,0x0018放校验和。写入时需要注意STC15系列EEPROM以扇区为单位擦除,写之前必须先发擦除命令,否则写入结果不确定。

typedef struct { unsigned char version; long zero_offset; float code_per_gram; unsigned char checksum; } CAL_DATA; CAL_DATA xdata cal; void EEPROM_ReadBytes(unsigned int addr, unsigned char *buf, unsigned char len) { unsigned char i; IAP_CONTR = 0x80; // 使能IAP IAP_CMD = 0x01; // 读命令 for (i = 0; i < len; i++) { IAP_ADDRL = (addr + i) & 0xff; IAP_ADDRH = (addr + i) >> 8; IAP_TRIG = 0x5A; IAP_TRIG = 0xA5; _nop_(); buf[i] = IAP_DATA; } IAP_CONTR = 0x00; // 关闭IAP } void EEPROM_WriteBytes(unsigned int addr, unsigned char *buf, unsigned char len) { unsigned char i; IAP_CONTR = 0x80; IAP_CMD = 0x02; // 写命令 for (i = 0; i < len; i++) { IAP_ADDRL = (addr + i) & 0xff; IAP_ADDRH = (addr + i) >> 8; IAP_DATA = buf[i]; IAP_TRIG = 0x5A; IAP_TRIG = 0xA5; _nop_(); } IAP_CONTR = 0x00; }

写入前要先把目标扇区擦掉,STC15的扇区大小常见为512字节,擦除命令是IAP_CMD = 0x03,触发序列和写命令一样。批量生产时,可以先在标准底板上做一次标定,把cal结构体读出来,再通过串口命令把结构体内容分批下发到其他底板,底板程序收到完整数据、校验和通过后自动写入EEPROM,实现“一块底板标定,多块底板复制”的流程。串口下发命令建议用固定帧头加长度字段加CRC,和正常采样数据区分开,避免量产时误触发写EEPROM。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询