简介:基于51单片机的电子秤设计是一个面向电子初学者的完整实战项目,以51单片机为核心,配合应变式压力传感器和LCD1602液晶显示模块,实现0~5公斤称重、实时显示与去皮功能。压缩包共100个文件,包含C语言源码、头文件、十六进制烧录文件、Proteus仿真工程、Keil工程文件及备份文件等,全部资料仅733KB,目录结构清晰,便于按模块对照学习。目前已有790人浏览学习,适合通过实际案例掌握单片机编程、传感器信号采集、模数转换以及液晶显示驱动的开发流程。资源提供完整源代码与可运行仿真,可从电路搭建、程序调试到软硬件联调逐步复现整个电子秤设计,深入理解重量传感器数据处理和去皮算法的软件实现,是一份高效实用的嵌入式入门资料,特别适合课程设计与毕业设计参考。
1. 基于51单片机的电子秤设计:从 Proteus 仿真到实物上秤的完整路线
做基于51单片机的电子秤设计,真正卡人的往往不是单片机本身,而是那个“称得准”。网上搜“51单片机电子秤”,十有八九是 STC89C52 + HX711 + 1602 液晶这套组合,再配一个 Proteus 仿真文件和 Keil5 工程。这套方案之所以成了默认选项,是因为 HX711 把传感器模拟前端最难办的差分放大和 24 位 ADC 全干了,留给单片机的只是读时序和算数。这篇文章按我实际拆过的流程走一遍:器件选型、原理图、驱动代码、仿真与实物调试,以及五条花了钱才记牢的避坑记录,适合刚入手 51 想复现完整项目的人,也适合要交课程设计却不想只交一堆空理论的读者。
2. 选型与原理图:传感器、ADC 和 Proteus 仿真三板斧
2.1 应变式称重传感器与量程选择
电子秤的核心是力传感器,常见的是铝制单点式应变传感器,四根线出来:红(E+激励正)、黑(E-激励负)、白(IN+)、绿(IN-),内部就是一个惠斯通电桥。受压时电桥失衡,差分电压变化。以量程 5kg 的传感器为例,灵敏度普遍在 1mV/V 左右,5V 激励下满量程输出大约 5mV——这个信号直接进单片机的 ADC 根本不现实,必须差分放大。
量程选择上有个容易被忽略的规矩:标称量程要留 1.5 到 2 倍余量。你要称的常见物体是 3kg,就买 5kg 传感器,而不是 3kg。因为传感器在接近满量程区间时线性度下降,而且称重时经常会顺手压一下,超过量程会永久损伤弹性体。
选型参数可以按这张表对:
| 参数 | 建议值 | 说明 |
|---|---|---|
| 量程 | 5kg / 10kg | 按实际称重上限的 1.5 倍选 |
| 灵敏度 | 1mV/V | 5V 激励下满量程约 5mV |
| 激励电压 | 3V 或 5V | HX711 的 AVDD 可输出 |
| 输出线数 | 4 线 / 6 线 | 6 线带反馈,长线场景更稳 |
2.2 HX711 为什么是民用电子的最优 ADC
如果十年前做电子秤,一般是用仪表放大器 AD620 加 ADC0804,电路板上一堆电阻,增益全靠手工调,温度一变又飘了。HX711 一颗芯片解决了全部问题:内置可编程增益放大器(通道 A 可选 64 倍或 128 倍增益),内置 24 位 Sigma-Delta ADC,还带稳压输出可以给传感器供电。外部几乎不需要模拟电路,成本几块钱,这决定了它在 51 电子秤项目里的统治地位。
HX711 的工作电压范围是 2.6V 到 5.5V,供电直接用单片机同一路 5V 即可。数据速率可选 10Hz 或 80Hz,通过芯片上 RATE 引脚电平控制:接低电平是 10Hz(推荐,抗混叠更好),接高是 80Hz。我一般接低。关键参数如下:
| 参数 | 数值 | 使用注意 |
|---|---|---|
| 通道 | A 和 B | 通道 A 带 64/128 倍增益,接传感器 |
| 增益 | 64 / 128 | 5kg 传感器配 128 倍 |
| ADC 位数 | 24 位 | 补码输出,符号位在最高位 |
| 数据速率 | 10Hz / 80Hz | RATE 脚拉低选 10Hz |
| 输出格式 | 串行 DOUT + SCK | 只需两根 IO |
注意 AVDD 是传感器激励电源,HX711 芯片内部带稳压,AVDD 与 DVDD 之间的电容布局要靠近引脚放,后面避坑部分会展开讲。
2.3 原理图要点与 Proteus 里的替代方案
单片机最小系统、按键、1602 显示这些常规部分不展开,重点说 HX711 和传感器的接法。在原理图里,传感器四线接 HX711 的 E+、E-、A+、A-,DOUT 和 SCK 分别接 P1.0 和 P1.1。1602 的数据口接 P0 口时必须加 10K 上拉排阻,P0 是开漏输出,不上拉就是白屏或乱码。
初期在 Proteus 里仿真时会发现一个现实问题:Proteus 自带元件库没有 HX711。常见做法有三种,我按实际踩过的顺序列一下:一是用库里自带的应变传感器加 AD620 和 ADC0832 自己搭模拟链路,费时间但最接近实物;二是直接用滑动变阻器分压模拟传感器差分输出,走一个量值进去,只验证软件逻辑;三是加载网上的第三方 HX711 模型,能用但版本兼容看运气。我一般选第二种,因为我们的目的是调程序逻辑,传感器模拟前端的正确性最终要靠实物验证。
3. 把原始值变成克:HX711 驱动、单位换算与数字滤波
3.1 HX711 时序驱动:25 个脉冲是硬规矩
HX711 的串行协议不复杂,但时序上有硬规矩。DOUT 在高电平表示忙,直到转换完成会拉低,主控检测到 DOUT 拉低后,在 SCK 上产生 25 个脉冲:前 24 个把 24 位数据一位一位移出来,第 25 个脉冲决定下一次转换的通道和增益。下面这段是能直接编译的读取函数:
#include <reg52.h> #include <intrins.h> sbit HX711_DOUT = P1^0; sbit HX711_SCK = P1^1; // 读取一次 24 位原始值,返回无符号左对齐值 unsigned long HX711_Read(void) { unsigned long value = 0; unsigned char i; HX711_SCK = 0; // DOUT 由高变低表示数据已就绪,等待它拉低 while (HX711_DOUT); for (i = 0; i < 24; i++) { HX711_SCK = 1; // 上升沿移出一位 value = value << 1; HX711_SCK = 0; if (HX711_DOUT) value |= 0x01; } // 第 25 个脉冲:选择通道 A + 增益 128 HX711_SCK = 1; _nop_(); HX711_SCK = 0; return value; }逻辑说明:while (HX711_DOUT);是等待数据就绪,DOUT 不拉低说明芯片还在转换中,这个循环必须卡住,否则移出来的数据全是错的。24 个脉冲里每个 SCK 上升沿对应一位数据读出,value左移一位后再根据 DOUT 电平置低位的 0 或 1。第 25 个脉冲不是数据位,它是给芯片内部状态机用的选通信号。
参数说明:这里把增益设为 128,是因为 5kg 传感器满量程输出 5mV,HX711 在 128 倍增益下输入范围约 ±20mV,正好落在合理区间。如果换成 10kg 传感器,满量程差分电压接近 10mV,128 倍依然合适。只用 64 倍增益的话,小重量段的分辨率会砍半,不划算。
还要注意一个补码问题:HX711 输出的是 24 位补码,value拼出来是无符号视角的 0 到 0xFFFFFF。最高位是符号位,要转成有符号数才能算重量。最简单的方式是读完后减去 0x800000,这个操作后面统一处理。
3.2 校准系数计算:空载偏移与标准砝码两点标定
读出来的原始值是 ADC 码,不是克。从 ADC 码到克,需要一个线性映射,做法是两点标定:先记录空载偏移量,再放一个已知重量的标准砝码,算出每一克对应的原始值增量。
long zero_offset; // 空载偏移量(去皮基准) float scale_factor; // 每克对应的原始值增量 // 把 HX711_Read 结果转成有符号数 long HX711_Read_Signed(void) { return (long)HX711_Read() - 0x800000L; } // 标定流程:空载取零,再放标准砝码 void Calibrate(void) { delay_ms(200); zero_offset = HX711_Read_Signed(); // 1. 空载时读零点 // 2. 提示放砝码,这里假设放 500g 标准砝码 delay_ms(800); long raw_with_weight = HX711_Read_Signed(); // 3. scale_factor = (有载raw - 空载raw) / 砝码克数 scale_factor = (float)(raw_with_weight - zero_offset) / 500.0f; } // 计算当前重量(克) float GetWeightGram(void) { long raw = HX711_Read_Signed(); float gram = (float)(raw - zero_offset) / scale_factor; return gram; }逻辑说明:HX711_Read_Signed()把无符号结果减 0x800000,把零点挪到 -2^23,这样正负重量都能表达。标定时分三步,先读空载偏移,再读带砝码的原始值,两者做差除以砝码克数得到scale_factor。实际称重时用当前原始值减偏移量除以每克原始值,就得到克数。
参数说明:这里的 500g 只是示例,如果你手里的标准砝码是 200g 或 1kg,把除数改成对应值即可。scale_factor的类型必须用 float,两个原始值相减动辄几十万,整数除法会直接舍成 0。另外标定前让传感器上电预热 1 到 2 分钟,冷机状态下零点漂移明显,直接标定出来的系数用起来会越来越偏。
3.3 去掉毛刺的数据滤波:去极值平均的取舍
称重时人站在秤上、手放上去、东西丢上去,都有冲击和振动,ADC 读出来会带毛刺。最朴素的滑动平均不够用,因为单个异常尖峰会把平均结果拉偏。我一般用去极值平均:连续采样 N 次,去掉最大值最小值,对剩下的做平均。这样既压制了随机噪声,又不会因为一次突变导致屏幕跳字。
#define SAMPLE_N 5 unsigned long GetFilteredRaw(void) { unsigned long buf[SAMPLE_N]; unsigned long sum = 0; unsigned char i, j; unsigned long tmp; for (i = 0; i < SAMPLE_N; i++) buf[i] = HX711_Read(); // 冒泡排序,把最大值和最小值挤到两头,只取中间 N-2 个 for (i = 0; i < SAMPLE_N - 1; i++) for (j = 0; j < SAMPLE_N - 1 - i; j++) if (buf[j] > buf[j + 1]) { tmp = buf[j]; buf[j] = buf[j + 1]; buf[j + 1] = tmp; } for (i = 1; i < SAMPLE_N - 1; i++) sum += buf[i]; return sum / (SAMPLE_N - 2); }逻辑说明:先采 5 次存进数组,冒泡一趟把数据排好序,掐头去尾后取中间 3 次平均。排序本身的开销在 51 上可以忽略,因为每次只有 5 个元素,而 HX711 在 10Hz 速率下每次读取本身要等 0.1 秒,真正吃时间的不是排序而是等转换。
参数说明:SAMPLE_N取 5 是均衡值,称重响应大约 0.5 秒,体感不拖沓。取 3 则抗脉冲能力弱,取 9 以上读数会明显“粘滞”,放上东西要等一秒钟才稳下来,适合对实时性不敏感的场景。如果做的是厨房秤这种静态称重,我建议用 5;如果是人体秤,可以提到 7,但代价是每跳一次数字都要刷新一两秒,用户会觉得反应慢。
4. 从 Keil5 到实物调试:串口打印与量程对齐
4.1 Keil5 工程的三个关键配置
很多人在 Proteus 里仿真通过,一转到 Keil5 建工程就翻车,问题不在代码,而在三个忽略项。第一是芯片型号,Proteus 里选了 AT89C52,实物用的是 STC89C52,在 Keil5 的 Device 列表里选 STC 系需要提前装 STC 的数据库补丁,没装就先用 AT89C52 型号,代码区别不大,只是下载方式不同。第二是 Output 选项卡里必须勾选 Create HEX File,否则编译出来的东西没法烧。
第三是晶振频率要和原理图一致。我用串口调试,所以晶振选 11.0592MHz,这样波特率误差为 0。如果只是跑纯逻辑不用串口,用 12MHz 更方便做时间计算。注意这里说的是 Keil5 工程里虚拟的晶振设置,它只影响编译期的时间计算,真正决定运行速度的还是板子上那颗物理晶振。
// 串口初始化:模式1,9600波特率,11.0592MHz void Uart_Init(void) { TMOD |= 0x20; // 定时器1设为模式2,自动重装 TH1 = 0xFD; // 9600 波特率重装值 TL1 = 0xFD; SCON = 0x50; // 串口模式1,允许接收 TR1 = 1; // 启动定时器1 }逻辑说明:TMOD |= 0x20而不是直接赋值 0x20,是为了不动定时器 0 的配置位。如果定时器 0 已经在做毫秒延时,直接把 TMOD 覆写等于把 T0 的工作模式改掉,延时会全面跑偏,这是常见低级血泪。
参数说明:9600 波特率下,TH1 和 TL1 都要是 0xFD,这是 11.0592MHz 晶振的唯一正确配法。换了 12MHz 晶振,这个值要改成 0xF3,否则串口出来全是乱码。实测中我把这个现象碰到了至少三次,每次都是换板子忘改晶振。
4.2 串口打印:让原始值开口说话
实物排错最核心的手段就是串口打印。LED 闪烁能确认程序跑到哪,但没法回答“ADC 值到底是多少”“标定系数是不是合理”这类问题。把原始值、偏移量、计算后的克数通过串口发到电脑,用串口助手盯着看,比任何仿真都直观。
#include <stdio.h> char putchar(char c) { SBUF = c; while (!TI); // 等待发送完成 TI = 0; return c; } void Debug_Print(long raw, long offset, float gram) { printf("raw=%ld offset=%ld gram=%.2f\r\n", raw, offset, gram); }逻辑说明:重定向 putchar 之后,printf才能在 51 上工作。while (!TI);是等发送中断标志置位,确保当前字符发完再发下一个,不然一字节还没发完就被下一个字节冲掉。TI清完才能再次发送,否则第一次 printf 之后串口永久卡住。
参数说明:%.2f在 Keil 里默认不开启浮点打印支持,如果连接头文件 + 勾选浮点选项后还打印乱码,检查两点:一是 printf 目标格式字符串里保留浮点优化选项,二是 51 的 printf 默认不支持浮点,需要在 C51 编译选项里把 Floating Point 的 printf 支持勾上。这是 Keil 环境最坑的配置之一,我以前被这个卡了一整个下午。
4.3 仿真通过但实物翻车:差别在哪
仿真通过只说明逻辑正确,实物跑不起来通常是硬件问题。常见差别是这三处:一是晶振电路,Proteus 里双击单片机设个频率就算完事,实物必须真实接 22pF 到 33pF 电容,有些开发板晶振起振慢,一上电单片机根本没跑;二是复位电路,STC 系列上电后要等几十毫秒复位完成,如果复位电容太便宜或者漏电,程序会反复复位,表现为数码管闪一下灭一下;三是 EA 引脚,STC89C52 的 EA 必须接 VCC,接错直接执行外部程序空间,板子毫无反应,这个我在帮人排错时见过太多次。
5. 电子秤项目避坑实录:五条值得写进笔记的经验
5.1 读回 0x800000:SCK 时序被中断打断
现象:上电后 HX711 读数固定在 0x800000 附近,不管放不放大砝码都没反应。
原因:读时序第 25 个脉冲之前被打断了。我当时的代码在读取 HX711 前开了定时器中断,中断里做了延时操作,恰好把 SCK 的高电平时间拉长,芯片把第 25 个脉冲误判成了下一个读取周期的数据脉冲。数据错位之后,读出来的东西固定是符号位。
解决:读取 HX711 期间临时关中断,读完整包数据再开。代码里用EA = 0; ... EA = 1;包住HX711_Read(),或者把第 25 个脉冲的延时控制好,确保 SCK 的脉冲宽度落在 1us 到 60us 之间,别被中断拖出界。
5.2 重量跳变五个字:供电纹波在捣乱
现象:空载时读数在零点附近每秒跳好几个字,用手摸一下传感器或者动一下供电线,跳动幅度更大。
原因:HX711 对 AVDD 供电要求高,电源纹波直接影响 ADC 精度。我那块面包板的电源是从 USB 转串口模块上取的,纹波大,AVDD 输入毛刺被 128 倍增益放大,表现在读数上就是持续低频抖动。传感器激励线又长,直接当起了天线。
解决:把 HX711 的 AVDD 和 DVDD 用一颗 100uF 电解电容 + 一颗 104 陶瓷电容并联去耦,电容尽量贴近芯片引脚。传感器信号线越短越好,避免和电源线平行走线。如果是面包板验证阶段,临时用两节电池供电对比测试能快速确认是不是电源问题。
5.3 1602 白屏:P0 口上拉电阻不能省
现象:1602 上电后白屏,有时候亮第一行方块,调节对比度电位器没用。
原因:P0 口是开漏结构,默认高电平要靠外部上拉。Proteus 里仿真是默认逻辑高,看不出来,实物没有上拉电阻时高电平信号浮空,指令数据根本送不进 1602。
解决:P0 口外接 10K 上拉排阻,这是最基本要求。另外 RS、RW、EN 控制线接 P2 口,P2 是准双向,不需要上拉。如果还白屏,查 VO 引脚对地电压,调到 0.4V 到 0.8V 之间,对比度才合适。
5.4 校准系数越来越偏:蠕变与零点漂移
现象:标定完当天很准,第二天同一块砝码称出来偏了 1 到 2g,三天后偏更多。
原因:应变传感器存在蠕变和温度漂移。蠕变是恒定载荷下应变片输出随时间缓慢变化,温度漂移则是弹性体受环境影响导致零点移动。标定只是把某一时刻的零点固化了,温度一变零点就搬家了。
解决:每次开机时自动做一次去皮校准,把当前空载值定为操作零点。再有一个习惯是上电后先预热 1 分钟再进入称重界面,让传感器到达热平衡。标定系数存下来,但偏移量一定要每次上电刷新。
5.5 Proteus 里能跑,实物一上电就飞:仿真替代元件的边界
现象:Proteus 仿真文件里重量显示完全正常,烧到实物上不是乱码就是读数恒零。
原因:Proteus 里没有 HX711,用滑动变阻器模拟了传感器差分电压,这就掩盖了一个事实:实物的 HX711 需要正确的时序和供电环境。仿真里你会把标定系数调得很完美,因为电位器分压是理想线性的,而真实的应变传感器有噪声、温漂和接触电阻。
解决:仿真只用来验证主逻辑,接进实物前先跑串口监视,把原始的raw值打出来,确认空载值、轻压值、满载值三个点的数量级。如果空载值和砝码值差值不稳,先别动算法,回头排查硬件。从那以后我每次做这类项目,都强制走一遍“串口看原始值→三点标定→断电上电复测”的流程,不再凭感觉调系数。希望帮到你。
本文还有配套的精品资源,点击获取