简介:基于51单片机的电子秤设计开发资料包,面向单片机初学者、嵌入式爱好者以及需要完成课程设计或毕业设计的学生,重点解决电子称重系统中传感器数据采集、模数转换、液晶显示与按键交互等环节的程序实现问题。内含完整Keil工程(.uvproj)及启动代码(STARTUP.A51),围绕HX711传感器驱动、LCD显示、键盘处理等模块提供C语言源文件与对应头文件,并附编译生成的列表文件、目标文件,可对照工程快速理解电子秤从称重到显示的全流程。
压缩包共27个文件,以c/h源文件、a51启动代码、uvproj工程配置、lst/obj编译中间文件为主,整体仅64KB,便于下载和对照学习。目前已有529人浏览学习。借助工程中main.c主程序结构、lcd.c显示模块、keyboard.c按键逻辑以及hx711.c采集驱动,可以快速搭建实验环境或移植到自己的电路板中;同时代码模块划分清晰,适合在现有框架上扩展单位切换、去皮清零等常用功能,是入门51单片机称重系统的实用参考资料。
1. 51 单片机做电子秤,难点不在称重而在信号链
做电子秤课程设计的人很多,但能把读数稳住、标定不出错的很少。51 电子秤看起来是“传感器 + 单片机 + 数码管”三板斧,实际上真正决定成败的是从应变片桥路到 AD 转换这一整条信号链:传感器输出只有毫伏级,偏置、温漂、电源纹波都会直接变成重量误差。很多人程序写得没问题,却卡在原理图上,或者卡在“为什么换一个传感器就要重新标定”。这篇文章顺着 51 电子秤的完整落地路径走一遍:先把原理图的关键节点讲清楚,再给出可直接抄的电子称程序框架,最后把标定、滤波和排错的手法补齐。适合正在做 51 单片机课程设计、或者想把 Proteus 仿真换成实物焊接的读者,有一定 C 语言基础就能跟下来。
2. 电子秤原理图怎么画:从传感器桥路到 HX711 再到 STC89C52
2.1 电阻应变式传感器:半桥还是全桥,激励电压怎么选
常见的 51 电子秤称重传感器是电阻应变式,内部是惠斯通电桥。原理上,应变片贴在弹性体上,受力后电阻变化,电桥失去平衡,在信号输出端产生一个差分电压。这个电压非常小,满量程时通常只有 2mV/V 左右。也就是说,如果给传感器供 5V 激励,满量程输出也只有 10mV,这个信号直接进单片机 ADC 是完全不可用的,必须经过仪表放大器放大。
选型时注意传感器是半桥还是全桥。半桥传感器内部只有两个应变片,需要外部再搭两个精密电阻补全电桥,常见于体重秤条状传感器;全桥传感器内部四个应变片都齐了,直接接激励和信号就行。对于 51 电子秤,全桥传感器接线更省事,一般输出四根线:E+(激励正)、E-(激励负)、S+(信号正)、S-(信号负)。
激励电压的选择直接影响精度。传感器灵敏度是相对值,激励越高,输出差分电压越大,但自热也越严重。5V 激励下传感器自身功耗一般只有几十毫瓦,短时间工作没问题;如果要长时间连续称重,建议降到 3.3V 并用稳压芯片单独供电,避免和数字电路共用电源导致纹波耦合进信号。
2.1.1 传感器信号为什么要差分采集
应变式传感器的输出是差分信号,S+ 和 S- 对地的共模电压在毫伏到十几毫伏之间波动。如果采用单端采集,把 S- 直接接地,那么地线上的任何压降、电源纹波都会叠加到信号里,而且无法区分是重量变化还是干扰变化。所以原理图上必须把 S+、S- 接入差分输入的 AD 芯片或仪表放大器,这也是为什么直接用 STC89C52 内部 ADC 做电子秤非常困难的原因——51 内置 ADC 通常是单端输入,分辨率也远不够。
2.2 HX711 是信号链的中枢:为什么不用 24 位 AD 芯片自己搭
很多初学者会问:能不能用 ADC0832 或者 STC 内置 ADC 来做电子秤?答案是能出数,但精度和稳定性很差。称重传感器满量程输出 10mV,如果用 5V 参考电压的普通 ADC,10mV 只占满量程的 0.2%,一个 8 位 ADC 的有效分辨率根本不够分。你需要的是一个带可编程增益放大器的差分 ADC。
HX711 是专为电子秤设计的 24 位 ADC 芯片,内部集成了低噪声仪表放大器,增益可选 32、64 和 128。对于常见的 1kg、5kg 量程传感器,用增益 128 档位,满量程输入只需 ±20mV,这恰好覆盖传感器输出范围。HX711 的数据输出是串行的,只需要两根线(DOUT 和 PD_SCK)就能和单片机通信,非常适合引脚资源紧张的 51 单片机。原理图设计时,把 HX711 放在传感器和单片机之间,模拟部分和数字部分尽量分开布线。
2.3 最小原理图:一份可直接抄的接线表
下面这份接线表是针对 STC89C52RC 的最小系统方案,传感器、HX711、单片机的连接方式如下:
| HX711 引脚 | 连接目标 | 说明 |
|---|---|---|
| E+ | 传感器红线(E+) | 激励正,经 LC 滤波后接 5V |
| E- | 传感器黑线(E-) | 激励负,接模拟地 |
| S+ | 传感器白线(S+) | 信号正,差分输入 |
| S- | 传感器绿线(S-) | 信号负,差分输入 |
| VSUP | 数字 5V | 芯片电源 |
| AVDD | 模拟 5V | 经 10Ω 电阻和 10uF 电容滤波 |
| DOUT | P1.0 | 串行数据输出 |
| PD_SCK | P1.1 | 串行时钟输入 |
| VFB、BASE | 悬空 | 不使用 |
| RATE | 悬空或接 GND | 默认 10Hz 输出速率 |
原理图里有几个关键细节。第一,AVDD 和 VSUP 不要直接连在一起,中间串一个小电阻再加去耦电容,把数字噪声和模拟供电隔离开。第二,传感器激励线 E+、E- 与 HX711 之间加一个 10uF 电解电容和 0.1uF 瓷片电容并联,抑制电源高频噪声。第三,PD_SCK 和 DOUT 线尽量短,不要和数码管段选线平行走线太长。
// HX711 接线示例(Keil C51 定义) sbit HX711_DOUT = P1^0; sbit HX711_SCK = P1^1;注意:STC89C52RC 的 P1 口是准双向口,读引脚前要先写 1,HX711 的 DOUT 线接 P1.0 时需要把 P1.0 置 1 才能正确读取外部电平,程序中需要在初始化时加上
HX711_DOUT = 1;。
3. 电子称程序怎么写:HX711 时序、称重公式与按键标定流程
3.1 HX711 的时序到底怎么读:DOUT 和 SCK 的配合
HX711 的数据输出时序是典型的同步串行协议。DOUT 为高电平时表示 AD 转换未完成,数据不可读。主控需要持续等待 DOUT 变低;DOUT 变低后,主控发送 25 个 SCK 脉冲,前 24 个脉冲依次移出 24 位数据,最高位在前,第 25 个脉冲用于让芯片进入下一次转换。输出格式是二进制补码,符号位扩展。
初始上电时,HX711 需要大约 400ms 的稳定时间,程序里要延时后再读取。另外,芯片有 A 通道和 B 通道,A 通道增益可选 128 或 64,B 通道固定增益 32。发送第 25 个脉冲之前,如果 SCK 持续保持高电平超过 60us,芯片会进入掉电模式,使用时要注意脉冲宽度别拖太长。
读取一次 AD 值的核心代码如下:
unsigned long HX711_Read(void) { unsigned long value = 0; unsigned char i; HX711_SCK = 0; // 时钟线拉低,准备读取 HX711_DOUT = 1; // P1.0 置 1,确保能读到外部电平 while(HX711_DOUT); // 等待 DOUT 变低,表示数据就绪 for(i = 0; i < 24; i++) { HX711_SCK = 1; // 上升沿移出一位数据 value = value << 1; // 先移位再读位 HX711_SCK = 0; if(HX711_DOUT) value++; } HX711_SCK = 1; // 第 25 个脉冲,启动下次转换 value = value ^ 0x800000; // 补码转原码 HX711_SCK = 0; return value; }这段代码有几个容易写错的地方。value = value << 1;必须在读 DOUT 之前执行,否则会把当前位挤掉。补码转原码采用异或0x800000的方式,把符号位取反,这样空载时读到的值会从 0xFFFFFF 附近回落到 0x000000 附近的区间,方便后续做去皮和标定。如果不需要支持负重量(比如吊秤的拉力方向固定),可以直接把返回值当作无符号数处理,但电子秤一般都要支持“秤盘上放东西之后重量增加”的正向变化,异或处理是更稳妥的做法。
3.2 从 AD 值到克数:线性标定公式
HX711 读出来的原始值不是克数,需要通过标定建立线性映射关系。称重传感器的输出在弹性变形范围内是线性的,因此标定公式是重量 = (当前读数 - 零点读数) * 标定系数。零点读数是空秤时多次读数的平均值,标定系数表示“每个 AD 计数对应多少克”。
标定系数的单位是克/计数,计算方法是:放一个已知重量的砝码,读取稳定后的 AD 值,用砝码重量除以(砝码读数 - 零点读数)。例如空秤读数为 3000,放 1000g 砝码后读数为 21000,那么标定系数就是1000 / (21000 - 3000) = 0.05556克/计数。称重时用(当前值 - 零点) * 0.05556就能得到克数。
程序里建议把所有运算放在长整型和浮点之间做好取舍:
long zero_offset = 0; // 零点读数 float scale_factor = 1.0; // 标定系数,单位:克/计数 float Get_Weight(void) { long ad_value; float weight; ad_value = (long)HX711_Read(); // 读取原始 AD 值 weight = (ad_value - zero_offset) * scale_factor; if(weight < 0) weight = 0; // 负值截断,避免显示负数 return weight; }提示:标定系数应该按“先测零点、再测满量程、最后计算”的顺序来做。不要在空载时直接修改标定系数,也不要在放砝码后再清零,否则会污染零点偏移量。
3.3 按键标定流程:先清零再放砝码
实际使用时,电子秤不能每次都重新写程序来标定,所以要在程序里实现按键标定状态机。常见方案是三个按键:去皮键(Tare)、标定键(Cal)、加减键用于微调。标定流程是:按住标定键进入标定模式,此时先记录空秤零点,然后提示放上标准砝码,程序读取稳定读数后自动计算标定系数。
下面是一个简化但完整的标定逻辑框架:
void Key_Calibrate_Machine(void) { unsigned char step = 0; unsigned long empty_val, load_val; float known_weight = 1000.0; // 标准砝码重量,单位克 if(cal_key_pressed == 1) // 检测到标定按键 { step = 1; Display_Prompt("EMPTY"); // 提示清空秤盘 } while(step == 1) { if(confirm_key_pressed) // 按确认键采集零点 { empty_val = HX711_Read_Average(10); zero_offset = (long)empty_val; step = 2; Display_Prompt("LOAD"); // 提示放砝码 } } while(step == 2) { if(confirm_key_pressed) // 按确认键采集砝码读数 { load_val = HX711_Read_Average(10); scale_factor = known_weight / (float)(load_val - empty_val); Save_To_EEPROM(zero_offset, scale_factor); // 保存参数 step = 0; } } }HX711_Read_Average(10)是连续读取 10 次取平均,目的是滤除振动和短时噪声。保存到 EEPROM 是为了断电后不需要重新标定,STC89C52 内部有 EEPROM 区,可以用 STC 的 ISP 下载工具定义在程序空间之外。直接把标定系数和零点保存在单片机内部 EEPROM,比每次上电重新标定要人性化得多。
4. 称重不稳、跳数、零点漂移:三个高频坑的排查顺序
4.1 跳数先查电源和地,别急着加滤波
51 电子秤最常见的故障是数码管重量值末位不停跳动,甚至每秒变化好几克。很多人的第一反应是加长滤波,但实际上跳数的根源往往是硬件。供电是一个重灾区:如果传感器激励和单片机共用同一个 5V 电源,数码管的段选电流会在几十毫安级别波动,这个波动直接耦合进传感器桥路,AD 值就会跟着抖。
建议的排查顺序是:先用万用表测 HX711 的 AVDD 和 VSUP,看是否在 4.8V 以上;再用示波器看 DOUT 信号的上升沿是否干净;最后才考虑程序滤波。AVDD 纹波超过 50mV 时,读数会表现出周期性跳变,跳变频率和数码管刷新频率一致,这种情况加滤波只能延缓显示,不能根治。
从原理图上解决跳数问题,常见做法是在 HX711 的 AVDD 引脚前端加10Ω + 10uF的低通滤波,传感器激励线 E+ 和 E- 之间并联 100uF 电解电容。模拟地和数字地在单片机处单点汇合,不要形成地环路。
4.2 滤波算法:滑动平均和去极值怎么选
如果电源检查后读数仍然有轻微波动,那就需要软件滤波。称重场景里,最常见的干扰是低频机械振动(人走路、桌面抖动)和高频电气噪声混合。针对不同噪声特征,滤波策略完全不同。
滑动平均适合平滑随机噪声,但对脉冲干扰敏感;中位值平均滤波(先排序再去掉最大值和最小值后求平均)适合既有随机噪声又有偶尔脉冲的情况。电子秤上我一般用后者,采样次数取 5 到 10 次,太少了滤不干净,太多了响应变慢,放上砝码之后要等几秒才稳定,用户体验很差。
unsigned long HX711_Read_Filtered(unsigned char times) { unsigned long buf[10]; unsigned char i, j; unsigned long sum = 0; for(i = 0; i < times; i++) { buf[i] = HX711_Read(); delay_ms(10); // 相邻采样间隔 10ms } // 冒泡排序,去掉最大值和最小值 for(i = 0; i < times - 1; i++) { for(j = 0; j < times - 1 - i; j++) { if(buf[j] > buf[j+1]) { buf[10] = buf[j]; buf[j] = buf[j+1]; buf[j+1] = buf[10]; } } } for(i = 1; i < times - 1; i++) sum += buf[i]; return sum / (times - 2); }注意这个函数的采样间隔问题:HX711 的默认输出速率是 10Hz,每次读数的间隔约 100ms。如果程序里连续调用HX711_Read()而不加延时,实际上读到的都是上一次转换的结果,数据没有新鲜度,滤波效果也会打折扣。所以循环里delay_ms(10)只是保证时序稳定,真正的采样节拍还应当由主循环的调度周期控制。
4.3 标定后不准:检查量程、放大倍数和机械形变
标定完成后读数仍然不准,常见原因集中在三个地方。
第一,量程匹配错误。HX711 增益选 128 时,差分管脚输入范围是 ±20mV。如果传感器满量程输出超过这个范围,正半周会削顶,称重到一半读数就不变了。处理办法是换增益 64,或者降低传感器激励电压。第二,传感器量程过大。用 5kg 的传感器称 50g 的东西,输出信号只有几百微伏,分辨率天然受限。第三,机械结构问题。传感器必须固定在稳定的底座上,如果秤盘直接压在传感器上但没有固定,砝码摆放位置不同会导致受力点偏移,同一个重物显示不同重量。
5. 把电子秤做准的一个验证技巧:三点标定与线性度回测
前面做的是两点标定,适合课程设计快速出数。但如果想让电子秤更接近真实商品秤的水平,建议做三点标定并回测线性度。
三点标定的做法是:在空载、半量程、满量程三个点分别记录读数,得到一个两段线性折线模型。第一段用零点到半量程计算标定系数,第二段用半量程到满量程重新计算标定系数。称重时先判断当前读数落在哪个区间,再使用对应的系数。这样可以抵消传感器在接近满量程时轻微的非线性偏差。
值得注意的细节是两点标定只保证了零点和满量程两个点准确,中间可能偏差 2 到 3 克;而三点标定可以让中间段误差明显下降。对于 5kg 量程、精度 1g 的电子秤,三点标定几乎是不二之选。
回测线性度的操作方法是:准备 100g、200g、500g、1000g、2000g 五个砝码,依次放上秤盘,记录每个点的显示值和实际值的差值,并用表格记录。例如:
| 标准重量(g) | 显示重量(g) | 误差(g) |
|---|---|---|
| 100 | 101 | +1 |
| 200 | 199 | -1 |
| 500 | 501 | +1 |
| 1000 | 1000 | 0 |
| 2000 | 2001 | +1 |
回测结果中,如果误差呈现中间正、两端负的规律,说明折线模型的分段点选得不够好;如果误差无规律,则需要回头检查采样滤波和机械固定。建议把回测结果和三个标定点的读数一起记录,在最后调整系数时对比旧系数和新系数的误差分布,优先保证常用量程段的精度。最后一步是验证去皮功能是否正确:放上容器按去皮,显示归零后再加砝码,称重结果应等于砝码重量,如果偏差超过一个砝码精度,需要重新检查零点偏移量是否在去皮时被覆盖。
本文还有配套的精品资源,点击获取