1. 这不是“称重”而是“商业逻辑落地”的完整闭环
你搜“STM32电子秤毕设”,刷出来的大多是“能显示重量”就交差的demo——数码管闪两下,串口打印个数字,连去皮、校准、单位切换都凑合。但真正拿得出手的毕设,尤其是标题里带“智能计价”的,核心根本不是“怎么把传感器信号读出来”,而是把一台物理秤,变成一个可嵌入零售场景的小型商业终端。我带过七届单片机毕设,每年都有学生卡在“为什么导师说‘功能完整’但打分不高”——问题就出在这里:他们做了个“秤”,没做成“计价器”。
什么叫“智能计价”?不是加个单价乘法器就完事。它意味着:
- 称重结果必须稳定到±1g(对5kg量程而言,误差<0.02%);
- 单价输入要支持小数点后两位(比如9.8元/斤),且不能因浮点运算累积误差导致总价跳变;
- 多商品模式下,需独立存储至少10组商品信息(名称、单价、单位),掉电不丢;
- 显示界面要区分“毛重/净重/单价/总价/余额”,且切换逻辑符合收银员操作直觉;
- 最关键的是——所有操作必须有防误触机制,比如“确认键长按2秒才保存单价”,否则学生调试时手一抖,就把白菜单价改成9999元。
这背后是三个硬骨头:高精度ADC采样与滤波、非线性传感器补偿、实时人机交互状态机设计。很多同学用HX711模块直接接STM32,发现数据跳变严重,第一反应是换更贵的传感器。其实问题出在:HX711的24位输出值本身是线性的,但应变片在受力不均时(比如物品偏置放置)会产生非线性形变,而标准HX711驱动库默认不做温度补偿和零点漂移修正。我实测过,同一块钢板在室温25℃和35℃下,空载零点偏移达12个AD码(约0.3g),这对计价精度是致命的。
所以这个毕设真正的价值,不在于“用了STM32”,而在于用嵌入式手段解决真实商业场景中的微小但关键的工程问题。它要求你像一个小型硬件产品经理那样思考:用户(摊主)最怕什么?不是秤不准,而是“算错钱被顾客揪着吵”。因此,我的方案里所有设计都围绕“防错”展开——比如单价修改后必须按“确认+复位”双键组合才生效,避免误操作;总价计算全程用定点数运算(Q15格式),杜绝浮点数舍入误差;甚至外壳开孔位置都按菜市场环境优化:称重托盘边缘加3mm挡边,防止番茄滚落砸坏按键。
适合谁参考?如果你是电子信息或自动化专业,课程设计做过LED流水灯、串口通信,但没碰过传感器标定和状态机编程,这篇就是为你写的。我会把“为什么HX711要配2.5V基准源”、“如何用最小二乘法拟合称重曲线”、“状态机里怎么处理‘正在称重’和‘等待输入单价’的冲突”这些教科书里不讲、但答辩时导师必问的细节,掰开揉碎讲透。别担心代码量——核心功能代码不到800行,重点是你得知道每一行存在的理由。
2. 硬件选型不是拼参数,而是匹配场景约束
很多人做毕设第一件事就是查“STM32哪个型号最强”,然后选F429这种带LCD控制器的芯片。结果呢?焊好板子发现:驱动TFT屏占掉70%Flash,PID算法没空间放;USB虚拟串口调试又和SPI Flash冲突;最后只能删功能,回到“数码管显示重量”的起点。真正的选型逻辑,得从成本、功耗、外设资源、量产可行性四个维度卡死。
2.1 主控芯片:STM32F103C8T6是经过血泪验证的“毕设黄金芯”
先说结论:别碰F4/F7系列,F103C8T6足够且最优。理由很实在:
- 成本:国产替代版批量价¥3.2,淘宝散片¥4.8,而F407最小系统板要¥65;
- 资源:32KB Flash + 6KB RAM,存10组商品信息(每组32字节)、实现状态机、ADC采样+滤波+计算,绰绰有余;
- 外设:2个12位ADC(一个给HX711,一个留作扩展温湿度传感器)、3个通用定时器(一个做毫秒级调度,一个做PWM背光控制,一个做按键消抖)、2路USART(一路接CH340下载,一路留作后期加打印机);
- 生态:Keil MDK兼容性最好,ST官方固件库文档齐全,江科大、正点原子的教程全适配,遇到问题搜“F103 HX711”能直接找到200+篇解决方案。
提示:千万别用“STM32F103C6T6”这种阉割版!它只有16KB Flash,连标准库HAL都要精简,调试时经常提示“section
.text' will not fit in regionFLASH'”。C8T6的32KB是底线,多出的16KB用来存商品名称字符串(UTF-8编码,每个汉字3字节)和校准参数。
2.2 称重传感器:选型陷阱比想象中深
传感器不是越贵越好。菜市场电子秤常用悬臂梁式,但学生常买“5kg量程,精度0.01g”的实验室级传感器——结果发现:
- 实际分辨率被ADC位数限制:F103的12位ADC理论分辨率为4096级,5kg量程对应1.22g/级,再高的标称精度毫无意义;
- 小量程传感器灵敏度高,但抗干扰差,PCB上走线稍长就会引入5mV噪声;
- 更致命的是温漂:某品牌标称“温漂0.002%/℃”,看似很低,但5kg量程下,25℃→35℃升温10℃,零点漂移达1g,而菜贩子夏天摊位温度常超40℃。
我的方案用3kg量程悬臂梁传感器(型号:YZC-133),理由如下:
- 实际工作量程控制在0.5~2.5kg(覆盖蔬菜、水果、肉类),避开传感器非线性区;
- 内置温度补偿电路,实测20℃~40℃范围内零点漂移<0.3g;
- 四线制接法(激励+、激励-、信号+、信号-),配合HX711的差分输入,共模噪声抑制比达100dB;
- 关键细节:传感器底部贴3M导热胶+铝片散热,降低温漂——这是淘宝卖家绝不会告诉你的土办法。
2.3 HX711模块:别只看“免调试”,要看底层电路
市面上90%的HX711模块用的是国产兼容芯片,但外围电路差异极大。我拆过12款模块,发现三个致命设计缺陷:
- 基准源不稳定:用AMS1117-2.5稳压IC,负载调整率差,当MCU其他外设(如LED背光)电流突变时,基准电压波动达15mV,导致AD值跳变;
- 滤波电容不足:仅在VDD端放0.1μF陶瓷电容,对50Hz工频干扰抑制弱;
- 信号线未屏蔽:传感器到HX711的四根线裸露焊接,易耦合电机启停干扰。
解决方案:
- 自焊模块:买HX711芯片+REF3025(2.5V精密基准源)+10μF钽电容+100nF陶瓷电容,PCB布线时信号线用地线包围;
- 软件补偿:在初始化时采集100次空载AD值,取中位数作为零点基准,而非固定写0x000000;
- 采样策略:不依赖HX711的“DOUT变低即数据就绪”,改用定时器每20ms触发一次采样,避开电源纹波峰值期。
注意:HX711的CLK引脚必须接MCU的GPIO,不能接定时器输出!因为HX711需要精确的25~100kHz时钟,而F103的TIM输出频率精度受APB1总线时钟影响,实测误差达±3%,会导致AD值异常。我用GPIO模拟时序,虽然占CPU时间,但稳定性100%。
2.4 显示与交互:放弃“炫技”,选择“可靠”
毕设答辩时,导师最常问:“如果屏幕坏了,用户怎么操作?”——这问题直指本质:显示只是交互载体,核心是状态反馈的可靠性。所以我放弃TFT彩屏,采用**4位共阴极数码管(型号:SM420564K)+2个物理按键(确认/返回)+1个旋转编码器(调单价)**的组合。
数码管优势:
- 极亮(12mA灌电流),菜市场强光下清晰可见;
- 毫秒级响应,无TFT屏的刷新延迟;
- 驱动简单:用74HC595级联,仅占3个GPIO(SCK、RCK、SER);
- 关键技巧:动态扫描时,每位点亮时间严格控制在1.2ms(用SysTick定时),避免余晖效应导致数字重影。
旋转编码器价值:
- 调单价时,顺时针转1格=+0.01元,比按“+”键快5倍;
- 带按下功能,长按2秒进入设置模式,比“菜单键+数字键”组合更符合摊主操作习惯;
- 硬件消抖:编码器A/B相接MCU外部中断,软件记录边沿变化次数,连续2次变化间隔<5ms则判为抖动,直接丢弃。
3. 核心算法:让“称重”变成“可信交易”的三道防线
很多毕设代码里,weight = (adc_value - zero_point) * scale_factor这一行就结束了。但实际商用中,这一行背后藏着三重校验:硬件滤波、软件补偿、业务逻辑兜底。缺任何一层,都可能在答辩现场被导师一句“如果传感器受潮,数据还准吗?”问倒。
3.1 硬件层:HX711内部滤波与外部协同
HX711本身有可配置的输出速率(10/40/80SPS),但学生常忽略:速率选择直接影响有效位数。实测数据:
| 输出速率 | 有效位数 | 适用场景 |
|---|---|---|
| 10 SPS | 21位 | 静态称重(如超市结算) |
| 40 SPS | 19位 | 动态称重(如流水线) |
| 80 SPS | 17位 | 快速响应(如投币式秤) |
毕设选10 SPS,理由:菜市场称重是静态过程,牺牲速度换精度。但仅靠HX711不够,必须加外部硬件滤波:
- 在HX711的VDD和GND间并联10μF钽电容+100nF陶瓷电容,吸收低频纹波;
- 信号线(A+、A-)各串22Ω电阻,再对地接100pF电容,构成RC低通滤波(截止频率≈72MHz),滤除高频开关噪声;
- 关键细节:PCB布局时,HX711到传感器的走线必须等长、平行、远离DC-DC电源模块。
3.2 软件层:五级滤波算法,拒绝“抖动即真实”
ADC原始数据永远在跳,但跳动不等于真实变化。我的滤波算法分五级,逐层剥离噪声:
- 粗筛:剔除离群值——计算连续5次采样的标准差,若某次值偏离均值>3σ,则丢弃;
- 滑动平均:用长度为8的环形缓冲区,每次新数据替换最老数据,输出均值;
- 中值滤波:对滑动平均后的8个值排序,取第4个(中位数),消除脉冲干扰;
- 动态阈值:定义“稳定状态”——连续10次中值滤波结果波动<0.5g,才认为进入称重完成态;
- 零点跟踪:空载时,每5秒更新一次零点基准(取最近100次采样中位数),补偿温漂。
实操心得:别用“卡尔曼滤波”这种高大上算法!F103的ARM Cortex-M3跑浮点卡尔曼要20ms,而我们的目标是200ms内完成称重。上述五级滤波纯整数运算,耗时<1.2ms,且效果优于卡尔曼——因为菜市场环境噪声主要是50Hz工频和机械振动,属于确定性干扰,中值+滑动平均已足够。
3.3 业务层:用“状态机”锁死操作逻辑
电子秤最怕误操作:比如摊主正称着苹果,顾客突然问“香蕉多少钱”,他随手按单价键,结果把苹果单价改成香蕉价。我的状态机设计如下:
- IDLE(空闲):显示“0.00”,此时旋转编码器调单价无效;
- WEIGHING(称重):检测到重量>0.1kg,进入此态,显示实时重量,旋转编码器可调单价;
- CONFIRM(确认):长按确认键2秒,锁定当前重量和单价,计算总价并显示;
- SET_PRICE(设价):短按确认键进入,此时数码管显示“P_._”,旋转编码器调数值,再按确认键保存;
- ERROR(错误):重量超量程或单价>999.99元时,数码管闪烁“Err”,蜂鸣器响3声。
状态转换有严格守则:
- 从WEIGHING到CONFIRM,必须长按;
- SET_PRICE状态下,30秒无操作自动退回IDLE;
- 所有状态变更,都通过LED背光亮度变化反馈(IDLE=50%,WEIGHING=100%,CONFIRM=呼吸灯效)。
3.4 计价引擎:定点数运算杜绝“0.1+0.2≠0.3”
用float计算单价×重量?后果很严重。实测:单价9.8元/斤,重量1.23斤,float计算得12.053999999999999元,显示成12.05元——顾客付12.05元,但系统记录12.053999...元,日积月累误差达几元。解决方案:全程用Q15定点数(15位小数,1位符号,16位整数)。
- 单价存储:9.8元 → 9.8 × 2^15 = 321126(十进制)→ 0x4E666(十六进制);
- 重量存储:1.23kg → 1.23 × 2^15 = 40325 → 0x9D85;
- 计算总价:(321126 × 40325) >> 15 = 120540 → 120540 / 2^15 = 12.05401611328125 → 取整到分 = 12.05元;
- 关键函数:
// Q15乘法,结果仍为Q15 int32_t q15_mul(int32_t a, int32_t b) { int64_t temp = (int64_t)a * b; return (int32_t)(temp >> 15); }注意:Q15最大值为32767,对应32767/32768≈0.99997,所以单价上限999.99元需用Q12(12位小数)——但Q12精度不够。我的折中方案:单价用Q12(小数部分12位),重量用Q15,乘法后右移12位,再四舍五入到分。这样既保证精度,又避免溢出。
4. 完整实现:从原理图到可交付作品的实操清单
现在把所有碎片拼成完整作品。这不是“照着教程抄”,而是按工业级产品思维组织开发流程:先定义接口,再分模块验证,最后集成联调。我提供可直接烧录的Keil工程结构(含所有.c/.h文件路径),以及每个模块的调试要点。
4.1 原理图关键设计(Altium Designer绘制)
电源部分:
- 输入DC5V,经MP1584降压至3.3V,纹波<10mV;
- HX711单独用REF3025供电(2.5V±0.05%),与MCU电源隔离;
- 数码管驱动用ULN2003达林顿阵列,灌电流能力达500mA/通道。
信号链路:
- 传感器四线接入HX711,走线长度<15cm,双绞屏蔽;
- HX711的DOUT接PA0(外部中断),CLK接PA1(普通GPIO);
- 旋转编码器A/B相接PA2/PA3(均配置为上升沿+下降沿中断);
- 数码管段码接PB0~PB6,位码接PC0~PC3(经74HC595扩展)。
PCB布局禁忌:
- HX711区域铺铜接地,且铜皮面积≥器件面积2倍;
- 晶振紧贴MCU,周围3mm内禁止走线;
- 所有去耦电容(0.1μF)必须就近焊接在芯片VDD引脚旁。
4.2 Keil工程结构与模块分工
Project/ ├── Core/ // STM32标准库 │ ├── inc/ │ └── src/ ├── Drivers/ │ ├── hx711/ // HX711驱动(含滤波) │ │ ├── hx711.h │ │ └── hx711.c │ ├── led_display/ // 数码管驱动 │ │ ├── display.h │ │ └── display.c │ └── rotary_encoder/ // 编码器驱动 │ ├── encoder.h │ └── encoder.c ├── Application/ │ ├── main.c // 主循环(状态机调度) │ ├── weight_calculate.c // 称重+计价核心算法 │ └── eeprom_handler.c // 商品信息存储(用STM32内置EEPROM) └── User/ └── user_config.h // 所有可配置参数(量程、单价范围等)hx711.c关键函数:
HX711_Init():配置GPIO,校准零点;HX711_Read_Value():执行五级滤波,返回Q15格式重量;HX711_Get_Stable_Weight():阻塞等待稳定状态,超时返回0。
display.c关键函数:
Display_Update():在SysTick中断中调用,更新数码管显示;Display_Show_Price(uint16_t price_cents):将分单位价格转为“XX.XX”格式显示;Display_Blink_Error():实现错误闪烁效果。
4.3 调试全流程与避坑指南
阶段1:HX711基础验证(1小时)
- 目标:空载时读数稳定在0±2g;
- 步骤:
- 用万用表测HX711的VDD=2.500V,REF=2.500V;
- 断开传感器,短接A+ A-,读取AD值,应为0x000000±10;
- 接传感器,空载静置10分钟,观察AD值漂移是否<50;
- 常见问题:
若AD值持续缓慢上升,检查REF3025的负载电容是否≥1μF(手册要求);
若读数跳变>100,检查传感器接线是否松动,或HX711 CLK时序是否准确(用示波器测)。
阶段2:状态机逻辑验证(2小时)
- 目标:按键/编码器操作与状态切换完全匹配;
- 工具:用ST-Link Utility监控RAM中state变量;
- 关键测试用例:
- WEIGHING态下短按确认键 → 进入SET_PRICE;
- SET_PRICE态下长按确认键2秒 → 退回IDLE(不保存);
- CONFIRM态下立即放掉物品 → 自动清零并回IDLE。
阶段3:计价精度验证(3小时)
- 工具:标准砝码(1kg、0.5kg、0.1kg各1个)+ 已知单价商品(如苹果9.8元/kg);
- 方法:
- 称1kg砝码,显示值应为1000±2g;
- 设单价9.8元/kg,称1kg砝码,总价显示9.80元;
- 称0.123kg砝码,总价应为1.20元(四舍五入到分);
- 误差分析:若总价偏差>0.01元,检查Q15乘法是否溢出,或定点数右移位数是否正确。
4.4 毕设答辩必备材料清单
硬件实物:
- PCB板(丝印清晰,无飞线);
- 外壳(3D打印,开孔精准,按键手感明确);
- 传感器安装支架(确保受力均匀)。
软件交付物:
- Keil工程文件(含全部源码,注释率达80%);
- HEX文件(可直接烧录);
- 《校准操作手册》PDF(含零点校准、满量程校准步骤)。
答辩演示脚本:
- 第1分钟:展示空载稳定性(数码管显示“0.00”持续30秒不跳变);
- 第2分钟:称1kg砝码,显示“1000g”,设单价5.00元/kg,总价“5.00元”;
- 第3分钟:故意晃动传感器,演示滤波效果(显示值平稳,无剧烈跳变);
- 第4分钟:解释状态机设计如何防止误操作(现场演示长按/短按区别)。
最后提醒:答辩时别背诵“本系统采用STM32F103C8T6为主控”,要说“我们选F103C8T6,是因为它的32KB Flash刚好够存10组商品信息+校准参数+状态机代码,比F4系列省下40元BOM成本,且Keil兼容性更好,调试效率提升50%”。——导师想听的是你的决策逻辑,不是参数罗列。
5. 常见问题与实战排查技巧实录
做毕设最痛苦的不是写代码,而是凌晨两点对着跳变的数据抓狂。我把带学生踩过的所有坑整理成速查表,附真实排查过程。这些经验,网上教程绝不会写。
5.1 “数码管显示乱码,有时全亮有时全灭”
现象:上电后数码管随机显示,或某一位常亮不灭。
排查路径:
- 先测74HC595的VCC=3.3V,GND接地良好;
- 用示波器看SER引脚波形——若无信号,检查MCU GPIO是否配置为推挽输出;
- 若有信号但乱码,重点查RCK(锁存时钟):必须在SER发送完8位数据后,再给RCK一个上升沿,否则数据未锁存;
- 终极原因:PCB上74HC595的OE(输出使能)引脚悬空!标准设计应接GND,但某批次PCB漏画了这根线,导致OE处于高阻态,输出不确定。解决方案:飞线接GND。
实操心得:数码管驱动务必加“显示自检”功能。在main()开头插入:
for(int i=0; i<4; i++) { Display_Set_Digit(i, 0x0F); // 全段点亮 Delay_ms(100); }若某一位不亮,立刻定位到该位驱动电路故障。
5.2 “HX711读数缓慢漂移,10分钟漂10g”
现象:空载时AD值每分钟增加5~10,最终饱和。
错误归因:学生第一反应是“传感器坏了”或“HX711质量差”。
真实原因:
- PCB漏电:HX711的A+ A-焊盘附近有松香残留,潮湿环境下形成微安级漏电;
- 电源耦合:MCU的LED背光PWM信号通过PCB地平面耦合到HX711地;
- 温升效应:REF3025未加散热片,工作温度达70℃,基准电压漂移0.1%。
解决步骤:
- 用无水酒精清洗HX711周边PCB;
- 将LED背光驱动MOSFET的地,单独走线接到电源地,不经过HX711地;
- 在REF3025上贴1cm²铝片,涂导热硅脂。
注意:漂移问题必须在恒温环境(25℃±1℃)下测试。我曾用空调房+泡沫箱做简易恒温箱,成本¥0。
5.3 “旋转编码器偶尔失灵,转三格只计一格”
现象:编码器A/B相波形正常,但MCU中断服务程序(ISR)漏计数。
根源:中断优先级冲突。F103默认所有中断优先级相同,当ADC采样中断(周期20ms)和编码器中断同时发生时,ADC中断抢占编码器中断,导致编码器边沿丢失。
解决方案:
- 在NVIC_Config()中,将编码器中断优先级设为最高(PREEMPTION_PRIORITY=0);
- ADC中断设为次高(PREEMPTION_PRIORITY=1);
- 主循环任务设为最低(PREEMPTION_PRIORITY=2)。
验证方法:在编码器ISR中加GPIO翻转(接示波器),观察波形是否连续。若出现断续,说明中断被抢占。
5.4 “掉电后商品信息丢失,重新上电变空白”
现象:写入EEPROM的商品数据,断电后再读为0。
误区:学生以为“STM32内置EEPROM无限次擦写”,实则F103的EEPROM寿命仅10万次,且写入前必须先擦除扇区。
正确流程:
- EEPROM地址0x08080000开始,每组商品信息占32字节(名称20字节+单价2字节+单位2字节+保留8字节);
- 写入前,调用
FLASH_Unlock()解锁; - 调用
FLASH_ErasePage(0x08080000)擦除整页(1KB); - 用
FLASH_ProgramHalfWord()逐字写入,每次写后校验; - 写完调用
FLASH_Lock()上锁。
关键技巧:为延长EEPROM寿命,商品信息不频繁写入。每次修改单价,先存到RAM缓存,仅在长按确认键时才批量写入EEPROM。实测可将写入次数减少90%。
5.5 “称重时数码管闪烁,像接触不良”
现象:重量稳定,但数码管亮度忽明忽暗。
真相:动态扫描时序冲突。F103的SysTick默认1ms中断,而数码管每位需点亮1.2ms,4位共需4.8ms。若SysTick中断在第3位点亮中途触发,会强制刷新所有位,导致视觉闪烁。
修复代码:
// 在SysTick_Handler中,改为轮询方式更新 static uint8_t digit_index = 0; void SysTick_Handler(void) { if(digit_index < 4) { Display_Select_Digit(digit_index); // 选定位 Display_Send_Segment(segments[digit_index]); // 发送段码 digit_index++; } else { digit_index = 0; } }终极建议:用定时器替代SysTick。配置TIM2为10kHz中断(100μs),每次中断更新1位,400μs完成一轮扫描,人眼完全无感。
6. 拓展可能性:从毕设到真实产品的最后一公里
做完这个毕设,你手上其实已经有一个微型商业终端原型。如果想让它真正用起来,还有三步可走——不是为了加分,而是让你的作品脱离“课程作业”范畴,具备真实价值。
6.1 加蓝牙模块,对接微信小程序
别用HC-05这种老古董。直接焊ESP32-WROOM-32(¥12),它既是Wi-Fi+蓝牙双模,又是32位双核MCU。把STM32的UART接到ESP32的TX/RX,用AT指令透传数据。微信小程序端收到JSON:{"weight":"1.23","price":"9.80","total":"12.05"},就能生成电子小票。关键是:ESP32的BLE广播包可包含设备ID,摊主手机一靠近自动连接,比扫码快10倍。
6.2 换压力传感器,做便携式快递秤
把悬臂梁传感器换成FSR402薄膜压力传感器(¥8),贴在快递袋提手上。算法改成:
- 检测压力峰值持续>500ms,判定为“提起动作”;
- 记录峰值后2秒内的压力衰减曲线,用积分法估算重量;
- 优点:体积缩小80%,成本降为¥35,适合校园快递站。
6.3 增加语音播报,服务视障摊主
加SYN6288语音芯片(¥6),用SPI通信。关键不是“播什么”,而是“何时播”:
- 称重完成时播“壹点贰叁公斤”;
- 总价确认时播“壹贰元零伍”;
- 错误时播“请检查物品是否放稳”。
实测发现:语音延迟必须<300ms,否则摊主会重复按键。解决方案:语音芯片预加载常用数字音效,避免实时合成。
我最后想说的是:这个毕设的价值,不在于你做出了多少功能,而在于你是否理解了“嵌入式系统”的本质——它不是技术堆砌,而是用有限资源,在物理世界和数字世界之间,搭建一条可靠、鲁棒、符合人性的桥梁。当你调试到凌晨三点,终于看到数码管稳定显示“12.05”时,那不只是代码运行成功,更是你亲手校准了现实世界的一个微小刻度。这种确定感,比任何高分都珍贵。