ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于STM32与HX711的智能体重秤开源项目设计与实现

基于STM32与HX711的智能体重秤开源项目设计与实现

从去年开始,我就在断断续续折腾一个基于STM32的智能体重秤项目,代码、原理图、仿真文件全部开源出来。做这个项目的原因很直接:市面上几十块的体重秤大多是专用IC方案,可玩性低,想加功能、换显示、调精度都得看厂商脸色;而用STM32做主控,等于把整个测量链路都攥在自己手里,从传感器采集到数据处理再到显示、上报,每一环节都能自己定义。这篇文章就把整个项目的设计思路、关键代码、避坑经验一次性讲透,给正准备做称重类产品或想拿STM32练手的朋友一份可以直接参考的完整方案。

项目整体并不复杂,核心就是应变式称重传感器加高精度ADC,再由STM32完成数据采集、滤波、标定和交互。但越是看起来简单的项目,越容易在细节上翻车——比如零点漂移、数值跳变、温漂影响。这些坑我在调试过程中基本都踩了一遍,文章里会专门整理成一个排查章节,方便你直接对照定位问题。

1. 项目概述与需求拆解

1.1 这个项目解决了什么痛点

很多人第一次接触称重项目时都会问:直接用51单片机或者Arduino不行吗?答案是可以,但体验完全不同。Arduino接到称重模块上确实几行代码就能出数,可一旦你面对的是批量校准、多设备一致性调整、低功耗待机、历史数据存储这些真实需求,Arduino的短板就会暴露无遗。STM32项目的好处在于,从底层寄存器到上层应用逻辑都可以自己控制,同样的传感器硬件,经过合理的滤波和校准算法处理,精度和稳定性会有质的提升。

我在设计这个体重秤时,给自己列了几个硬性需求:

第一,量程要覆盖日常使用场景,至少能测到180kg,分度值要能做到0.1kg。第二,显示不能局限于单纯的数字,需要带有体重状态提示,比如BMI区间划分。第三,支持离线存储最近若干次测量记录,这样即使不接手机也能回溯数据变化。第四,要有低功耗设计,用电池供电时静态电流控制在微安级别。

这些需求看起来不多,但每一项都会反过来影响硬件选型。量程与分度值决定称重传感器和ADC的选型,离线记录决定是否需要外部存储芯片,低功耗则直接关系到供电方案和MCU睡眠模式的使用。整个项目就是在这些约束条件下逐步收敛出一个最合理的方案。

1.2 硬件选型与核心参数

主控芯片我选了STM32F103C8T6,也就是大家常说的“蓝丸”核心板常用型号。选它不是因为性能有多强,而是资料丰富、价格适中、内部Flash和RAM在这个项目中刚好够用,而且它有多种低功耗模式,适合做电池供电设备。

体重测量链路的核心是传感器和ADC。这里必须区分两个概念:称重传感器输出的是毫伏级模拟信号,STM32自带的ADC虽然能用,但12位的分辨率配合电桥输出信号,理想情况下也只能勉强分辨约0.5kg左右的重量变化,实际噪声影响下可能连1kg都分不出来。要实现对0.1kg级别的分辨,必须外接高精度ADC。我选了HX711,它是一颗24位高精度ADC,专为称重场景设计,内部自带稳压、可编程增益放大器和差分输入,性价比极高。

传感器方面采用最常见的电阻应变式半桥或全桥传感器。全桥传感器由四个应变片组成惠斯通电桥,供电电压典型为5V,灵敏度约为1mV/V到2mV/V。也就是说,在5V供电下,满量程输出大约为5mV到10mV。这个信号直接进HX711的差分输入,配合可编程增益放大128倍,就能把微弱信号放大到ADC可以充分量化的范围。

显示模块我用了I2C接口的OLED屏幕,0.96英寸,128x64分辨率。选OLED的原因一是省电,二是在强光下也能看清读数,三是不需要背光驱动,I2C占用引脚很少。如果需要更大显示面积,也可以换成LCD1602或者TFT屏,代码里只要保留显示接口层,替换起来并不麻烦。

供电方案上,整个系统既支持USB供电也支持锂电池供电。电池经过一颗低压差线性稳压器降到3.3V给MCU和OLED供电,HX711的模拟电源由稳压器输出供电。实测整机待机电流约20uA,工作状态平均电流约25mA,一节500mAh锂电池在每天测量两次的情况下,理论上可以使用数月。

2. 硬件设计原理图详解

2.1 称重电桥与HX711信号链

既然核心是称重,电路里最关键的信号链路就是从电桥到HX711差分输入这一段。很多人画原理图时容易忽略的一点是:HX711的AVDD模拟电源必须干净,不能直接和数字电源混在一起。我在原理图中给AVDD加了单独的LC滤波,L用磁珠,C用10uF并联0.1uF的组合,这样能显著降低电源纹波对ADC采样的干扰。

电桥输出端到HX711的输入脚之间加了简单的RC低通滤波,截止频率大概在几十赫兹。为什么需要这个?因为人体站在秤上时,传感器输出信号中包含高频抖动成分,如果不做硬件滤波,这些高频分量很容易造成ADC采样结果的随机波动。虽然软件里也可以再做数字滤波,但硬件上先把高频成分滤除,能大幅降低软件滤波压力。

这里把重点引脚连接列出来方便你画图时对照:

信号HX711引脚STM32引脚说明
数据输出DOUTPA1数据准备好后拉低,读取时按位读取
串口时钟PD_SCKPA0上升沿读取数据,25个脉冲完成一次转换
模拟电源AVDD3.3V经LC滤波后接入
数字电源VCC3.3V就近接0.1uF去耦电容
地AGND/DGND单点接地模拟地与数字地单点连接

要注意DOUT和PD_SCK的时序关系并不是普通SPI,而是HX711自定义的一种准SPI协议。它没有片选,也没有寄存器地址,只需要通过时钟脉冲数来触发读取和配置增益。第25个脉冲用于切换通道和增益,这个细节在代码章节会专门展开。

2.2 STM32最小系统与电源设计

STM32最小系统包含晶振、复位电路、启动模式选择和去耦电容。我用的板子焊接了8MHz晶振,系统时钟通过内部PLL倍频到72MHz。实际上这个项目对主频要求不高,即使跑到48MHz也完全够用。但是有一点要注意:如果使用了低功耗停止模式,外部晶振在Stop模式下会停振,唤醒后需要重新配置时钟,这段代码必须写对。

复位电路就是经典的RC复位,10k上拉电阻加100nF对地电容。启动模式BOOT0和BOOT1都拉低,让芯片从主Flash启动。每个电源引脚都放了100nF高频去耦电容,另外在主电源输入端加了10uF钽电容作为储能电容,确保电机或者屏幕刷新等瞬时大电流情况下电源电压不出现大的跌落。

电源设计上,系统支持两种输入方式:USB的5V或者锂电池的3.7V到4.2V。经过AMS1117-3.3稳压后输出3.3V。这里有个容易犯的错误:直接用3.3V给HX711供电其实没有问题,但称重传感器的激励电压最好稳定。如果使用5V供电给传感器桥路,灵敏度输出更高,信噪比更好;使用3.3V供电则输出信号幅度偏低,ADC噪声占比会增大。我在方案中选择了5V给传感器激励,理由是HX711的AVDD可以从5V接入,同时稳压器输出的3.3V单独供给MCU和OLED,实现模拟与数字的电源隔离。

2.3 显示与交互模块设计

OLED屏幕采用I2C接口连接STM32的I2C1,SCL对应PB6,SDA对应PB7。I2C上拉电阻取4.7k,走线尽量短。由于OLED模块内部已经带有驱动芯片SSD1306,MCU只需要通过I2C写显存数据即可,不需要额外的行列驱动电路。

交互部分我加了三个按键:一个“去皮”键,一个“切换显示”键,一个“开机/关机”键。开机键直接控制MOS管开关电路的通断,实现整机断电而非待机电流消耗。去皮键在测量状态下将当前载荷清零,这样可以把皮重或者用户手中抱着的物品重量扣除。切换键用来在不同显示模式间切换,比如实时体重、BMI参考、历史记录等。

按键处理用GPIO外部中断加软件消抖。消抖时间取20ms,实测下来手感比较稳定。如果你用的按键是自锁式而非轻触式,逻辑上要做相应修改。我做的是轻触式设计,按下有效,同时支持短按和长按的区分。

3. 核心代码实现思路

3.1 HX711驱动与时序控制

HX711的驱动代码是整个软件的基础,很多人都见过网上流传的版本,但拿来直接用时会发现两个问题:一是没有考虑超时机制,一旦传感器断线或者时序错乱,程序会卡死在读取循环中;二是通道增益切换写得不清晰,导致测量数据偶尔出现跳变。

我的驱动代码在读取前加了超时判断,等待DOUT拉低最多50ms,超时则返回错误码并在OLED上提示传感器异常。读取的本质是:DOUT拉低表示转换完成,此时通过PD_SCK输出脉冲,每个上升沿DOUT输出一位数据,从最高位到最低位依次读取,共24位。

关键代码如下:

uint32_t HX711_Read(void) { uint32_t count = 0; uint8_t i; while (HX711_DOUT_READ() && timeout_ms < 50); if (timeout_ms >= 50) return 0xFFFFFFFF; for (i = 0; i < 24; i++) { HX711_SCK_HIGH(); delay_us(1); count = count << 1; HX711_DOUT_READ() ? count++ : 0; delay_us(1); HX711_SCK_LOW(); delay_us(1); } HX711_SCK_HIGH(); delay_us(1); HX711_SCK_LOW(); delay_us(1); return count; }

读取完成后,第25个脉冲的作用是设置下一次转换的通道和增益。如果保持25个脉冲且通道为A、增益为128的默认配置,下一次读取仍然是通道A的128倍增益。如果要切换增益,可以通过不同的脉冲数配置,比如通道B只有固定的32倍增益。实际项目中我始终固定使用通道A、增益128,因为这样分辨率最高。

3.2 滤波算法与去零漂移处理

HX711原始读数即使在同一载荷下也会有一定的噪声波动,尤其当电源纹波偏大或传感器激励不稳时,波动幅度可能达到几十甚至上百个字。如果直接把原始值映射成重量,显示数字就会跳来跳去,体验很差。

我采用了两级滤波方案。第一级是中值滤波,连续采样11次,去掉最大值和最小值后对剩余9个值取平均。中值滤波能有效剔除突发干扰,比如人体轻微晃动造成的瞬间冲击。第二级是一阶低通滤波(滑动平均),本质上就是对中值滤波后的结果再做惯性平滑。这样做的好处是响应速度与稳定性可以兼顾。

一阶低通的系数选择很关键。系数太大,显示值跟手但噪声大;系数太小,显示过渡太慢,人站上秤后要等好几秒才能稳定。我实测把人站上瞬间的阶跃响应控制在2秒左右比较合适。系数取0.15到0.25之间,具体可用以下代码动态调整:

float weight_filtered = 0.0f; float alpha = 0.2f; float Weight_GetFilteredValue(float raw_weight) { if (weight_filtered == 0.0f) { weight_filtered = raw_weight; } else { weight_filtered = alpha * raw_weight + (1.0f - alpha) * weight_filtered; } return weight_filtered; }

初次上电时,如果不做传感器零点标定,读数往往不等于零,而是有一个固定偏移。这个偏移来自于传感器在无载荷时的电桥不平衡以及ADC自身的失调电压。解决方式是上电后进行一次软校准:连续读取32次,取平均值作为零点值。后续所有测量结果都必须减去这个零点值后再做重量映射。

3.3 标定方法与单位换算

HX711的输出值是无量纲的ADC原始码值,要和真实重量建立对应关系,必须经过标定。工程上最常用的是两点标定法。

我先在传感器上加载一个已知标准砝码,比如10kg,记录此时ADC原始值减去零点后的差值。然后以这个差值和已知重量计算比例系数。为了减小随机误差,标定时同样做多次采样取平均。

比例系数的计算公式如下:

float weight = (raw_value - zero_offset) / calibration_factor;

这里calibration_factor就是单位重量对应的ADC码值。比如10kg载荷下,扣除零点后测得ADC差值为412000,那么calibration_factor就是41200,单位是“码值每公斤”。有了它,任意ADC码值除以这个系数就能得到公斤数。

单点标定的局限性在于传感器非线性。低成本传感器在量程末端通常有非线性误差,所以精确做法是两点甚至多点标定。我的代码里预留了多点标定表的扩展接口,通过查表加线性插值的方式修正非线性部分。如果你只要求家用精度,单点标定就够了。

3.4 低功耗与睡眠管理

低功耗设计不能只靠MCU的Sleep模式,还要考虑外围器件的功耗组成。HX711在睡眠模式下电流可降到微安级,只要把PD_SCK拉高并保持超过60us即可进入睡眠模式。OLED通过关掉显示驱动和降低亮度也能明显省电。但更彻底的做法是通过MOS管切断整个后级电源,只保留MCU的待机供电和按键唤醒电路。

我的方案是这样设计的:正常测量完成后,系统在空闲10秒后进入低功耗状态,关闭OLED电源,将HX711置为睡眠模式,然后让MCU进入Stop模式,等待外部按键中断唤醒。Stop模式下的电流大约在十几微安,加上HX711和OLED的漏电流,整机维持在20uA左右。

唤醒后的处理顺序是:重新配置时钟系统、初始化OLED和HX711、进行软零点校准、最后进入正常测量循环。这里必须提醒一点:STM32从Stop模式唤醒后,如果使用了HSE晶体,要等待HSE稳定后再切换系统时钟,否则可能直接跑飞或产生HardFault。

4. 仿真与验证过程

4.1 仿真环境搭建与效果验证

STM32开发中,完整的硬件仿真器加上实物验证当然是最可靠的路径,但在没有硬件的情况下,先用Proteus等工具做电路级仿真也可以提前发现不少逻辑问题。我在项目初期用仿真验证了HX711时序、OLED初始化流程和简单称重算法的可行性。

Proteus仿真中搭建的核心模块包括STM32F103、HX711、应变传感器模型和OLED显示模型。仿真环境的限制在于传感器模型是理想化的,不会出现真实电路中的噪声和温漂,因此仿真通过只能说明逻辑正确,不能说明测量精度达标。我的建议是:仿真用来做功能验证和代码调试,精度验证必须依赖实物。

仿真过程中也踩过一个坑:Proteus里HX711模型的DOUT拉低时序和真实芯片略有差异,如果完全按照仿真时序来写代码,上真机后可能会多等或少等一个周期。解决办法是给数据读取加上超时和重试机制,这在上一节已经提到。

4.2 实物联调的关键步骤

拿到实物PCB后,我的调试步骤是固定的,这样做可以快速定位问题而不是盲目测数据。

第一步,不焊MCU,先给电源部分通电,测量各点电压是否正确,确认没有短路。第二步,焊上MCU和OLED,烧录一个最简单的GPIO翻转程序,确认系统时钟和调试器工作正常。第三步,焊上HX711和传感器,用示波器查看DOUT信号,确认上电后DOUT会正常拉低。第四步,跑通驱动代码,读取原始ADC值,观察数值稳定性。第五步,进行标定,验证线性度。最后,才进入整体功能联调。

有一个调试经历值得分享:在某次调试验证中,读取到的ADC值始终在某个区间附近跳动,更换传感器和HX711都没有解决。排查了很长时间,最后发现是HX711的电源走线太长且经过了一个地环路,导致DOUT信号翻转时的干扰耦合到了模拟输入端。把地线就近单点连接后,问题立刻消失。这类问题很难通过代码解决,必须回到硬件布线层面。

4.3 功能扩展:历史记录与看门狗

历史记录功能的实现思路是在Flash的某个扇区保存最近30条测量记录。STM32F103C8T6的Flash有64KB,每次测量记录占8字节(时间和体重值),30条只占240字节,完全放得下。但要注意Flash擦写寿命是有限的,写入次数过多会损坏扇区。所以我采用环形存储策略:记录写满后再从头覆盖,同时每次写入前检查剩余空间,避免频繁擦写同一扇区。

软件定时器的实现选用STM32的SysTick作为时间基准,每毫秒产生一次中断。测量数据每100ms做一次整体刷新。界面交互上,OLED每次只显示一行主数据,配合两个副数据区域,一个是BMI,一个是最新历史记录对比。这样的界面布局信息密度合适,不会在128x64的小屏上显得拥挤。

看门狗这里值得多说一句。加了IWDG独立看门狗后,主循环里必须定期喂狗。但如果程序在某个阻塞函数里卡死,看门狗会触发复位。问题来了:如果秤正在测量过程中复位,读数和标定就会被打断。所以我的喂狗方式不是简单的在主循环喂一次,而是在滤波刷新和显示刷新两个关键节点分别喂狗,保证任何情况下系统都能快速恢复到正常测量流程。

5. 调试常见问题与排查技巧

5.1 数值跳变与读数不稳定

这是称重项目里被问得最多的问题,没有之一。现象是显示器上的数值上下跳动,幅度有时超过0.3kg。原因通常有以下几种:

现象特征可能原因排查方向
跳动幅度大且无规律电源纹波过大用示波器检查HX711 AVDD波形
跳动有周期性50Hz工频干扰检查屏蔽与接地,软件加50Hz陷波
人站上后持续漂移传感器受力不均检查秤体机械结构是否平整
数值缓慢爬升温漂或蠕变传感器本身特性,软件做补偿修正

软件上应对跳动最有效的方式是前面说的中值加滑动平均的双重滤波。硬件上则要注意HX711的输入走线尽量短且远离数字信号线,最好在传感器输出线上套磁环或者加RC滤波。此外,传感器金属外壳接地也能明显减少人体耦合的工频干扰。

5.2 零点漂移和温漂处理

零点漂移是称重系统的又一个经典问题。刚上电时把秤清零,使用几分钟后没放东西读数却变了100克甚至更多。温漂是主要原因,传感器应变片的电阻温度系数不一致导致电桥输出随温度变化。

软件层面的补偿思路是把温度也纳入标定模型。我在PCB上预留了DS18B20的温度传感器接口,虽然基础版本没有使用,但在代码框架里预留了温漂修正函数。具体做法是:在传感器无载荷状态下,记录不同温度下的零点偏移量,拟合一条温漂曲线,实际测量时根据当前温度动态校正零点。

如果你不想增加温度传感器,也有一个变通方法:每次上电都自动执行零点校准。因为体重秤的使用习惯是每次使用时先上电,站在秤上之前会有一个短暂的稳定期,利用这段时间自动清零,可以抵消绝大多数缓慢漂移。

5.3 批量校准与一致性调整

单个样机调好很容易,批量生产时每台秤的传感器差异会导致同一份代码跑出的结果不同。这就需要一个便于操作的校准流程。

我的做法是在代码中增加一个校准模式:上电时按住切换键不放,进入校准状态,然后依次加载已知砝码,系统自动记录每个加载点的ADC平均值并写入Flash。产线上做完这一步,再把设备切换到正常模式,输出的结果就能保持一致。

为了让校准更稳定,我还会在每次校准时检查传感器加载前后的ADC变化量,如果变化量低于某个阈值,就提示加载砝码不到位,需要重新校准。同时,标定数据写入Flash后要加上校验字段,防止数据损坏导致测量异常。

实操中另一个很重要的细节是:不同传感器对激励电压的敏感度不同,如果同一批传感器有两种不同的额定激励电压,混用后校准系数会不太一致,最好在物料采购时就统一规格,从源头避免这个问题。

5.4 项目可复用的地方与后续扩展

这个项目虽然是一个体重秤,但它涵盖的底层能力几乎是所有称重类或者应变测量类产品的基础。把传感器输入通道从HX711换成其他ADC,就是拉力计;把OLED换成WiFi模块,配合云平台就能做成体脂秤的数据上传方案;在现有硬件上加个心率传感器,又可以向健康管理方向延伸。

真正值得复用的不只是代码,而是这套从硬件设计、滤波处理、标定方法到低功耗管理的完整方法论。以后再做任何数据采集类项目,这套思路都可以直接迁移。

最后再分享一个我在实际使用中的体会:这类小型嵌入式项目最容易出问题的往往不是代码逻辑,而是电源和布线的细节。如果某天你的读数莫名其妙开始跳动,不要急着改代码,先去摸一摸电源芯片烫不烫,再用示波器测一下HX711的AVDD纹波。硬件上没有“玄学问题”,所有看似莫名的问题,最终都能在原理图或者PCB上找到对应的原因。把排查顺序反过来,效率会高很多。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进