简介:基于STM32单片机与HX711称重传感器的电子秤超重报警Proteus仿真资源包,面向嵌入式初学者、电子设计爱好者及课程设计开发者。压缩包内含282个文件,包括C/H语言源程序、Keil工程配置文件、Proteus仿真工程文件(pdsprj)、HEX固件以及PDF说明文档等,整体大小18.44MB,结构清晰,便于快速查阅与二次开发。目前已有1367人学习下载,可帮助理解高精度重量采集、阈值比较和声光报警的设计流程,仿真环境能直观还原系统运行状态。通过完整源码与配套电路,读者既能掌握STM32外设驱动与HX711数字滤波处理,又能在Proteus中直接观察称重与报警联动效果,为后续真实硬件调试和功能扩展提供清晰参考。
1. 项目概述与整体设计思路
1.1 电子秤仿真系统能做什么
拿到这份资料,先把它的功能摸清楚:这是一个基于STM32单片机、配合HX711称重传感器专用ADC芯片实现的电子秤全流程项目,区别在于它不需要实物电路板,直接靠Proteus仿真软件跑通整个逻辑。系统支持实时重量读取、LCD屏幕显示、按键去皮、报警阈值设置,以及超重时的声光报警。仿真环境里用电位器模拟称重传感器的微小差分电压变化,调节电位器就相当于往秤盘上放东西,读数会跟着变,超过设定阈值后蜂鸣器响、LED亮。整个过程完整复现了一台工业电子秤的核心工作流。
对于正在做课程设计、毕业设计,或者单纯想搞懂“单片机怎么读取称重传感器数据”的开发者来说,这套资料的价值很明显:源代码是可编译、可烧录、可仿真的,不是那种网上满天飞的残缺代码。你不需要先买一套HX711模块和称重传感器回来,就能把整套测量逻辑、标定流程、报警机制全部跑通,等理解了原理之后再转实物,踩坑成本直线下降。
1.2 为什么选STM32+HX711+Proteus这套组合
先说说方案选型。电子秤这类项目,市面上有很多实现路径,比如用51单片机加个ADC0809、或者直接用STM32内部ADC采集。但这个项目偏偏选了HX711作为采集前端,这里面的门道值得琢磨。
称重传感器的输出信号是微伏到毫伏级别的差分电压,如果用普通ADC直接采样,分辨率根本不够。HX711是专门为称重应用设计的24位高精度ADC芯片,内部自带低噪声可编程增益放大器(PGA),最多可以放大128倍,直接把传感器出来的微弱信号放大到单片机能够稳定读取的范围。再加上它内置稳压电路,可以直接为传感器提供稳定的激励电压,外围电路极简,一块小板子就能搞定。相比STM32内部12位ADC,HX711在精度和抗干扰方面都是碾压级的优势。
选择Proteus做仿真,核心原因是成本低、调试效率高。实物方案需要买模块、焊板子、接线,一旦出问题排查起来费时费力;仿真环境里改一条线、换一个参数只需要几秒钟。而且Proteus对STM32F103系列的支持已经比较成熟,可以加载Keil生成的hex文件直接运行,配合虚拟示波器、虚拟终端调试程序,非常适合学习阶段反复折腾。
2. HX711测量原理与信号链路
2.1 电阻应变片与电桥原理
想弄懂这个项目,先得明白称重传感器内部是怎么回事。市面上常见的单点式称重传感器,里面核心部件是4片电阻应变片,它们以全桥方式连接。桥路的供电端是E+和E-,信号输出端是S+和S-。没有任何负载时,电桥四个桥臂的电阻相等,输出差分电压近似为零。当重物压在传感器弹性体上时,弹性体发生形变,贴在它表面的应变片跟着被拉伸或压缩,电阻阻值发生变化——其中对臂的两片电阻增大,另外两片减小。
电桥平衡被打破后,S+和S-之间就会出现一个微小的差分电压。这个电压有多小呢?一般来说,额定负载下传感器的灵敏度在1mV/V到2mV/V之间。也就是说,如果供电电压是5V,满量程时输出差分信号大约只有5mV到10mV。这种级别的微弱信号,如果不经过放大,单片机ADC根本无法准确分辨。而这正是HX711存在的意义。
2.2 HX711芯片关键特性
HX711最核心的几个参数,搞清楚了才算入门。第一,它内部集成了24位Σ-Δ型ADC,输出二进制补码格式的转换结果。第二,可编程增益放大器支持A通道128倍或64倍增益、B通道32倍增益,称重场景最常用的就是A通道128倍。第三,片上稳压器可以输出稳定的模拟电源给传感器桥路供电,大大简化了外围设计。第四,数据输出速率可选10SPS或80SPS,这个速率对电子秤这种对实时性要求不高的场景完全够用,而且低速率本身就能起到一定的滤波效果。
HX711和单片机之间是典型的串行通信方式,只有两根线:PD_SCK(时钟)和DOUT(数据)。但注意,这种通信不是标准的SPI接口,它没有片选线,时序也需要严格按照芯片手册来,实际上属于一种类似SPI的定制协议。很多初学者在这里栽跟头,代码写不出来。放心,后面的章节我会把时序讲透。
2.3 数据读取时序与增益选择
HX711的数据读取时序不复杂,但细节非常多。芯片空闲时DOUT保持高电平,当一次转换完成后,DOUT会被拉低,此时通知单片机“数据准备好了”。单片机检测到DOUT为低电平后,开始产生时钟脉冲。每给PD_SCK一个高脉冲,DOUT引脚上就输出一位数据,先高位后低位,连续24个脉冲正好读完24位数据。读完后,还需要继续给1到3个额外时钟脉冲,这决定了下一次转换的增益和通道:
- 第25个脉冲:下一次使用A通道,128倍增益
- 第26个脉冲:下一次使用B通道,32倍增益
- 第27个脉冲:下一次使用A通道,64倍增益
称重项目基本上都选第25个脉冲,也就是通道A、128倍增益。有一点特别容易踩坑:读出来的24位数据是二进制补码格式,最高位是符号位。如果你用无符号整数去接收,当传感器受到拉力或零点附近负漂移时,读数就会变成一个很大的正数,整个显示逻辑就乱了。所以正确做法是累加时使用有符号类型(int32_t),或者读出无符号数后手动处理符号扩展。
下面是HX711读取数据的核心函数,这个函数可以直接抄进工程使用:
int32_t HX711_Read(void) { uint8_t i; int32_t value = 0; // 等待DOUT拉低,表示转换完成 while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)); // 读取24位数据,高位在前 for (i = 0; i < 24; i++) { GPIO_SetBits(GPIOB, GPIO_Pin_0); // SCK拉高 delay_us(5); value = value << 1; // 左移一位 if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)) { value++; } GPIO_ResetBits(GPIOB, GPIO_Pin_0); // SCK拉低 delay_us(5); } // 第25个脉冲,设置A通道128倍增益 GPIO_SetBits(GPIOB, GPIO_Pin_0); delay_us(5); GPIO_ResetBits(GPIOB, GPIO_Pin_0); delay_us(5); return value; }读取完成后,还有一个重要操作:延时。HX711完成一次转换需要一定时间,即使是最高速的80SPS,转换周期也有12.5ms。所以主循环里读取频率不要过高,最好做一次读取间隔10ms以上的控制。否则可能出现DOUT还没拉低,你就去等待转换完成,结果卡死主程序。
3. 硬件设计与接线
3.1 Proteus元器件准备与选型
仿真工程里涉及的核心元器件主要有STM32F103C8主控、HX711模块模型、LM016L字符液晶(Proteus里1602的标准模型)、蜂鸣器、LED、按键,以及一个用于模拟称重传感器信号变化的电位器。这里重点说明一下传感器部分的处理思路。
Proteus元件库中其实没有现成的“称重传感器”符号,很多教程会选择用两个可变电阻串联搭一个桥路来模拟。如果你拿到的资料里已经画好了仿真电路,直接打开用就行。如果你打算自己从零搭一个,最省事的做法是:在HX711的S+和S-之间接一个精密电位器,调节电位器改变输入电压差,等效于传感器输出的微小变化。这种方式虽然不能完全反映电桥的动态特性,但用来验证数据读取、标定、报警这条逻辑链路已经足够了。
3.2 引脚分配与接线表
为了后续对照代码理解,这里把最常用的一组引脚分配整理成表格。不同版本的资料引脚可能略有差异,但逻辑一致,只要对应修改程序里的宏定义即可。
| HX711引脚 | STM32引脚 | 说明 |
|---|---|---|
| VCC | 3.3V | 模块供电 |
| GND | GND | 共地 |
| SCK | PB0 | 时钟输入 |
| DOUT | PB1 | 数据输出 |
| E+ / E- | 传感器激励 | 供桥电压 |
| 外设 | STM32引脚 | 说明 |
|---|---|---|
| 1602 LCD RS | PA0 | 寄存器选择 |
| 1602 LCD EN | PA1 | 使能信号 |
| 1602 LCD D4-D7 | PA2-PA5 | 四线数据 |
| 蜂鸣器 | PA6 | 超重报警 |
| LED指示灯 | PA7 | 超重指示 |
| 按键K1(去皮) | PB8 | 触发去皮 |
| 按键K2(阈值+) | PB9 | 增大报警阈值 |
| 按键K3(阈值-) | PB10 | 减小报警阈值 |
LCD选择四线模式而不是八线模式,主要目的是节省GPIO资源。STM32F103C8虽然有足够多的引脚,但省下来的IO可以留给后面的功能扩展,而且四线接法布线也更简单,实际效果完全不受影响。
3.3 STM32电源设置与最小系统
在Proteus里仿真STM32,最容易被忽略的就是电源设置。很多人在原理图中放置了STM32F103C8芯片,但芯片却无法正常工作,排错半天发现是芯片根本没有供电。STM32F103C8的VDD和VSS引脚在Proteus模型中是默认隐藏的,需要手动确保电源网络正确连接。
具体操作建议:在原理图编辑区放置POWER符号,电压值设为+3.3V,放置GROUND符号接GND,然后通过导线分别连接到芯片逻辑电路的电源网络。如果仿真跑不起来,检查一下“System -> Set Power Rails”里面的网络映射,确认VDD网络对应+3.3V、VSS网络对应GND。另外,如果代码里配置了外部晶振做系统时钟,仿真电路里也要放一个8MHz晶振接到OSC_IN和OSC_OUT引脚,两侧各加一个20pF左右的电容到地。如果不想在仿真里折腾晶振,也可以把代码改成使用内部HSI时钟,这样最小系统连晶振都可以省略。
4. 软件代码实现与关键逻辑
4.1 初始化与数据读取函数
软件部分我按功能模块拆开讲。首先是GPIO和启动初始化。HX711的SCK引脚配置为推挽输出,DOUT引脚配置为上拉输入。上拉很重要,因为HX711的DOUT是开漏输出结构,虽然模块板上通常已经有上拉电阻,但在代码层面配置输入的上下拉模式是一个保险操作,可以避免浮空带来的误触发。
主函数里还要做原子的延时初始化。本项目的延时函数主要用于HX711时序中的高电平保持时间,以及主循环的采样间隔控制。HX711对时序要求不算苛刻,但SCK高电平保持时间至少要有1微秒才算稳妥。所以延时函数的精度直接影响通信可靠性——这里建议不要用那种编译器优化掉的空循环延时,最好用系统定时器或者DWT计数器来实现微秒级延时。如果你用的是标准库或者HAL库,直接使用delay_us()这种现成函数即可。
4.2 称重标定与重量计算原理
用HX711读出来的原始值,本身并不是重量,它只是一个和输入差分电压线性相关的数字量。要想让显示值成为克或者千克,必须做标定。标定本质上就是确定两个参数:零点和缩放系数。
第一步,读取零点。在空秤状态下连续读取10到20次数据,取平均值作为零点值zero_value。
第二步,读取标准重量。在秤盘上放置一个已知重量的标准砝码,比如1kg,同样读取多次数据取平均,记为load_value。
第三步,计算缩放系数:
scale = (load_value - zero_value) / 1000.0f; // 单位:g这里的1000表示标准砝码的质量是1000克,单位按克计算。之后任意时刻的重量计算公式为:
weight_g = (current_raw - zero_value) / scale;需要特别提醒:scale必须用浮点数保存,否则整数除法会丢失精度。在实际工程里,这个标定过程通常通过一个按键触发,配合LCD提示“放上1kg砝码”。在仿真调试时,可以先把电位器调到某个固定位置,假装它是1kg标准负载,标定完成后,再去旋转电位器观察重量的线性变化。关键是要理解:电位器旋转角度和“重量”之间不一定是线性关系,但HX711读数和输入电压之间是严格线性的,标定算法本身不关心前端是什么,只需要知道“某个输入对应某个已知重量”这一点。
4.3 零点追踪与数据滤波
在真实电子秤里,长时间使用后零点会漂移——温度变化、传感器蠕变、应变片老化都会导致空秤读数不再回到初始零点。如果零点不准,后续所有重量计算都不准。最简单的零点追踪思路是:每次检测到秤盘上的载荷长期接近零时,自动把当前读数更新为新的零点。
这里的难点在于如何判定“秤盘空载”。一个实用的做法是:连续采集N次数据,如果这N次数据的最大值和最小值之差小于某个阈值(比如2g对应的原始读数),且平均值也小于某个偏移量,就认为当前处于空秤状态,把平均值赋值给零点。需要注意的是,零点追踪不能太激进,否则用户正在加载物体但物体很轻时,零点会被错误更新,导致显示值被“吃掉”。较为稳妥的做法是增加一个持续判定,比如连续500ms都满足空载条件才更新零点。
在Proteus仿真中,可以用电位器模拟小幅漂移来验证零点追踪是否生效。数据滤波方面,最常用的是一阶滑动平均值。我这里使用窗口为8的滑动平均滤波,既能抑制工频(50Hz)干扰带来的小幅波动,又不至于让响应变得过于迟钝:
#define FILTER_N 8 int32_t filter_buf[FILTER_N]; uint8_t filter_index = 0; int32_t filter_sum = 0; int32_t Data_Filter(int32_t raw) { filter_sum -= filter_buf[filter_index]; filter_buf[filter_index] = raw; filter_sum += raw; filter_index = (filter_index + 1) % FILTER_N; return filter_sum / FILTER_N; }这段代码的核心是滑动窗口求和:把新值加入缓冲区、旧值移出,用总和除以窗口长度得到平均结果。相比每次循环都重新求和的笨办法,这种方式计算量恒定,适合单片机这种资源有限的平台。
4.4 超重报警与显示逻辑
显示部分,1602液晶初始化成4位模式后,第一行显示实时重量,单位g,第二行显示当前报警阈值和状态。按键K2、K3用于调整报警阈值,K1实现去皮功能。去皮的本质很简单:把当前读数设为新的零点,这样传感器上已有的容器重量就会被扣除,显示的是容器内物品的净重。实现方式就是触发一个标志位,在标定代码中把zero_value更新为当前滤波后的读数即可。
超重报警逻辑放在主循环的末端执行:
if (weight_g > limit_weight) { GPIO_SetBits(GPIOA, GPIO_Pin_6); // 蜂鸣器响 GPIO_SetBits(GPIOA, GPIO_Pin_7); // LED亮 } else { GPIO_ResetBits(GPIOA, GPIO_Pin_6); GPIO_ResetBits(GPIOA, GPIO_Pin_7); }实际产品中,蜂鸣器不能一直长响,最好用定时器产生2kHz左右的方波驱动,并且在报警持续一定时间后自动关闭,防止刺耳噪音。仿真阶段驱动蜂鸣器让引脚持续输出高电平即可。如果你手头工程里的蜂鸣器模型是有源蜂鸣器,高电平触发是可行的;如果是无源蜂鸣器,需要切换GPIO输出方波。这一点在移植到实物板时一定要确认模块类型。
5. Proteus仿真搭建与调试
5.1 从零搭建STM32仿真工程的步骤
如果你选择的是打开现成仿真文件,跳到5.2继续看。如果你想自己从零复刻整个仿真电路,可以照下面的步骤操作:
第一步,新建Proteus工程,在元件库中放置STM32F103C8、LM016L、HX711(如果元件库里有;没有的话可以用电位器+电阻网络模拟输入)、按键、蜂鸣器、LED和必要电阻电容。第二步,按照3.2节的接线表连接电路。第三步,双击STM32芯片,在“Program File”中加载Keil编译生成的hex文件,晶振频率填8000000(对应外部8M晶振)。第四步,点击运行按钮。如果程序正常运行,1602会显示初始重量。
这里必须提醒一个Proteus的操作细节:STM32F103C8默认情况下程序运行需要配置启动文件选择,也就是BOOT0和BOOT1引脚电平。在仿真中,BOOT0要接低电平,BOOT1接低电平,让芯片从主Flash启动。很多人的仿真一直卡在“芯片没有响应”的状态,就是忘记了这两个引脚的电平设置。在Proteus里,你可以直接不连接这两个引脚,但为了标准,建议显式接GND。
5.2 仿真运行时的典型故障排查
我在调试类似项目时整理了一份高频问题排查表,这里直接分享出来:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 1602无显示或只有背光 | 对比度电位器未调好;D4-D7接错;初始化时序问题 | 调节LCD V0引脚电压到1V左右;对照代码核对引脚映射 |
| 重量读数固定不变 | 电位器未接入HX711输入回路;程序没读取到数据 | 用虚拟示波器看SCK和DOUT波形;确认DOUT引脚上拉 |
| 读数跳得很厉害 | 供电纹波大;采样次数太少;未做滤波 | 给HX711 VCC加104电容;程序开启滑动滤波 |
| 超重不报警 | 阈值设置过大;GPIO配置错误 | 先把阈值调小;单步调试检查weight_g与limit_weight比较结果 |
| 芯片运行但外设无响应 | 系统时钟配置与仿真晶振不一致 | 核对代码里的时钟配置和Proteus晶振频率一致 |
| 编译报错找不到头文件 | Keil工程缺少固件库路径 | 检查C/C++选项卡中的Include Path是否包含所有库目录 |
排查这类问题的核心思路是“从源头往前推信号链路”。先确认HX711是否在产生数据(示波器看DOUT波形),再确认数据是否被正确读入(用调试器在线看变量),最后再看显示和报警逻辑。永远不要跳过前面几点直接去调显示。
5.3 从仿真到实物移植的注意事项
仿真跑通之后,如果打算做成实物,有几个仿真里发现不了的坑需要提前知道。
第一个是基准电压和供电质量。仿真里的电源都是理想电压源,实物的HX711模块对电源纹波特别敏感,建议VCC和GND之间并接一个100uF电解电容和0.1uF陶瓷电容,给传感器激励端也加滤波电容。第二个是传感器接线颜色。市面上常见的称重传感器有红黑绿白四根线,红色接E+,黑色接E-,绿色接S+,白色接S-,但不同厂家颜色定义偶尔会有差异,最好用万用表先判断桥臂电阻再接线。第三个是共地问题。STM32和HX711模块必须共地,否则通信电平无法保持稳定,这是很多实物调试中“读数随机漂移”的根本原因。
还有一点值得注意:Proteus仿真中HX711的通信时序是理想化的,实物中信号线较长时容易引入串扰,这时候SCK引脚上的信号质量可能变差。解决办法是缩短杜邦线长度,或者在SCK和DOUT线上各自串联一个100欧姆左右的电阻用来抑制振铃。
6. 实测体验与个人经验
这套项目我完整跑过一遍,从Proteus仿真到实物移植都做了。整体印象是:HX711+STM32这个组合对于学习“传感器信号链”非常有帮助——从微弱信号生成、放大、ADC采集到数字滤波、标定换算,再到控制输出,一整条链路非常完整。但有几个细节直到我实际调试时才真正体会到。
零点追踪这个功能,网上资料提的很多,但真正把逻辑做好的不多。最简单的实现方式是在主循环里周期性检测空载状态并更新零点,但要注意这个逻辑和按键去皮功能的关系:如果用户手动去皮后又触发了零点追踪,很可能覆盖掉用户刚刚设定的去皮零点。我的处理方式是在去皮之后的一个固定时间窗口内(比如10秒)暂停零点追踪,让用户完成容器放置。这个窗口大小在仿真里无所谓,但在实物中直接影响用户体验。
另一个经验是关于标定参数保存。仿真环境里全局变量掉电就丢,可以接受。但实物产品中,标定好的scale和zero_value如果不保存,每次上电都要重新标定,非常痛苦。建议在工程中引入EEPROM模拟,或者用STM32内部Flash的最后一个扇区存储这两个参数,上电时直接读取并校验。这个功能在仿真中也能验证,只是需要用代码模拟掉电场景。
最后分享一个小技巧:在Proteus仿真里调试HX711时,可以把PA9(USART1_TX)连接到虚拟终端,把每次读取的原始值和换算后的重量值直接打印出来。这样比用LCD查看快得多,可以直观看到滤波前后数据的区别,也能帮助你快速判断标定参数是否合理。把这套调试方法养成习惯,以后做其他传感器项目也会省下大量时间。
本文还有配套的精品资源,点击获取