简介:基于STM32与HX711的电子秤称重超重报警Proteus仿真全套资料,面向嵌入式初学与电子设计开发者,用来学习传感器采集、超重判断和虚拟仿真流程。压缩包共282个文件,约18.44MB,包含C源程序、HX711驱动、STM32工程、Proteus仿真文件以及原理图、PDF文档等,覆盖从代码到仿真的完整链路。项目使用STM32作为主控,通过HX711高精度ADC读取重量数据,超阈值时触发报警,并在Proteus中完成整体验证。内容还含有hex、axf生成文件,便于直接烧录与调试。目前已有1367人学习下载,适合想快速上手STM32称重项目、或需要参考完整例程的开发者。资料目录结构清晰,配合源程序与仿真,可自行修改阈值和报警逻辑,扩展为不同量程的电子秤设计。 把HX711接到STM32上做电子秤,基本是每个学嵌入式的人绕不开的一个完整项目。原因很简单:它把“模拟小信号采集”这件事讲透了——应变片的微弱差分电压变化,经过仪表放大器和24位ADC变成一串数字,再换算成g、kg甚至带一位小数显示出来,整个过程环环相扣,任何一个环节没做对,秤上显示的就是跳动的、完全不可信的数值。这篇就围绕这个STM32+HX711电子秤项目,把我从Proteus仿真搭建到称重算法调试的完整过程摊开讲,包括HX711驱动时序、去皮和零点追踪、超重报警策略,以及仿真和实物之间那些资料里不会明说的差异。
1. 方案选型:为什么电子秤项目都在用STM32+HX711
1.1 HX711这颗芯片到底做了什么
很多人第一次见到称重传感器的时候是蒙的:传感器输出只有毫伏级的差分电压,STM32内部的12位ADC根本没法直接采集那么小的信号,直接把传感器输出接到单片机的PA0上,读出来的值要么是0,要么是乱跳。HX711就是来解决这个问题的:它内部集成了低噪声仪表放大器和一个24位高精度ADC,能把传感器输出的微弱信号放大到ADC可分辨的范围,然后以数字方式输出给MCU。
这颗芯片的接口非常简洁,和单片机通信只需要两根线:PD_SCK(时钟)和DOUT(数据)。内部自带时钟振荡器,不需要额外挂晶振,供电范围也很宽,典型的2.6V到5.5V都可以正常工作。它有两个差分输入通道,通道A可以设置增益64或128,通道B固定增益32,称重场景基本都用通道A增益128,因为传感器输出的信号太微弱,需要足够大的放大倍数才能在ADC读数上体现出来。
1.2 为什么这个项目适合先仿真再动手
电子秤项目用Proteus仿真有一个很实际的好处:先把逻辑跑通,再花钱买硬件。仿真的重点不是让你省掉焊接这一步,而是让你在没有应变片、没有砝码的情况下,先把HX711的时序调对,把标定算法的逻辑理清,把超重报警的条件状态机写好。这些东西如果直接在实物上调试,一旦接线错误或者传感器损坏,排查起来非常痛苦。
仿真中的“应变片”可以用四个电阻搭成惠斯通电桥来模拟,其中一个电阻用可变电阻RV代替,调节RV的阻值就能模拟传感器受力时的电阻变化。电桥的输出接到HX711的A+和A-,通过改变RV就能观察到称重读数的变化,这个方式在仿真阶段完全够用。整体方案的可行性能在仿真里得到验证,后面接实物时就会轻松很多。
2. Proteus环境搭建:那些没写进教程的接线细节
2.1 元件清单与HX711仿真模型
在Proteus中搭这个电路,元件不算多,但每个元件的参数都得设置正确。我先说元件清单,后面再逐个讲容易出错的地方。
| 元件 | 型号/参数 | 用途 |
|---|---|---|
| 主控 | STM32F103C8T6 | 数据处理、逻辑控制 |
| 称重ADC | HX711 | 传感器信号放大与AD转换 |
| 传感器模拟 | 电阻桥 + 可变电阻RV | 模拟应变片受力变化 |
| 显示 | LCD1602 | 实时显示重量 |
| 蜂鸣器 | 有源蜂鸣器 + 三极管 | 超重报警 |
| LED | 红色LED + 限流电阻 | 报警指示 |
Proteus元件库里直接搜索HX711就能找到模型,不同版本可能存在细微差别,常见的是HX711的库文件里已经包含了完整功能。我用的版本里这个模型可以直接工作,但要注意它的电源引脚:VCC接5V,模拟电源和数字电源最好统一供电,GND共地。
2.2 STM32F103C8T6在仿真里的电源设置
这可能是整个Proteus仿真里最容易被忽略的一步:STM32F103C8T6的VDD要接3.3V,VDDA也一定要接3.3V,VSSA接地。很多人只接了VDD,结果程序怎么烧都跑不起来,就是因为VDDA和VSSA没接。
BOOT0引脚必须接GND,这样芯片才会从Flash启动。NRST复位引脚在仿真电路里可以串一个10k电阻上拉到3.3V,再接一个100nF电容到地,虽然仿真中很多时候直接接3.3V也能跑,但按照规范电路接出来更接近真实硬件,后续转实物时直接复用这套电路。
在Proteus里要双击单片机芯片,在Program File选项中选择编译生成的.hex文件。还有一个容易被忽略的配置是外部时钟频率,如果代码里用的是外部8MHz晶振,这里就要把Clock设置为8MHz;如果代码里用的是内部高速时钟HSI,仿真里的时钟设置也要和代码一致,否则串口波特率、定时器延时这些和时间相关的功能会全部对不上。
2.3 电桥模拟传感器和报警电路的接法
在Proteus里模拟称重传感器,我习惯用四个10k电阻组成惠斯通电桥,其中R1用RV(可变电阻)代替,这样转动RV就能改变电桥的输出电压差。电桥的两个输出点分别接到HX711的A+和A-,电桥的供电可以直接从HX711模块的E+和E-引出。调节RV的时候,HX711读到的原始ADC值会往一个方向变化,这就模拟了传感器受力时的输出变化。
报警电路建议用三极管驱动,STM32的GPIO引脚直接驱动蜂鸣器其实电流不够。工程中常用的接法是:GPIO通过1k电阻接到NPN三极管基极,三极管的集电极接蜂鸣器负极,蜂鸣器正极接5V,发射极接地。GPIO输出高电平时三极管导通,蜂鸣器工作。同时在蜂鸣器两端反向并联一个二极管(续流二极管),防止产生反向电动势。
3. HX711驱动代码:两线协议读出24位称重数据
3.1 时序逻辑是理解HX711的关键
HX711和MCU的通信机制首先要有一个清晰的认识:DOUT引脚在数据准备好之前保持高电平,一旦HX711完成了模数转换,DOUT就拉低,告诉MCU“我来数据了”。MCU检测到DOUT拉低后,开始在PD_SCK上发送一连串脉冲,每一个脉冲的上升沿让HX711从最高位开始移出一位数据,连续24个脉冲就把完整的24位数据全部读出来了。
很多初学者会困惑,为什么最后还要多送一个脉冲?因为这个第25个脉冲有双重作用:一是让HX711进入下一次转换,二是用来选择下一次的通道和增益。我代码里使用通道A、增益128,所以第25个脉冲后保持PD_SCK为低电平,HX711会在内部完成通道切换,然后自动开始下一次ADC转换。
3.2 完整的HX711读取函数
在STM32上使用标准库或者HAL库都可以,关键是GPIO方向要配置正确:PD_SCK配置为推挽输出,DOUT配置为浮空输入,因为DOUT是由HX711驱动的。下面是核心读取函数:
// HX711引脚定义 #define HX711_SCK_PORT GPIOB #define HX711_SCK_PIN GPIO_PIN_6 #define HX711_DOUT_PORT GPIOB #define HX711_DOUT_PIN GPIO_PIN_7 /** * @brief 读取一次HX711原始数据,返回24位有符号数 */ int32_t HX711_ReadOnce(void) { uint32_t raw = 0; uint8_t i; uint16_t timeout = 10000; // 等待DOUT拉低,表示数据就绪 while (HAL_GPIO_ReadPin(HX711_DOUT_PORT, HX711_DOUT_PIN) != GPIO_PIN_RESET) { if (--timeout == 0) { return 0; // 超时保护,防止程序死等 } } for (i = 0; i < 24; i++) { HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET); raw <<= 1; if (HAL_GPIO_ReadPin(HX711_DOUT_PORT, HX711_DOUT_PIN) != GPIO_PIN_RESET) { raw++; } HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET); } // 第25个脉冲:回到通道A,增益128 HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET); // 24位二进制补码转换成有符号数 if (raw & 0x800000) { raw |= 0xFF000000; } return (int32_t)raw; }这里我特别加了一个等待超时保护。实际项目中如果HX711没有正常工作,DOUT可能一直保持高电平,不加超时保护的话程序就会死等在这里,看起来像整个系统卡死。这个细节资料里一般不写,但真机调试时特别有用。
3.3 数据滤波是称重稳定的基础
HX711虽然标称是24位ADC,但有效精度受噪声影响很大。当数据输出速率设置为80Hz时,噪声会明显增加,读出来的数据最后几位一直在跳动。仿真过程中也一样,直接看着LCD上的重量值,会发现小数点后面一直不稳。
解决办法是多次读数取平均,这是最简单也最有效的办法:
/** * @brief 多次采样取平均,带去除最大值最小值的中值平均滤波 */ int32_t HX711_ReadFilter(void) { int32_t buf[8]; int32_t sum = 0; uint8_t i, j; int32_t temp; for (i = 0; i < 8; i++) { buf[i] = HX711_ReadOnce(); } // 简单冒泡排序 for (i = 0; i < 7; i++) { for (j = 0; j < 7 - i; j++) { if (buf[j] > buf[j + 1]) { temp = buf[j]; buf[j] = buf[j + 1]; buf[j + 1] = temp; } } } // 去掉最大和最小值,中间6个取平均 for (i = 1; i < 7; i++) { sum += buf[i]; } return sum / 6; }这个中值平均滤波的思路是先把一组采样值排序,去掉最大最小值,防止偶然的尖峰干扰把平均值拉偏。实测下来,采样8次取中间6次平均,在仿真和实物上的稳定性都相当不错。
4. 称重算法:去皮、标定、零点追踪一个都不能少
4.1 从ADC原始值到重量的换算逻辑
HX711读出来的原始数值本身没有单位,它和重量存在一个线性关系,但具体系数取决于传感器的灵敏度、激励电压和HX711的增益。所以必须先标定,再使用。
标定分两步。第一步是空载标定:什么都不放,连续读取16次采样值取平均,得到空载偏置offset。第二步是加载标定:放一个已知重量的砝码,比如100克,读取采样值,记为standard_raw,然后计算出换算系数:
float scale = (float)(standard_raw - offset) / 100.0f;之后任意时刻的重量就是:
float weight_g = (float)(current_raw - offset) / scale;这里有一个很容易出错的点:current_raw必须和offset、standard_raw来自同一个滤波函数,不能一会儿用单次读取值,一会儿用滤波值,否则计算结果会跳变得很厉害。我的习惯是统一使用HX711_ReadFilter去拿所有参与运算的采样值。
4.2 去皮的本质是修改offset
去皮功能很多人一开始理解偏了,以为去皮是把重量清零或者改变scale。实际上去皮是在空载状态下,重新把当前采样值赋值给offset。这样即使秤盘上放了容器,此时显示的也是0克,后续加进去的物料才会被正确计算出来。
void Tare(void) { offset = HX711_ReadFilter(); }务必要理解,offset一变,所有后续的重量计算都会基于新的零点,而scale保持不变。如果去皮时把scale一起改了,就会导致整个量程全部错误。这个逻辑虽然简单,但我在几次给学生看代码的时候发现,有不少人确实把offset和scale搞混了。
4.3 零点追踪:解决重量自己漂移的问题
做电子秤项目时,最讨厌的故障就是秤上什么都没有,显示值却从0慢慢漂到2克、3克甚至更多。这种漂移的来源有两个:一是应变片的电阻值会随温度变化,导致电桥平衡点缓慢偏移;二是机械结构变形和应力释放带来的蠕变效应。ADC读数的绝对值在漂,但秤上没有真实负重。
零点追踪的处理思路是:设定一个阈值,如果在一段时间内,当前读数与offset的差值始终小于这个阈值,就判定当前变化是漂移而非真实加载,于是把offset更新为当前读数。这里阈值的选择很关键,太小了起不到追踪效果,太大了会把真实的小重量也给吞掉。以100克的常见量程为例,我会把阈值设为大约十分之一量程对应的ADC变化值。
void Zero_Tracking(int32_t current_raw) { static uint8_t stable_cnt = 0; int32_t diff = current_raw - offset; int32_t threshold = 200; // 这个值根据实际标定结果调整 if (diff < 0) { diff = -diff; } if (diff < threshold) { stable_cnt++; if (stable_cnt >= 10) // 连续10次稳定,重置零点 { offset = current_raw; stable_cnt = 0; } } else { stable_cnt = 0; } }我把这个函数放在主循环里,每隔200毫秒调用一次,实测下来零点漂移的问题能得到明显压制。要注意的是,既然开启了零点追踪,系统就会自动消除缓慢漂移,相当于不再有严格的“绝对零点”,但它换来的是用户实际体验的大幅提升。
5. 超重报警策略与仿真的验证方法
5.1 报警阈值与回差,防临界抖动
超重报警的逻辑看起来很简单:超过阈值就响。但在实际编写判断条件时,直接用一个阈值会导致临界点反复跳动——当秤上重量恰好卡在阈值附近,比如阈值设置5000克,实际重量4999克和5000克之间波动时,蜂鸣器会频繁启动和停止,非常烦人。
解决办法是引入回差控制,也就是滞回比较。当重量超过5000克时触发报警,报警后只有重量降至5000克减去一定差值(比如200克)即4800克以下时,才解除报警。这样报警状态一旦触发就不容易被临界抖动打断。
#define ALARM_THRESHOLD_G 5000 #define ALARM_HYSTERESIS_G 200 uint8_t alarm_active = 0; void Alarm_Control(float weight_g) { if (alarm_active) { if (weight_g < (ALARM_THRESHOLD_G - ALARM_HYSTERESIS_G)) { alarm_active = 0; // 下降并低于阈值,解除报警 } } else { if (weight_g > ALARM_THRESHOLD_G) { alarm_active = 1; // 超重,触发报警 } } }除了回差之外,还可以加持续确认机制:连续3次采样都超过阈值才触发报警,这样能滤除短暂干扰。仿真里可以用一个可变电阻快速旋动模拟重量突变,验证这个状态机是否正确。
5.2 仿真中如何验证报警动作
在Proteus里跑通整个流程后,我建议按下面这几个步骤去做验证:
先观察空载状态。启动仿真后,打开串口观察窗口或者查看LCD显示,如果原始ADC读数没有稳定下来,说明滤波或者接线有问题。
接着调节RV模拟加载。向右旋转RV,观察LCD上的重量值是否同步变化,变化方向是否和预期一致。如果顺时针旋转RV时重量变大,那这次调节就是“加载”方向。
然后直接超载。把RV调到使重量超过5000克的阻值位置,观察LED是否点亮、蜂鸣器引脚的电平是否翻转。此时报警状态应该持续保持,即使轻微回调RV让重量降到4950克,报警仍然不解除,这个就是回差在起作用。
最后验证解除报警。继续逆时针调节RV,让重量降到4800克以下,报警才应该解除。这几个步骤能完整验证报警逻辑的正确性。
5.3 我在仿真中踩过的坑
第一个坑是仿真中HX711读到的数据一直是0。排查下来发现是STM32的GPIO引脚配置错了,SCK引脚没有配置为推挽输出,一直在高阻态,导致时钟脉冲没送到HX711。解决方法是仔细检查GPIO模式配置,并且确认SCK和DOUT的引脚编号与代码定义一致。
第二个坑是蜂鸣器在初始化时就一直响。原因是在主程序执行到GPIO初始化的那一刻,引脚默认电平不确定,三极管被误触发导通了。解决办法是在初始化阶段先把控制蜂鸣器的GPIO拉低,再去配置其他外设。这个经验在实物调试中同样适用,一开始蜂鸣器响不响,基本能判断GPIO初始电平有没有设置好。
第三个坑是LCD1602显示乱码。大多数情况下是引脚映射不一致,尤其在使用Proteus仿真时,LCD的数据线D0-D7分别接到了哪些GPIO,必须和代码中的LCD驱动一一对应。为了减少这种问题,我倾向于参考江科大那套LCD1602驱动思路,直接用并口模式映射到同一个GPIO端口,这样写起来和排错都比较省心。
6. 资料包的完整链路与实际扩展方向
6.1 这份资料包里到底有什么
整套项目资料包含四个核心部分:源程序工程文件、Proteus仿真文件、原理图PDF和芯片数据手册。源程序里除了HX711驱动,还包含了LCD1602显示、蜂鸣器报警、按键去皮等功能的完整代码,工程基于STM32标准库搭建,主循环结构清晰,适合对照学习。
Proteus仿真文件里已经把电路搭好,双击单片机加载对应的.hex文件就能直接跑。原理图PDF可以作为转实物时的接线参考,数据手册是调试HX711时查官方时序和电气特性用的。这些资料放在一起,基本上从仿真验证到实物搭建的链路都有覆盖。
6.2 从仿真切到实物,必须注意的改动点
仿真和实物之间存在几个明显的差异。第一,仿真里的可变电阻RV响应是线性的,而真实传感器的应变片输出存在一定的迟滞和非线性,所以实物标定时需要多取几个重量点,至少要两点标定,有条件的话做三到五点的分段标定会更准。第二,实物的电源噪声远大于仿真,HX711的电源引脚附近一定要加100nF和10uF的滤波电容,模拟电源和数字电源要分开走线。
第三,蜂鸣器驱动电路在实物上务必用三极管或者MOS管驱动,GPIO直接驱动有源蜂鸣器时,虽然偶尔能响,但长时间工作会导致单片机引脚过载。最后还有一个容易被忽略的点:真实称重传感器的接线颜色并不统一,拿到实物传感器后第一件事是查手册确认各个引线的功能,再把电桥信号接对,否则HX711读出来的可能是负值。
6.3 后续可以扩展的方向
这个项目的扩展空间其实很大。如果想做高精度小量程电子秤,可以换用量程更匹配的传感器,并把HX711的增益调整到64,同时配合更严格的滤波算法。如果想做物联网电子秤,可以在现有代码基础上加一个串口发送函数,把重量数据通过UART发送给ESP8266或者蓝牙模块,这样手机端和云平台就能实时看到重量数据。或者更进一步,在报警逻辑中增加LED闪烁方式和蜂鸣器声音模式的区分,让不同的报警类型能直观识别。
我个人的实际体会是,这个项目值得花时间从头到尾亲手调一遍。第一次跑通时可能只是照着资料搭好了电路和代码,但当你真正理解了标定、去皮、零点追踪和回差这些概念之间的关系之后,再去接触任何带传感器采集的项目,思路都会清晰很多。做嵌入式最怕的是只会抄代码,而电子秤这个项目恰好是一个把模拟前端、数字接口、数据处理和状态控制串在一起的完整链路,值得每个学单片机的人亲手做一遍。
本文还有配套的精品资源,点击获取