基于STC89C52的电子秤仿真实战:HX711称重传感器与1602液晶显示 📅 发布时间:2026/9/15 15:31:13 👁 浏览次数: 简介以STC89C52为核心的电子秤设计与Proteus仿真项目融合1602液晶显示、压力传感器数据采集与声光报警功能面向单片机初学者、电子设计竞赛参与者及课程设计学生可帮助快速建立“传感器—单片机—显示/报警”的完整系统概念。压缩包内含15个文件大小仅87KB覆盖C语言源程序、Keil工程配置文件uvproj/uvopt、编译生成的hex烧录文件、Proteus仿真原理图DSN以及列表备份等多类文件结构清晰便于直接加载与调试。目前已有298人学习下载。该资源提供了一整套可运行的电子秤实现方案从重量采集、1602实时显示到超阈值声光报警的完整软件流程与硬件仿真电路读者既能学习STC89C52的编程与控制方法也能掌握Proteus联合仿真和排错技巧对课程设计或入门嵌入式开发具有切实参考价值。1. 电子秤仿真项目STC89C52与1602显示能做多准一块 5kg 量程的电阻应变式称重传感器满量程输出通常只有 5mV 到 10mV。这个电压直接送进单片机无论多好的算法都读不出有效重量因为 STC89C52 内部根本没有 ADC。电子秤仿真项目刚好把这个问题拆成清晰的四段传感器输出差分毫伏信号HX711 负责放大和 24 位采样STC89C52 做时序判断与数据换算1602 液晶显示最终重量。很多人仿真跑通了、烧到实物上却出现乱码或者数字乱跳原因往往不在算法而在电桥激励、零点标定和 LCD 初始化时序这三件事上。这篇按硬件链路、核心程序、Proteus 联调、精度标定四层把完整路径讲清楚适合刚学完 51 单片机中断和定时器、准备用课设练手的人也能给已经搭过秤但精度不理想的开发人员一些排错方向。2. STC89C52电子秤硬件链路称重传感器、HX711与1602液晶的接线与原理2.1 电阻应变式称重传感器为什么必须配 HX711电阻应变式传感器内部是四个应变片组成的惠斯通电桥。秤体受力时贴在弹性梁上下表面的应变片一个被拉伸、一个被压缩电桥失去平衡在两个信号端之间产生与载荷成正比的差分电压。以常见的 1mV/V 灵敏度计算5V 激励电压下满量程只有 5mV这个幅度接近普通运放的失调电压直接用逐次逼近型 ADC 采集会损失大量有效位数。HX711 是为这类桥式传感器设计的专用芯片内置可编程增益放大器A 通道增益可选 128 或 64B 通道固定 32采样分辨率是 24 位。增益 128 时5mV 满量程信号被放大到 640mV 左右24 位采样理论上能把 5kg 量程分辨到毫克级。当然实际还受电源纹波、机械振动和 PCB 布线噪声限制最终稳定显示到 1g 需要软件滤波配合。HX711 与 STC89C52 的接口非常精简只有两根线没有 SPI 地址、没有寄存器配置引脚方向连到 STC89C52说明VCC电源5V数字电源兼作 IO 电平参考GND电源GND与单片机共地E激励传感器红线电桥正激励E-激励传感器黑线电桥负激励A信号传感器绿线差分信号正端A-信号传感器白线差分信号负端DOUT输出P2.4转换完成/数据输出SCK输入P2.5时钟输入注意 HX711_VCC 和 AVDD 是两个电源域常见模块板上有稳压。实物接线时如果 AVDD 由 5V 直接供给建议在 AVDD 引脚就近加 10μF 电解电容和 104 去耦电容电桥激励线尽量短。2.2 1602液晶与STC89C52的引脚接线和时序匹配1602 液晶控制器是 HD44780 标准时序STC89C52 在 12MHz 晶振下普通指令周期约 1μs满足 HD44780 对使能脉冲最低 230ns 的要求直接用 IO 模拟时序即可。接线我习惯这样安排8 根数据线放 P1 口RS、RW、E 放 P2 高位HX711 的 SCK、DOUT 放 P2 低两位按键放独立 IO全部错开。1602 引脚连到作用VSSGND电源地VDD5V电源正V010k 电位器中间抽头对比度约 1V~3VRSP2.60 命令 / 1 数据RWP2.70 写 / 1 读EP2.5使能脉冲D0~D7P1.0~P1.78 位数据总线一个高频翻车点是 P0 口。P0 是开漏结构直接驱动 1602 数据线时高电平只有微弱上拉屏幕会显示乱码或直接黑屏。如果数据线不得不挂在 P0必须在总线上外接 10k 排阻上拉。保险做法是数据线放 P1P0 留给矩阵键盘或其他复用功能省掉一批焊接问题。LCD 工作方式上有人图省事把 RW 直接接地、跳过忙检查。仿真里这样能跑国产 1602 屏响应稍慢时就会丢字节。我建议保留忙检查虽然多几行代码但换屏、换线后出问题能快速定位。#define LCD_DB P1 // 数据口接 P1 sbit LCD_RS P2^6; sbit LCD_RW P2^7; sbit LCD_E P2^5; void LCD_WaitBusy(void) { unsigned char status; LCD_DB 0xFF; // 数据口全部置 1准备读状态 LCD_RS 0; LCD_RW 1; do { LCD_E 1; delay_us(2); status LCD_DB; LCD_E 0; } while(status 0x80); // D7 为 1 表示忙 LCD_RW 0; }LCD_WaitBusy读的是 1602 状态字节的最高位为 1 时内部正处理当前指令不能接受新命令。读状态期间 RS 必须拉低否则读到的是 DDRAM 数据而不是状态。这个函数放在每条写指令之前代价是每次操作多花几十微秒对电子秤这种低频刷新完全无所谓。2.3 供电、去耦与仿真里看不到的坑STC89C52 的 P2 口是准双向 IO输出高电平能力比 P0 强驱动 HX711 的 SCK 完全够用。但 HX711 的 DOUT 是漏极开路输出硬件上应当接一个 10k 上拉到 VCC。很多模块内部已经带了上拉直接连也正常如果发现读到的数据恒为 0xFFFFFF 或恒为 0优先检查这一路。仿真和实物差异最大的地方在地和电源。Proteus 里所有器件共地且零阻抗实物上电桥激励线如果和单片机电源线共用一根细线重量变化时 HX711 的参考点会跟着波动反映出来就是“空载数字慢慢爬”或者“加 100g 显示 98g、加 500g 显示 470g”。处理办法是传感器激励单独走线模拟地 AGND 和数字地 DGND 在电源入口单点汇合。这些在仿真阶段都不会暴露但早知道能省掉后面一大半调板时间。3. 电子秤核心程序HX711采样、线性标定与1602显示代码实现3.1 Keil C51 工程组织与三个文件的职责Keil C51 建工程时芯片型号直接选 AT89C52 或者 STC89C52两者寄存器定义完全兼容。工程里我习惯拆成三个文件lcd1602.c只管屏幕初始化、清屏、写字符hx711.c只暴露一个HX711_Read函数main.c做业务逻辑。这样后面积累新项目屏幕驱动和称重驱动都能直接复用。hx711.c里两个引脚定义放在头文件统一管理#ifndef __HX711_H__ #define __HX711_H__ #include reg52.h sbit HX711_DOUT P2^4; sbit HX711_SCK P2^5; long HX711_Read(void); #endif头文件里做引脚映射改板子时只改这里不必满工程找。3.2 HX711 读取代码中断保护、24 位补码与超时保护HX711 的通讯比 SPI 简单但是对时序敏感。数据手册的逻辑是DOUT 从高变为低表示一次转换完成之后 SCK 每产生一个脉冲DOUT 移出一位数据连续 24 个脉冲读完 24 位第 25 个脉冲用来选择下一次的增益A 通道 128 增益对应 25 个脉冲。long HX711_Read(void) { unsigned long value 0; unsigned char i; unsigned int timeout 0; EA 0; // 关中断防止串口打断位读取 HX711_SCK 0; HX711_DOUT 1; while(HX711_DOUT) // 等待 DOUT 拉低出现就绪信号 { if(timeout 60000) // 超过约几十毫秒判为异常 { EA 1; return 0; } } for(i 0; i 24; i) { HX711_SCK 1; // SCK 上升沿到来 value 1; if(HX711_DOUT) value | 1; // 读出 1 位 HX711_SCK 0; } HX711_SCK 1; // 第 25 个脉冲选择 A 通道 128 增益 HX711_SCK 0; EA 1; if(value 0x800000UL) // 24 位有符号数做符号扩展 value | 0xFF000000UL; return (long)value; }EA 0是这函数最关键的一行。飞行中如果串口中断或定时器中断插进来SCK 波形中间多出毛刺读出的数值会整体错位。数据手册对 SCK 高低电平只要求微秒级STC89C52 每条指令 1μs 左右自然满足。超时保护很重要传感器没接、DOUT 悬空时while会卡死整个程序加个计数器退出主程序至少还能显示错误码。返回值做了符号扩展。24 位补码直接塞进 32 位long高 8 位不会自动补 1。这里先判断第 23 位是否为 1是负数就补0xFF000000否则保持原样。3.3 主程序零点、标定系数与重量换算电子秤的测量模型就是一条直线重量(g) (当前读数 - 零点读数) / 标定系数标定系数单位是“码值/克”。每次上电先读一次空载值存进zero之后用标准砝码现场算出scale。代码里我把重量按 0.1g 为单位显示拆位时全部用整数运算避免单片机做浮点格式化输出。#include reg52.h #include lcd1602.h #include hx711.h long zero 0; float scale 430.0f; // 初始值实际以现场标定为准 sbit KEY_TARE P2^2; void ShowWeight(unsigned long w_10g) // 参数单位 0.1g { unsigned char k, b, s, g; k (w_10g / 1000) % 10; b (w_10g / 100) % 10; s (w_10g / 10) % 10; g w_10g % 10; LCD_SetPos(1, 0); LCD_Char(W); LCD_Char(); LCD_Char(0 k); LCD_Char(0 b); LCD_Char(0 s); LCD_Char(.); LCD_Char(0 g); LCD_Char(g); } void KeyScan(void) { if(!KEY_TARE) { delay_ms(20); // 软件消抖 if(!KEY_TARE) { zero HX711_Read(); // 当前状态作为新零点 while(!KEY_TARE); // 等待松手防止连续触发 } } } void main(void) { long raw; float weight; LCD_Init(); LCD_Puts(0, 0, STC89C52 Scale); delay_ms(10); zero HX711_Read(); // 开机空载标零 while(1) { raw HX711_Read(); weight (float)(raw - zero) / scale; if(weight 0.0f) weight 0.0f; // 去皮后负重量保护 ShowWeight((unsigned long)(weight * 10.0f 0.5f)); delay_ms(200); // 200ms 刷新一次屏幕 KeyScan(); } }raw - zero两个long相减正负方向由传感器受压方向决定。有的模块空载读数是正值加压后读数变大也有的接线方向相反加压后读数变小。统一在标定时确认差值是正数即可。ShowWeight里拆位用除法和取模显示范围到 9999.9g覆盖 5kg 量程转换时0.5f是做四舍五入避免出现99.9g变成100.0g时却显示100.0g之前先闪一下99.9g这类边界问题。3.4 1602 初始化唤醒序列与忙检查1602 的初始化在网上版本很多最容易踩的坑不是命令顺序错了而是没有给控制器留唤醒时间。HD44780 在上电后需要一个自检过程直接写 0x38 可能被忽略。正确做法是先给三次 0x30前两次之间延时超过 5ms第三次后进入 8 位总线模式再发功能设置指令。void LCD_Init(void) { delay_ms(15); // 上电等待 LCD_WriteCmd(0x30); delay_ms(5); LCD_WriteCmd(0x30); delay_ms(1); LCD_WriteCmd(0x30); LCD_WriteCmd(0x38); // 8 位数据、2 行、5x7 点阵 LCD_WriteCmd(0x0C); // 显示开、光标关 LCD_WriteCmd(0x01); // 清屏 LCD_WriteCmd(0x06); // 写入后地址加 1 }LCD_WriteCmd内部先调忙检查再写指令。仿真里 LM016L 响应快省略忙检查也能显示但实物换到慢速屏时字符就会乱。初始化这段代码在任何项目里都建议保留它是排查“屏不亮”的第一道分水岭复位后能亮、不显示内容基本就是初始化时序问题完全不亮先量电源和对比度。4. 用Proteus搭建电子秤仿真STC89C52工程配置与联调4.1 原理图中的元件选型与 HEX 加载Proteus 8 的库文件里不一定有 STC89C52 这个精确型号一般用 AT89C52 替代指令和引脚兼容。1602 用 LM016L这是 Proteus 自带模型接口和真实 HD44780 一致。放置后双击单片机在 Program File 里加载 Keil 编译出来的.hex晶振设 12MHz。AT89C52 在 Proteus 里默认接好电源但复位电路建议画上RST 引脚接 10μF 电容到 VCC、10kΩ 电阻到 GND。仿真里不画复位也能跑画上更接近实物习惯。另一个细节是 P0 口仿真模型对高电平的判定比实物宽松不接上拉也可能看起来正常实物就翻车。所以原理图里 P0 即使没用也别养成悬空习惯。4.2 没有 HX711 模型时的三种仿真替代方案Proteus 的官方库不保证每个版本都带 HX711直接拖一个传感器模型更不现实。常见做法是换一种方式验证软件逻辑而不是在仿真里复现 HX711 的 24 位精度。第一种用两个电位器模拟电桥差分输出接 ADC0809 或 ADC0832 进单片机。这个方案走的是“模拟量采集→数字换算→显示”的完整链路能验证主流程但 ADC0809 是 8 位代码里HX711_Read要改成读逐次逼近结果仿真通过后仍需把函数换回真实现。第二种用电压源直接接到 STC89C52 的一个 IO 口上模拟传感器读数变化。这种方式只验证按键去皮和显示刷新不具备采集链路意义。第三种是我最常用的在 Keil 工程里加一个编译宏仿真时把HX711_Read替换成一个返回键盘输入值的模拟函数。显示、去皮、标定这些代码一行不改通过矩阵键盘或者串口助手输入重量验证整套交互逻辑。等烧录到实物时只要把宏去掉、真实HX711_Read参与编译即可。#ifdef SIMULATION long HX711_Read(void) { return sim_weight_code; // 这个变量由键盘中断按 100g 步进修改 } #endif这个方案的优点是把“读传感器”和“处理重量”完全解耦仿真能跑说明标定公式、显示拆位、去皮逻辑没问题仿真通过后实物接上 HX711 仍不稳问题就集中在硬件时序和供电排查范围一下子缩小。4.3 联调顺序先显字符、再显数据、最后调去皮仿真联调最忌讳一上来就跑完整工程黑屏时不知道是 1602 初始化挂了还是 HX711 读卡住了。我先做三步先注释掉 HX711 相关代码main里只调LCD_Init然后循环写一串固定字符。这一步过不了后面所有逻辑都无法观察。屏幕满格方块时调 V0 对比度电位器仿真里把它拧到 1V~2V黑屏且没有光标检查 RS、RW、E 三根控制线是否接反。第二步把唯一一次zero HX711_Read()换成固定值例如zero 8388608主循环里给raw一个确定的数字比如9000000看显示是否等于(9000000-8388608)/scale的预期结果。这一步验证的是换算公式和拆位函数不依赖传感器。第三步再接真实读取函数并且把读取结果直接映射到 1602 的第二行做十六进制显示肉眼观察HX711_Read返回值是否随电位器变化。三步都过仿真才算真正通过。真实项目里很多人跳过第二步结果标定系数算错还以为是靠“调程序”能解决实际上公式项写反了。4.4 仿真与实物的差异核对表检查项Proteus 仿真表现实物板注意事项P0 口上拉悬空有时也正常P0 必须外接 10k 排阻1602 对比度LM016L 模型自适应V0 要调电位器多在 1V~3V按键抖动按下瞬间电平稳定必须消抖至少在代码里延时 20ms传感器信号无电磁干扰信号线用屏蔽线单点接地HX711 DOUT模型直接拉低漏极输出必要时加 10k 上拉供电纹波理想 5V激励电压波动会转成重量漂移电桥激励对电压敏感5V 激励下灵敏度 1mV/V如果激励电压跌到 4.9V满量程输出就少了 0.2%在 5kg 量程上是 10g 的误差。仿真里电源永远稳定实物的 USB 供电通常也够但长期项目建议用独立 5V 线性电源或者高精度 LDO。5. 电子秤精度的四个进阶细节标定系数、零点漂移、滤波与按键去皮5.1 标定系数计算的正确时序标定不是放一个砝码读个值就行。推荐两段式上电稳定 30 秒后空载状态下连续读 20 次HX711_Read取平均存为zero放 1000g 标准砝码同样读 20 次取平均得到raw_std则scale (float)(raw_std - zero) / 1000.0f标定后用 500g 砝码验证。如果显示 498g说明 1000g 点标定没问题但线性度一般检查机械结构是否有卡滞如果显示 500g 但在 100g 时偏 2g说明传感器在低量程段非线性明显这种往往是秤台预压力没调好。数据手册里桥式传感器的线性误差通常在满量程的 0.02%~0.05%非线性超过这个范围就先别怀疑软件。5.2 零点漂移与 STC89C52 内部 EEPROM 保存空载读数随时间缓慢漂移是桥式传感器常态主要来源是温度变化导致应变片阻值偏移。每次开机重新标零能解决大部分问题但使用时放上容器再按去皮软件里其实就是把zero临时覆盖成当前读数。注意去皮后如果移走容器重量显示会变成负数main里那句if(weight 0.0f) weight 0.0f;就是挡这个场景。标定好的zero和scale掉电会丢。常见做法是存到 STC89C52 的内部 EEPROMData Flash利用 ISP 下载时划分出的扇区或者用定时写入策略检测到去皮键长按时写一次避免频繁擦写。上电时先读 EEPROM读到有效标志就用存储值否则执行一次完整标定流程。5.3 滑动平均滤波与采样节奏HX711 的内部转换速率默认 10Hz一次转换约 100mswhile循环里 200ms 读一次并不会丢数据。重量数字最后一个字跳多半是机械振动或电源纹波被采进去了。最稳的软件方案是滑动平均#define FILTER_N 8 long filter_buf[FILTER_N] {0}; unsigned char filter_idx 0; long HX711_Filtered(void) { long sum 0; unsigned char i; filter_buf[filter_idx] HX711_Read(); filter_idx (filter_idx 1) % FILTER_N; for(i 0; i FILTER_N; i) sum filter_buf[i]; return sum / FILTER_N; }filter_buf初值必须全 0否则上电后前几秒读出的平均值是乱的。N 取 8 时配合 200ms 采样周期重量突变后约 1.6 秒才能稳定到新值装袋称重会觉得反应慢追求手感可以把 N 降到 3或者改用限幅滤波与上次差值超过 50g 直接信任新值小于 50g 做比例融合。电子秤不是越快越好显示器只刷新 5 次/秒过快的响应反而让人觉得读数飘。5.4 用逻辑分析仪验证 HX711 时序实物不读数据时别急着改代码先看波形。逻辑分析仪两个通道分别接 DOUT 和 SCK触发方式选 DOUT 下降沿能看到一次完整的传输过程DOUT 拉低后 SCK 出现 25 个脉冲第 24 个脉冲结束后数据线恢复高电平第 25 个脉冲用于锁存下次增益。重点确认三件事SCK 高电平和低电平宽度都不小于 1μs连续 24 个脉冲之间没有毛刺第 25 个脉冲之后 DOUT 确实回到高电平。毛刺多数来自中断。HX711_Read内已经用EA 0关全局中断但有些代码把定时器中断开着且中断函数里有延时操作EA 0也不能完全避免引脚被外部干扰。STC89C52 的 P2 口带内部上拉SCK 高电平被拉低的风险不大DOUT 数据线反而建议在靠近单片机一端加 10k 上拉让上升沿更陡减少采样点的误判。本文还有配套的精品资源点击获取