☰
HX711与51单片机驱动实战:从读数飘到精准称重
2026/10/14 17:40:09 网站建设 项目流程

简介:这份资源是面向51单片机初学者与电子制作爱好者的HX711压力传感器驱动代码包,用于搭建电子秤、配料称重等需要高精度重量检测的小型项目。包内共12个文件,约18KB,以Keil工程文件为主,包含uvproj工程配置、uvopt调试选项、c源码、h头文件、hex可烧录文件,以及编译过程产生的lst、m51、obj、lnp等中间文件,结构完整,可直接打开编译与烧录。代码围绕51单片机与HX711的通信展开,涵盖IO口初始化、增益与预设值设定、时钟脉冲读取DATA电平、滑动平均或低通滤波处理,以及串口或LCD显示输出等环节,并涉及电源接地、抗干扰布线与传感器校准等注意事项。目前已有4298人学习下载,适合想快速跑通称重模块、理解时序读取与滤波思路的读者参考借鉴。

1. 称重模块读数飘到怀疑人生?先搞懂 HX711 与 51 单片机的底层逻辑

做过电子秤、配料机或者简易检重台的朋友,大概率都碰过 HX711 这颗 24 位模数转换芯片。它便宜、外围简单、自带 PGA,配一个应变片桥式传感器就能读出重量。可真正把它接到 51 单片机上跑起来,很多人第一反应是:数值怎么一直在跳?空载时读数几百上千地飘,放上砝码又对不上克数。这不是玄学,是时序、供电和校准三件事没对齐。这份「hx711压力传感器51单片机代码」资源,核心就是给你一套能在经典 8051 平台上直接编译、烧录、跑通的驱动与标定流程,把「读得到」变成「读得准」。它适合正在做课程设计、小批量称重设备、或者第一次从 Arduino 转到 51 的嵌入式从业者。下面我按实际调试顺序,把原理、接线、代码、标定和翻车点一次讲透。

2. HX711 的通信时序与 51 单片机 IO 模拟:为什么不能直接用库

2.1 两线协议的本质:PD_SCK 与 DOUT 的配合节奏

HX711 没有 I2C、没有 SPI,只有两根线:PD_SCK(时钟输入)和 DOUT(数据输出)。它靠时钟脉冲数决定下一次转换的通道和增益。当 DOUT 从高变低,表示一次转换完成,此时给 PD_SCK 发 25 到 27 个脉冲,前 24 个上升沿依次移出 24 位数据(补码形式),第 25 个脉冲决定下一轮通道 A 增益 128,第 26 个选通道 B 增益 32,第 27 个选通道 A 增益 64。51 单片机没有硬件 SPI 能直接匹配这个「脉冲数即配置」的怪协议,所以必须用普通 IO 口手动翻转电平。常见做法是选两个准双向口,比如 P1.0 做 SCK、P1.1 做 DOUT,读之前先把 SCK 拉低,等 DOUT 变低再开始打脉冲。

这里有个容易被忽略的细节:51 的 IO 口作为输入前必须写 1,否则内部下拉管会一直把线拉低,DOUT 永远读不到高电平。很多新手代码里写DOUT = 1;再读,就是这个原因。另外,HX711 要求 SCK 高电平持续时间不小于 0.2 微秒,低电平也不小于 0.2 微秒,51 在 12MHz 晶振下一条指令约 1 微秒,不加延时反而可能太快导致数据错位。我一般会在 SCK 拉高后插一个_nop_(),拉低后再插一个,保证时序余量。

2.2 51 平台上的 IO 模拟代码骨架

下面这段是资源包里驱动文件的核心读取函数,我把它拆开讲清楚每一行的意图。代码基于 Keil C51 环境,晶振按 11.0592MHz 或 12MHz 都兼容。

#include <reg52.h> #include <intrins.h> sbit HX711_SCK = P1^0; // 时钟线,输出 sbit HX711_DOUT = P1^1; // 数据线,输入 // 读取一次 24 位原始值,并配置下一轮为通道A增益128 long HX711_Read(void) { long value = 0; unsigned char i; HX711_SCK = 0; // 空闲时时钟保持低电平 HX711_DOUT = 1; // 51 作为输入前先写1,释放总线 while(HX711_DOUT); // 等待 DOUT 变低,表示转换完成 for(i = 0; i < 24; i++) // 移出 24 位数据,MSB 在前 { HX711_SCK = 1; _nop_(); // 保证高电平宽度 value = value << 1; // 左移一位,准备接收新位 HX711_SCK = 0; _nop_(); // 保证低电平宽度 if(HX711_DOUT) value++; } // 第 25 个脉冲:选择下一轮通道A、增益128 HX711_SCK = 1; _nop_(); HX711_SCK = 0; _nop_(); // 24 位补码转成 32 位有符号数 if(value & 0x800000) value |= 0xFF000000; return value; }

逻辑说明:while(HX711_DOUT);是阻塞等待,实际项目里建议加超时计数,否则传感器没接好程序会死在这里。24 次循环里先拉高再读,是因为 HX711 在上升沿输出数据位,但 51 读端口需要一点建立时间,所以先拉高、空操作、再拉低,读的是拉低前的稳定值。最后那个补码扩展不能省,否则负值会变成巨大的正数,标定时直接翻车。

参数说明:通道和增益由第 25、26、27 个脉冲决定。资源里默认走通道 A 增益 128,对应满量程差分输入 ±20mV,适合大多数称重传感器。如果你用的是压力传感器输出信号较大,可以改成通道 B 增益 32,把第 25 个脉冲改成第 26 个即可,但注意通道 B 的稳定性通常不如 A。

2.3 硬件接线与供电去耦

接线本身不复杂:传感器四线(E+、E-、A+、A-)接 HX711 的对应端子,HX711 的 VCC 和 GND 接 51 开发板的 5V 和地,SCK、DOUT 接两个 IO。但供电质量直接决定读数噪声。HX711 内部有稳压和振荡器,对电源纹波敏感。我一般会在 HX711 的 VCC 和 GND 之间并一个 10uF 电解加一个 0.1uF 瓷片,位置尽量靠近芯片引脚。传感器激励电压 E+ 也是从 HX711 出来的,如果走线太长,压降会导致灵敏度下降,线长超过 30 厘米就考虑用屏蔽线并把屏蔽层单端接地。

还有一点:51 开发板上的 5V 往往来自 USB 或者 7805,如果同时驱动数码管或继电器,电流波动会串到 HX711 上。资源包里建议单独用一路 LDO 给 HX711 供电,或者至少在电源入口加磁珠隔离。这个细节在空载读数上体现最明显,不加去耦时最后几位可能跳几十个码,加了之后能压到个位数。

3. 从原始码到克数:标定流程与滤波参数怎么定

3.1 两点标定法的数学推导

HX711 读出来的是 24 位原始码,和实际重量是线性关系:重量 = (原始值 - 零点值) / 刻度系数。零点值就是空载时的稳定读数,刻度系数是放上已知砝码后(原始值 - 零点值) / 砝码重量。听起来简单,但实际操作里零点会随温度和时间漂移,所以资源包里用了「上电采集多次取平均作为零点」的策略。具体做法是上电后连续读 20 次,去掉最大最小各 5 个,剩下 10 个求平均。这样能避开上电瞬间的冲击和偶发干扰。

刻度系数建议用接近满量程的砝码来标,比如 5kg 传感器用 2kg 或 3kg 砝码,不要用 100g 小砝码,否则相对误差被放大。标定一次后把系数写进代码的常量里,或者存到 51 的 EEPROM(如果有)。资源包里提供了串口打印原始值的功能,方便你在标定时实时观察。

3.2 滑动平均与中值滤波的取舍

原始码即使稳定,最后几位也会跳。直接拿来算重量,显示会一直抖。资源包里给了两种滤波:滑动平均和中值滤波。滑动平均取最近 N 次读数的算术平均,N 越大越稳但响应越慢;中值滤波取最近 N 次的中位数,对突发尖峰抑制好但计算量大。51 的 RAM 和算力有限,我一般推荐 N=8 的滑动平均,配合每 100ms 读一次,显示刷新率够用,内存占用也小。

#define FILTER_N 8 long filter_buf[FILTER_N]; unsigned char filter_idx = 0; long Filter_Avg(long new_val) { long sum = 0; unsigned char i; filter_buf[filter_idx] = new_val; filter_idx = (filter_idx + 1) % FILTER_N; for(i = 0; i < FILTER_N; i++) sum += filter_buf[i]; return sum / FILTER_N; }

逻辑说明:环形缓冲区避免了大数组搬移,每次只替换最旧的一个值。filter_idx用取模回绕,51 上取模运算较慢,如果 FILTER_N 是 2 的幂,可以改成filter_idx = (filter_idx + 1) & (FILTER_N - 1);提速。参数说明:FILTER_N 越大越稳,但超过 16 后响应明显迟滞,配料场景建议 4 到 8,静态称重可以到 16。

3.3 串口输出与上位机观察

标定时光看数码管不够,资源包里用 UART 把原始值和换算后的重量一起打印出来,波特率 9600,格式是「raw:xxxxxx weight:xxxx g」。这样你能在电脑串口助手上看到跳变范围,判断是传感器问题还是代码问题。51 的串口初始化很简单,定时器 1 工作方式 2,TH1=TL1=0xFD 对应 9600(11.0592MHz 晶振)。注意发送前要等 TI 置位,发完清 TI,否则会丢字符。这个串口打印在正式产品里可以关掉,但调试阶段强烈建议保留。

4. 避坑与排查:HX711 接 51 最常见的五个翻车现场

4.1 读数一直是 0 或者 0xFFFFFF

现象:串口打印原始值恒为 0 或 -1,怎么放砝码都不变。原因通常是 DOUT 线没接好,或者 51 的 IO 口模式设错。51 的 P0 口作为输入需要外接上拉电阻,如果你把 DOUT 接在 P0 上又没加上拉,读回来永远是 0。解决:换到 P1、P2、P3 这类有内部上拉的准双向口,或者给 P0 加 10k 上拉排阻。另外检查 HX711 的 VCC 是否真的供上电,用万用表量一下。

4.2 数值乱跳,最后几位像随机数

现象:空载时原始值在几百个码范围内波动。原因有三:电源纹波大、SCK 时序太快、地线环路。先加去耦电容,再把 SCK 拉高拉低之间加_nop_()甚至两个。如果还跳,检查传感器屏蔽线是否单端接地,51 的地和 HX711 的地是否共点。我遇到过一块开发板,USB 供电时跳得厉害,换成电池供电立刻稳定,这就是地环路引入的工频干扰。

4.3 放上砝码数值反而变小

现象:加重物,原始值往负方向走。原因是传感器 A+ 和 A- 接反了,或者砝码放到了传感器受力反方向。解决:交换 A+ 和 A- 接线,或者在代码里把刻度系数取负。注意不要只改显示符号,原始值方向错了会影响后续滤波和判稳逻辑。

4.4 标定后小重量准,大重量偏差大

现象:用 100g 标定,测 1kg 时误差几十克。原因是传感器非线性或者量程不匹配。称重传感器在满量程 20% 到 80% 区间线性最好,用太小砝码标定等于把误差集中在小信号段。解决:换用接近满量程 50% 到 80% 的砝码重新标定,并在代码里做两点校准而不是单点。资源包里预留了分段校准的接口,可以存两个系数。

4.5 程序跑一会儿就死机

现象:上电正常,几分钟后读数不变,复位才好。原因是while(HX711_DOUT);死等,传感器线松了或者受干扰导致 DOUT 一直不拉低。解决:加超时计数,比如循环 10000 次还没等到就返回一个错误码,主循环里检测到错误就重新初始化 HX711。这个改动很小,但能避免现场设备「假死」。

5. 进阶技巧:用定时器中断做非阻塞采样与判稳输出

把采样放在主循环里轮询,遇到数码管动态扫描或者按键处理就会拉长采样间隔,滤波效果变差。更稳的做法是用定时器 0 产生 10ms 中断,在中断里做状态机:每 10 次中断(100ms)启动一次 HX711 读取,读完置标志位,主循环只负责换算和显示。这样采样节奏和显示刷新解耦,读数一致性明显提升。

volatile bit hx711_ready = 0; volatile unsigned char tick_cnt = 0; void Timer0_ISR(void) interrupt 1 { TH0 = 0xDC; // 10ms @ 11.0592MHz TL0 = 0x00; tick_cnt++; if(tick_cnt >= 10) // 每 100ms 采样一次 { tick_cnt = 0; hx711_ready = 1; } } // 主循环里 while(1) { if(hx711_ready) { hx711_ready = 0; raw = HX711_Read(); filtered = Filter_Avg(raw); weight = (filtered - zero_offset) / scale_factor; if(weight < 0) weight = 0; Display(weight); } Key_Scan(); }

逻辑说明:中断只负责计时和置标志,实际读取和滤波仍在主循环,避免中断里做长时序操作。参数说明:定时器初值按晶振频率算,11.0592MHz 下 10ms 对应 0xDC00,12MHz 下略有偏差,可以用TH0 = (65536 - 10000) / 256;自动计算。判稳输出是另一个实用技巧:连续 5 次采样值波动小于阈值(比如 5 个码)才认为重量稳定,此时再锁存显示或触发继电器,能有效防止配料过程中误动作。

从那以后我每次接 HX711 都强制走一遍「先看原始值、再加去耦、再标定、最后加判稳」的流程,不再一上来就调换算公式。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询