智能计价电子秤这题目,每年毕业设计里都会冒出来,而且每年都会做成两种极端:一种是模块拼起来,功能能走到位,答辩演示没问题,但一问到原理就支支吾吾;另一种是每个模块都吃透了,从传感器原理到标定算法都能讲得清清楚楚,答辩直接变成加分项。我自己做了好几轮类似项目,我的体会是:它不是"没题选才做的刚需题",而是恰好值得认真对待的嵌入式综合训练,做扎实了反而比堆一堆花哨模块的题目更受益。
这篇文章就按我实际做过的完整流程来走,把基于STM32的智能计价电子秤从方案选型、硬件接线、软件架构、标定算法到调试排错全捋一遍。每个环节我都会说清楚为什么这样做、怎么做、坑在哪里。不管你是准备拿这个题做毕设,还是想自己动手复刻一台桌面电子秤,这些实操信息应该都能帮你少走不少弯路。
1. 系统整体设计与方案选型
1.1 这台电子秤的核心功能需求
做设计的第一步永远不是画电路图,而是把需求写清楚。我在动手前给自己列过一张功能清单,后面所有软硬件选择都围着它在转:
- 称重范围:0~5kg,按传感器量程走,这是单点式称重传感器最常用的量程档位
- 计价功能:单价以"元/千克"为单位输入,总价由重量乘以单价得到
- 去皮功能:放上容器后按去皮键,显示净重,净重等于毛重减去皮重
- 清零功能:空秤状态下按清零键,把传感器零点漂移修正回来
- 超载报警:重量超过量程上限,蜂鸣器响,屏幕给出提示,不再计算总价
- 显示要求:同时显示重量、单价、总价三要素
这张清单看起来简单,但它直接决定了软件要用状态机、按键怎么分配、标定参数要不要存掉电。比如"去皮"和"清零"这两个操作,乍一看很像,实际上完全是两个变量,一个记的是皮重,一个记的是零点偏置,混在一起代码迟早要出问题。
有的同学一上来就想加OLED、语音播报、蓝牙APP,我的建议是:扩展功能永远可以有,但前提是核心链路先跑通。核心链路就是"重量准、操作顺、显示对",这三点做不好,加再多外设都是花架子。
1.2 主控与外设选型的思考过程
主控我用的是STM32F103C8T6,就是网上最常见的"蓝板"核心板。这个选择并不新奇,但确实是最合适的。72MHz主频跑HX711这种低速数据采集绰绰有余,GPIO数量也够用:LCD1602的I2C两根线、4×4矩阵键盘8个引脚、HX711两根线、蜂鸣器一个引脚,加起来不到20个引脚,C8T6完全撑得住。再加上开发资料成熟,Keil MDK建工程有标准模板,RCC时钟配到72MHz就能跑,对毕设阶段的调试效率非常友好。
称重传感器选的是电阻应变片式单点称重传感器,量程5kg,灵敏度约2mV/V。这里有个很容易被忽略的计算:满量程输出等于激励电压乘以灵敏度,按5V供电算就是5V乘2mV/V等于10mV。这个10毫伏级别的信号要是直接扔给STM32内置12位ADC,参考电压3.3V下每个最小单位约0.8mV,称重精度基本没法看。所以必须加专用ADC前端,HX711就是干这个的:
- 24位Delta-Sigma ADC,内置可编程增益放大器,增益可选32/64/128
- 增益128时满量程输入范围约±20mV,和称重传感器的真实输出匹配得很好
- 两线数字接口(SCK、DOUT),占用引脚少,时序好写
- 内部带稳压输出,可以直接给桥式传感器当激励电压源
显示模块选LCD1602加PCF8574转接板,走I2C接口。选它的理由不是它先进,而是它经典、够用、答辩好讲。两行显示,一行放重量加单位,一行放单价加总价,信息刚好排满。如果嫌分辨率低,换成I2C接口的OLED也完全可行,只要改显示驱动层,上层业务逻辑不用动。
4×4矩阵键盘负责全部输入。我的键位分配是:0~9数字键、星号做小数点、A键去皮、B键清零、C键确认/进入单价设置、D键删除。数字键管输入,功能键管操作,互不干扰,状态机也容易分。
2. 硬件模块拆解与接线要点
2.1 称重传感器与HX711:模拟信号链路
传感器的四根线是很多人第一处接错的地方。常见单点式传感器线色为:红线接E+正激励,黑线接E-负激励,绿线接S+正信号,白线接S-负信号。不同厂家可能颜色不同,但原则一致:激励两根线接HX711模块的E+和E-,信号两根线接S+和S-,按模块丝印对号入座即可。
HX711模块和单片机的连接是最简单的那几根线:
- DOUT接PA7
- PD_SCK接PA6
- VCC接5V
- GND接GND
我第一次做的时候直接把模块VCC接了3.3V,结果传感器激励电压偏低,满量程输出被压到6mV左右,读数噪得厉害。后来查手册才明白,HX711内部稳压输出大约在4.3V附近,给桥式传感器做激励时最好整机供5V,模块自己会稳压。这一点我在后面所有项目里都当成铁律来遵守:HX711模块宁可多接一根5V线,也别图省事并联到3.3V上。
HX711的采样速率由RATE引脚决定,默认10Hz,拉高到80Hz。电子秤用10Hz就对了,既跟得上放东西的速度,又能留给软件足够的滤波样本。关于时序,DOUT在转换完成后会由高电平拉低,程序先等这个下降沿再开始读SCK脉冲;SCK时钟频率控制在几百kHz以内,我习惯用约2微秒一个脉冲,完全满足手册要求。还有一个细节:程序里等待DOUT拉低这一步经常被写成死循环,如果接线或者供电有问题,程序就会卡死在这一行。建议实际工程里加一个超时出口,比如循环等待超过100毫秒就强制退出并返回错误标志。
2.2 显示与交互:LCD1602与矩阵键盘
带PCF8574转接板的LCD1602,接线直接用I2C:SDA接PB7,SCL接PB6,VCC和GND接3.3V。PCF8574的I2C地址大部分是0x27,但也有相当一部分模块出厂是0x3F。屏幕显示乱码的时候,第一件事不是查接线,而是把地址改一下试试,这个坑我至少见人踩过十次。
矩阵键盘的原理是行列扫描:4根行线加4根列线,共8个IO。检测时逐行拉低,然后读列线状态,哪一列变低,就说明该位置按键被按下。实际接线我放在GPIOA的低位和GPIOB的低位,避开I2C和HX711占用的引脚。关键问题是STM32的GPIO要配置成上拉输入,否则按键松开时引脚浮空,读出来的键值乱七八糟。不少第一次做矩阵键盘的人栽在这个点上,现象是"按键总是自己触发",其实压根不是按键坏了,是上下拉电阻没配。
除了接线,矩阵键盘还有两个软件层面的讲究:消抖和释放检测。机械按键按下去会抖动几十毫秒,不消抖就会出现按一次触发两三次的情况;释放检测则是为了避免长按连续触发数据。我实际用的办法是扫描到按下后延时20毫秒再读一次确认,然后等按键释放后再执行按键动作,整套逻辑放到一个独立函数里,状态机只关心返回值。
2.3 电源与抗干扰的隐性细节
电子秤本质上是一套模拟信号处理系统,电源处理不好,后面一切软件努力都白搭。毕设不用画复杂的PCB,但电源和结构上至少有这几件事值得做:
- STM32核心板用USB供电或5V适配器供电,板上自带LDO稳压到3.3V
- HX711模块接5V,不要接3.3V,保证传感器激励电压充足且稳定
- 在HX711模块的VCC和GND之间靠近模块并一个100nF陶瓷电容,滤掉高频干扰
传感器的机械固定同样关键。传感器要牢牢固定在秤体底座上,托盘通过支架压在传感器悬臂上,托盘不能晃动,也不能摩擦到外壳。如果秤架不稳固,软件里滤波写得再好也救不回来。我自己一开始用双面胶把传感器粘在塑料底座上,稍微碰一下桌子读数就跳几克,后来改成螺丝固定加泡沫垫减震,稳定性立刻上了一个档次。这一条看着不起眼,却是整个项目能不能到"精度达标"的分水岭。
3. 软件架构与核心算法实现
3.1 状态机划分:把功能变成流程
这种带按键交互、带多种模式的设备,最忌讳的就是在主循环里用一堆if-else堆逻辑。我用一个大的switch-case把系统分成三个主要状态:
- IDLE:默认称重界面,循环刷新重量和总价,响应去皮、清零、单价设置键
- INPUT_PRICE:单价输入界面,数字键和小数点键生效,显示当前输入的单价
- OVERLOAD:超载状态,蜂鸣器报警,限制继续操作,等重量回落才恢复
从IDLE进入INPUT_PRICE靠功能键C,输入完成再按C确认并返回IDLE。去皮和清零在IDLE状态有效,在INPUT_PRICE状态则被忽略,避免误触。状态转移清晰后,代码里不会出现"按键功能互相打架"的问题。
为什么不直接写线性流程?因为秤的操作不是"读重量、显示、结束"这种一次性过程,而是需要时刻响应不同状态下按键、显示、报警的并发需求。状态机把"当前处于什么模式"和"当前要做什么动作"解耦,后面加扩展功能时只需要加新状态,不必去改原来那一大坨逻辑。
3.2 HX711驱动与重量数据采集
HX711的读数据时序不复杂,但关键点必须把握准:先等DOUT由高变低,表示转换完成;在SCK上送24个脉冲,高位在前读出24位数据;第25个脉冲设置增益和通道,同时启动下一次转换。对应代码我直接用标准外设库写,如果你用HAL库移植也就换几个GPIO读写函数的事。
uint32_t HX711_Read(void) { uint32_t val = 0; while(GPIO_ReadInputDataBit(GPIOA, HX711_DOUT) == Bit_SET); // 等待转换完成 for(uint8_t i = 0; i < 24; i++) { GPIO_SetBits(GPIOA, HX711_SCK); delay_us(1); val <<= 1; if(GPIO_ReadInputDataBit(GPIOA, HX711_DOUT) == Bit_SET) val++; GPIO_ResetBits(GPIOA, HX711_SCK); delay_us(1); } GPIO_SetBits(GPIOA, HX711_SCK); // 第25个脉冲:A通道增益128 delay_us(1); GPIO_ResetBits(GPIOA, HX711_SCK); return val; }读出来的是一个无符号整数,但HX711输出的实际是24位有符号数,为了后续计算方便我会转成规范的有符号数:
int32_t raw = (int32_t)HX711_Read(); if(raw & 0x800000) raw -= 0x1000000; // 把24位补码扩展为int32_t如果不做这步转换,遇到传感器信号为负的情况,无符号数参与减法会产生边界问题,轻则标定错乱,重则显示负数完全没规律。我建议从一开始就按有符号处理,省得后面查得头皮发麻。
3.3 滤波与标定:让数字变成可信的重量
滤波方面,我用的是去极值平均法:连续取11个样本,去掉一个最大值和一个最小值,剩下9个求平均。这种方法对突发脉冲干扰很有效,响应速度也能接受。10Hz采样率下,滤波加显示大概一秒钟刷新一轮,称重的体感正常。如果觉得响应偏慢,可以用滑动平均加一阶低通,新值等于0.7乘旧值加0.3乘新样本,但要注意稳定性和滞后之间的取舍。实测下来,普通秤用去极值平均是最稳的组合。
标定才是让重量数据变准的核心环节。HX711原始码并不直接等于克数,它受传感器灵敏度、激励电压、增益和电路噪声共同影响,每套系统的换算系数都不一样。我用的是两点标定:
- 空载时读20次原始值求平均,记为offset
- 放一个已知重量的砝码,我用的500g标准砝码,读20次原始值求平均,记为val_m
- 计算scale = (val_m - offset) / 500
之后每次计算重量就是:
float weight_g = (float)(raw_avg - offset) / scale; // 单位克这一段我给出完整的标定函数,可以直接抄进工程里:
#define CAL_MASS_G 500.0f int32_t offset_cal = 0; float scale_cal = 1.0f; void Calibrate_Zero(void) { offset_cal = Get_Average_Raw(20); // 空载平均 } void Calibrate_Scale(void) { int32_t raw_m = Get_Average_Raw(20); // 放砝码后平均 scale_cal = (raw_m - offset_cal) / CAL_MASS_G; } float Get_Weight_G(void) { int32_t raw = Get_Average_Raw(11); // 当前滤波平均 return (float)(raw - offset_cal) / scale_cal; }5kg量程内两点标定够用,但我在实测中发现4kg以上会有轻微非线性偏差,误差能到一两克。想要更高精度,建议取500g、2kg、4kg三个点做分段线性插值,把每个分段的scale单独算,整体误差可以压到0.5克以内。这个多点标定思路我会在论文的实验部分重点展示,答辩时很有说服力。
标定参数必须存掉电保存。最简单的办法是写进STM32的Flash末页模拟EEPROM,或者外挂一块24C02的I2C EEPROM。如果标定参数不保存,断电重新上电就要重新标定,答辩现场没砝码会很尴尬。
3.4 计价逻辑与功能补全
计价逻辑本身不复杂,但有个细节值得提:金额计算别用float直接做。我习惯把单价存成整数"分"而不是浮点的"元",比如输入3.5元每千克,内部存350。计算总价时:
uint32_t price_kg_fen = 350; // 单价,单位分/千克 uint32_t total_fen; total_fen = (uint32_t)(weight_g * price_kg_fen / 1000.0f + 0.5f);weight_g是浮点克数,乘上单价的"分/千克"要除以1000换算成"分/克",最后加0.5做四舍五入。显示的时候再把total_fen拆成元和分。这样处理能避开浮点误差在金额上显示的怪问题,比如总价显示成9.999999元这种。
去皮和清零的实现逻辑要分清楚:清零是把当前滤波后的原始值记为新的零点偏置,修正传感器本身的漂移;去皮是把当前重量记为皮重,显示净重等于毛重减皮重。代码里是两个独立变量,上一个秤台动作不会互相覆盖。
超载报警要加去回差逻辑。重量大于等于5kg进入OVERLOAD状态,蜂鸣器响两下,屏幕提示超载;要等重量回落到4.9kg以下才自动恢复。如果不做这个滞回区间,重量正好卡在5kg边界附近时,报警会一直反复触发,观感很差。
4. 完整实操:从裸板到能称重
4.1 硬件组装与开发环境准备
硬件组装其实没什么高难度,核心板加模块加杜邦线就能搭起来。我建议按这个顺序来:先把HX711模块和传感器接好,再接到STM32核心板,然后把LCD1602的I2C接上,最后接矩阵键盘和蜂鸣器。每一步都用杜邦线明确标好线色,不要全堆在一起再通电。
软件环境就是Keil MDK加ST-LINK或者核心板自带串口下载。新建工程时直接用标准外设库模板,网上现成模板很多,关键是确认SystemInit把系统时钟配到72MHz。如果下载时报错"连接失败"或者"检测不到芯片",先检查ST-LINK驱动,再检查BOOT0跳线,BOOT0要接GND,也就是正常运行模式。还有个常见操作:按住复位键,点击下载,下载瞬间松开复位,成功率会高很多。
4.2 分步调试:先读数据,再显示,再计价
调试节奏很重要,我的原则是"分模块验证,不一步到位"。整套联调我拆成了以下几步:
第一步,先让HX711读数据。写个极简程序,把原始值直接通过串口发到PC,看空载时数值是否稳定在一个小区间。如果没有数值、数值不变或者乱跳,就从接线和供电开始排查,不要急着往下走。
第二步,加滤波后看数据。用手按压传感器托盘,观察滤波后的值是否随压力明显变化。这一步能确认从传感器到HX711的整条模拟链路是通的。
第三步,接LCD1602,先写一个Hello World确认屏幕能正常显示,再接入重量显示。I2C地址和初始化时序在这一步解决。
第四步,接矩阵键盘,单独测试每一个按键,串口打印对应键值,确认行列映射正确。
第五步,把计价逻辑、去皮、清零、报警全部装进来,做完整的功能联调。
这套顺序最直接的好处是,任何一步出问题,故障范围都很小,不会出现整个系统都不行但完全不知道哪里不行的状态。
4.3 整机标定与精度验证
标定我习惯做两遍。第一遍是粗标定,让秤能正常读数;第二遍是精标定,把误差往最小里压。
粗标定流程很简单:空载让程序跑20次平均值存offset,放500g砝码跑20次平均值存scale,然后直接看500g显示是否接近500g。如果显示偏差大,就按偏差比例反向微调scale,比如显示成495g,就说明scale偏大,往下调1%。
精标定要重点看零点稳定性和满量程线性。我实测的一组数据是这样的:
| 砝码质量 | 显示质量 | 误差 | 结论 |
|---|---|---|---|
| 100g | 99.90g | -0.10g | 合格 |
| 500g | 500.05g | +0.05g | 合格 |
| 2kg | 2000.40g | +0.40g | 合格 |
| 3kg | 3001.20g | +1.20g | 轻度非线性 |
可以看出,3kg附近误差开始变大,这是传感器本身非线性的体现。如果接受不了,就加分段校正:保存500g、2kg、3kg三个点的实际原始码,在代码里做分段插值,显示前先判断当前重量落在哪个区间,再用对应的斜率计算。我这样做之后,全量程误差可以控制在0.5g内。
零点稳定性还有个容易忽略的地方:传感器安装后要先预压几次,放上砝码再拿掉,循环几次,让机械结构应力释放掉。不预压的话,上电后读数会缓慢漂移,这个不是bug,是传感器机械特性,等结构稳定再重新标定就行。
5. 常见问题与真机调试心得
5.1 重量漂移、跳变、归零不了
这大概是整个项目里最磨人的一类问题,我总结了几种典型场景和对应的排查方向:
- 刚上电读数缓慢漂移几十克:传感器机械结构没稳定。先预压几次,再等两分钟,稳定后重新标定
- 空载读数反复跳变:电源纹波大或者接地不良。给HX711单独接5V,所有GND要共地并靠牢,将模拟部分和数字排线隔开
- 放上物品后读数持续朝一个方向飘:传感器蠕变,源自应变片或弹片的固有特性。让读数稳定两三秒再算总价,或在算法里做低速补偿
- 回零回不到零:弹性滞后造成的,无法完全消除,只能增强秤体刚性,减少活动部件
还有一种是读数周期性波动,每秒跳几个字,多半是工频干扰。可以尝试给HX711模块加个小金属屏蔽罩并接地,或者在采样时序上避开干扰窗口。这个问题在实验室和宿舍的供电环境下表现差异很大,现场演示前一定要在演示地点实测一遍。
5.2 显示异常与按键不灵
LCD1602显示乱码,我遇到的无非三种情况。第一种是I2C地址不对,0x27和0x3F之间改一下就好;第二种是屏幕对比度没调好,满屏方块或者一团黑,用螺丝刀调背板上的蓝色电位器;第三种是初始化时序不对,PCF8574虽然接了I2C,但LCD1602的初始化序列仍然要完整走一遍:功能设置、显示开关、清屏、输入模式,一样都不能少,否则屏幕显示的内容就是乱的。
按键不灵的核心原因还是消抖和上下拉。消抖在扫描函数里做简单,检测到按下后延时20毫秒再读一次,两次结果一致才确认。释放检测也要做,否则会出现按住数字键连续输入一大堆值的问题。每次按键按下时让蜂鸣器短响一下,手感也会更好。
5.3 提升作品档次的几个细节扩展
如果核心链路已经稳定,想让作品在毕设里出彩,我建议挑一个扩展点做深就够了,不需要堆模块。以下四个方向都是我测试过确实可行的:
一是加交易记录功能。每次称重结束,把时间、重量、单价、总价通过USART发到PC上位机,或者用SPI接口存进Micro SD卡模块,做成"交易流水查询"。这个功能把系统从中断扩展到了数据采集与存储,论文里很有说头。
二是加当日营业总额累计。用一个变量累加每次交易金额,掉电保存到EEPROM,显示在LCD的一个附加界面里。这个小功能在答辩演示时非常直观。
三是加温度补偿。HX711读数受温度影响,实测5℃温差能让5kg传感器零点漂移1到2克。用一颗DS18B20采集环境温度,在标定算法里加入温漂修正项,可以把零漂压到0.3g左右。这个创新点既能写算法又能写实验,含金量不低。
四是做HMI交互优化。比如用旋转编码器代替矩阵键盘输单价,或者加一个带背光的独立按键板,把操作区域的工业设计做得更像商品。这种"完成度"的提升在答辩现场往往比功能堆砌更让人印象深刻。
我个人做完这类项目最大的感受是,电子秤真正难的从来不是某一个模块,而是把所有模块配合起来之后的稳定性。一台秤打开电源,放上物品,显示一个稳定且准确的数字,这背后是机械结构、模拟链路、数字滤波、标定算法和状态机逻辑的协同。把任何一处抠松了,整机表现立马打折。文章里写的这些步骤和坑位,都是我实打实踩过之后整理出来的。照着这个流程走,你有更大把握在有限时间内搞定一套真正稳定、可演示、能讲透的智能计价电子秤。
最后再分享一个答辩相关的小技巧:做完功能后,把你每块电路、每段核心代码的"为什么"整理成文档,再把标定曲线、误差表这类实验数据做成图表放进论文附录。这些东西在答辩现场,比PPT里任何花哨的动画都有说服力。电子秤这个题目的上限不低,关键就看你怎么把每一步做得扎实。