资讯详情

资讯详情

基于STM32与HX711的智能体重秤设计:硬件与滤波算法实践

1. 项目整体设计与方案选型1.1 为什么选STM32而不是传统51单片机智能体重秤这个题目在嵌入式学习圈里算是经典中的经典。但我一开始并不想用51单片机来做——不是看不起51而是51的资源确实卡得难受。做体重秤你至少要处理称重传感器的小信号放大、ADC采样、按键交互、LCD显示、数据存储这几件事叠在一块儿51的IO复用和外设资源会搞得你很狼狈。STM32的性价比就体现出来了。我用的型号是STM32F103C8T6也就是圈内俗称的蓝丸核心板。这颗片子主频72MHz有高达64KB Flash和20KB SRAM片上集成了3个USART、2个SPI、2个I2C、3个ADC以及一堆定时器。对付一个体重秤资源是富余的但富余不代表浪费——正因为外设够多你才能用标准库写出结构清晰的工程而不是像51那样靠各种模拟时序去骗外设干活。一个很实际的点STM32的ADC是12位分辨率配合HX711这种外置24位高精度ADC整体称重精度能做到0.1kg级别完全可以满足家用体脂秤的需求。而51的10位ADC在称重场景下做标定时分辨率吃紧误差控制也麻烦。1.2 传感器方案对比电阻应变片与HX711体重秤核心的传感元件是称重传感器市面上最常见的是悬臂梁式电阻应变片传感器。原理不复杂应变片贴在弹性体上受力时弹性体发生形变应变片的电阻值随之变化通过惠斯通电桥将电阻变化转换为毫伏级别的差分电压信号。这里必须说明应变片传感器的输出信号非常微弱——满量程输出通常在1~2mV/V。拿最常见的5kg量程传感器举例5V供电时满量程差分输出大约也就5~10mV。这么小的信号如果直接用STM32内置ADC去采等于拿米尺量头发丝直径误差大得没法接受。所以选择在传感器后面再接一颗HX711。这颗芯片是专为称重应用设计的24位高精度ADC内部集成了可编程增益放大器PGA增益可以选32、64或128倍。我配置为128倍增益配合5V供电的传感器满量程信号被放大到300mV级别ADC的LSB分辨率就能覆盖到小数点后三位做家用称重完全够用。HX711另一个方便的地方是数字接口只有两根线PD_SCK和DOUT。整个读写时序就是SCK时钟配合DOUT数据线逐位读取24位数据分三次读出来编程模型极其简单比SPI还省事GPIO模拟就能搞定。1.3 实现功能清单与整体架构我做的这个版本核心功能包括实时称重量程0~150kg分辨率0.1kg智能去皮支持一键去皮归零方便称量放在秤上的容器按键校准支持两点法校准空载校准和标准砝码校准校准参数存入内部Flash数据显示LCD1602显示重量值和单位重量稳定后自动锁定读数超重报警超过设定上限后蜂鸣器报警自动关机无操作一段时间后进入低功耗模式仅演示模式真实低功耗需调整硬件整套系统的数据流是压力 - 传感器电阻变化 - 惠斯通电桥输出差分电压 - HX711放大并完成24位ADC转换 - STM32读取数字量 - 软件滤波 重量换算 - LCD显示 / 按键交互 / 蜂鸣器输出。我用一个类比来帮助理解传感器就像一个麦克风把重量这种物理量转换成极其微弱的声音信号HX711相当于一个前置放大器加高精度录音机把微弱信号放大并记录下来STM32则相当于大脑负责解读这份录音把它翻译成我们看得懂的数字再决定接下来表演什么。2. 硬件设计解析与原理图拆解2.1 核心电路模块组成原理图设计围绕四个模块展开电源电路、HX711称重采集电路、STM32最小系统、显示与交互电路。电源这块儿我踩过坑要重点讲一下。体重秤一般用电池供电常见方案是两节18650串联7.4V或单节锂电池3.7V然后降压到3.3V给STM32和LCD供电5V给传感器激励。我最初直接用了AMS1117-3.3结果发现HX711模拟电源纹波偏高称重数据跳动严重。后来在AMS1117输出端加了一个10uF钽电容并联一个0.1uF陶瓷电容对地再串一个磁珠给HX711的AVDD供电问题才得到缓解。HX711的模拟电源我选用芯片内置稳压模式在VSUP和VAVDD之间接了100Ω电阻VAVDD对地用0.1uF和10uF电容滤波。这组RC组合直接影响传感器激励电压的稳定性我试过直接省略100Ω电阻数据曲线的毛刺明显增多所以千万别为了节省几个元件省掉它。STM32最小系统就是标准的8MHz晶振加两个20pF负载电容BOOT0和BOOT1都拉低NRST上拉到3.3V然后在VDD和VDDA上各加去耦电容。这里有个细节VDDA必须单独接一个10uF电容做模拟电源滤波否则ADC采样性能会劣化——虽然在这个项目中HX711承担了AD转换角色但ST官方勘误表里明确提过VDDA的滤波要求养成好习惯总没坏处。2.2 称重采集电路接线细节HX711与传感器之间是典型的四线制差分连接。传感器引出线定义如下红色线E接HX711的VAVDD黑色线E-接HX711的VSUP接地端白色线S接HX711的INA正向差分输入绿色线S-接HX711的INA-反向差分输入接线时必须保证差分信号线尽量短、尽量远离高频信号源。我之前走过一次弯路把传感器信号线布得离I2C总线很近结果每次通信握手瞬间称重数据就跳十几个数字量。后来把信号线加屏蔽处理并让两者保持3cm以上距离才算干净。HX711的输出DOUT接到STM32的PB0PD_SCK接到PB1两个引脚之间串联330Ω电阻做保护。这样做的好处是万一HX711损坏能一定程度上保护MCU引脚不被打坏——这是从某位硬件老工程师那儿学来的多花两个电阻能省一次烧芯片的麻烦。2.3 显示与按键交互电路设计LCD1602显示模块的数据线和控制线需要占用6个GPIO。数据线D4~D7我接到了PA0~PA3RS接PA4EN接PA5。注意前面提到VDDA接了滤波电容PA0这个引脚还兼职了ADC通道的功能——我发现有些同学在这个引脚上接了其他外设导致采样干扰其实只要你不在项目中同时使用ADC去采其他模拟信号复用是没有问题的。按键部分我用的是行列扫描的思想但总按键数只有4个直接使用了独立按键方案去皮、校准、单位切换、开关机四个按键分别接到不同的GPIO并配置为上拉输入模式。按键按下时读到低电平同时用一个100nF电容并联到按键两端做硬件消抖。软件里我再加了一层20ms延时消抖双保险。蜂鸣器我用的是有源蜂鸣器接到PB6通过NPN三极管驱动。有源蜂鸣器内部自带振荡源只要给它通上电就能发声比无源蜂鸣器少一个PWM控制逻辑很适合这个项目。R1电阻我用4.7kΩ做基极限流实测声音洪亮度刚刚好。2.4 原理图阅读顺序与PCB布局建议拿到原理图先别急着看连线我的习惯是分模块看先找电源入口理清电源树再看核心MCU确认时钟和复位是否完整然后逐个外设模块确认信号连接最后回头看去耦电容和滤波电路是否到位。PCB布局上我强烈建议把传感器信号线布在顶层底层大范围内铺地做屏蔽HX711尽量靠近传感器焊盘减少长距离走线的噪声拾取电源部分和数字电路分区布局。如果你只是做仿真不涉及实际打板那可以跳过这段但如果你打算移植到PCB记住信号先到电源靠边这个原则能少走很多弯路。3. 软件核心实现与代码解析3.1 工程架构与模块划分软件工程我按照功能模块拆分成几个文件命名和职责如下main.c主循环状态机的切换入口hx711.c/hx711.hHX711的驱动包含读数和毛刺消除filter.c/filter.h滑动平均滤波和卡尔曼滤波两者对比用weight.c/weight.h称重逻辑去皮、校准、单位换算lcd1602.c/lcd1602.hLCD驱动key.c/key.h按键扫描和防抖buzzer.c/buzzer.h蜂鸣器控制这种按功能拆分的好处是调试时不用全局乱翻代码出问题直接定位到对应模块。尤其是当你后续想移植到其他STM32型号甚至换成其他MCU只需要替换驱动层的文件业务逻辑可以几乎不用改动。主流程采用一个简单的状态机构架状态包括初始化状态、称重待机状态、去皮状态、校准状态、报警状态。每种状态对应独立的状态处理函数事件通过按键触发转移。写状态机最关键的点是每个状态只做自己该做的事不要让称重状态里的代码去操作校准流程的变量不然bug排查起来会让人抓狂。3.2 HX711驱动时序与毛刺消除HX711的读取时序分为两步拉低DOUT等待数据就绪然后PD_SCK连续输出脉冲按位读取数据。完整读一个24位数据需要25个脉冲前24个用于数据位第25个用于设置下一次转换的增益。核心代码片段如下uint32_t HX711_Read(void) { uint32_t data 0; uint8_t i; while (HX711_DOUT_READ()); // 等待DOUT变低表示数据就绪 for (i 0; i 24; i) { HX711_SCK_HIGH(); delay_us(1); data data 1; if (HX711_DOUT_READ()) data; HX711_SCK_LOW(); delay_us(1); } for (i 0; i 25; i) // 第25个脉冲设置增益128 { HX711_SCK_HIGH(); delay_us(1); HX711_SCK_LOW(); delay_us(1); } return data; }注意HX711的数据输出是二进制补码形式最高位是符号位。我们称重场景通常只关心正向压力但搬运传感器时可能出现反向力导致读数为负值。我在代码里做了符号处理如果读到最高位为1就按负数处理。这里有一个在网上很容易踩坑的细节HX711的DOUT变低并不代表数据新了它变低只说明转换完成。如果你读取间隔太短可能连续读到旧数据。我加了数据变化检测逻辑连续两次读数完全相同就直接丢弃防止LCD显示的重量值卡死在一个旧值上。3.3 滤波算法滑动平均与卡尔曼对比HX711的原始数据是有噪声的主要体现在体重秤上人站立瞬间的数据波动以及微小震动引起的跳动。我试过两种滤波方案滑动平均滤波和卡尔曼滤波。滑动平均滤波器代码很简单维护一个长度为10的环形缓冲区每次从缓冲区取平均值作为滤波结果#define FILTER_LEN 10 int32_t filter_buf[FILTER_LEN]; uint8_t filter_index 0; int32_t SlidingAverageFilter(int32_t raw_value) { int32_t sum 0; filter_buf[filter_index] raw_value; filter_index (filter_index 1) % FILTER_LEN; for (uint8_t i 0; i FILTER_LEN; i) sum filter_buf[i]; return sum / FILTER_LEN; }滑动平均的优点是响应平滑缺点是有滞后。人站上秤的瞬间真实重量已经达到70kg滑动平均可能需要几百毫秒才能从50kg追到70kg。所以我后来换了卡尔曼滤波。卡尔曼滤波在这个场景的核心思想是用上一个状态的重量估计值结合当前的测量值按照噪声协方差加权得到一个更接近真实值的结果。实现代码量大概40行但每轮迭代只有几个乘法和加法。实测下来卡尔曼滤波的整定时间为滑动平均的1/3左右且超调量更小。卡尔曼滤波的两个关键参数是过程噪声协方差Q和测量噪声协方差R。我在体重秤项目中Q初始设为0.01R初始设为1.0。Q越大表示系统越信任测量值R越大表示越信任预测值。实际调试中我从Q0.001开始以十倍步进调整直到单位阶跃响应既不过冲也不拖沓最终Q定格在0.05。如果你不想深究数学推导直接用经验值也能有不错的效果——这不是玄学是工程上的常用简化。3.4 重量换算与两点校准法HX711读出的原始数字量是多少这个数字本身没有物理意义需要通过标定建立与真实重量的映射关系。我使用的换算模型是线性模型真实重量 (原始读数 - 零点读数) / 比例系数标定过程为两点法空载时记录零点读数zero_value放上标准砝码我用10kg标准砝码后记录满量程读数ref_value。比例系数的计算公式为scale_factor (ref_value - zero_value) / 标准砝码质量校准参数保存在STM32内部Flash的末尾扇区。每次上电时读取Flash数据如果校验标志位错误则自动进入校准模式防止设备因参数丢失而无法正确称重。我在代码里还加了去皮逻辑当用户按下去皮键当前读数作为皮重值记录显示重量等于实测重量减去皮重。这个在称散装食材时特别实用。需要提醒的是去皮后比例系数不能变化否则会出现去皮后称重越来越不准的诡异现象——这是因为有人去皮时重新计算了比例系数导致正常标定被覆盖。3.5 LCD1602驱动要点LCD1602是经典字符型液晶屏驱动本质上就是一系列时序操作初始化设置到4位模式节省IO、清屏、设置光标位置、写入字符数据。代码不复杂但时序要求比较严格。我习惯将LCD驱动封装为三个基础函数LCD_WriteCommand写命令、LCD_WriteData写数据、LCD_SetCursor设置光标位置。上层业务只需要调用LCD_ShowString(row, col, string)就能在指定位置显示字符串。有个细节容易忽略LCD1602在每次上电后需要等待约40ms才能执行第一条命令否则初始化可能失败。如果你发现液晶屏显示乱码或者不出来字优先检查这段延时而不是怀疑代码逻辑——这是我被折腾过一整晚的教训。3.6 仿真工程搭建与调试这个项目还提供了Proteus仿真工程文件方便没有实物的同学跑通整体逻辑。仿真拓扑包含STM32F103C8T6最小系统、HX711的Proteus模型我用的是库中HX711器件配合电位器模拟传感器输出、LCD1602、按键和蜂鸣器。在Proteus里模拟应变片传感器方法是用电位器调整分压值来模拟差分电压变化。实际操作中你需要给HX711的输入一个可控电压源改变电压值来模拟不同重量。我在仿真工程里挂了一个按键控制的步进电压源每按一次增加0.5mV模拟重量增加的过程。这样一来你可以在纯软件环境中验证标定流程和状态机逻辑。仿真有一处与实物不同要注意仿真环境中的噪声远小于真实传感器所以滤波算法在仿真里效果看起来完美无瑕但移植到实物后数据跳跃可能比仿真明显。这不是你的代码错了而是硬件噪声谱不同。建议仿真调通逻辑后实物调试时直接信任自己的滤波处理而不是怀疑算法本身否则容易陷进无止境的仿真对、实物错的纠结里。4. 常见问题与排查技巧实录4.1 称重数据跳变不止如何排查这是最频繁出现的问题具体表现是空载时LCD显示的重量在0.1kg范围内飘来飘去。我总结了一套排查顺序按照从简到繁第一步检查电源纹波。示波器看HX711的AVDD引脚电压如果在ADC转换期间有周期性跌落优先加大滤波电容容值。我实测把AVDD的10uF改成47uF后数据跳变幅度缩小了差不多一半。第二步检查传感器本身。用手压或释放传感器观察读数变化是否线性。如果不线性大概率应变片粘贴位置有偏差这种只能更换传感器。第三步检查接线和布线。看DOUT和SCK两根信号线是否与传感器模拟信号线有交叉或平行走线必要时加屏蔽或干脆飞线隔离。第四步检查软件。排除数据重复读取的问题后再检查滤波器长度是否合理。滤波器长度太长会导致重量值迟迟追不上实际值看起来像是跳变其实是它一直在追赶太短则滤不掉噪声。我折中选择了10点滑动平均加卡尔曼滤波的组合。4.2 标定后称重不准怎么判断问题点很多人做完两点校准后称中等重量还挺准但称重物或轻物误差变大。这种非线性现象多半不是算法问题而是传感器本身的非线性误差。好的称重传感器非线性误差指标在0.03%FS以内你买的几块钱的传感器实际可能是0.1%甚至更差。解决思路有两个一是提高校准点数量采用三点分段线性校准把0到满量程分成两段甚至三段每段单独计算比例系数二是在标定时使用接近实际使用重量的砝码——如果你主要称50~80kg的人就用一个50kg砝码做标定比用一个10kg砝码标定后在80kg处读数更准。另外传感器在长时间受力后会产生蠕变。你拿同一个砝码多次上秤读数可能发现第一次是5.0kg放了一分钟后再看变成5.05kg。这个属于传感器的机械特性软件上可以采用稳定时间锁定法解决——检测到重量变化后延时3秒如果3秒内变化量小于0.1kg就锁定显示当前读数后面的蠕变就不理会了。4.3 仿真通过但实物不工作最常见的原因我在带别人的项目时发现一个规律仿真跑得好好的一到实物就黑屏或者蜂鸣器乱叫十有八九是硬件问题而不是代码问题。第一个高发原因是晶振没起振。用示波器量MCU的OSC_OUT引脚正常应该看到8MHz正弦波。如果看到的是直流电平检查晶振两个引脚的对地电容值是否与晶振的负载电容匹配。对于8MHz晶振20pF基本通用但如果你用了12MHz晶振还沿用20pF负载电容起振也可能出问题。第二个高发原因是复位引脚被意外拉低。检查NRST引脚是否有元件或接线错误导致持续低电平。第三个高发原因是VDD和VDDA接错位置或者某个电源引脚漏接去耦电容导致MCU工作不稳定。一个傻瓜式排查法把程序下载后用万用表量MCU的PA4引脚电压如果LCD初始化成功这个引脚会是高电平否则是低电平。通过量关键引脚的静态电平能快速定位程序有没有跑起来比一步步看仿真断点高效得多。4.4 快速定位问题的Debug三板斧对于这种中小型嵌入式项目我调试时常用的三板斧第一板斧是串口打印。STM32F103C8T6有三个USART我把USART1重映射到PA9/PA10波特率115200打印HX711原始读数、滤波后读数和换算重量。所有定位问题都靠这组三件套数值对比——如果原始读数稳定而滤波后不稳定那就是滤波参数问题如果原始读数就不稳定那就是硬件问题。第二板斧是翻转GPIO看时序。比如在每次HX711数据读取完成之后翻转一次某个空闲GPIO用示波器量这个引脚的翻转频率就能算出系统的实际数据更新速率判断程序是否卡死在某个循环里。第三板斧是分模块底线测试。只保留HX711驱动屏蔽所有其他功能确认读数正常后再依次开启LCD显示、按键、蜂鸣器。哪一步开启后出现异常问题就锁定在哪一步的兼容性上。4.5 问题速查表现象可能原因解决方法LCD不显示或乱码初始化延时不足、接线错误、对比度电阻不当检查40ms延时、量接线、调整Vo引脚电位器空载重量显著飘移电源纹波大、传感器信号线受干扰加大模拟电源滤波电容、隔离信号线读数严重偏低HX711增益设置不对、传感器灵敏度不匹配确认第25个脉冲配置的增益与程序一致按键无响应消抖时间太长、GPIO模式配置错误检查按键GPIO是否配置为上拉输入、缩短消抖时间标定参数保存后重启丢失Flash写入地址错乱、校验标志未设置确认写入扇区、先擦后写、正确配置校验标志称重数值与实际差距恒定偏移零点读数记录不准确上电后延迟3秒再取零点值避开上电瞬间的稳态波动5. 项目扩展思路与后续改进方向5.1 从能称重到会思考增加体脂率与心率检测当前版本做的是纯粹的重量测量但市面上更吸引用户的智能体重秤通常还会展示体脂率、BMI、肌肉量等衍生数据。这些并不是简单地在重量上叠加几个公式就完事——体脂率测量一般依赖生物电阻抗分析法BIA需要给秤面通入微弱的交流电流通过测量人体阻抗来估算身体成分。如果你有硬件条件升级可以在秤面两侧分别增加不锈钢电极片再接一个AD5933高精度阻抗转换芯片通过I2C总线与STM32通信。这样不仅可以称体重还能在体重稳定后自动启动阻抗测量再结合身高、年龄、性别等参数在LCD上输出体脂率和基础代谢率。软件逻辑上把之前的重量状态机扩展为测量中 - 稳定锁定 - 阻抗测量 - 结果显示整个流程依然清晰。5.2 增加通信能力蓝牙与WiFi数据上报体重秤收集的数据如果不被记录和分析价值就少了一大半。我后续计划在这个项目中增加蓝牙BLE模块型号如HC-05或CC2541把重量数据通过串口无线上传到手机App或者在WiFi环境下直接转发到服务器。硬件改动的最小路径是把USART2空出来接蓝牙模块拨码开关切换AT指令模式和数据透传模式。软件上新增一个数据帧协议格式定为帧头0xAA 长度1字节 设备ID 重量值4字节浮点数 时间戳4字节 CRC校验2字节。做协议帧时一定要加上CRC校验否则手机端偶尔收到一个被干扰的错误数据排查起来相当费神。5.3 将去板级项目转化为产品级设计的几点心得从能跑的Demo到能量产的产品中间隔着很多东西挑几个核心的跟你们掏心窝子聊一聊低功耗设计实验板上AMS1117静态电流好几个毫安家用电池供电根本扛不住。产品级方案建议用DC-DC降压芯片如TPS5430静态电流做到微安级别。HX711芯片本身有断电模式软件上可以采用10秒无操作进入待机-唤醒策略实测睡眠电流能压到30uA以下。可靠性设计家用体重秤会面临潮湿、静电、频繁踩踏等问题。硬件上加TVS管保护传感器输入端PCB上做三防漆涂覆按键改用硅胶密封这些都是产品化必须考虑的。校准流程自动化生产线上手动一个个标定根本来不及。可以在主板上留一个UART校准口通过上位机向主板发送校准指令主板自动完成空载采样 - 加载砝码 - 写入Flash - 回读验证的完整流程。整个环节只需要操作员把秤放到治具上按一下启动按钮剩下全部自动化。我个人的体会是一个看上去简单的小项目真正把它吃透并往产品方向推的时候涉及的深度是教科书上想象不到的。这个智能体重秤项目虽然只是一个起点但它涵盖了模拟信号链、数字接口通信、滤波算法、状态机设计、Flash存储管理甚至UI交互每一个点单独拧出来都足够深入研究。尤其是HX711到卡尔曼滤波这一段从传感器原始噪音到稳定读数的旅程几乎是所有测量类嵌入式项目都要走一遍的路熟悉这套流程之后再切换到其他传感器项目你会发现自己上手速度明显加快。最后分享一个实际操作经验把代码、原理图、仿真工程完整开源出去最宝贵的不是让初学者抄作业而是让更资深的工程师来挑毛病。我把自己这个项目发到社区后收到了不少有价值的反馈——有人提醒我HX711的PD_SCK引脚需要在初始化时配置为推挽输出防止上电瞬间的干扰有人建议校验Flash参数时用双备份机制防止写入掉电导致参数丢失。这些建议直接提升了代码的健壮性。所以说做开源项目不光是输出更是难得的学习机会。希望你们的体重秤项目也能经历这种被他人审视、打磨、变强的过程。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →