ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

STM32电子秤设计与实现:基于HX711的高精度称重方案

2026/9/9 16:05:45 拓冰建站 浏览量
STM32电子秤设计与实现:基于HX711的高精度称重方案 简介面向嵌入式入门及电子秤方案开发者这份基于STM32与HX711的电子秤工程提供了从传感器信号采集到重量显示的一整套代码实现可直接作为小量程称重产品的原型参考。资源共98个文件其中包含44个C语言源文件和44个头文件另有Keil工程配置uvprojx/uvoptx、启动文件、清理脚本及说明文档压缩包仅498KB目录划分清晰便于按模块阅读和移植。程序基于STM32F4系列通过GPIO模拟HX711的SCK与DOUT时序实现高精度A/D数据读取代码中加入限幅平均滤波算法有效抑制称重噪声同时提供去皮和比例调整功能方便用户校准零点与量程。工程内还包含系统时钟、串口、延时等基础驱动模块适合希望在真实代码中理解HX711通信协议、GPIO操作及数字滤波处理的开发者学习使用。目前该资源已有2980人学习是一份高性价比的STM32称重应用示例。1. 先把HX711和电子秤的原理搞明白1.1 HX711到底是什么为什么要选它STM32做电子秤最绕不开的一个芯片就是HX711。很多刚接触称重项目的朋友第一反应是“直接用STM32的ADC不就行了吗”真这么做的人基本都栽了跟头。原因很简单称重传感器输出的差分信号只有毫伏级别而STM32内置ADC的分辨率是12位参考电压通常是3.3V算下来一个LSB对应大约0.8mV连传感器满量程输出的十分之一都分辨不出来更别提做个精度还过得去的电子秤了。HX711是专门为桥式传感器设计的24位高精度ADC芯片内部集成了低噪声可编程增益放大器PGA、稳压电源和一个串行通信接口。它可以直接给传感器的电桥供电同时把微弱的差分信号放大到可以被24位ADC采样的范围再将结果通过一个简单的两线接口输出给单片机。说白了HX711就是专门干“把微小重量变化变成数字”这件事的省掉了外面一堆运放和基准源成本几块钱稳定性却比用分立元件搭的方案好得多。这也是为什么你在淘宝上搜称重模块十个里有八个都是HX711。它和STM32的搭配几乎是教科书级的组合HX711负责模拟前端的数据采集STM32负责数据处理、显示、按键、通信这些所有“不擅长模拟电路”的活儿。1.2 称重传感器应变片桥的基本原理理解了HX711还得知道它前面接的传感器是个什么东西。绝大多数电子秤用的是电阻应变式称重传感器内部核心是一个惠斯通电桥由四个应变电阻组成。当传感器受到压力发生微小形变时电桥两臂的电阻值发生变化导致差分输出端的电压不再平衡这个电压差就和施加的压力成正比。这个电压差有多小呢以常见的5kg量程传感器为例满量程输出灵敏度通常在1mV/V左右。如果激励电压是5V满量程差分输出也只有大约5mV。你没看错5kg的力压上去输出才5毫伏。所以必须经过HX711内部那级增益可选的放大器常见配置是128倍增益把信号放大到ADC可采样的范围。搞清楚这一层关系后面对代码里的“原始值”心里就有底了。你在程序里读到的那个整数并不是重量而是经过放大、采样、量化后的原始AD值。把它变成克数需要经过标定这部分后面专门讲。2. 硬件连接与模块选型2.1 引脚定义与接线以STM32F103C8T6为例HX711模块的引脚非常少一共就两排传感器接口四个脚E、E-、A、A-单片机接口四个脚VCC、GND、SCK、DOUT外加一个数字电源VCC。其中E和E-是给传感器电桥供电的A和A-是接收差分信号的输入。以最常见的STM32F103C8T6蓝色Pill板为例推荐接线如下HX711引脚STM32引脚说明VCC5V或3.3V建议5V传感器激励电压高信噪比更好GNDGND必须共地SCKPA0任意GPIO时钟输入由STM32产生脉冲DOUTPA1任意GPIO数据输出HX711转换为电平信号给STM32E传感器红线电桥正激励E-传感器黑线电桥负激励A传感器绿线差分信号正A-传感器白线差分信号负这里有个容易搞错的细节很多HX711模块上面已经自带了传感器接口的丝印你只需要把传感器的四根线按颜色插上去就行。但不同厂家的传感器线色定义不完全统一最可靠的办法是用万用表测一下红黑两线之间的电阻最大通常是激励端绿白两线之间电阻其次信号端。别光看颜色就往上怼我已经见过好几次因为线序接反导致读数一直是负数的案例了。2.2 供电与布线的几个坑硬件上最容易出问题的不是接线对不对而是电源和噪声。HX711虽然内部有稳压但对外部电源的纹波还是比较敏感的。如果你用开发板的3.3V LDO供电板载稳压芯片本身就带一定纹波读数会出现周期性的缓慢漂移。实测下来直接用USB 5V给HX711的VCC供电比用3.3V供电的稳定性要好一截因为传感器激励电压高了同样的应变对应的差分信号幅度更大相当于变相提高了信噪比。另外一个容易踩的坑是SCK和DOUT的线长和走线。这两根线如果太长或者和电机、继电器这类大电流线路靠得太近容易引入共模干扰导致数据偶发跳变。解决办法有两个一是尽量缩短HX711到传感器之间的线缆二是SCK和DOUT上各加一个10kΩ上拉电阻到3.3V用更强的驱动能力抵抗干扰。注意HX711的DOUT输出的是开漏信号虽然很多模块上已经自带上拉但如果你用的是那种裸芯片方案上拉电阻必须自己加否则STM32读到的一直是低电平。3. 软件编写从零写HX711驱动3.1 驱动代码时序理解是关键HX711的通信协议是两线制的一个时钟线SCK一个数据线DOUT。工作流程是这样的平时DOUT保持高电平当HX711内部完成一次AD转换后DOUT会拉低告诉单片机“数据准备好了”。单片机检测到DOUT变低后开始向SCK发送25个脉冲前24个脉冲对应读出24位数据高位在前第25个脉冲决定下一次转换的通道和增益。这里有个很关键的细节脉冲的宽度不能太短。HX711数据手册上给出SCK高电平和低电平的最小时间是0.2微秒你用STM32的GPIO翻转来产生脉冲完全能满足时序要求不需要用到什么高级外设。但如果你的系统主频跑得很高比如168MHz的F4系列建议在每次翻转之间加几个空指令或者使用短延时避免脉冲太窄导致误码。驱动代码的写法有很多种标准库和HAL库都有本质就是对两路GPIO的读写。下面给出一个精简但完整可用的驱动以标准库为例#include hx711.h // 初始化GPIOSCK为推挽输出DOUT为浮空输入或上拉输入 void HX711_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // SCK - PA0推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // DOUT - PA1上拉输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_SetBits(GPIOA, GPIO_Pin_0); // SCK默认高电平 } // 读取一次AD值返回24位有符号数 int32_t HX711_Read(void) { uint32_t data 0; uint8_t i; // 等待DOUT变低表示数据准备好 while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1)); for (i 0; i 24; i) { GPIO_SetBits(GPIOA, GPIO_Pin_0); // SCK拉高 data data 1; GPIO_ResetBits(GPIOA, GPIO_Pin_0); // SCK拉低 if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1)) { data; } } // 第25个脉冲设置下次转换通道和增益此处选择A通道128倍增益 GPIO_SetBits(GPIOA, GPIO_Pin_0); GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 24位数据以补码形式存储转换为有符号数 return (int32_t)(data ^ 0x800000) - 0x800000; }这段代码有个小细节值得说一下数据读取是在SCK拉低之后去读DOUT的电平而不是拉高的时候读。因为HX711是在SCK的下降沿把数据移出到DOUT上的你在SCK低电平期间读DOUT才能保证读到的是稳定的当前位。很多初学者喜欢在拉高后马上读也能工作但时序上不够稳健在干扰大的环境里更容易出错。如果你用的是HAL库逻辑完全一样把GPIO的读写替换成HAL_GPIO_ReadPin和HAL_GPIO_WritePin就行。另外网上有的驱动加了一个超时检测防止DOUT一直不拉低导致程序卡死在while循环里这个建议加上特别是量产项目一颗传感器虚焊就能让整个系统死等。3.2 24位有符号数的处理HX711输出的24位数据是二进制补码形式最高位是符号位。在存储进uint32_t变量时如果不做处理直接当无符号数用会得到0xFFFFFF正数和0x800000负数这种值无法区分正负。所以上面代码里用了(data ^ 0x800000) - 0x800000这个技巧先翻转符号位再减去0x800000实际上就是把24位补码转换成了标准的32位有符号整数。实际测试中空载时HX711读数一般在几十万到几百万之间具体数值取决于传感器量程和增益。读数可能为正也可能为负因为电桥输出不一定是绝对零点上一节提到的线序接反也会导致符号翻转。千万不要在程序里假设读数永远是正数否则去皮功能做出来会非常别扭。4. 标定与滤波让数据真正稳定4.1 两点标定法把AD值变成克数拿到AD原始值之后接下来要做的事情就一个找到AD值和真实重量之间的线性关系。HX711配合应变式传感器在量程范围内线性度非常好所以用两个点的标定就足够了这就是所谓的“两点标定法”。步骤很简单在空秤状态下读取一个稳定的AD值记为zero_value。放上一个已知重量的砝码比如500g读取AD值记为weight_value。计算比例系数scale (weight_value - zero_value) / 500.0单位是“每克对应的AD值”。之后任意时刻的重量为weight (current_value - zero_value) / scale。这里有几个容易出问题的地方。首先砝码的重量越接近满量程越好比如5kg的传感器最好用2kg或3kg的砝码标定这样计算出的scale更准确。如果只用50g的小砝码标定传感器本身的非线性误差会被放大到整个量程。其次标定砝码要放在传感器受力正中心的位置偏心加载会引入额外的弯矩导致标定结果偏大或偏小。我个人习惯把zero_value和scale两个参数保存在STM32内部Flash的最后几个扇区这样每次上电不需要重新标定。注意写入Flash之前要确认扇区没有被程序代码占用F103C8T6的Flash一共64KB把自己写的参数放在最后一个页0x0800FC00附近是比较安全的做法。4.2 数据滤波方案别让读数像心电图电子秤是一个对稳定性要求很高的系统而HX711的裸数据在无载荷状态下也会有正负几个码的跳动。如果是几百克的微小重量几个码的跳动可能对应一两克肉眼可见地跳数字非常影响体验。所以滤波是必不可少的环节。最常见的做法是滑动平均滤波维护一个长度为N的环形缓冲区每次采样后计算平均值。N取8到16之间比较合适太小了滤波效果不明显太大了反应迟钝。注意电子秤的这个“迟钝”是需要刻意保留的称重场景下我们希望显示值平滑过渡而不是像体温计一样跳来跳去所以稍微牺牲一点响应速度是可以接受的。下面给一个简单的滑动平均滤波实现#define FILTER_LEN 10 int32_t filter_buffer[FILTER_LEN]; uint8_t filter_index 0; int32_t filter_sum 0; int32_t HX711_Filter(int32_t new_value) { filter_sum - filter_buffer[filter_index]; // 去掉最旧的数据 filter_buffer[filter_index] new_value; // 写入新数据 filter_sum new_value; filter_index (filter_index 1) % FILTER_LEN; return filter_sum / FILTER_LEN; }如果你发现滑动平均之后读数仍然不够稳可以考虑配合中值滤波使用——先取5个原始值排序取中间值再对这个中间值做滑动平均。这种组合滤波在称重场景下效果很好但代价是单次转换时间变长HX711的转换速率默认10Hz每次读取需要100ms如果整个系统只需要每秒更新几次显示完全够用。注意特别追求速度的场景比如动态称重可以调节HX711的速率选择引脚RATE引脚拉高可到80Hz但代价是噪声明显变大滤波效果也会打折扣。静态称重基本都用10Hz就够了。5. 常见问题与排查实录5.1 读数为0、数据乱跳、数值为负——一张表说清楚做一个电子秤项目十个有九个的问题都出在“数据看起来不对”上面。我把这几年被问得最多的问题整理成了一个速查表基本覆盖了HX711项目里80%的坑现象可能原因排查方法读数一直为0DOUT始终为高GPIO配置错误或芯片未供电用万用表量HX711的VCC和GND检查DOUT引脚配置是否为输入模式读数一直满量程/0x7FFFFFSCK和DOUT接反了对照原理图重新检查接线读数在正负之间乱跳供电不稳、传感器线缆过长、接触不良改用5V供电缩短传感器线缆检查焊接点读数为负传感器信号线A/A-接反了交换绿白两根线数据稳定但重量不对标定系数计算错误重新用标准砝码标定并确认标定时读数是否已稳定DOUT一直为低程序卡死在等待循环HX711损坏、SCK没有正常拉高拉低用示波器查看SCK是否有脉冲若无则检查GPIO是否被复用其中“DOUT一直为低”这个问题最隐蔽因为HX711转换完成后DOUT会被拉低如果程序在读完一次后没有正确发送第25个脉冲HX711会一直认为数据还没被读走DOUT就一直保持低电平下一次读取就会卡死。所以如果你改过驱动代码一定要检查第25个脉冲是否完整执行。5.2 一个真实的排查案例我自己做过一个项目客户反馈称重数据每隔几秒就跳变一次幅度有十几克。一开始怀疑是传感器质量问题换了好几个都没解决。后来用示波器挂在HX711的电源引脚上才发现每当板上的继电器吸合时电源就有一个明显的跌落尖峰。原因是继电器和HX711共用了同一路5V电源继电器线圈启动瞬间电流高达上百毫安直接把供电电压拉下去了。后来在HX711的VCC和GND之间加了一个100μF电解电容和一个0.1μF陶瓷电容并把继电器的供电单独走线问题就消失了。这个案例说明HX711这类模拟前端芯片对电源纯净度的敏感程度远超你的预期。PCB布板时给HX711的电源引脚加一个RC滤波甚至用一颗单独的LDO供电都是值得的投入。另外一个常见问题是开发板上电时STM32的GPIO默认状态。很多STM32的引脚在上电瞬间是浮空状态如果它恰好和HX711的SCK接在一起可能在初始化之前产生乱脉冲导致HX711进入错误状态。解决方法是上电后在初始化代码里先把SCK引脚拉低并配置为推挽输出再去初始化DOUT为输入。这样能保证HX711在复位后处于一个已知的静止状态。6. 从电子秤到更多场景的一些经验6.1 去皮功能的实现细节电子秤必备的功能之一就是去皮。硬件上去皮就是一个按键按下后把当前读数作为新的零点。但软件实现有个坑如果去皮后直接修改zero_value会导致重量显示出现跳变。正确做法是维护一个tare_offset变量清零时把当前读到的AD值累加到tare_offset上实际重量用(current_value - tare_offset - zero_value) / scale计算。这样去皮前后的显示值连续不会被“重置到零”的过程打断。去皮功能还有个边界情况要考虑如果秤台上放了超过满量程的物体去皮的零点本身就超载了显示出来的重量就会失真。所以量产产品里一般会在去皮前判断当前读数是否在正常范围内超出量程则拒绝去皮并给出提示。6.2 给自己的项目留一点扩展空间做完一个HX711电子秤你会发现自己已经掌握了一整套“弱信号采集”的套路传感器选型、模拟前端设计、ADC时序驱动、数据处理与标定、系统抗干扰。这套东西换个传感器就能用在很多其他项目里比如拉力计、压力计、液位检测甚至简易的电子天平。从代码架构的角度建议把HX711的驱动、滤波、标定分成独立模块用接口和上层业务解耦。这样后面如果想换更高精度的ADC芯片比如ADS1232或者MCP3561只需要替换最底层的驱动文件上层显示、按键、通信完全不用动。我自己的项目就是先基于HX711跑通了整个系统后来客户要求更高精度换硬件后只改了驱动文件里读数的部分基本算是无缝切换。做嵌入式的过程中我最大的体会是像电子秤这种看似简单的项目真正动手做一遍能在模拟前端、时序、软件架构、抗干扰设计这些方向上积累很多课本上学不到的经验。你踩过的每一个坑最后都会变成下一块板子上的设计准则。本文还有配套的精品资源点击获取