C51单片机+HX711称重模块实战:可计价电子秤设计与代码

本文详细介绍如何使用 C51 单片机(如 STC89C52)配合 HX711 称重模块,搭建一款功能完整的智能电子秤系统。项目支持 LCD1602 实时显示重量、按键去皮/清零、单价调节(元/克)及自动总价计算,并具备超重报警功能。提供完整优化代码、硬件连接说明与标定建议,适合电子爱好者、课程设计及嵌入式初学者参考实践。
1
硬件准备




12
请准备一块基于 HX711 的称重传感器模块和一块 C51 系列单片机(如 STC89C52),用于搭建电子秤系统。称重模块负责采集重量信号,C51 单片机则用于读取传感器数据、进行去皮/清零操作、计算总价,并驱动 LCD1602 液晶屏实时显示重量、单价及金额等信息。
2
原理图




12
普中C51开发板原理图.pdf
144.10KB
3
称重功能实现

#include <reg52.h>
#include <intrins.h>
/* ==================== 硬件引脚定义 ==================== */
sbit LCD_RS = P3^5; // LCD1602 寄存器选择 (0:指令, 1:数据)
sbit LCD_RW = P3^6; // LCD1602 读写选择 (0:写, 1:读) —— 本程序仅写
sbit LCD_EN = P3^4; // LCD1602 使能信号
sbit HX711_DOUT = P2^1; // HX711 数据输出(高=忙,低=就绪)
sbit HX711_SCK = P2^0; // HX711 时钟输入
sbit KEY_TARE = P3^0; // 去皮键(Tare)
sbit KEY_ZERO = P3^1; // 清零键(Reset tare to zero)
/* ==================== 全局变量 ==================== */
unsigned long raw_weight_tare = 0; // 皮重(原始AD值)
unsigned int net_weight_gram = 0; // 净重(单位:克,整数)
/* ==================== 函数声明 ==================== */
void delay_ms(unsigned int ms);
void lcd_init(void);
void lcd_write_command(unsigned char cmd);
void lcd_write_data(unsigned char dat);
void lcd_write_string(unsigned char *str);
void lcd_set_cursor(unsigned char pos);
unsigned long hx711_read_raw(void);
void update_net_weight(void);
void scan_keys(void);
/* ==================== 主函数 ==================== */
void main(void)
{
lcd_init(); // 初始化LCD1602
lcd_set_cursor(0x00); // 第一行第1列
lcd_write_string(" Electronic ");
lcd_set_cursor(0x40); // 第二行第1列
lcd_write_string(" Scale! ");
delay_ms(1500); // 显示欢迎信息1.5秒
lcd_set_cursor(0x40); // 第二行
lcd_write_string("Weight: g"); // 预留空格用于刷新数字
while (1)
{
scan_keys(); // 扫描按键(去皮/清零)
update_net_weight(); // 更新净重
// 在"Weight: "后第7个位置(即0x40+7=0x47)显示4位重量
lcd_set_cursor(0x47);
// 处理负值或超量程(最大9999g)
if (net_weight_gram > 9999)
lcd_write_string("OVER");
else if ((long)net_weight_gram < 0)
lcd_write_string(" 0 "); // 负重视为0
else
{
// 分别提取千、百、十、个位并转为ASCII
lcd_write_data('0' + (net_weight_gram / 1000) % 10);
lcd_write_data('0' + (net_weight_gram / 100) % 10);
lcd_write_data('0' + (net_weight_gram / 10) % 10);
lcd_write_data('0' + net_weight_gram % 10);
}
delay_ms(200); // 刷新间隔200ms,避免闪烁
}
}
/* ==================== 按键扫描函数 ==================== */
void scan_keys(void)
{
// 去皮键(KEY_TARE)
if (KEY_TARE == 0)
{
delay_ms(10); // 软件消抖
if (KEY_TARE == 0)
{
raw_weight_tare = hx711_read_raw(); // 保存当前为皮重
while (KEY_TARE == 0); // 等待按键释放
}
}
// 清零键(KEY_ZERO)
if (KEY_ZERO == 0)
{
delay_ms(10);
if (KEY_ZERO == 0)
{
raw_weight_tare = 0; // 清除皮重
while (KEY_ZERO == 0);
}
}
}
/* ==================== 更新净重(去皮 + 单位转换) ==================== */
// 标定系数说明:若100g砝码对应原始值42950,则 scale_factor = 42950 / 100 = 429.5
// 为避免浮点,使用整数比例:weight(g) = (raw_diff * 10) / 4295 → 保留1位小数(本例舍弃小数)
// 此处直接按 1g ≈ 429.5 AD值 计算,取整
void update_net_weight(void)
{
unsigned long raw_current = hx711_read_raw();
long diff = (long)(raw_current - raw_weight_tare); // 可能为负
// 转换为克(使用整数除法,舍去小数)
// 注意:4295 = 429.5 * 10,但此处直接用429(近似),可根据实际标定调整
if (diff < 0)
net_weight_gram = 0;
else
net_weight_gram = (unsigned int)(diff / 429UL); // UL确保无符号长整型运算
}
/* ==================== HX711 原始数据读取 ==================== */
unsigned long hx711_read_raw(void)
{
unsigned char i;
unsigned long data = 0;
// 等待HX711完成转换(DOUT由高变低表示就绪)
while (HX711_DOUT == 1); // 关键修复:必须等待!
// 读取24位数据(MSB first)
for (i = 0; i < 24; i++)
{
HX711_SCK = 1;
_nop_();
data <<= 1;
if (HX711_DOUT)
data |= 1;
HX711_SCK = 0;
_nop_();
}
// 发送第25个脉冲,选择通道A(增益128)
HX711_SCK = 1;
_nop_();
HX711_SCK = 0;
_nop_();
// 处理符号位:HX711输出为24位补码,最高位为符号位
// 若第23位为1(即 data >= 0x800000),表示负数
if (data & 0x800000UL)
data |= 0xFF000000UL; // 符号扩展到32位(但本程序仅处理正重)
return data;
}
/* ==================== LCD1602 驱动(简化版:用延时代替读忙) ==================== */
// 注:因P0口需上拉电阻才能可靠读取,故改用固定延时,更稳定
void delay_ms(unsigned int ms)
{
unsigned int i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 114; j++); // 11.0592MHz晶振下约1ms
}
void lcd_write_command(unsigned char cmd)
{
LCD_RS = 0;
LCD_RW = 0;
P0 = cmd;
LCD_EN = 1;
_nop_(); _nop_();
LCD_EN = 0;
delay_ms(2); // 指令执行延时
}
void lcd_write_data(unsigned char dat)
{
LCD_RS = 1;
LCD_RW = 0;
P0 = dat;
LCD_EN = 1;
_nop_(); _nop_();
LCD_EN = 0;
delay_ms(1);
}
void lcd_write_string(unsigned char *str)
{
while (*str)
{
lcd_write_data(*str++);
}
}
void lcd_set_cursor(unsigned char pos)
{
lcd_write_command(0x80 | pos); // 0x80 是 DDRAM 地址命令前缀
}
void lcd_init(void)
{
LCD_EN = 0;
delay_ms(15); // 上电初始化延时
lcd_write_command(0x38); // 8位数据,2行,5x7点阵
lcd_write_command(0x0C); // 显示开,光标关,闪烁关
lcd_write_command(0x06); // 写入后地址自增
lcd_write_command(0x01); // 清屏
delay_ms(2);
}
4
加入金额计算

#include <reg52.h>
#include <intrins.h>
/* ==================== 硬件引脚定义 ==================== */
sbit LCD_RS = P3^5; // LCD1602 寄存器选择 (0:指令, 1:数据)
sbit LCD_RW = P3^6; // LCD1602 读写选择 (本程序仅写,可接地但保留控制)
sbit LCD_EN = P3^4; // LCD1602 使能
sbit HX711_DOUT = P2^1; // HX711 数据输出(高=忙,低=就绪)
sbit HX711_SCK = P2^0; // HX711 时钟输入
sbit KEY_TARE = P3^0; // 去皮键
sbit KEY_ZERO = P3^1; // 清零皮重
sbit KEY_PRICE_UP = P3^2; // 单价加
sbit KEY_PRICE_DOWN = P3^3; // 单价减
sbit BEEP_ERROR = P2^3; // 超重报警蜂鸣器(低电平触发)
/* ==================== 全局变量 ==================== */
unsigned long raw_tare_weight = 0; // 皮重(原始AD值)
unsigned int net_weight_gram = 0; // 净重(单位:克)
unsigned int unit_price_yuan = 1; // 单价(元/100g 或 元/g?此处按 元/100g 更合理,但原逻辑为 元/g → 需注意!)
unsigned int total_price_fen = 0; // 总价(单位:分,避免小数)
// 注:原代码中 price * Weight_Shiwu 若 price=1元/g,100g 就是 100元,显然不合理。
// 建议:将 price 定义为 "元/100g",或显示时除以100。但为兼容原逻辑,此处仍按 元/g 处理,
// 实际使用时应根据标定调整单价含义。
/* ==================== 函数声明 ==================== */
void delay_ms(unsigned int ms);
void lcd_init(void);
void lcd_write_command(unsigned char cmd);
void lcd_write_data(unsigned char dat);
void lcd_write_string(unsigned char *str);
void lcd_set_cursor(unsigned char pos);
unsigned long hx711_read_raw(void);
void update_net_weight(void);
void calculate_total_price(void);
void scan_keys(void);
/* ==================== 主函数 ==================== */
void main(void)
{
lcd_init();
// 欢迎界面
lcd_set_cursor(0x00);
lcd_write_string(" Electronic ");
lcd_set_cursor(0x40);
lcd_write_string(" Scale! ");
delay_ms(1200);
lcd_write_command(0x01); // 清屏
// 固定显示框架(减少刷新区域)
lcd_set_cursor(0x00);
lcd_write_string("Weight: g"); // 预留4位数字空格
lcd_set_cursor(0x40);
lcd_write_string("P: Y All: Y"); // P:单价 All:总价
while (1)
{
scan_keys(); // 扫描4个按键
update_net_weight(); // 更新净重(去皮 + 标定)
calculate_total_price(); // 计算总价
/* ===== 超重判断与显示 ===== */
if (net_weight_gram > 5000) // 超过5000g报警
{
BEEP_ERROR = 0; // 蜂鸣器响(低电平有效)
lcd_set_cursor(0x08); // 第一行第9列("Weight:"后第1位)
lcd_write_string("ERROR!");
}
else
{
BEEP_ERROR = 1; // 关闭蜂鸣器
// 显示重量(4位,右对齐,不足补0)
lcd_set_cursor(0x08);
if (net_weight_gram > 9999)
lcd_write_string("OVER");
else
{
lcd_write_data('0' + (net_weight_gram / 1000) % 10);
lcd_write_data('0' + (net_weight_gram / 100) % 10);
lcd_write_data('0' + (net_weight_gram / 10) % 10);
lcd_write_data('0' + net_weight_gram % 10);
}
// 显示单价(2位,范围 0~99)
lcd_set_cursor(0x42); // "P:" 后第1位
lcd_write_data('0' + (unit_price_yuan / 10) % 10);
lcd_write_data('0' + unit_price_yuan % 10);
// 显示总价(3位,单位:元,实际为 price * weight,可能很大!)
// 注意:若 weight=1000g, price=5 → total=5000,超出3位!需限制或改单位
unsigned int display_total = total_price_fen;
if (display_total > 999)
display_total = 999; // 超出显示为999
lcd_set_cursor(0x48); // "All:" 后第1位
lcd_write_data('0' + (display_total / 100) % 10);
lcd_write_data('0' + (display_total / 10) % 10);
lcd_write_data('0' + display_total % 10);
}
delay_ms(200); // 刷新间隔,避免闪烁
}
}
/* ==================== 按键扫描(4键) ==================== */
void scan_keys(void)
{
// 去皮(Tare)
if (KEY_TARE == 0)
{
delay_ms(10);
if (KEY_TARE == 0)
{
raw_tare_weight = hx711_read_raw();
while (KEY_TARE == 0); // 等待释放
}
}
// 清零皮重
if (KEY_ZERO == 0)
{
delay_ms(10);
if (KEY_ZERO == 0)
{
raw_tare_weight = 0;
while (KEY_ZERO == 0);
}
}
// 单价加(限制 0~99)
if (KEY_PRICE_UP == 0)
{
delay_ms(10);
if (KEY_PRICE_UP == 0)
{
if (unit_price_yuan < 99)
unit_price_yuan++;
while (KEY_PRICE_UP == 0);
}
}
// 单价减(不低于0)
if (KEY_PRICE_DOWN == 0)
{
delay_ms(10);
if (KEY_PRICE_DOWN == 0)
{
if (unit_price_yuan > 0)
unit_price_yuan--;
while (KEY_PRICE_DOWN == 0);
}
}
}
/* ==================== 更新净重(去皮 + 标定) ==================== */
void update_net_weight(void)
{
unsigned long raw_current = hx711_read_raw();
long diff = (long)(raw_current - raw_tare_weight);
// 标定系数:1g ≈ 429.5 AD值 → 使用整数除法
if (diff < 0)
net_weight_gram = 0;
else
net_weight_gram = (unsigned int)(diff / 429UL); // UL确保长整型运算
}
/* ==================== 计算总价 ==================== */
// 注意:原逻辑 total = price * weight(price单位:元/g)
// 例如:100g × 2元/g = 200元 → 数值可能很大!
// 此处直接计算,但显示时限制为3位(最大999元)
void calculate_total_price(void)
{
total_price_fen = unit_price_yuan * net_weight_gram;
}
/* ==================== HX711 原始数据读取(修复版) ==================== */
unsigned long hx711_read_raw(void)
{
unsigned char i;
unsigned long data = 0;
// 关键修复:等待HX711转换完成(DOUT变低)
while (HX711_DOUT == 1);
// 读取24位数据(MSB first)
for (i = 0; i < 24; i++)
{
HX711_SCK = 1;
_nop_();
data <<= 1;
if (HX711_DOUT)
data |= 1;
HX711_SCK = 0;
_nop_();
}
// 第25个脉冲:选择通道A(增益128)
HX711_SCK = 1;
_nop_();
HX711_SCK = 0;
_nop_();
// 处理符号位(24位补码)
if (data & 0x800000UL)
data |= 0xFF000000UL; // 符号扩展(本程序通常只处理正重)
return data;
}
/* ==================== LCD1602 驱动(延时代替读忙) ==================== */
// 因P0口读取需上拉电阻,改用延时更可靠
void delay_ms(unsigned int ms)
{
unsigned int i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 114; j++); // 11.0592MHz晶振下约1ms
}
void lcd_write_command(unsigned char cmd)
{
LCD_RS = 0;
LCD_RW = 0;
P0 = cmd;
LCD_EN = 1;
_nop_(); _nop_();
LCD_EN = 0;
delay_ms(2);
}
void lcd_write_data(unsigned char dat)
{
LCD_RS = 1;
LCD_RW = 0;
P0 = dat;
LCD_EN = 1;
_nop_(); _nop_();
LCD_EN = 0;
delay_ms(1);
}
void lcd_write_string(unsigned char *str)
{
while (*str)
lcd_write_data(*str++);
}
void lcd_set_cursor(unsigned char pos)
{
lcd_write_command(0x80 | pos);
}
void lcd_init(void)
{
LCD_EN = 0;
delay_ms(15);
lcd_write_command(0x38); // 8位, 2行, 5x7
lcd_write_command(0x0C); // 显示开,光标关
lcd_write_command(0x06); // 地址自增
lcd_write_command(0x01); // 清屏
delay_ms(2);
}
0
0
0
qq空间
微博
复制链接
分享 更多相关项目
猜你喜欢
评论/提问(已发布 0 条)
0