尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ADS1232称重底板程序开发:时序、滤波与标定

ADS1232称重底板程序开发:时序、滤波与标定 简介针对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的硬件SPISTC15F32S2内部有硬件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固定选择通道1GAIN0 / 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; // 模式18位UART允许接收 AUXR | 0x04; // T2做波特率发生器 AUXR 0xFE; // T2不分频 T2L 0xE8; // 115200bps22.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算的。调试日志建议只打印滤波后的码值和一个简单头标志例如RAW1234567\r\n先用串口助手确认没有乱码再开始分析读数稳定性。注意ADS1232通道切换后内部数字滤波器需要几个转换周期才能稳定。若底板双通道轮流采样切换A0/A1后必须丢弃前2~3次读码否则每次切换都会在结果里看到一个台阶跳变。4. ADS1232参数调优与排错从读数跳到稳定显示4.1 增益、数据率与满量程先算再采样ADS1232的PGA增益由GAIN0、GAIN1引脚的电平组合决定SPEED引脚选择10SPS或80SPS。这个选择不是随便定的它决定传感器输出范围和分辨率之间的匹配关系。满量程差分输入按公式估算满量程 ±VREF / Gain。例如VREF取AVDD5V增益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改用10SPSAVDD与DVDD间加RC滤波码值稳定但重量显示漂移传感器蠕变或温度漂移缩短滤波窗口加入温度补偿串口打印数据偶发错位波特率初值与IRC频率不匹配核对STC-ISP下载时设置的IRC频率双通道切换后第一个值异常数字滤波器未稳定丢弃切换后的前3次读码排查时优先从电源入手。ADS1232的AVDD如果直接取自开关电源纹波会在末位码上体现出来。常见做法是在AVDD引脚前串联10Ω电阻再加10uF和0.1uF电容到模拟地。另一个高频坑是DOUT和SCLK走线过长且没包地单片机侧加上拉电阻后SCLK边沿反射会造成误读把SCLK串联33Ω电阻可以有效缓解。5. 进阶技巧把ADS1232标定参数写进STC15F32S2内部EEPROM5.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。本文还有配套的精品资源点击获取
返回列表