电子秤这项目放在嵌入式入门进阶这个节点上真的是再合适不过了。做之前你可能以为就是个“称重单价×重量金额”上手之后才发现模拟前端采集、滤波算法、量程标定、按键交互、低功耗、掉电保存这些全都要碰一遍几乎是把一套小产品的完整链路走通了。网上关于STM32的智能计价电子秤资料不少但大多数只贴个代码、给个原理图真正把“为什么这么设计”“调试时为什么卡住”讲清楚的不多。这篇就把我自己做这个项目时从硬件选型到软件实现的完整思路翻出来结合实测中踩过的坑给准备做毕业设计、比赛作品或者单纯想练手的朋友一份能直接照着做的方案。先交代一下我做的这套东西是什么主控STM32F103C8T6称重传感器加HX711差分ADC采集12864 OLED显示三个按键完成去皮、单价设定、累计等操作带超量程报警和掉电参数保存。整机静态电流控制到几十微安两节18650供电实测能用很久。项目不算复杂但做完它对STM32的GPIO、定时器、外部中断、Flash读写、低功耗模式这些资源的理解会明显上一个台阶比单纯点灯、驱动外设要有意思得多。1. 项目概述与整体设计思路1.1 智能计价电子秤的应用场景与功能定位先想清楚产品到底要解决什么问题。菜市场散称区、水果店、快递驿站按重量算运费、小卖部按斤卖干货这些场景里传统的机械秤早就被电子秤替代了但市面上一两百块的低端电子秤问题不少按键手感差、显示模糊、没有单价记忆换个商品要重新按单价高峰期排队很烦。我这个项目做的就是“够用且可控”的智能计价秤称重精度到克以内单价直接按键输入金额自动计算去皮方便断电后再开机单价、标定参数都不用重新弄。这套方案适合三类人一是准备嵌入式方向毕业设计的学生功能点覆盖广、指标容易写二是做课程设计或电子设计竞赛的队伍硬件成本不到一百块软件实现不依赖复杂算法但又能体现工程思维三是对称重、压力传感这类模拟采集感兴趣的开发者想搞明白HX711为什么能放大几微伏的信号、该怎么滤波标定。我最终定下来的核心指标是这样的量程5kg显示分辨率1g精度误差控制在±1g以内常温、水平放置、标准砝码条件下实测支持0.00到999.99元/千克的单价输入去皮范围覆盖全量程单价和标定参数在掉电后不丢失。这个规格对应菜市场散称场景已经完全够用甚至比不少低端商品秤的体验要好。1.2 为什么选STM32而不是其他方案选型这件事值得多说两句。你完全可以用专用秤重芯片比如芯海、纳芯微的测量SoC集成度高、出厂校准好改一下寄存器就能量产但这样做完你对核心原理还是黑的而且这类芯片资料少、开发工具不友好不适合学习。反过来用51单片机也行HX711读取是GPIO模拟时序51的IO完全够用但51的资源太紧张显示要刷屏、按键要扫描、Flash还要存参数程序写起来处处受限后期扩展几乎没有空间。STM32F103C8T6在这个项目里是性价比很高的选择。它是M3内核、72MHz主频、64KB Flash、20KB RAM跑一个裸机状态机绰绰有余还有足够的定时器做低功耗唤醒、硬件I2C/SPI备用。最关键的是生态成熟CubeMX图形化初始化、HAL库封装后新手不用跟寄存器死磕老手也能用标准库或者寄存器操作抠细节。如果预算紧张或者想走无线化换成ESP32或ESP8266也行但注意它们内部ADC噪声大、不适合直接采称重信号老老实实外挂HX711的话又多一个通信环节。所以追求稳定、练嵌入式基本功STM32是当前最优解。1.3 系统总体架构整个系统分四层物理层是称重传感器把重量变成微弱的差分电压信号采集层是HX711带24位Sigma-Delta ADC和128倍可编程增益控制层是STM32负责读数据、滤波、标定、算金额、管理按键和显示交互层包含OLED、蜂鸣器、按键。数据流是这样的传感器输出毫伏级差分信号 → HX711内部放大并转为24位数字量 → STM32通过GPIO模拟时序读取原始码值 → 软件滤波和标定换算成重量 → 结合单价计算金额 → 显示调节。这个架构看着简单但每一层的设计选择都有讲究。比如为什么不用STM32内置ADC直接采传感器因为应变片电桥满量程输出只有10mV到20mVSTM32内置12位ADC的参考电压是3.3V对应分辨率也就0.8mV左右直接采连1g的毫伏变化都分辨不出来。外接HX711相当于把“小信号放大高精度采样”这个脏活外包出去主控只做读数和逻辑分工清晰。2. 硬件方案与关键电路设计2.1 核心器件选型与替换建议称重传感器我用的是最常见的电阻应变式平行梁传感器量程5kg四线制红黑绿白满量程灵敏度约2mV/V。选5kg是因为我手头有标准砝码方便校验如果你想称更大的东西换成10kg或50kg完全没问题电路不用改动。传感器量程有个经验法则日常使用重量最好不要超过量程的70%保留超载余量否则长期压着会让弹性体变形、零点漂移加剧。HX711是24位ADC内部集成稳压电路和差分输入最大增益128倍时输入信号满幅约±20mV正好匹配应变片电桥输出。它有两个参考产品经常被拿来对比一是ADS1232同样的24位Sigma-Delta、带内部温度传感器、增益可选1/2/64/128价格稍贵但性能略好二是国产的CS1231封装更小、功耗更低、SPI接口适合对体积敏感的手环、体脂秤方案。对学校项目来说HX711芯片两块钱左右、模块几十块资料漫天飞最折腾人也最不容易翻车。主控选了STM32F103C8T648脚LQFP封装价格在几块钱到十几块钱之间波动Flash 64KB、RAM 20KB。如果画PCB嫌LQFP焊接麻烦可以用STM32F103C8T6的最小系统板插面包板或直接飞线一样能做。我后来画了块PCB但初期验证阶段就是用核心板加模块飞线完成的逻辑完全一致。显示部分我推荐0.96寸I2C接口的SSD1306 OLED128×64分辨率。它功耗低、显示内容灵活能同时显示金额、单价、重量三组数据还能调大字号。缺点就是强光下看不清如果秤要放在户外或者阳光直射环境老老实实换LED数码管或者LCD1602都行数码管在强光下的可读性远好于OLED。按键用三个轻触开关就够了后面我讲具体怎么分配功能。2.2 传感器、HX711与STM32的接线规划硬件接线这件事我一开始以为照着网上原理图连就行实际连的时候还是翻车了一次把传感器红黑接反HX711读数直接满幅失真。这里把最终确认可用的连线表列出来信号传感器/HX711STM32引脚说明HX711 SCK模块SCKPA1时钟输入低到高移位HX711 DOUT模块DOUTPA0数据输出低电平表示转换完成HX711 VCC模块VCC5V或3.3V推荐5V内部稳压后供电更稳HX711 GND模块GNDGND必须与单片机共地传感器红E接HX711激励正激励电压一般为5V传感器黑E-接HX711激励负传感器绿S接HX711通道A正输入传感器白S-接HX711通道A负输入这个引脚规划有几个细节要注意PA0和PA1我同时需要考虑外部中断和复用功能万一其他模块占用就冲突了所以原理图阶段就要把引脚资源表画出来避免做PCB时才发现引脚打架。HX711的VCC我用的5V因为它内部稳压电路会把AVDD稳到5V或者按芯片手册配置给传感器电桥提供一个稳定的激励源。如果你用3.3V供电也可以工作但信噪比会下降实测零点跳动会略微增大。HX711模块和单片机之间靠GPIO模拟时序不需要任何协议库这就意味着只要引脚电平兼容就行3.3V单片机读5V模块的数字输出也没问题DOUT输出高电平约为VCC而HX711模块如果VCC5V其DOUT高电平是5V左右STM32引脚一般是FT兼容5V的PA0/PA1是兼容5V的可以直连如果用的引脚不是FT电平容忍最好串个1k电阻分压。2.3 电源、稳压与硬件抗干扰设计电源部分如果只求能跑用USB供电就够了。但既然是“智能计价电子秤”的项目我建议直接上电池顺便体验一把低功耗设计两节18650串联供电经LM2596或者MP1584降压到5V再给单片机和HX711。注意一点HX711的模拟供电和数字供电要好好滤波AVDD对地加一个47uF电解电容和0.1uF陶瓷电容并联这个组合能显著抑制ADC读数跳动我实测滤波电容从无到有零点稳定性肉眼可见地提升。硬件抗干扰这块给新手几个明确建议第一传感器线要双绞线或者屏蔽线而且尽量远离电机、继电器、PWM走线。称重系统最怕的就是高频噪声耦合进模拟前端虽然HX711是差分输入能抑制共模干扰但线要是拉太长、挨着大电流走线照样会跳字。第二模拟地和数字地单点汇合。HX711模块的GND和单片机GND之间用一根尽量粗的短导线或者直接在PCB底层大面积铺铜连在一起不要绕圈也不要形成环路。第三按键脚、复位脚加上拉电阻和100nF滤波电容能减少按键抖动引起的外部中断误触发。蜂鸣器如果是无源蜂鸣器用三极管驱动注意续流二极管或者RC吸收防止关闭瞬间的反电动势干扰传感器。3. 核心代码实现与关键算法3.1 工程搭建与代码分层策略这一节把软件工程的做法捋顺很多人写嵌入式的通病是“main.c五百行、寄存器满天飞”项目能跑但改一个显示函数要全局搜维护成本高。我先用STM32CubeMX生成基础工程选STM32F103C8T6芯片配置好PA0/PA1为GPIO输入/输出、外部中断、三个按键GPIO、OLED的I2C如果选I2C OLED可以把I2C1 SCL/SDA设为PB6/PB7、一个定时器作为软件时基比如TIM4做1ms中断、Flash和RTC相关时钟。生成代码之后我额外建了四个目录Core/ -- CubeMX生成的启动文件和内核相关 Drivers/ -- HAL库 App/ -- 业务逻辑weigh、key、ui、data_store、main_task Middlewares/ -- OLED驱动、HX711驱动等第三方代码App目录下的文件才是整个项目的核心main.c只做初始化和调用App_Init()、App_Loop()。这样的分层带来的直接好处是我想把HX711驱动从GPIO模拟改成硬件SPI只用改Drivers层上层业务不用动我想加一个Wi-Fi模块上报数据新增一个network文件就行不影响称重逻辑。这个习惯如果你现在养成了后面做嵌入式Linux或者更大项目时会轻松很多。3.2 HX711 读取与滤波从时序到稳定读数HX711的数字接口很简单DOUT拉低表示转换结束SCK产生24个上升沿把24位数据依次移出第25个上升沿让芯片进入下一次转换。数据是二进制补码格式最高位是符号位读取后要转成有符号数。基础读取函数的经典写法如下uint32_t HX711_Read(void) { uint32_t count 0; uint8_t i; // 等待DOUT变低表示转换数据已经准备好 while (HAL_GPIO_ReadPin(HX711_DOUT_GPIO_Port, HX711_DOUT_Pin) GPIO_PIN_SET); for (i 0; i 24; i) { HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_SET); delay_us(1); count count 1; if (HAL_GPIO_ReadPin(HX711_DOUT_GPIO_Port, HX711_DOUT_Pin) GPIO_PIN_SET) { count; } HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET); delay_us(1); } // 第25个脉冲让HX711开始下一次转换 HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_SET); delay_us(1); HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET); return count; }读取一次只能得到一个原始码值但HX711的输出速率默认10Hz通道A 128增益也就是说每100ms才刷新一次。如果直接把这个码值去标定显示零点处会看到个位数字一直跳这不是芯片坏了而是正常噪声叠加。解决办法是在应用层做滤波我试过三种方案差异明显滑动平均滤波N8实现简单动态响应快但偶尔一个毛刺会把结果拉偏。 中值滤波窗口5能干掉脉冲毛刺但输出不平滑显示数字会有“台阶感”。 滑动平均中值组合先取5个原始值排序取中值再对连续4个中值做滑动平均实测读数稳定性和动态响应都能接受。这个组合的成本是多读几次数据好在HX711读取本身非常快用不了多少CPU。最终我确定了每次称重显示周期大约200ms更新一次取组合滤波后的结果既不会让人感觉秤“迟钝”也不会看到读数疯狂跳变。3.3 标定流程与重量换算所有称重设备的核心都在标定。简单说标定就是建立“ADC原始值”和“实际重量”之间的映射关系。工程上最可靠的是两点标定法零点取空秤读数满点取已知标准砝码读数然后线性插值。我标定的流程是这样的首先保证秤在水平台面上、传感器四个角受力均匀如果四个角受力不同读数会是斜的这一点下面讲常见坑。空秤时连续取50次组合滤波后的原始值求平均记为zeroRaw。放上一个精确的标准砝码我用500g和2kg两个档位分别校验取50次求平均记为refRaw。比例因子factor (refRaw - zeroRaw) / 标准重量单位g。实际重量weightGram (当前原始值 - zeroRaw) / factor。这套逻辑写起来不复杂但有一个小坑factor如果用浮点数运算单片机的小数点保留要注意我直接用float最终显示用取整精度实测能到±0.5g左右如果为了固件精简用整数运算很容易把小数部分截掉导致称重出现系统性偏差。所以标定参数保存的时候我会把zeroRaw和factor都转成uint32_t但计算时明确转成float再做算数。标定参数存哪很多方案会外挂AT24C02 EEPROM但我觉得对F103来说没必要直接用内部Flash的一个扇区。F103的Flash按页擦除每页1KB小容量芯片每页可能不同这个要看芯片手册我选最后一个扇区把结构体一次性写入。注意写Flash前要整个扇区擦除擦除操作耗时要几十毫秒不能在正常称重过程中插进来否则会卡主循环。3.4 去皮逻辑与置零切换去皮是计价秤的刚需功能。买菜时用户得先放个容器再按去皮把容器重量清零。实现逻辑不复杂记录当前实际重量作为皮重tareWeight显示重量 实际重量 - 皮重。但有个细节容易忽略去皮之后实际重量不能超过传感器量程减去皮重再加载货否则传感器过载。所以我的代码里专门加了一个判断if (actualWeight (MAX_LOAD - tareWeight)) { // 提示超量程蜂鸣器报警置零后不能正常计价 }另外“去皮”和“置零”要分清置零是把当前值当作零点存储到标定参数里对系统来说相当于永久改了零点去皮只是临时把当前值当作皮重下次重新上电就恢复。我这套系统里短按去皮键执行去皮长按则进入置零操作这样同一个键承担两个功能UI上也能提示“去皮”和“清零”状态。3.5 单价输入、金额计算与显示布局单价输入是纯交互逻辑设计不好用起来特别别扭。我用了三个按键K1负责单价数字输入和位选K2负责加减K3负责确认与去皮。具体交互方案初次输入单价时OLED先显示0.00K1每按一次切换到位数百位、十位、个位、小数第一位、小数第二位当前选中的位反色闪烁K2在选中位上加一超过9自动归零确认后K3锁定单价此时每放置一次商品称重刷新后自动计算金额 重量(kg) × 单价(元/kg)。金额计算我用的是整数分的方式避免浮点误差weightGram是float单价unitPrice是uint16_t单位是分/千克。总金额cents (uint32_t)(weightGram / 1000.0f * unitPrice)最后两位小数显示到“分”。这样做的好处是显示和计算都不用担心浮点丢精度后面给外部设备传金额也方便。显示界面我切成上下两行上方用16号字体显示金额中下方用12号字体分别显示单价和重量单位用kg和元/kg因为OLED亮度衰减和刷新次数有关我还做了30秒无操作自动熄屏但保持称重和计价正常进行。3.6 掉电保存、低功耗与报警设计掉电保存我用了前面说的内部Flash方案。值得强调的是保存不能太频繁——Flash擦写寿命一般是1万次到10万次你要是每次称重都保存几个月就废了。我的策略是单价和标定参数只在“单价确认”或“置零”时保存一次称重过程中的临时数据不保存掉电后恢复出厂零点标定即可。低功耗这块我把板子整机电流降到了可接受范围三个手段叠加OLED熄屏HX711进入掉电模式PD_SCK持续高电平超过60us即可进入掉电这个寄存器操作是在读取函数里完成的读完第25个脉冲后如果让SCK保持高电平一段时间芯片进入掉电单片机进入STOP模式用RTC闹钟每100ms唤醒一次去检测重量变化。如果检测到重量变化超过阈值就唤醒屏幕并恢复正常工作超过几分钟无动作再进休眠。这套逻辑做完以后两节18650供电大概能撑很久比一直开机刷屏省太多。报警设计也不复杂超量程或金额超过上限时PWM驱动蜂鸣器响三短声OLED第一行闪烁显示“Over”之类的提示。4. 联调流程、调试技巧与常见问题排查4.1 我把调试步骤总结成了顺序清单拿到一个新板子或者飞线搭的电路一上来就写完全部代码然后烧录这不叫调试叫赌运气。我推荐的拆解顺序是先点亮最小系统和串口——确认时钟、复位、下载通道没问题再单独读HX711原始值并串口打印确认模拟前端和传感器接线正确然后调OLED显示确认I2C通信和地址没问题再写按键扫描最后才把称重、显示、按键组合起来跑完整流程。这样做的核心原因是隔离变量。如果一上来就烧全部代码出问题大概率不知道是显示没初始化还是键没扫到还是标定参数不对。每层都验证通过以后组合阶段出问题就只可能出在状态机逻辑或时序上。我的Type-C串口打印帮了大忙比如调试时发现某个按键偶尔响应看一下按键扫描打印的原始电平马上就知道是没去抖还是中断优先级冲突。4.2 高频问题排查实录含速查表做这个项目的人大概率遇到过下面这些问题我把自己的排查过程整理成表方便你照着来现象可能原因排查与解决板子上电无反应电源接反、复位电容没焊、BOOT0悬空进不了Flash启动测3.3V/5V对地电压检查BOOT0拉低、复位脚电平用示波器看8M晶振HX711读数一直0x7FFFFF或极值传感器E和E-接反、差分输入短路、HX711供电不够用万用表量E/E-电压差确保约1V级差交换红黑线零点读数跳动超过几个码电源纹波大、传感器线与干扰源太近、滤波窗口小加AVDD大电容屏蔽线单端接地加大滤波次数称重结果四角偏差大传感器受力不在中心、平台刚性不足重新固定传感器保证四个安装孔受力均匀不要用胶粘OLED不显示或花屏I2C地址错误、接线反、波特率过高、SSD1306复位时序不对用I2C扫描工具确认0x3C或0x3D地址SCL/SDA交换把I2C速度降到100k按键偶尔不响应内部/外部上拉缺失、未去抖GPIO配置上拉软件延时10-20ms去抖或用定时器扫描状态机下载程序失败下载引脚被复用、芯片锁死、BOOT0没拉低按住复位下载Flash全擦检查PA13/14未被占用必要时串口ISP清锁定这里单独说一下“I2C读设备ID读到A1A1”的问题。虽然这个项目用的是OLED不是ILI9341但我在帮别人排查SPI屏时见过太多次这种怪现象读ID得到的值是0xA1A1而不是芯片真实ID。这种问题十有八九不是芯片坏了而是读ID的寄存器地址不对、时序参数没匹配比如SPI模式搞成了Mode 0而芯片要Mode 3、或者数据位顺序反了。我自己的经验是凡是遇到这种“读数看起来有规律但就是不正常”的问题先别调代码拿逻辑分析仪看一眼波形CLK、MOSI、MISO三根线的相位对不对一目了然。很多嵌入式问题都是这种排查思路——先确认硬件时序再怀疑固件逻辑。4.3 调参心得我标定时遇到的真实案例标定环节我遇到过一个很典型的问题第一次标定时我把500g砝码放上去显示552g偏了10%。检查了HX711增益、滤波都没问题后来才发现是秤盘底下的支撑结构没做稳传感器弹性体上放砝码时整个平台轻微晃动等效于给传感器加了一个额外的偏置力矩。把支撑结构换成四角平衡的铝板后标定一次通过。还有一个容易忽视的问题温度漂移。传感器在刚开机的前几分钟内部应变片会因为通电自热和环境影响发生零点漂移我控制的方案是开机后做一个“预热”流程显示“Init...”倒计时几秒才开始允许称重这个细节在商品秤上是标配咱们自己做的也应该加上。5. 项目复盘与后续扩展方向5.1 我在这套方案里验证过的几个关键结论整个项目做完最大的收获倒不是“会用STM32读传感器了”而是对“嵌入式产品的完整生命周期”有了体感。硬件选型直接决定后面的工作量比如用了I2C OLED虽然省引脚但I2C调试本身对新手是个坎用了HX711虽然价格便宜、资料全但它的10Hz输出速率让滤波算法的窗口长度不能太大否则动态响应会差到让人想砸秤。代码分层确实能帮你节省大量时间。我写这套系统时因为前两个星期已经把weigh、ui、key这些模块分开并且各自调试通了最后联调只花了一个下午出bug也能快速定位到模块而不是在main函数几百行里翻。标定和称重原理这部分值得多看几遍传感器手册。我后来试着用STM32内部ADC直接采集效果很差才真正理解为什么这类产品都外挂专用ADC——不是STM32不行而是应用场景需要的是微伏级分辨率和长期稳定性这恰恰是通用MCU内置ADC的短板。5.2 站在项目基础上的扩展方向这个项目改造成本很低我列几个自己考虑过或者已经动手试过的方向供你参考第一加联网远程监控。用一个ESP8266模块通过串口或者SPI和STM32通信把重量、金额、交易流水定时上传到云平台。市面上就有用巴法云这类物联网平台做数据看板的手机端就能看到秤的状态这在商超管理、无人售货柜里很有实用价值。注意加联网之后功耗策略和本地数据备份都要重新设计不能因为网络模块把整个系统的低功耗毁掉。第二把裸机改造成RTOS系统。这个项目逻辑不算复杂裸机状态机完全能Hold住但如果你想让OLED刷新、HX711采集、按键扫描三个任务解耦用FreeRTOS或RT-Thread明显更舒服。特别是OLED刷新可以用低优先级任务按键和称重用高优先级实时性更有保障。换个RTOS之后再看这个项目你会发现代码组织方式又能优化一轮。第三换更高精度的传感器和ADC。把HX711换成24位的AD7190或者国产高精度ADC传感器换成0.02%精度等级再配合温度补偿整机可以做到实验室分析天平级别的分辨率。不过这个级别的项目要处理的问题从“代码”变成“硬件和计量”成本也会上去。最后再分享一个我的小技巧不管用什么开发环境我都习惯在工程里加一个自检函数上电后自动打印关键模块的硬件状态——读HX711原始值、OLED地址、Flash里标定参数的CRC校验值。这样即使隔了几个月再拿起这个板子也能快速判断硬件是好是坏不至于一上来就陷入“是不是代码坏了”的怀疑循环。这点对我这种喜欢把项目翻出来复用的人来说比什么都管用。