ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

HX711与51单片机称重方案:从硬件到标定的完整实战

HX711与51单片机称重方案:从硬件到标定的完整实战 简介资源是hx711压力传感器搭配51单片机的完整驱动代码与工程文件面向电子秤、配料系统等需要高精度重量检测的单片机开发者和入门学习者。压缩包共12个文件包含C源文件、头文件、Keil工程uvproj/uvopt、编译生成的hex烧录文件以及lst、m51等中间文件整体仅18KB结构精简、易于直接复用。目前已有4294人学习下载。代码实现了传感器与单片机的时序通信、数据读取、滑动平均滤波和重量换算输出等核心环节并给出IO配置与增益设置的参考写法通过研究main.c和hx711.c可掌握从传感器原始信号到稳定显示值的处理思路还能利用hex文件快速验证运行效果适合用于称重计、料斗秤等项目的二次开发与学习调试。 做了几年嵌入式称重、测力这类需求几乎年年都会碰到。小家电要加压力检测工业设备要做张力控制实验室的测力装置、物流称重台绕来绕去都离不开一颗HX711和一块51单片机。今天这篇就把这套方案从硬件到代码再到校准完完整整过一遍代码部分直接给能用的版本标定思路和常见坑也会一起讲清楚。刚接触压力传感器采集的新手可以照着抄想快速出方案的老手也能拿来当模板。1. 选型逻辑HX711这类ADC比普通ADC强在哪1.1 为什么称重方案喜欢用HX711先说结论HX711是专为桥式传感器设计的24位高精度ADC芯片内部集成了低噪声可编程放大器PGA增益可选32倍、64倍、128倍还带稳压输出可以直接给应变片电桥供电。它把“微小信号放大”和“高分辨率采集”两件事合成一颗芯片外围电路简单到令人发指。称重传感器输出的信号有多小以最常见的5kg量程、灵敏度1.0mV/V的悬臂梁传感器为例桥路激励电压按4.3V算满量程差分输出只有4.3mV。如果你用普通单片机内置的10位ADC参考电压5V一个LSB大约是4.88mV也就是说满量程信号只占1个LSB左右根本测不出来。就算用12位ADC理论分辨率也就1.22mV一个字实际噪声下去依旧惨不忍睹。HX711打开128倍增益后4.3mV的信号被放大到约550mV再交给24位ADC去量化参考电压取4.3V时满量程对应数字量约107万。如果想达到1g分辨能力一整个量程只需要分出5000份就行107万的有效分辨率绰绰有余。换句话说HX711把原本需要“仪表放大器高分辨率ADC精密基准源”一堆昂贵器件才能做到的事用一颗几块钱的芯片就完成了。1.2 51单片机到底带不带动这颗芯片很多朋友一听到“24位ADC”就担心51单片机性能不够这里其实有个误区。HX711走的是串行数据输出DOUT引脚拉低表示转换完成PD_SCK引脚上给脉冲数据就一位一位移出来本质上就是一个“带中断标志的移位寄存器”。51单片机用两个普通GPIO就能模拟这套时序完全不需要硬件SPI、I2C这类外设。更何况HX711默认输出速率只有10Hz每次读取24位数据只需要24个脉冲51单片机执行一条IO翻转指令也就1到2个机器周期12MHz晶振下约1到2微秒读一次数据撑死几十微秒。哪怕把滤波、去毛刺、标定全算进去对CPU的占用率也不到1%。所以这套方案真正契合的一点是HX711把模拟部分全部包办51单片机只需要跑逻辑和显示。你可以用STC89C52、STC12C5A60S2、AT89S52甚至老掉牙的80C51都行编译工具用Keil C51代码写出来兼容性极好。后续如果要升级到STM32HX711模块和驱动思路几乎可以原封不动搬过去这也是这套方案生命周期长的原因。2. 硬件连接与电路设计要点2.1 模块引脚与单片机接线市面上常见的HX711模块分为两种一种是裸芯片加最小电路引出A/A-、B/B-、DOUT、PD_SCK等引脚另一种是“称重模块”直接把传感器接线端子和电源做在板子上比如红色PCBA那种。无论哪种和51单片机连接的部分其实就四根线VCC、GND、DOUT、PD_SCK。我习惯的接线方式用一个表格列清楚HX711模块引脚51单片机引脚说明VCC5V电源模块工作电压5V供电最稳GNDGND共地DOUTP1.0数据输出转换完成时由高变低PD_SCKP1.1时钟输入上升沿移出数据RATEGND默认接地选择10Hz输出速率E/E-传感器激励给应变片电桥供电A/A-传感器信号差分信号输入这里有个细节RATE引脚接GND是10Hz接VCC是80Hz。称重场景强烈建议用10Hz一是数据稳定二是ADC内部的数字滤波器在10Hz模式下对50Hz工频干扰抑制更好。如果你用80Hz模式测动态力读数会明显跳得更厉害需要更强的软件滤波配合。2.2 电源、布线与去耦的实操经验HX711对电源纹波相当敏感这是新手翻车最多的地方之一。芯片内部虽然有稳压但如果你用开关电源给整个系统供电而电容又离芯片很远读数就会莫名其妙地来回跳。实测下来最稳的做法是在HX711的VCC和GND之间靠近芯片位置加一个100nF瓷片电容和一个10uF电解电容两个电容并联高频低频一起滤。如果模块上已经自带电容也要确认一下位置是否合理。很多便宜模块上就一颗104我自己做板子时会额外在AVDD输出脚再接一颗10uF钽电容效果立竿见影。另外传感器到模块之间的连线尽量短最好用屏蔽线屏蔽层单端接地。应变片桥路输出的是毫伏级差分信号长线裸奔等于一根天线工频干扰、电机干扰全收进来了。实验室环境还没什么一旦放到生产现场旁边有空压机、变频器、伺服电机你就知道屏蔽线有多重要了。3. 51单片机代码实现与逐段解读3.1 通信时序和前两个关键判断HX711的串行通信协议不复杂但有两处必须理解到位否则代码就是抄了个表面第一DOUT拉低表示一次转换完成数据准备好了我们读数据之前要先等DOUT变低。转换期间DOUT是高电平如果程序不管三七二十一直接开始读读出来的就是上一次的残留数据。第二PD_SCK每来一个上升沿DOUT线上就移出一位数据高位在前总共需要24个脉冲移出24位数据。第25个脉冲的作用是让DOUT重新变高同时启动下一次转换。时序不能搞反PD_SCK的脉冲宽度也不能太短。虽然手册上要求的只是微秒级别但我在51这种靠软件翻转IO的环境里一般不做什么延时直接两条语句翻转速度也不会超限。如果你用非常快的ARM单片机反而要注意加一点延时别让脉冲宽度小于手册要求的时序。3.2 读取原始值的核心函数先给一个我实际在用的读取函数注意这个版本做了符号扩展后面我会解释为什么必须这么做sbit HX711_DOUT P1^0; sbit HX711_SCK P1^1; long HX711_Read(void) { unsigned long raw 0; unsigned char i; // 等待数据就绪DOUT从高变低 if(HX711_DOUT) return last_value; // 超时保护直接返回上次结果 for(i 0; i 24; i) { HX711_SCK 1; raw 1; if(HX711_DOUT) raw | 1; HX711_SCK 0; } // 第25个脉冲DOUT恢复高电平启动下一次转换 HX711_SCK 1; raw 0xFFFFFF; HX711_SCK 0; // 24位二进制补码做符号扩展变成32位有符号数 if(raw 0x800000) raw | 0xFF000000; last_value (long)raw; return last_value; }先说数据类型的问题。标准51的int只有16位最大只能表示32767而HX711输出的24位数据范围是-8388608到8388607所以读取结果的变量必须声明为unsigned long或者long这是个特别容易踩的坑。很多人把数据存进int变量里结果重量还没加到一半就溢出了数字瞬间变成负数。再说符号扩展。HX711输出的24位数据是二进制补码最高位是符号位。如果你只做正向称重传感器上一直只有压力那补码的最高位始终是0怎么处理都行。但实际应用中传感器空载时会有一个“零点偏置”这个偏置可能是正也可能是负去皮之后更是经常出现负的AD差值。如果不做符号扩展一个负的24位补码会被当成一个巨大无比的正数后面的去皮、求平均、算重量全乱套。3.3 滤波处理让数据不再狂跳HX711读出来的原始数据在没有任何滤波的情况下通常会有几十个字的随机跳动。10Hz模式下还稍微好点如果用了80Hz模式跳动上百个字都很正常。所以软件滤波是必须的。我推荐的做法是“去极值平均滤波”连续读5次去掉一个最大值和一个最小值剩下3次求平均。这种滤波方式对尖峰干扰的抵抗能力比单纯滑动平均强而且运算量也不大。51单片机没有硬件除法但除以3也可以用乘除近似处理或者直接用浮点反正每秒只算几次性能完全扛得住。实现代码如下#define FILTER_NUM 5 unsigned long HX711_Filter(void) { unsigned long buf[FILTER_NUM]; unsigned long sum 0; unsigned long max, min; unsigned char i; min 0xFFFFFFFF; max 0; for(i 0; i FILTER_NUM; i) { buf[i] HX711_Read(); if(buf[i] max) max buf[i]; if(buf[i] min) min buf[i]; } for(i 0; i FILTER_NUM; i) { if(buf[i] max) { buf[i] 0; break; } // 删掉一个最大值 } for(i 0; i FILTER_NUM; i) { if(buf[i] min) { buf[i] 0; break; } // 删掉一个最小值 } for(i 0; i FILTER_NUM; i) sum buf[i]; return sum / 3; }实测下来5次去极值平均在10Hz采样率下大概0.5秒出一个稳定的重量值人眼看数码管或者LCD已经非常平稳不会一直跳最后一位。3.4 去皮、标定与重量换算拿到稳定的AD值后还需要两步才能换算成重量去皮和求标定系数。去皮很容易理解空秤状态下读取20次AD值求平均作为零点偏移量offset。后面每一次读数都先减去这个偏移量剩下的才是“由外力引起的AD变化量”。标定系数需要放一个标准砝码。比如放一个500g的砝码稳定后读取AD值求平均然后offset 空秤时的平均AD值; ad_with_load 带500g砝码时的平均AD值; scale (ad_with_load - offset) / 500.0f; // 单位AD值每克得到scale之后任意时刻的重量就是weight (current_ad - offset) / scale;我习惯把offset定义成longscale定义成floatcurrent_ad用long。注意这里一定要用500.0f而不是500否则整数除法会把scale截成整数精度损失很严重。51单片机虽然做浮点运算速度慢但称重场景每秒就算几次完全不是瓶颈。标定时还有一个容易被忽略的点scale必须在系统组装完成、传感器安装到位后再标定。同样一颗传感器放在不同机械结构上由于受力路径、安装预紧力的差异标定系数可能差出好几个百分点。我见过有人拿传感器裸体标定完装进设备后又抱怨精度差其实就是没做“整机标定”。4. 常见问题与排查技巧实录4.1 数据一直为零或者满量程不变这个问题的排查顺序我建议这样走先看传感器桥路接线有没有接反E和E-是激励A和A-是信号接反了或者接错位置数据要么不动要么直接顶到满量程。再看HX711模块上的增益选择电阻很多模块出厂焊好了128倍增益如果你用B通道或者改了增益读出来的数值范围会完全不同。最后用手按压传感器看DOUT有没有反应如果DOUT一直是高电平说明芯片压根没转换基本就是供电或者芯片损坏的问题。4.2 数据漂移大、来回跳这个问题排在第一位的原因是电源纹波。先别急着调软件拿示波器看HX711的VCC引脚如果上面有几十毫伏的纹波滤波算法再先进也白搭。优先在电源脚加电容或者把模块供电和电机、继电器供电彻底分开。第二位的原因是机械振动比如秤盘固定不牢、传感器安装面刚度不够这种属于结构问题软件只能缓解不能根治。4.3 程序运行一段时间后卡死这是我踩过最深的坑主循环里读取HX711如果DOUT因为某种原因长期保持高电平程序就会一直卡在while(HX711_DOUT)这句死循环里。传感器松线、芯片故障、电磁干扰把DOUT信号拉高都可能触发。解决办法就是在等待循环里加超时计数超时后直接返回上一次的有效值程序继续往下跑最多就是称重数据不更新但系统不会整个瘫痪。我给的代码里用if(HX711_DOUT) return last_value;就是这个目的。4.4 标定完成但四角加压数值差很多这种情况通常不是ADC的问题而是机械结构的问题。传感器是悬臂梁结构受力方向必须垂直于应变梁平面如果四角加压读数差异超过0.5%大概率是秤盘刚度不够、或者传感器安装螺栓拧得太紧导致应变片预变形。可以试着松开安装螺丝在传感器和支架之间垫一层薄橡胶垫再重新标定往往能改善不少。另外称重传感器出厂时的“角差”也可以通过软件多点标定修正但属于进阶玩法先把机械装好是最省事的方案。5. 一些实测心得最后分享几条我在这套方案上攒下来的体会。第一标定系数尽量用高精度砝码至少是标准砝码别拿几瓶矿泉水糊弄。500g的矿泉水瓶误差可能超过数十克标出来的系数不准后面所有称重都跟着不准。第二如果你要量产或者做长期运行的设备建议每次上电后先做一次自动去皮因为传感器和结构件在温度变化后零点漂移是真实存在的手动调零调到手抽筋不如软件里加一个上电自动去皮功能。还有一个我自己常用的调试技巧把原始AD值通过串口实时打印出来而不是直接打印重量。因为一旦做去皮和除法运算很多中间信息就被“处理掉”了你就看不出数据是稳定还是抖动、零点偏移量是多少。看到原始AD值很多问题一眼就能定位。这套代码和思路用在STM32、ESP32上也没问题只要把sbit定义换成普通的GPIO操作读取函数三个核心逻辑保持24个数据脉冲、第25个复位脉冲、符号扩展不变就能跑通。后面如果想往物联网方向做加个ESP8266或者蓝牙模块把重量数据传上去一个远程称重终端就出来了扩展空间很大。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进