
这次我们来看一个基于 51 单片机的智能电子秤项目。对于嵌入式开发者和电子爱好者来说自己动手做一个电子秤是理解传感器、模数转换和单片机编程的绝佳实践。这个项目的核心在于如何利用 HX711 称重传感器模块和 51 单片机实现一个具备基本称重、计价和显示功能的智能系统。它不依赖复杂的操作系统或高端芯片成本低代码透明非常适合学习。本文将带你从零开始理解智能电子秤的硬件构成、软件逻辑和调试要点。我们会重点关注 HX711 与 51 单片机的接口通信、称重数据的滤波校准算法以及如何驱动 LCD1602 或数码管进行显示。无论你是想完成课程设计、毕业设计还是单纯想深入掌握单片机应用开发这篇文章都能提供一套完整的、可落地的实现方案。1. 核心能力速览能力项说明核心控制器51 系列单片机如 STC89C52称重传感器基于应变片的电阻式压力传感器通常为 5kg/10kg 量程ADC 模块HX711 高精度 24 位 A/D 转换芯片显示单元LCD1602 液晶屏 或 4 位/8 位数码管输入单元4x4 矩阵键盘 或 独立按键用于单价设置、去皮等主要功能基本称重、去皮功能、单价设置、金额计算、超重报警通信接口通常为 GPIO 模拟 SPI 与 HX711 通信无标准通信接口供电方式5V DC 供电可通过 USB 或适配器适合场景单片机教学、课程设计、毕业设计、电子DIY、简单商用秤原型开发2. 适用场景与使用边界这个基于 51 单片机的智能电子秤项目主要适用于以下几个场景嵌入式学习与教学作为单片机课程的综合性实验涵盖了 GPIO 控制、定时器、中断、外部芯片驱动HX711、人机交互键盘、LCD等多个核心知识点。电子竞赛与毕业设计提供了一个完整的“传感器MCU显示”的系统框架学生可以在此基础上增加蓝牙/Wi-Fi 数据传输、数据存储、上位机软件等功能进行拓展。DIY 爱好者实践成本低廉所有元器件易于获取动手制作一台可用的电子秤能带来极大的成就感。简单商用原型验证可用于验证称重和计价逻辑但由于 51 单片机性能和精度限制不适合高精度、高可靠性、复杂环境的正式商用产品。使用边界与注意事项精度限制受限于 HX711 和传感器本身以及 51 单片机的处理能力精度通常为 0.1g~1g 级别无法达到专业计量级别。量程限制需要根据所选压力传感器的量程如 5kg来使用严禁超载否则会永久损坏传感器。环境稳定性称重结果易受温度、振动、供电电压波动影响。学习项目中可通过软件滤波改善但工业环境需额外硬件补偿。安全性本项目为学习原型不应用于任何正式的贸易结算、医疗称重或法律认可的计量场合否则可能因精度误差导致纠纷或事故。3. 环境准备与前置条件在开始动手之前你需要准备好以下软硬件环境。硬件清单主控芯片STC89C52RC 单片机或其他兼容的 51 内核单片机一片。称重模块HX711 模块一个通常附带一个铝合金应变式压力传感器如 5kg 量程。显示设备LCD1602 液晶屏带背光16字x2行一个或 4位共阳数码管一套。输入设备4x4 矩阵键盘一个或 3-4 个独立按键。其他元件11.0592MHz 晶振一个30pF 瓷片电容两个用于晶振10uF 电解电容、10K 电阻用于复位电路220Ω 电阻若干用于 LED 限流如果使用数码管则需要更多蜂鸣器一个用于超重报警LED 一个电源指示杜邦线公对公、母对母若干开发与供电USB 转 TTL 下载器如 CH340、CP2102 模块一个用于给单片机烧录程序。5V/2A 直流电源适配器一个或可靠的 USB 供电口。软件清单集成开发环境IDEKeil uVision 5C51 版本。这是最主流的 51 单片机 C 语言开发工具。烧录软件STC-ISPSTC 官方烧录工具。用于将 Keil 编译生成的.hex文件下载到单片机中。电路设计软件可选Proteus 8 Professional。用于前期电路仿真验证代码逻辑可以极大提高调试效率。知识前置条件基础的 C 语言编程能力。了解 51 单片机的基本结构如 GPIO、定时器、中断。能够阅读简单的电路原理图。4. 硬件电路设计与连接智能电子秤的硬件系统围绕单片机展开以下是核心模块的连接示意图和要点。系统框图[5V电源] - [稳压电路] - [STC89C52单片机] | |--- GPIO --- [HX711模块] --- [压力传感器] | |--- GPIO --- [LCD1602 或 数码管驱动电路] | |--- GPIO --- [4x4矩阵键盘 或 独立按键] | |--- GPIO --- [蜂鸣器报警电路]各模块连接详解以 LCD1602 和 4x4 键盘为例单片机最小系统连接晶振、复位电路确保单片机可以正常工作。HX711 模块连接VCC- 单片机系统5VGND- 单片机系统GNDDT(数据线) - 单片机任意 IO 口如P1.0SCK(时钟线) - 单片机任意 IO 口如P1.1E,E-,A-,A连接到压力传感器的对应输出线通常传感器有红、黑、绿、白四线需参照传感器和 HX711 模块说明书配对。LCD1602 连接8位并行模式RS(数据/命令选择) -P2.0RW(读/写选择) -P2.1(通常接地始终写模式)E(使能端) -P2.2D0-D7(数据线) -P3.0至P3.7VCC-5V,VSS-GND,VEE(对比度) - 通过一个 10K 电位器到 GNDA(背光正极) -5V,K(背光负极) -GND4x4 矩阵键盘连接将键盘的 4 行R1-R4和 4 列C1-C4分别连接到单片机的 8 个 IO 口例如P1.2到P1.5接行P1.6到P1.7及P2.3,P2.4接列。蜂鸣器连接蜂鸣器正极通过一个 220Ω 电阻连接到单片机 IO 口如P2.5负极接 GND。在 Proteus 中仿真你可以先在 Proteus 中搭建此电路进行仿真。需要加载以下元件库AT89C52(单片机)LCD1602(液晶)KEYPAD-PHONE(矩阵键盘)BUZZER(蜂鸣器)HX711模块和压力传感器在 Proteus 中可能没有直接模型可以用一个可调电阻模拟传感器输出并用虚拟终端监视单片机与 HX711 的通信数据来模拟。5. 软件程序设计核心代码解析程序采用模块化设计主要包括主程序、HX711驱动、LCD1602驱动、键盘扫描、数据处理等模块。5.1 HX711 驱动程序HX711 通过 GPIO 模拟 SPI 时序进行通信。核心是读取 24 位 AD 值。// hx711.h #ifndef __HX711_H__ #define __HX711_H__ #include reg52.h // 定义引脚根据实际连接修改 sbit HX711_DOUT P1^0; // 数据输出 sbit HX711_SCK P1^1; // 时钟输入 void HX711_Init(void); long HX711_Read(void); void Get_Weight(void); #endif// hx711.c #include hx711.h // HX711 初始化 void HX711_Init(void) { HX711_SCK 1; // 初始时钟高 HX711_DOUT 1; } // 读取 HX711 的 24 位 AD 值 long HX711_Read(void) { unsigned long count 0; unsigned char i; HX711_DOUT 1; HX711_SCK 0; count 0; while(HX711_DOUT); // 等待 HX711 准备就绪DOUT 变低 for(i0; i24; i) { // 读取 24 位数据 HX711_SCK 1; count count 1; // 左移一位为下一位数据腾出空间 HX711_SCK 0; if(HX711_DOUT) { count; } } // 发送第 25 个脉冲选择通道和增益通道A增益128 HX711_SCK 1; count count ^ 0x800000; // 对最高位符号位取反得到补码 HX711_SCK 0; return ((long)count); } // 全局变量 long Weight_Shiwu 0; // 实物重量经校准和滤波后的值 long Weight_Maopi 0; // 毛皮重量空载时的 AD 值 // 获取重量核心函数包含滤波和校准 void Get_Weight(void) { long weight 0; char i; // 多次采样取平均软件滤波 for(i0; i10; i) { weight HX711_Read(); } weight / 10; // 减去皮重开机清零或手动去皮时记录的 Weight_Maopi weight weight - Weight_Maopi; // 校准系数转换AD值 - 实际重量克 // 系数 K 需要标定K (已知重量物体的AD值 - 皮重AD值) / 已知重量 // 例如放上 500g 砝码测得 weight_ad 1000000则 K 1000000 / 500 2000 // 假设我们标定得到的 K 2000.0 float K 2000.0; Weight_Shiwu (long)(weight / K); // 计算实际重量单位g // 超重判断与报警假设量程 5000g if(Weight_Shiwu 5000) { // 触发蜂鸣器报警 Buzzer 0; // 假设 Buzzer 连接 P2^5 } else { Buzzer 1; } }5.2 LCD1602 显示驱动程序// lcd1602.h #ifndef __LCD1602_H__ #define __LCD1602_H__ #include reg52.h #include intrins.h // 引脚定义 sbit LCD_RS P2^0; sbit LCD_RW P2^1; sbit LCD_EN P2^2; #define LCD_DataPort P3 // 8位数据端口 void LCD_Init(void); void LCD_WriteCommand(unsigned char Command); void LCD_WriteData(unsigned char Data); void LCD_SetCursor(unsigned char Line, unsigned char Column); void LCD_ShowString(unsigned char Line, unsigned char Column, unsigned char *String); void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length); #endif// lcd1602.c #include lcd1602.h // 忙检测简化版直接延时 void LCD_Delay(void) { unsigned char i, j; _nop_(); i 2; j 199; do { while (--j); } while (--i); } // 写命令 void LCD_WriteCommand(unsigned char Command) { LCD_RS 0; LCD_RW 0; LCD_DataPort Command; LCD_EN 1; LCD_Delay(); LCD_EN 0; LCD_Delay(); } // 写数据 void LCD_WriteData(unsigned char Data) { LCD_RS 1; LCD_RW 0; LCD_DataPort Data; LCD_EN 1; LCD_Delay(); LCD_EN 0; LCD_Delay(); } // 初始化 void LCD_Init(void) { LCD_WriteCommand(0x38); // 8位数据接口两行显示5*7点阵 LCD_WriteCommand(0x0C); // 显示开光标关闪烁关 LCD_WriteCommand(0x06); // 读写后光标右移整屏不移 LCD_WriteCommand(0x01); // 清屏 } // 设置光标位置 void LCD_SetCursor(unsigned char Line, unsigned char Column) { if(Line 1) { LCD_WriteCommand(0x80 | (Column - 1)); } else if(Line 2) { LCD_WriteCommand(0x80 | (Column - 1 0x40)); } } // 显示字符串 void LCD_ShowString(unsigned char Line, unsigned char Column, unsigned char *String) { unsigned char i; LCD_SetCursor(Line, Column); for(i0; String[i]!\0; i) { LCD_WriteData(String[i]); } } // 显示数字十进制定长 void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length) { unsigned char i; LCD_SetCursor(Line, Column); for(iLength; i0; i--) { LCD_WriteData(Number / LCD_Pow(10, i-1) % 10 0); } } // 需要实现一个简单的幂函数 LCD_Pow unsigned int LCD_Pow(unsigned int X, unsigned int Y) { unsigned int Result 1; while(Y--) { Result * X; } return Result; }5.3 键盘扫描程序4x4矩阵键盘// keypad.h #ifndef __KEYPAD_H__ #define __KEYPAD_H__ #include reg52.h unsigned char Key_Scan(void); void Key_Process(unsigned char KeyNum); #endif// keypad.c #include keypad.h // 引脚定义根据实际连接修改 sbit KEY_R1 P1^2; sbit KEY_R2 P1^3; sbit KEY_R3 P1^4; sbit KEY_R4 P1^5; sbit KEY_C1 P1^6; sbit KEY_C2 P1^7; sbit KEY_C3 P2^3; sbit KEY_C4 P2^4; // 键盘键值映射表 unsigned char Key_Table[4][4] { {1, 2, 3, A}, // A 可设为“去皮” {4, 5, 6, B}, // B 可设为“清零” {7, 8, 9, C}, // C 可设为“设置单价” {*, 0, #, D} // * 和 # 可作为功能键D 可设为“确认” }; unsigned char Key_Scan(void) { unsigned char Row, Col, KeyValue 0; // 列线输出低电平行线读取 KEY_C1 0; KEY_C2 1; KEY_C3 1; KEY_C4 1; if(KEY_R1 0) {Row0; Col0;} else if(KEY_R2 0) {Row1; Col0;} else if(KEY_R3 0) {Row2; Col0;} else if(KEY_R4 0) {Row3; Col0;} else {Row4;} // 无按键 KEY_C1 1; KEY_C2 0; KEY_C3 1; KEY_C4 1; if(KEY_R1 0) {Row0; Col1;} else if(KEY_R2 0) {Row1; Col1;} // ... 同理扫描其他列 // 为节省篇幅此处省略 KEY_C3 和 KEY_C4 的扫描代码逻辑相同 if(Row ! 4) { KeyValue Key_Table[Row][Col]; // 简单延时消抖 DelayMs(10); // 等待按键释放 while(!KEY_R1 || !KEY_R2 || !KEY_R3 || !KEY_R4); DelayMs(10); } return KeyValue; } // 按键处理函数在主循环中调用 void Key_Process(unsigned char KeyNum) { static float UnitPrice 0.0; // 单价 static float TotalPrice 0.0; // 总价 switch(KeyNum) { case A: // 去皮 Weight_Maopi HX711_Read(); // 记录当前 AD 值为皮重 break; case B: // 清零 Weight_Maopi 0; UnitPrice 0.0; TotalPrice 0.0; break; case C: // 进入单价设置模式需配合显示和数字键输入 // 此处需实现一个状态机进入设置模式用数字键输入单价用‘D’确认 // 例如LCD显示“Set Price:”然后读取数字键组合最后确认 break; case D: // 确认在设置模式下 // 退出设置模式保存单价 break; case 1 ... 9: case 0: case .: // 在设置单价模式下将这些字符拼接到单价字符串中 break; default: break; } // 计算总价总价 重量(kg) * 单价(元/kg) // Weight_Shiwu 单位是 g需转换为 kg TotalPrice (Weight_Shiwu / 1000.0) * UnitPrice; }5.4 主程序框架// main.c #include reg52.h #include lcd1602.h #include hx711.h #include keypad.h void main(void) { unsigned char KeyValue 0; // 初始化所有模块 LCD_Init(); HX711_Init(); // 开机显示 LCD_ShowString(1, 1, Smart Scale); LCD_ShowString(2, 1, Init...); DelayMs(1000); LCD_WriteCommand(0x01); // 清屏 // 获取初始皮重开机自动去皮 Weight_Maopi HX711_Read(); DelayMs(500); while(1) { // 1. 获取重量 Get_Weight(); // 2. 扫描键盘 KeyValue Key_Scan(); if(KeyValue ! 0) { Key_Process(KeyValue); } // 3. 更新显示 LCD_ShowString(1, 1, Weight:); LCD_ShowNum(1, 8, Weight_Shiwu, 5); // 显示5位重量单位g LCD_ShowString(1, 13, g); LCD_ShowString(2, 1, Price:); // 显示单价和总价需要将浮点数转换为字符串显示此处简化 // LCD_ShowFloat(2, 7, UnitPrice, 2, 2); // 假设有显示浮点数的函数 // LCD_ShowString(2, 12, Y/kg); // LCD_ShowString(2, 15, Total:); // LCD_ShowFloat(2, 21, TotalPrice, 2, 2); // 显示总价 // 4. 延时控制刷新率 DelayMs(200); // 约5Hz刷新率 } }6. 系统调试与标定流程代码写完后烧录到单片机硬件连接无误接下来是最关键的调试和标定阶段。6.1 上电基础测试电源与最小系统上电后检查单片机是否发热电源指示灯如有是否亮起。用万用表测量单片机 VCC 引脚是否为稳定的 5V。LCD 显示测试如果 LCD 不亮或显示乱码检查对比度电位器是否调节合适。RS,RW,E, 数据线连接是否正确、牢固。初始化代码中的命令顺序和延时是否正确。键盘测试编写一个简单的测试程序将按下的键值显示在 LCD 上确保每个按键都能正确识别。6.2 HX711 与传感器调试这是整个项目的难点。通信测试编写一个简单的程序循环读取 HX711 的原始 AD 值并通过串口如果单片机有或 LCD 显示出来。观察空载时数值是否稳定在一个较小范围内波动。现象如果读数始终为 0 或 0xFFFFFF可能是 DT/SCK 线接反、接触不良或 HX711 模块损坏。现象如果读数剧烈跳变可能是电源不稳、传感器未正确连接或地线干扰。标定Calibration这是将 AD 值转换为实际重量的关键步骤。步骤一清零确保秤盘空载记录此时的 AD 值AD_zero。这就是“毛皮重量”Weight_Maopi。步骤二加载已知重量放置一个已知精确重量的砝码如 500g记录此时的 AD 值AD_weight。步骤三计算系数 KK (AD_weight - AD_zero) / 已知重量。例如AD_zero 100000,AD_weight 1100000, 砝码 500g则K (1100000-100000)/500 2000。步骤四验证放上另一个已知重量的物体如 200g用公式重量 (当前AD值 - AD_zero) / K计算看结果是否接近 200g。如果偏差大可能需要重新标定或检查传感器线性度。6.3 功能集成测试基本称重放置不同重量的物体观察 LCD 显示是否稳定、准确。去皮功能放上容器按‘A’键去皮显示应归零。再向容器中加物显示应为净重。单价设置与计算进入单价设置模式输入单价如 12.5确认后放置物品检查总价计算是否正确总价 重量(kg) * 单价。超重报警放置超过量程如 5kg的重物检查蜂鸣器是否报警。7. 常见问题与排查方法问题现象可能原因排查方式解决方案LCD 无显示或乱码1. 电源/地线未接好2. 对比度调节不当3. 控制线连接错误4. 初始化时序错误1. 检查 VCC/GND2. 调节对比度电位器3. 对照原理图检查 RS, RW, E, D0-D74. 用示波器或逻辑分析仪看时序1. 确保供电稳定2. 缓慢调节电位器至字符清晰3. 重新焊接或插接4. 检查LCD_Delay函数和命令顺序HX711 读数始终为0或固定值1. DT/SCK 线接反或接触不良2. HX711 模块损坏3. 传感器损坏或接线错误4. 程序读取时序错误1. 交换 DT/SCK 试试2. 更换模块测试3. 测量传感器桥路电阻通常每对约 1kΩ4. 用逻辑分析仪抓取 SCK/DT 波形1. 确认接线2. 更换模块3. 参照传感器说明书接线红黑绿白4. 对照 HX711 数据手册调整时序重量显示不稳定跳动1. 机械振动或风吹2. 电源纹波大3. 软件滤波不足4. 传感器受力不均1. 在稳定平台上测试2. 用示波器看 5V 电源3. 增加采样次数或改用更高级滤波算法如滑动平均、卡尔曼4. 确保物体放在秤盘中心1. 避免环境干扰2. 给系统增加稳压和滤波电容3. 优化Get_Weight中的滤波算法4. 调整传感器安装去皮或清零后重量不为零1. 皮重记录时机不对有微小抖动2. 传感器存在零点漂移1. 在稳定状态下进行去皮操作2. 增加一个“稳定检测”逻辑当重量连续多次变化小于阈值时才允许去皮1. 手动确保空载稳定后再按去皮键2. 在代码中实现自动零点跟踪较复杂键盘某些键无反应1. 该键对应的行或列线接触不良2. 程序扫描逻辑有误漏掉了该列或行1. 用万用表导通档检查按键按下时对应线路是否连通2. 单步调试检查扫描该键时的 IO 口状态1. 重新焊接按键或检查杜邦线2. 仔细检查Key_Scan函数中对应行和列的扫描代码蜂鸣器不响1. 蜂鸣器极性接反有源蜂鸣器2. 驱动 IO 口配置错误应为推挽输出3. 报警条件未触发1. 交换蜂鸣器两极试试2. 检查程序是否将该 IO 口设置为低电平输出3. 调试查看Weight_Shiwu是否大于报警阈值1. 确认是有源还是无源蜂鸣器正确连接2. 在初始化中设置 IO 口模式3. 检查超重判断逻辑8. 项目优化与扩展方向完成基础功能后可以考虑以下优化和扩展让项目更具挑战性和实用性提高精度与稳定性软件滤波升级将简单的平均滤波改为滑动平均滤波或一阶滞后滤波。温度补偿增加 DS18B20 等温度传感器根据温度变化对读数进行补偿需事先标定温度系数。自动零点跟踪程序自动监测长时间空载下的微小漂移并缓慢修正零点。增强人机交互菜单系统通过按键实现多级菜单方便设置参数、查看历史记录等。声光提示不同的操作按键、超重、完成配合不同的 LED 闪烁或蜂鸣器声音。增加数据功能存储增加 AT24C02 等 EEPROM 芯片存储标定系数、单价等信息掉电不丢失。通信增加 HC-05 蓝牙模块或 ESP8266 Wi-Fi 模块将称重数据发送到手机 App 或电脑上位机实现数据记录和远程监控。改进显示OLED 显示改用 I2C 接口的 OLED 屏显示更美观节省 IO 口。语音播报增加 SYN6288 等语音合成模块用语音播报重量和金额。扩展应用场景计数功能已知单个物品重量通过总重自动计算物品数量。分选功能设置重量上下限配合继电器控制实现自动分选轻触开关模式。这个基于 51 单片机的智能电子秤项目麻雀虽小五脏俱全。它串联了模拟信号采集、数字信号处理、人机交互和嵌入式软件设计等多个环节。成功调试的过程就是不断发现问题、分析原因、动手解决的过程这比单纯看理论收获大得多。建议你先在 Proteus 中仿真通过主要逻辑再动手焊接实物可以节省大量排查硬件问题的时间。最后务必记住安全与合规边界这个学习项目是你踏入嵌入式世界的一块坚实垫脚石。