
前阵子帮一个学弟完善他“基于STM32的智能计价电子秤”的毕业设计顺手把这套设计从选型到编码、再到标定调参的完整思路又捋了一遍。这类题目在单片机和STM32相关的毕设里出镜率非常高核心不外乎用STM32读取称重传感器的微弱信号经过处理后换算成实际重量再按单价算钱最后把结果展现在屏幕上。听起来不复杂但真正做完你会发现在“读数准”和“用起来顺手”这两件事上坑还真不少。这篇文章就围绕我的实际设计过程展开适合准备做单片机电子秤毕设的同学也适合想把STM32信号采集链彻底玩明白的嵌入式初学者。选型、电路、代码、标定、调试这条线走下来你收获的绝不止一个能跑通的demo更重要的是弄懂“传感器输出的小信号是怎么一步步变成屏幕上的重量数字”的完整过程。文章里我会把每一步选择的理由、计算公式的推导、以及我实调中踩过的典型问题都写出来直接当设计文档参考也完全没问题。1. 系统架构与器件选型思路1.1 主控选型STM32F103C8T6为什么够用且好用做电子秤之前先要回答一个问题主控用什么。51单片机当然能驱动HX711也能显示重量但一旦你尝试加单价设置、累计金额、历史记录、甚至后续扩展蓝牙模块51的资源立刻变得捉襟见肘。相比之下基于ARM Cortex-M3内核的STM32F103C8T6主频72MHz有64KB Flash和20KB RAM12位ADC、多个定时器、I2C、SPI、USART一应俱全价格也已经降到了十几块钱的级别。这个资源盘子做一套电子秤绰绰有余而且学习资料极其丰富出问题也好排查。当然用STM32不是因为它必须被用上而是因为它能让你把精力集中在“传感器信号处理”和“业务逻辑”这些真正有价值的部分而不是把时间耗在折腾外设资源不够、扩展起来处处受限的尴尬上。毕设选题评价时“可扩展性”也是评委很看重的一个点你如果用的是51后续想加语音播报、WiFi上传数据、OLED显示曲线难度和成本都远超STM32方案。1.2 称重传感器与HX711电子秤的核心信号链电子秤最关键的部件是力传感器常用的是电阻应变式压力传感器。它里面有四个电阻应变片贴着弹性体通常是铝合金件贴成惠斯通电桥。当重物压上去弹性体产生微米级形变应变片电阻值跟着变化电桥输出电压就会产生毫伏级别的变化。这个变化极其微弱举个例子一个量程5kg的传感器灵敏度约为2mV/V桥路供电5V时满量程输出也才10mV。10mV是什么概念STM32内置ADC参考电压一般是3.3V哪怕按12位分辨率算1LSB约为0.8mV。也就是说满量程信号才相当于十几个LSB直接用MCU内部ADC根本测不出精度。这个问题的行业通用解法就是在传感器和MCU中间加一颗高精度ADC 放大芯片比如这里要重点说的HX711。HX711是一颗24位Delta-Sigma型ADC内置低噪声可编程增益放大器PGA增益可选32、64、128倍。它专门为桥式传感器设计内置稳压电路可以直接给传感器供电且功耗极低。你不需要关心内部复杂的Delta-Sigma调制原理只需要按芯片手册规定的时序把24位数据读出来即可。选这颗芯片另一个重要原因就是便宜且模块成熟几块钱就能买到带引脚接线的现成模块适合毕设项目快速落地。1.3 显示与交互OLED、按键与整体方案取舍显示部分我推荐0.96寸I2C接口的OLED屏优点有三个一是I2C只需要两根线接线极简二是屏幕自刷新不需要像LCD1602那样频繁写命令三是显示内容丰富可以同时显示重量、单价、金额、累计信息。LCD1602不是不行但它显示容量有限很多页面得翻屏演示观感差一些。如果你手头只有LCD1602也不是不能用只是我为这个项目最终选了OLED。按键交互方面我设计了四个按键去皮、单价、单价-、累计/清零。每一个都采用GPIO外部中断或轮询扫描方式处理配合消抖逻辑。这里有一个容易忽略的设计点按键按下后界面必须立刻反馈否则用户会以为没按上而反复按。所以我在主循环里保持约20ms扫描一次按键并用状态机管理界面状态确保“点击-响应”的延迟人眼看不出异常。另外考虑到有些同学后续想加HX711之外的数据输出功能我预留了一路串口PA9/PA10调试时打印原始码值和计算后的重量非常方便。这个串口在毕设答辩现场还能用来演示“实时数据监控”配合串口助手或自己写的小上位机展示效果很加分。2. 硬件电路设计与实物搭建2.1 引脚分配与连接我的具体引脚分配如下表所示。这个分配原则是把HX711的数据线放在普通GPIOOLED用硬件I2C1按键放在带外部中断能力的引脚上串口留给调试。功能模块信号STM32引脚HX711PD_SCKPB0HX711DOUTPB1OLED屏SCLPB6I2C1_SCLOLED屏SDAPB7I2C1_SDA按键1去皮KEY1PA0按键2单价KEY2PA1按键3单价-KEY3PA2按键4累计KEY4PA3串口调试TX/RXPA9/PA10HX711模块和STM32之间只需要两根信号线连接方式极其简单PD_SCK接PB0DOUT接PB1。但这里有个细节要提醒如果HX711模块以5V供电它的DOUT输出高电平接近5V。STM32F103C8T6的大部分引脚标注FT的可以容忍5V普通引脚不行所以最好选择标有FT的引脚来连接DOUT或者用电阻分压/电平转换。PB0和PB1在F103C8T6里属于FT引脚实测没有问题。PD_SCK是输入引脚3.3V高电平对5V供电的HX711来说也已经超过它的高电平阈值VDD*0.5约2.5V所以能正常识别。2.2 电源系统和去耦电源是整个电子秤最容易出暗病的地方。我最初图省事直接用USB线给STM32核心板供电结果读数一直跳。原因很简单USB供电本身纹波大HX711内部虽然有一定的电源抑制能力但对电源噪声仍然敏感而且核心板上的AMS1117线性稳压把5V降到3.3V之后噪声依旧存在。后来我改成独立5V适配器供电并在STM32核心板电源入口处并接一个100uF电解电容和0.1uF瓷片电容同时在HX711模块的VCC和GND之间也加上一个100uF电容。这个改动立竿见影数据跳动幅度下降非常明显。所以我的建议是电源部分不要省至少保证给HX711模块供电的5V是相对干净的最好用纹波小于50mV的电源适配器不要指望从USB取电还能做得非常稳定。这里还需要注意模拟地与数字地的处理。在核心板和HX711模块之间信号地必须共地否则I2C和HX711时序都会异常。如果自己做PCB需要把模拟地HX711的AGND和数字地DGND单点连接不要大面积铺地直接连成一片。买现成模块则简单些GND接在一起即可。2.3 机械结构安装注意事项别小看机械安装传感器读数稳不稳一半看结构。我用的是铝制悬臂梁式传感器需要在称重平台上做固定。安装时要特别注意两个原则一是传感器固定面要用螺丝拧紧且螺丝不能松动二是被称物要尽量放在托盘正中心避免秤盘偏载产生额外的扭力。另外一个很常见的问题就是共振。如果平台是一块薄铁板放重物瞬间会产生机械振荡振荡频率可能落在几Hz到几十Hz之间反映到读数上就是“数字跳个不停”。解决办法是在软件里加数字滤波这个我在后面详细说同时在硬件上给传感器底部垫一层橡胶垫。实操下来垫橡胶垫后振荡峰值明显衰减滤波压力小了很多。3. 软件架构与核心模块实现3.1 软件分层与状态机如果代码全部怼在主循环里前期调通很容易但后期改需求会非常痛苦。我建议按“驱动层-算法层-应用层”三层结构组织代码驱动层负责HX711时序读写、OLED显示、按键扫描、串口打印只做硬件操作不参与业务判断。算法层负责滤波、去皮、重量换算、标定参数维护输入是原始ADC值输出是稳定的重量值。应用层负责计价逻辑、显示页面切换、按键事件处理。这种分层最大的好处是每一层都能单独测试。比如写驱动层时可以先写一个读HX711原始值的循环函数再用串口打印原始码值验证时序对不对写算法层时可以用固定数组模拟传感器数据跑滤波算法不依赖真实硬件。这个思路在你调试陌生外设时能帮你快速定位问题到底出在硬件接线上还是软件逻辑上。应用层我用一个简单的状态机管理显示页面状态包括正常称重、设置单价、累计金额显示。按键事件推动状态迁移例如在正常称重页面长按“单价”进入设置单价页面设置完成后超时5秒自动返回。状态机代码虽然看起来多但逻辑清晰不容易出现“按键乱跳”的问题。3.2 HX711数据驱动时序详解HX711的通信用两根线完成时序很固定手册上的描述我转换成容易理解的版本等待DOUT变为低电平。DOUT高电平表示芯片正在转换或数据未准备好变低代表有24位新数据可读。DOUT变低后MCU从PD_SCK引脚输出脉冲。每输出一个下降沿或者按上升沿读取具体以自己代码为准这里用常见的循环移位方式DOUT上的一位数据被送出来。持续24个脉冲就能依次读到最高位到最低位的24位数据。第25个脉冲的作用是选择下一次转换的通道和增益。高电平持续时间不同会导致芯片在脉冲结束前启动下一次转换所以要严格按“第25个脉冲拉高后马上拉低”来操作才能确保选择通道A、增益128。一个典型的读取函数如下uint32_t HX711_Read(void) { uint32_t data 0; uint8_t i; while (HX711_DOUT_READ()); // 等待DOUT变低 for (i 0; i 24; i) { HX711_PD_SCK_HIGH(); data data 1; if (HX711_DOUT_READ()) data; HX711_PD_SCK_LOW(); delay_us(1); } HX711_PD_SCK_HIGH(); delay_us(1); HX711_PD_SCK_LOW(); delay_us(1); return data; }24位数据是二进制补码形式实际操作中可以直接当作有符号数处理。如果把uint32_t数据左移到符号位位置再右移回来得到int32_t值负重量比如传感器被反向拉也能正确处理。这里我不展开核心是保证“24位数据完整读出时序不出错”。3.3 滤波算法实践HX711输出的原始码值其实一直在一个范围内波动哪怕没有放任何东西。波动的来源包括传感器本身的噪声、电源纹波、机械微振动、甚至空气流动。这种波动如果不处理显示屏上的数字会不停跳动体验极差。我最开始只做了一次读取就直接换算结果重量在空载时能跳十几克根本没法用。后来依次尝试了三种方案多次采样取平均值比如连续读10次取平均能降噪但效果有限而且响应速度变慢。滑动平均滤波维护一个长度为N的环形数组每次读入新值就移除旧值再取平均。N取10-20时稳定性和响应速度平衡得很好。限幅滤波滑动平均先做一阶限幅如果新值与上次值之差超过设定阈值则丢弃否则才进入滑动平均数组。这个组合能很好地抑制脉冲干扰比如不小心碰了一下秤盘产生的尖峰。我最终采用的是“限幅滤波 15点滑动平均”的组合。限幅阈值要看实际量程5kg量程下我设为200个原始码值换算成重量大约是50g左右的跳变限制。对于瞬间冲击干扰这个阈值能挡住80%以上的毛刺。这里还想提一句如果你的平台机械振动严重可以考虑更高级的卡尔曼滤波。但就电子秤这个应用场景来说卡尔曼的响应速度和参数调试成本明显大于收益我感觉有点杀鸡用牛刀了。把基础滤波做好再加上合适的机械减震已经能达到不错的效果。3.4 重量计算、去皮与计价逻辑重量计算的核心公式只有一条weight_g (raw_value - offset) / scale;其中raw_value是经过滤波后的24位原始码值。offset是空载秤盘上无重物时测得的基准码值。scale是“每克重量对应的码值变化量”也就是标定斜率的倒数换算出来的比例因子。去皮功能本质是重新设置offset用户把容器放在秤上按下“去皮”按键系统记录当前raw_value为新的offset这样显示重量归零。这时容器重量虽然真实存在但被“扣除”了再往容器里加重物时显示的净重就是容器内物品的重量。这个逻辑和超市电子秤完全一致毕设答辩时很好讲清楚。计价逻辑更简单amount weight_g / 1000.0f * unit_price; // 单位价格按“元/500克”或“元/千克”来设定我设的是单价单位为“元/500克”这是国内菜市场最常见的方式。实际写代码时要注意weight_g是整数克数除1000之前先转成浮点数否则整数除法会直接把小数丢掉导致金额永远为0。累计功能我实现的是每次称重完成并按下“累计”键后把当前金额加到累计总额里同时把当前重量清零准备下一次称重。累计总额存在RAM里掉电丢失但演示时足够。如果你希望掉电保存可以用STM32内部Flash做一个简单的读写操作不过要注意擦写次数限制毕竟Flash寿命约1万次频繁写入会缩短寿命。毕设作品一般不会有这个隐患但如果做产品就要改用外部EEPROM。4. 标定校准与精度调优4.1 两点标定的数学原理所有称重设备出厂前都要做校准毕设作品也不例外。HX711的输出码值和实际重量之间基本呈线性关系所以只需要两个已知点就能算出斜率和截距。这里我用最简单的两点标定法空载状态下读取N次原始码值取平均记为raw_offset。放上一个标准砝码例如500g读取原始码值取平均记为raw_known。计算scalescale (raw_known - raw_offset) / known_weight_g;如果standard_weight是500g那么scale的单位就是“码值/克”。之后任意时刻重量weight_g (current_raw - raw_offset) / scale;举个例子空载时raw_offset830000放500g砝码后raw_known1025000那么scale(1025000-830000)/500390也就是每克对应390个码值。此时读取到当前raw920000则重量(920000-830000)/390≈230.77g。这个数值带入之前的按键单价逻辑就能正确算出金额。要注意的是这个线性模型在实际传感器上会有轻微的非线性误差标定点附近的精度最好离标定点越远误差可能略大。如果追求更高精度可以增加标定点做分段线性插值但对本科毕设来说两点标定已经完全够用。4.2 在线标定流程实现为了方便现场调试我做了一个串口标定工具通过串口发送命令进入标定模式先发送“Z”记录空载offset再放置标准砝码发送“C 500”记录500g对应的raw然后计算scale并存进变量可选保存到STM32内部Flash。这个方式的优点很明显不用反复烧录程序就能重复标定现场答辩万一传感器受温度影响偏移了也能快速重新标定。如果你不想用串口用按键实现类似流程也可以按住“去皮”键3秒进入标定模式再按“累计”键确认每个标定点。但按键流程代码会稍多一点故障排查也更费劲所以我个人倾向串口方案。标定参数存储方面我最终没有用Flash而是一个常量保存到代码里因为毕设演示环境固定重新编译烧录也就十几秒。如果考虑做成产品则需要把offset和scale存到外部Flash或者片上Flash专门区域上电时读取并校验防止参数丢失。4.3 误差来源与改善策略标定完还会遇到一个比较头疼的问题同样的砝码上午称和下午称重量不一样或者开机一段时间后数字慢慢漂移。这背后有几个典型误差源温度漂移传感器的弹性模量会随温度变化应变片的灵敏度也会变。如果传感器贴在阳光直射的位置漂移会更明显。解决办法是保持环境温度稳定不要用体温去捂传感器。传感器蠕变持续加载重物时弹性体在恒定应力下会发生微小形变导致读数缓慢增加。这属于传感器本身特性无法根治但选质量好一点的传感器能明显减小。电源漂移HX711的参考电压由电源决定如果电源电压随时间缓慢变化ADC转换结果也会跟着变。所以强调“干净的5V电源”非常重要。机械结构松动螺丝没拧紧哪怕只松了一点读数也会随机漂。排查的时候先重新拧一遍所有固定螺丝往往有奇效。改善精度的策略有几个立竿见影的手段一是把传感器供电和HX711的AVDD接到同一点避免传感器激励电压和ADC参考电压不一致引入误差二是软件上每5秒做一次零点漂移补偿只有当前重量为0时才更新offset这个做法能让长时间使用时的空载零点保持稳定三是每次标定前先让设备上电预热5-10分钟让内部温度和电源达到稳定状态再标定。5. 调试实录与毕设落地经验5.1 常见问题排查汇总我在整个项目调试过程中记录了不少问题和对应的解决方案整理成速查表遇到类似问题可以直接对照。现象可能原因排查与处理方法读数为0或随机跳变HX711接线错误、DOUT一直为高检查E/E-、S/S-和电源接线用示波器或万用表看DOUT是否有拉低动作读数慢慢向上飘传感器蠕变、温度变化预热设备后标定称重时减少长时间单点持重重量突然跳到很大值电源尖峰、机械冲击触发滤波失效加强限幅滤波给传感器加橡胶缓冲垫按下去皮键没反应按键消抖配置问题、GPIO模式错误检查按键上拉/下拉配置确认扫描周期合理OLED不显示或花屏I2C地址不对、接线松了、速度过快先用I2C扫描程序查地址再调低I2C时钟频率到100kHz以下标定后小重量不准标定点太单一、offset没更新重新进行完整两点标定并确保空载读数稳定后再记录offset单价无法设置按键长按逻辑短路、状态机没切换打日志确认按键事件是否进入设置页面逐条排查状态机分支5.2 几个典型的调试现场记录有一个问题我印象很深设备第一次能正常称重但断电重启后读数变成负数一大截。排查了很久才发现是标定后我把offset变量定义成了全局变量但上电初始化时用了另一个未初始化的变量参与计算结果第一次读取的raw_offset和标定后的offset不一致。这种“初始化和标定参数割裂”的问题写成代码时容易忽略。最终我在上电流程里加了明确的初始化顺序先读传感器原始值确定初始offset再加载已保存的标定参数覆盖offset。另一个问题是HX711读取函数偶尔会卡死。原因是最开始的代码在等待DOUT变低时用了死循环如果芯片因为上电时序问题一直不输出低电平程序就停在那里。后来我加了超时机制如果等待超过100ms就跳过一次读取返回上一次的有效数据。这个改动不仅解决了卡死问题还让系统在传感器异常时能继续运行并显示错误标记硬件故障排查起来更直观。还有一次是OLED显示金额一直为0看起来很诡异。查代码发现是单价变量类型为int而重量除以1000后再乘以单价int类型在除法时直接截断了小数。改成float类型后问题解决。这个坑在C语言里非常典型凡是做除法运算都要注意运算子类型整型除以整型就是整数除法哪怕你赋给浮点型变量也不会自动变好。5.3 毕设演示与答辩的小建议如果这个项目是拿去交毕设或参加答辩我有几个从实际评审反馈中总结出的建议。第一个演示前一定预留“预热校准”时间。不要在演示现场刚开机就称重数据漂移会让评委觉得你的系统不稳定。提前10分钟上电放好砝码在机器旁边正式开讲前快速放一次砝码验证读数是否正常。第二个准备好“去毛刺”的演示脚本。评委可能会问你如果东西放歪了、读数跳了怎么办你可以现场演示偏载误差然后切换到显示页面展示你预留的“偏载提示”状态。这个细节非常加分因为它说明你考虑了真实使用场景。第三个代码和文档对应起来。评委不一定有时间看所有代码但一定会翻设计文档的数据流图或系统框图。你必须在文档里把“传感器-HX711-STM32-OLED显示”这条数据链路写清楚并注明每一级的信号类型和精度参数。把HX711的24位ADC精度、128倍增益这些关键参数放到文档醒目位置远比空谈“系统稳定可靠”更有说服力。写在最后的一点个人体会这套设计从画板到调通我前后花了大约三周的业余时间。最深的体会是电子秤这种题目看着简单但真正要把“准”和“稳”做出来牵扯到传感器信号调理、电源设计、滤波算法、标定方法等多方面知识非常适合作为单片机毕设——它既有硬件又有软件既有模拟信号又有数字信号既有底层驱动又有上层逻辑是一道信息量很足的综合性考题。如果你正准备复现这个项目我的建议是先把HX711的原始码值读出来、能在串口稳定打印再谈滤波和标定。不要一上来就追求显示重量。底层稳了上层就是水到渠成的事。希望这篇记录能帮你少走点弯路把这套设计做出一个连自己都满意的状态。