1. 引言
这是一套基于STC89C52(REGX52.H)的智能称重分拣系统,核心硬件包括:
- HX711:24 位高精度 A/D 转换芯片,负责读取称重传感器(电阻应变式)的原始数据;
- DS1302:实时时钟芯片,用于记录每次称重的日期与时间;
- AT24C02:I²C 接口的 EEPROM,用于断电保存累计合格数、次品数、标准重量、误差阈值等关键参数;
- LCD1602:两行 16 字符液晶屏,用于人机交互显示;
- 蜂鸣器 + LED:超重/异常报警输出。
本文从代码实现角度,逐段讲解这段代码的工作原理、数据流向与各模块的协作方式,帮助你彻底读懂这套称重分拣系统的每一行代码。文末附有函数职责总览表与状态机流程图,方便你快速把握全局。
2. 系统整体架构与模块划分
2.1 硬件连接概览
| 外设模块 | 接口类型 | 与单片机连接 | 作用 |
|---|---|---|---|
| HX711 | 自定义 GPIO 时序 | P 口(SCK/DOUT) | 读取称重传感器原始值 |
| DS1302 | 三线串行 | RST/SCLK/IO | 提供实时时钟 |
| AT24C02 | I²C | SCL/SDA | 掉电保存统计数据 |
| LCD1602 | 并行 8 位 | P0/P2 等 | 显示模式、重量、时间 |
| 蜂鸣器 | GPIO 输出 | 任意 I/O | 超重报警 |
| LED | GPIO 输出 | P2.4 | 超重指示 |
2.2 软件模块划分
3. 代码逐段精讲
3.1 全局变量与宏定义
sbit LED=P2^4;unsignedintWeight_Maopi=0,Range=1000,qupi,num;longWeight_Shiwu=0;unsignedcharnum_Store,Weight_Store_Num=0,mode,m4;unsignedcharERROR1,Bad_Num1,Keynum,TimeSetSelect=7,TimeSetFlashFlag;unsignedcharWeight_Shiwu_Flag1,num_Flag,Weight_Shiwu_Flag2;unsignedintWeight_Standard1,FanWei_Max,FanWei_Min;intWeight_Standard=200;chartemp,m3,ERROR,Bad_Num,sum,Month,Day,Hour,Minute,m3_Flag;unsignedchari,Weight_Flag,KeynumFlag1,k8,m3_qingling;floatT;#defineGapValue426.04讲解要点:
Weight_Maopi(毛皮重量)与qupi(去皮值)是两个容易混淆的变量:前者是 HX711 原始读数,后者是去皮后的偏移量,二者在Get_Weight()中分别承担「零点偏移」与「去皮」两个不同阶段的补偿。Weight_Shiwu用long是因为 HX711 原始值可达 24 位有符号数,unsigned int(16 位)会溢出。GapValue是标定系数,注释已说明不同传感器需单独校准——它决定了原始 A/D 值到克数的换算比例,是称重系统能否准确的关键参数。### 3.2 去皮函数Get_Maopi()
voidGet_Maopi(){qupi=Weight_Shiwu;}逻辑分析:该函数把当前净重存入qupi作为去皮基准。它依赖Weight_Shiwu已被Get_Weight()更新过,因此调用顺序很关键——必须先执行Get_Weight()得到当前重量,再调用Get_Maopi()记录去皮值,后续称重时Get_Weight()会减去该值得到净重。
3.3 重量读取与超重判断Get_Weight()
voidGet_Weight(){Weight_Shiwu=HX711_Read();Weight_Shiwu=Weight_Shiwu-Weight_Maopi;Weight_Shiwu=(unsignedint)((float)(Weight_Shiwu)/GapValue)-qupi;Weight_Shiwu-=18600;if(Weight_Shiwu<=0||Weight_Shiwu>5000){Weight_Shiwu=0;}if(Weight_Shiwu>0&&Weight_Shiwu<=5000){if(Weight_Shiwu>Range&&mode==2){Buzzer_Time(100);LED=0;}elseif(mode==2&&Weight_Shiwu<=Range){LED=1;}}}逐行讲解:
| 代码行 | 作用 | 潜在问题 |
|---|---|---|
HX711_Read() | 读取 24 位原始 A/D 值 | 需确认返回类型是有符号还是无符号 |
- Weight_Maopi | 减去毛重(零点偏移) | Weight_Maopi初始为 0,首次需先标定 |
(float)/GapValue | 原始值 → 克数换算 | GapValue需按传感器实测标定 |
- qupi | 去皮偏移 | 与Weight_Maopi功能重叠,易混淆 |
-= 18600 | 经验补偿值 | 硬编码魔法数,建议改为宏定义并注释来源 |
>5000归零 | 超量程保护 | 量程上限写死,建议用宏MAX_WEIGHT |
关键点说明:
18600是经验补偿值,用于修正传感器零点漂移,属于硬编码常量,理解时需结合具体传感器特性;- 量程上限
5000是超量程保护阈值,超过该值重量被强制归零,避免异常读数; - 超重判断仅在
mode==2(称重模式)下生效,其他模式不触发报警。### 3.4 时间设置与显示TimeSet()/Timeshow()
TimeSet()是状态机式的按键处理:
Keynum==1:切换当前编辑的时间字段(年→月→日→时→分→秒循环);Keynum==5:当前字段加 1,并做边界回绕(如月份 1–12、日期按大小月/闰年判断);Keynum==6:当前字段减 1,同样做边界回绕。
亮点:闰年判断(DS1302_Time[0]%4==0)已实现,逻辑完整。
实现细节:
- 大小月判断通过查表或分支实现,日期边界按月份天数回绕;
- 按键处理为单次触发,未做消抖与长按加速。### 3.5 数据存储
AT24C02_*系列
voidAT24C02_Writebyte_Data(){AT24C02_Writebyte(0,sum);Delay(5);AT24C02_Writebyte(1,Bad_Num);Delay(5);// ... 共 10 个字节}存储布局表:
| EEPROM 地址 | 存储内容 | 数据类型 | 说明 |
|---|---|---|---|
| 0x00 | sum | unsigned char | 累计合格数 |
| 0x01 | Bad_Num | unsigned char | 累计次品数 |
| 0x02–0x03 | Weight_Standard | int(低/高字节) | 标准重量 |
| 0x04 | ERROR | unsigned char | 误差阈值 |
| 0x05–0x08 | 月/日/时/分 | unsigned char | 最后称重时间 |
| 0x09 | m3_Flag | unsigned char | 模式 3 标志 |
写入机制说明:
- 每次写 10 个字节都带
Delay(5),可改为「先写后校验读回」; - 建议增加**校验和(checksum)**字节,防止掉电写入不完整导致数据损坏;
AT24C02_Writebyte_qingling_data()与AT24C02_Writebyte_qingling()功能几乎重复,可合并为一个函数并加参数。
3.6 显示函数族Display_*
| 函数名 | 显示内容 | 适用模式 |
|---|---|---|
Display_M1() | MODE 1 + 时间占位 | 模式 1 |
Display_M2() | MODE 2 MAX + 当前重量 | 模式 2 |
Display_M3() | MODE 3 ERROR + 标准重量 | 模式 3 设置 |
Display_M3_Measure() | MODE 3 NUM + 当前重量 | 模式 3 测量 |
Display_M3_Store() | “save data …” | 模式 3 保存 |
Display_M4_Standard() | Weight + ERROR | 模式 4 查看标准 |
Display_M4_Num() | SUM + BadNum | 模式 4 统计 |
Display_M4_Time() | 最后称重时间 | 模式 4 时间 |
Display_M4_Send() | “SEND DATA ?” | 模式 4 确认发送 |
Display_M4_successful() | “send successfully” | 模式 4 发送成功 |
设计评价:每个显示函数职责单一、命名清晰,是这段代码中结构最规范的部分,值得保持。
6. 案例补充:一个完整的称重分拣场景
场景:某小型电子厂需要对电阻进行重量分拣,标准重量 200g,误差 ±5g。
操作流程:
- 上电→ 系统自动读取 AT24C02 中保存的标准重量 200g、误差 5g;
- 模式 2 称重→ 放上电阻,LCD 显示实时重量;
- 超重判断→ 若 >205g,蜂鸣器报警、LED 亮,判定为次品;
- 模式 3 批量测量→ 连续测量 10 个,自动统计合格/次品数;
- 模式 4 查看统计→ 显示 SUM=8、BadNum=2,并通过串口发送到上位机;
- 掉电保护→ 数据自动存入 AT24C02,下次上电可恢复。
串口输出示例:
last time: 10-11 20:27 last standard: error: 5 weight: 0200 last data: sum: 008 bad: 0027. 函数职责总览表
| 函数名 | 所属模块 | 职责 | 依赖 |
|---|---|---|---|
Get_Maopi() | 称重 | 记录去皮值 | Weight_Shiwu |
Get_Weight() | 称重 | 读取并换算重量、超重报警 | HX711、蜂鸣器、LED |
Timeshow() | 时间 | 读取 DS1302 并刷新 LCD | DS1302、LCD |
TimeSet() | 时间 | 按键设置时间(含闰年判断) | 按键、DS1302 |
AT24C02_Readbyte_Data() | 存储 | 从 EEPROM 读回全部参数 | AT24C02 |
AT24C02_Writebyte_Data() | 存储 | 保存全部参数到 EEPROM | AT24C02 |
Display_M1~M4_*() | 显示 | 各模式界面刷新 | LCD1602 |
UART_Send_num() | 通信 | 串口发送统计结果 | UART |
8. 总结
这段代码功能完整、模块划分清晰,尤其是显示函数族和 AT24C02 存储设计值得肯定。主要改进空间集中在:
- 消除魔法数,用宏定义替代硬编码;
- 收敛全局变量,用结构体或状态机封装;
- 补齐边界处理(按键消抖、变量类型、初始化);
- 增加数据校验,提升掉电可靠性。
如果你需要,我可以进一步:把TimeSet()重构为查表法、为Get_Weight()增加数字滤波(滑动平均)、或补充完整的main()主循环框架。