简介:这是一个基于51单片机的电子秤设计完整工程包,面向单片机初学者与电子爱好者。项目以51单片机为核心,结合电阻应变片传感器、LCD1602液晶显示与去皮功能,实现5公斤量程的实时称重,覆盖信号采集、A/D转换到显示输出的完整流程。压缩包共100个文件,主要包含C语言源程序、头文件、Keil工程文件(uvproj/uvopt)、编译生成的hex文件以及Proteus仿真工程(pdsprj/pdsbak),可对照源码理解代码逻辑,也能直接打开仿真查看运行效果,整体仅733KB,携带使用方便。已有790人学习下载。资料提供了完整代码与仿真环境,适合用于巩固51单片机编程、传感器应用和Proteus操作,是课程设计或课外自学的高价值参考。
1. 给台秤装上一颗51内核:电子秤设计到底在解决什么问题
很多初学者第一次接触“基于51单片机的电子秤设计”,以为它就是一个放大器加一个单片机,读个数然后显示。真做起来你会发现,这个项目把传感器信号链、AD采集、软件滤波、标定、人机交互全串了一遍。硬件上你至少要把称重传感器的差分小信号送到24位的HX711里做转换,软件上则要处理来自机械振动、供电纹波和温度漂移的噪声,最终让显示值在一个相对稳定的区间里停下来。这个设计能直接落地到小量程商用秤、实验室称量、工位配料称重这类场景里,它适合正在学51和Proteus仿真的在校生,也适合想把手头电子秤改成定制量程的硬件工程师。Signal path 上的每个环节都会影响最终精度,这也是它和普通单片机实验项目拉开差距的地方。
整条链路拆开看,从应变片受力变形到数码管显示重量,中间隔着至少五个环节:称重传感器输出微伏级的差分电压、HX711把差分电压放大并转换成24位数字、单片机按时序把那24位数据读进来、软件做滤波和去皮、最后做线性标定把“ADC原始值”映射成“克数”。任何一个环节理解不到位,做出来的秤要么跳字,要么低量程不准,要么满量程漂移。下面我按自己做这个项目时的顺序,从硬件选型讲到代码实现,再给你几条实在的排错经验。
2. 最小硬件系统:称重传感器、HX711和STC89C52怎么搭
2.1 为什么选HX711而不是51内部ADC或ADC0809
常见的51单片机内部不带ADC,就算带也只有8到10位,按一个5kg量程、满量程输出2mV/V来算,10位分辨率对应的重量步进大约有5克,这远远达不到电子秤显示0.1克的需求。外接并行ADC0809是12位,理论步进能到1.2克左右,但它的参考电压和线性度都不理想,而且并行接口占用的IO口太多,和显示模块抢引脚。这个项目里最可靠的做法是选一颗HX711:它在芯片内部集成了低噪声可编程增益放大器,通道A的增益可以选64或128,通道B固定为32,配合5V供电时,它能把传感器输出的微弱差分信号放大到适合内部Σ-Δ ADC采样的电平。
HX711的数据接口只有两个引脚:PD_SCK和DOUT,一个做时钟输入、一个做数据输出,逻辑上就是一个移位寄存器。单片机通过SCK引脚发脉冲,每个脉冲让DOUT移出一位数据,连续24个脉冲就能读出一个完整的24位二进制补码。这样的串行时序占用IO口少,和驱动LCD1602或数码管的电路不冲突,整个系统的接线难度就降下来了。实际布线时我会把HX711放在传感器引出线和单片机之间,AVDD用5V供电,VSUP也用同一路5V,这样模拟和数字部分的参考是同一个源,读数的稳定性会好一些。
2.2 硬件清单与接线逻辑
一个能跑起来的称重模块只需要这些零件:5kg或10kg的平行梁式称重传感器一只、HX711模块一块、STC89C52单片机最小系统板一块、四位数码管或LCD1602一个、两个按键(去皮键和开关机键)、一个5V稳压电源。传感器一般引出红、黑、绿、白四根线,红色接E+也就是传感器的激励正端,黑色接E-,绿色接S+也就是信号正端,白色接S-。HX711模块上通常已经印好了E+、E-、S+、S-四个接线端子,直接把传感器四根线锁进去就行,注意屏蔽层要单独接外壳地,别加到信号地上去。
单片机这侧主要是看时序:把HX711的DOUT接到P1.0,PD_SCK接到P1.1,这两个引脚一个做输入一个做推挽输出就可以。其余IO口用来驱动数码管,我一般用P0口做段选、P2口的低4位做位选,P3.2和P3.3接两个按键。电源部分要特别处理一下:HX711的模拟供电和单片机数字供电建议在进板处用一颗10Ω电阻做单点隔离,并在每路电源附近各放一颗100uF电解电容和一颗104陶瓷电容去耦。这样做能让显示值在电机启动或继电器吸合的瞬间不那么容易跳变。
2.3 传感器的量程选择和激励电压
选传感器量程时别满打满算,电子秤的传感器建议工作在量程的60%到80%。例如你要做一台最大称重3kg的秤,就选5kg传感器而不是3kg传感器。原因是称重传感器的线性区和滞后区往往在接近满量程的地方恶化,留出余量能让校准后的线性度更好,同时机械过载的容错空间也更大。激励电压方面,5V供电时传感器满量程输出大约按2mV/V标定,也就是说5kg传感器在5V激励下满量程差分输出约10mV,这个电压直接进51自带的ADC是不够的,必须经HX711的128倍增益放大到约1.28V满量程,才能让24位ADC的分辨率真正被用起来。
如果把HX711的增益设成64,虽然输入范围更大、噪声稍微小一点,但对传感器满量程输出利用率会下降。我一般做小量程高精度的称重会选5kg传感器配128倍增益,做大量程或者需要抗饱和的场景才调到64倍。接好线之后先别急着写标定程序,第一步应该上电看HX711是否输出稳定数值。打开串口监控,每秒打印一次读数,如果数值在小范围内跳动且没有周期性漂移,说明传感器和HX711接线没问题,可以进入下一步软件开发;如果数值无限增大或跑到满量程附近,基本是传感器信号线接反或接线接触不良。
3. 读取HX711的时序代码:让单片机把24位数据稳定读回来
3.1 HX711读数的完整C语言函数
HX711的时序要求非常精确,但对51单片机这种12T内核来说,直接用普通IO模拟就能满足,不需要外部定时器干预。读取的流程是先等DOUT引脚变低,这表示转换完成、数据已准备好,然后单片机向PD_SCK发24个正脉冲。前23个脉冲的下降沿会依次把最高位到最低位移出到DOUT引脚上,第24个脉冲的作用是完成一次完整的读取并让芯片进入下一次转换,采集完第24个脉冲的下降沿之后,DOUT会重新变高,表示这一轮转换结束。如果此时SCK继续发脉冲,超过25个,HX711会进入掉电模式,所以代码里要做严格的脉冲计数。
#include <reg52.h> sbit HX711_SCK = P1^1; // 时钟引脚 sbit HX711_DOUT = P1^0; // 数据引脚 unsigned long HX711_Read(void) { unsigned long value = 0; unsigned char i; HX711_SCK = 0; // 确保时钟为低 while(HX711_DOUT); // 等待DOUT变为低,表示转换完成 for(i = 0; i < 24; i++) { HX711_SCK = 1; // 产生上升沿,数据移出一位 HX711_SCK = 0; // 时钟拉低,准备下一个位 value = value << 1; // 把已有的结果左移一位 if(HX711_DOUT) { // 读取当前位 value++; } } HX711_SCK = 1; // 第25个脉冲,让芯片进入下一次转换 value = value ^ 0x800000; // 做符号位取反,得到无符号绝对值表示 HX711_SCK = 0; return value; }这里有个细节要说明:HX711输出的是二进制补码格式,正满量程时值为0x7FFFFF,负满量程时值为0x800000。把0x800000做一次异或,是为了让空载时的读数大体落在0x800000附近,方便后面做去皮和标定的差值计算。如果你不做这一行,后面就会发现空载读数在一个很大的基数上变化,去皮时要减掉的数非常大,还容易引入舍入误差。我在第一次接触这个模块时也跳过符号处理,直接拿原始值去标定,结果发现线性拟合总是差两三个字,后来才意识到是符号位没有转成无符号表示。
3.2 为什么要在主循环里先读25次再做平均
直接调用上面的函数会发现,读回来的数值一直在抖动,特别是在传感器上有轻微机械振动时。HX711虽然内置数字滤波器,但它的输出速率默认是10SPS,也就是说每秒出10个新数据。对电子秤应用来说,这个速率够快但还不够平滑,你需要自己做软件滤波。最常见的做法是连续读若干次,去掉一个最大值和一个最小值,再对剩下的数据求平均。这种中位值平均滤波法能同时抑制随机噪声和偶然的脉冲干扰,对电子秤这种信号变化相对缓慢的系统非常合适。
#define FILTER_N 5 // 采样次数 unsigned long HX711_Get_StableValue(void) { unsigned long buf[FILTER_N]; unsigned char i, j; unsigned long temp; unsigned long sum = 0; for(i = 0; i < FILTER_N; i++) { buf[i] = HX711_Read(); } // 简单冒泡排序,去掉最大值和最小值 for(i = 0; i < FILTER_N - 1; i++) { for(j = 0; j < FILTER_N - 1 - i; j++) { if(buf[j] > buf[j+1]) { temp = buf[j]; buf[j] = buf[j+1]; buf[j+1] = temp; } } } for(i = 1; i < FILTER_N - 1; i++) { sum += buf[i]; } return sum / (FILTER_N - 2); }这段代码的思路是先把采集到的5个原始值排序,去掉最大和最小,剩下3个求平均。滤波个数FILTER_N不宜太大,51单片机主频只有12MHz,一次完整读取加排序大约耗时2毫秒,如果滤波次数加到20次,虽然更平滑但会拖慢响应速度,放上砝码后显示值要等很久才稳定。对10SPS的HX711来说,取5到7次是比较舒服的区间。你可以在实际调试时加一个按键做滤波深度切换,感受一下5和7在读数速度和稳定性上的差异,这个体验比看任何理论都直观。
3.3 读取失败的典型现象:DOUT一直为高
新手最常遇到的一个坑是while(HX711_DOUT)等不到DOUT变低,程序卡死在等待循环里。原因是HX711与51单片机之间的供电没有共地,或者传感器信号线没有接好。HX711的DOUT引脚默认是推挽输出,数据准备好后会拉低,但如果芯片没工作,它可能一直保持高电平。排查思路是先量HX711的VSUP和AVDD是否有电压,再看传感器四个端子有没有接错,最后确认SCK引脚有没有被其他外设占用。把SCK接到P1口时,如果P1口还接了按键或蜂鸣器,要确认没有程序把这些引脚拉低或拉高,否则时序会被破坏。
DOUT为高的另一个常见原因是传感器满量程输出电压超过HX711的输入范围。5V激励下的10mV信号经128倍放大后约为1.28V,对5V供电的HX711来说是安全区,但如果你换了10V激励的传感器或接错激励电压,导致差分输入电压超过ADC的满量程,HX711会持续输出饱和值,看起来就像DOUT永远不拉低。这时候用万用表量一下传感器的S+和S-之间的静态电压,正常应该在0毫伏附近,如果稳定在几十毫伏,说明传感器受到了偏置或接线错误。
4. 去皮、量程切换与线性校准:称完怎么保证数值真实
4.1 软件去皮和硬件去皮的区别
去皮在电子秤里有两种做法。硬件去皮是用一颗精密电位器调整HX711的输入偏置,让空载时ADC输出正好在某个固定值附近,这种方式适合批量生产,因为每一台秤的传感器静态输出都有差异,生产线上会用电位器逐台调零。开发阶段和低产量场景更多用软件去皮:上电后读取空载稳定值,把它存入一个零位变量,之后每次显示重量都先减去这个零位值。软件去皮的好处是零位可以随时更新,放上容器后按一下去皮键,当前读数就会被设为新的零点,重量显示自动变成净重。
我一般会把去皮功能做成按键事件,按下后读取当前稳定值并保存到EEPROM或FLASH中,下次上电直接载入,避免每次开机都要重新去皮。STC89C52内部自带EEPROM,可以直接使用ISP下载时预留的扇区。在Keil5里写一个简单的EEPROM读写函数,把四字节零位数据拆分存储,读取时再拼回去。这里要注意,STC的EEPROM擦写寿命在一万次左右,虽然听上去不多,但正常使用时一天去皮几十次也能用好几年,完全够用。如果你做的秤需要频繁换容器去皮,建议把去皮键做成双击长按才生效的模式,防止误触导致零位频繁被重写。
4.2 两点标定加三点校验是最稳妥的路径
标定的目的是把去皮后的ADC差值转换成实际重量。线性传感器在量程内可以近似为一根直线,所以理论上有两个点就够了:零点和一个标准砝码点。实际操作里,我会至少做两次标定,第一次放上标准砝码后按标定键记录“克数对应ADC值”,第二次再换一个不同重量的砝码做验证。如果验证点的误差超过预期,就要考虑传感器是否线性度不足,这时候两点标定就不够用了,需要在量程内取三个标准点做分段线性插值。
常见的做法是维护一个三元素数组,记录每个标定点对应的ADC原始值和实际重量。在运行时先判断当前ADC落在哪两个标定点之间,再做线性插值,这样能补偿传感器中间段的非线性。参数配置上建议留一个标定模式,比如长按标定键进入P1状态,放第一块砝码并输入重量,再按确认键进入P2状态,放第二块砝码,依次类推。标定数据存EEPROM后,每次开机自动加载。对生产场景来说,这个设计能让每台秤独立校准,不必依赖元器件的一致性。
4.3 用表格看透两个标定点的换算关系
下面用一个50kg量程、HX711增益128的简化示例说明标定表的组织方式:
| 序号 | 标定重量 / kg | ADC原始差值 | 每克对应ADC增量 |
|---|---|---|---|
| 零点 | 0 | 0 | - |
| 点1 | 1.000 | 985 | 0.985 |
| 点2 | 5.000 | 4988 | 0.998 |
| 点3 | 10.000 | 10012 | 1.001 |
从表格可以看出,传感器和HX711组合后的实际增益并不完全一致,零点到点1的每克ADC增量和点2到点3之间有大约1.6%的偏差。如果你只用两个点做线性映射,在10kg称重时误差会接近160克,这对电子秤来说是难以接受的。所以只要条件允许,我都建议做三到五个标定点,上位机把数据采集下来拟合一条曲线,或者直接在单片机上做分段线性插值,代码量增加不多,精度提升是肉眼可见的。
标定参数在保存时要注意数据类型。ADC原始值是无符号长整型,重量可以用浮点或乘以100的整数表示。用整数可以避开浮点运算带来的资源和精度问题。比如重量以克为单位、分辨率0.1克时,可以用short类型存10倍克数,也就是最大支持6553.5克,对小量程秤足够。换算时先乘后除,避免中间数据被截断,例如计算重量时用“ADC差值 * 存储的标定系数 / 10000”而不是“ADC差值 / 10000 * 标定系数”,否则乘法溢出或除法截断会引入系统性偏差。
5. 常见问题避坑:电子秤调试时的6个血泪教训
5.1 读数跳变剧烈,从几百跳到几千
现象:放上重物后显示值来回乱跳,数字跨度非常大,完全没法读数。
原因:最常见的是传感器供电不稳,或HX711的电源和单片机共用了一个带继电器的电源模块,继电器动作时电压跌落导致ADC参考电压变化。另一种可能是传感器信号线虚接,接触电阻随机变化造成差分电压抖动。
解决:先用万用表测传感器供电电压,确认稳定在5V加减0.25V以内。再把HX711的AVDD改为LC滤波,比如串一颗100uH电感加一颗10uF电容,阻断高频噪声。最后把传感器接头改成接线端子并拧紧,不要用杜邦线悬空接。做完这三步,读数跳变一般会收敛到个位数。
5.2 空载上电读数一直下降
现象:开机时显示零位,但几分钟内读数持续下降,过一段时间才稳定。
原因:HX711和传感器都有温漂,传感器内部的应变片电阻会随温度变化,导致电桥输出缓慢漂移。开机瞬间传感器自身发热和电路板温升都会让零点跑偏。
解决:让系统预热2分钟再执行零位校准,或者软件里做自动零点跟踪。零点跟踪逻辑是当净重小于某个阈值比如0.5克且持续超过5秒时,自动把当前值更新为零位。这个功能商用秤里都有,它能抵消大部分温度漂移和长期蠕变。
5.3 放上砝码显示值随时间缓慢增长
现象:放上标准砝码后,显示值前几秒正确,但三十秒后慢慢变大几十克甚至几百克。
原因:这通常是传感器过载或材料蠕变。平行梁传感器在满量程附近会持续发生弹性体内部应力释放,导致输出信号缓慢变化。如果砝码重量已经接近甚至超过传感器额定值,这种现象会非常明显。
解决:检查砝码是否超出传感器量程,超了就换更大规格的传感器。如果没超,说明传感器余量留小了,比如5kg传感器长期称4.8kg会导致严重蠕变,应该换10kg规格让工作点落在60%以内。这个现象也提醒我们,标定完成后需要至少静置30分钟再复测一次,复测通过才能算真正标定完成。
5.4 去皮后放上重物显示为负数
现象:按下去皮键后显示归零,但放上重物后显示值变成负数或非常小的正数。
原因:去皮瞬间读到的ADC值不稳定,或者去皮读取时刚好碰到一个滤波边界值。也可能是去皮后零点更新了,但显示计算时用了旧的差值基准。
解决:在按键去皮的执行函数里连续调用5次稳定读取函数,取最后一次结果作为新的零位。同时检查显示代码的运算顺序,确保是“当前稳定值 - 零位值”而不是反过来。如果还会复现,把去皮成功与否做成标志,去皮失败时蜂鸣器响一声提示用户重新操作。
5.5 标定成功后断电再上电数据就不对了
现象:标定数据写入EEPROM后现场测试正常,但掉电重启后显示偏差变大。
原因:EEPROM写入时没有关中断,或者写入地址与程序代码区重叠导致数据被擦除。也可能是标定过程中的ADC原始值没有进行多次平均,写入的是瞬时值。
解决:STC单片机的EEPROM操作前要关闭总中断EA,写入后再恢复。写入前先读出旧数据做对比,如果相同就跳过擦写,减少无效擦除。标定数据的保存格式固定为4字节CRC校验,上电读取时先算CRC,不匹配就回退到出厂默认系数。这样做还能防止EEPROM擦写中断导致数据半写半不写的损坏情况。
5.6 数码管亮度闪烁或显示乱码
现象:数码管显示时有时无,或者在不该亮的段出现暗亮。
原因:P0口作为段选输出时没有加上拉电阻,高电平驱动能力不足。位选的扫描频率太低,人眼分辨出闪烁,或者定时器中断里显示刷新和重量计算抢占了CPU时间。
解决:P0口外接4.7k到10k的排阻上拉到5V。显示刷新放到定时器中断里做,主循环只负责重量计算和按键扫描。刷新频率要做到至少60Hz,四位数码管每一位点亮时间约4毫秒,这样才能看不出闪烁。如果还是闪,检查是不是主循环里的读HX711占用了太长时间,导致中断响应被拖延。
6. 进阶验证技巧:用砝码序列和重复性测试给电子秤做体检
完成基础功能之后,最值得做的一件事是建立一套简单的验证流程,用标准砝码对电子秤做重复性和线性测试。先准备三五个不同质量的砝码,从最小量程逐步加到最大量程,再逐步减回零位,记录每个重量点的显示值和误差。这里要注意“加载”和“卸载”两条曲线通常会不重合,这个滞后量就是传感器的迟滞误差,往往比线性误差还大。如果迟滞误差超出预期,可以在软件里加一个方向判断,在重量增加和减少时分别使用不同的标定系数,能抵消一部分机械滞后。对于大多数业余制作来说,这个功能不是必需,但对精度有要求的小批量生产就非常有价值。
验证的另一个重点是重复性,同一个砝码放上取下十次,记录每次的显示值,算平均值和最大偏差。偏差在正负一个显示分度值以内算是合格。比如0.1克分辨率的秤,十次重复测量最大偏差不能超过0.1克。如果重复性超标,多半是传感器机械安装的问题,比如秤盘固定螺丝松动、传感器受力点没有居中等。这时候调软件没有用,回过来检查机械结构才是对的。这个验证方法不需要精密仪器,一把游标卡尺和一套标准砝码就能把问题定位到机械还是电路。
我自己做这类项目时养成的一个习惯是保留一个待机状态和自动关机功能,没有按键操作90秒后进入低功耗模式,用定时器唤醒而不是直接断电,这样EEPROM里的标定数据不会丢失,下次开机直接按一个键就恢复称重。这个细节看似不起眼,但在电池供电的便携秤上能明显延长使用时间。整个方案做下来,你会发现硬件上的血泪经验往往比代码调试更费时间,尤其传感器安装、供电去耦和接地方案这几个点,一步没做好,后面标定做什么都快不了。希望这些经验对你有所帮助,照着这套路径做,你手里的电子秤很快就能从“能称”进入“称得准”的阶段。
本文还有配套的精品资源,点击获取