news 2026/10/15 14:08:41

STM32驱动HX711电子秤实战:从接线、校准到滤波避坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32驱动HX711电子秤实战:从接线、校准到滤波避坑

简介:这份资源面向嵌入式初学者与电子设计爱好者,提供一套基于STM32微控制器的电子秤完整工程,用于学习称重数据采集、处理与显示的全流程。项目以STM32为核心,配合重量传感器与内置ADC完成模拟信号数字化,并通过12864液晶屏实时显示重量,代码中预留了精度调整空间,便于自行优化标定与滤波算法。压缩包共146个文件,约1.64MB,以36个h头文件与33个c源文件为主体,涵盖外设驱动与业务逻辑,另有Keil工程文件、编译中间产物及hex固件等,结构接近真实开发工程。资源已有3235人学习,适合作为课程设计、毕业设计或自学练手参考,读者可从中获取传感器采样、A/D转换、显示驱动、低功耗管理及工程组织等实践思路,并在此基础上扩展单位切换、过载保护等功能。

1. 从一颗 HX711 说起:为什么 STM32 做电子秤是门“看起来简单、做起来全是细节”的活

如果你手头正好有一块 STM32 最小系统板、一个 5kg 量程的电阻应变片传感器和一个 HX711 模块,那你离一台能用的电子秤其实只差“把线接对、把时序写对、把滤波调对”这三步。但真正上手你会发现,事情没那么线性:HX711 读回来的原始码值会跳、去皮之后零点会漂、砝码放上去读数要等两三秒才稳、串口打印出来的重量小数点后两位一直在抖。这些不是玄学,是 24 位 ADC 在低电平信号下的正常表现,也是 STM32 电子秤这个方向最值得拆开讲的地方。

这份资源围绕的就是“用 STM32 驱动 HX711 采集称重传感器,完成重量计算、去皮、校准、超限报警和显示输出”这一整套链路。它适合三类人:正在做课程设计或毕设、需要一份能跑通的参考实现的学生;想从 51 单片机升级到 STM32、第一次接触 24 位 ADC 时序的嵌入式新手;以及手上已经有硬件、但卡在“读数不稳、校准不会、代码跑不通”的从业者。下面按硬件接线、驱动时序、重量换算、滤波校准、避坑排查、进阶技巧的顺序,把这份资源拆到能照着复现的程度。

2. 硬件链路与 HX711 时序:先把“读得到数”这件事做扎实

2.1 称重传感器、HX711 与 STM32 的接线逻辑

电阻应变片式称重传感器本质是一个惠斯通电桥,受力后电桥失衡,输出一个毫伏级的差分电压。这个信号太小,STM32 自带的 12 位 ADC 直接采基本没戏,所以中间必须加一级专用 24 位 ADC,HX711 就是干这个的。它内部集成了稳压、 PGA 和 24 位 sigma-delta ADC,输出的是串行数据,STM32 只需要两根普通 GPIO 就能读。

典型接线是:传感器四线(E+、E-、A+、A-)接 HX711 的对应端子,HX711 的 VCC/GND 接 3.3V 或 5V,DT(数据)和 SCK(时钟)接 STM32 任意两个 GPIO。这里有个新手最容易翻车的点:传感器线色不统一,红黑绿白对应的不一定是 E+/E-/A+/A-,接反了读数会反向或者直接饱和。常见做法是先按模块丝印接,读出来是负值就把 A+/A- 对调,不要靠猜。

// hx711.h —— 引脚宏定义,按实际接线改 #define HX711_DT_PORT GPIOB #define HX711_DT_PIN GPIO_Pin_0 #define HX711_SCK_PORT GPIOB #define HX711_SCK_PIN GPIO_Pin_1 // 初始化:DT 设为输入,SCK 设为推挽输出 void HX711_Init(void) { GPIO_InitTypeDef gpio; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); gpio.GPIO_Pin = HX711_DT_PIN; gpio.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入,DT 空闲时为高 GPIO_Init(HX711_DT_PORT, &gpio); gpio.GPIO_Pin = HX711_SCK_PIN; gpio.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出 gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(HX711_SCK_PORT, &gpio); GPIO_ResetBits(HX711_SCK_PORT, HX711_SCK_PIN); // SCK 默认拉低 }

这段初始化的关键在 DT 用上拉输入。HX711 在数据未准备好时 DT 输出高电平,准备好后拉低,上拉能保证悬空时不会误判。SCK 默认拉低是因为 HX711 要求 SCK 高电平超过 60us 会进入掉电模式,初始化阶段保持低电平最安全。

2.2 24 位数据的读取时序与通道增益选择

HX711 的读取时序不复杂,但时序参数必须卡准。一次完整读取是:等 DT 变低,然后发 25 到 27 个 SCK 脉冲,前 24 个脉冲在上升沿移出数据(高位在前),第 25 个脉冲决定下一次的通道和增益。脉冲数对应关系是:25 个脉冲选通道 A 增益 128,26 个选通道 B 增益 32,27 个选通道 A 增益 64。电子秤一般用通道 A 增益 128,所以发 25 个脉冲。

// 读取一次原始值,返回 24 位有符号数 long HX711_ReadRaw(void) { long value = 0; uint8_t i; // 等待 DT 拉低,超时退出防止死等 uint32_t timeout = 0; while (GPIO_ReadInputDataBit(HX711_DT_PORT, HX711_DT_PIN) == 1) { if (++timeout > 100000) return 0; // 硬件没接好时不要卡死 } for (i = 0; i < 24; i++) { GPIO_SetBits(HX711_SCK_PORT, HX711_SCK_PIN); __nop(); __nop(); // 短延时,保证高电平够宽 value = value << 1; if (GPIO_ReadInputDataBit(HX711_DT_PORT, HX711_DT_PIN)) value++; GPIO_ResetBits(HX711_SCK_PORT, HX711_SCK_PIN); __nop(); __nop(); } // 第 25 个脉冲,选通道 A 增益 128 GPIO_SetBits(HX711_SCK_PORT, HX711_SCK_PIN); __nop(); __nop(); GPIO_ResetBits(HX711_SCK_PORT, HX711_SCK_PIN); // 24 位补码转有符号数 if (value & 0x800000) value |= 0xFF000000; return value; }

逻辑上要说明三点。第一,超时退出是血泪经验,传感器没接或 HX711 没供电时 DT 永远不拉低,没有超时整个程序就卡死在这一句。第二,__nop()延时是给 SCK 高电平留时间,STM32 主频高的时候不加延时可能超过 HX711 的时序上限。第三,补码转换不能省,24 位数据最高位是符号位,不扩展成 32 位有符号数,负值会变成很大的正数,去皮和校准全乱。

3. 从原始码值到克:校准系数、去皮与滤波的工程做法

3.1 两点校准法算出比例系数

HX711 读出来的是 ADC 原始码值,不是重量。要变成克,需要一个比例系数,工程上最稳的是两点校准:空载读一次记为零点,放一个已知砝码读一次,两者差值除以砝码重量就是“每克对应的码值”。

typedef struct { long zero_offset; // 空载零点 float scale; // 每克码值 float tare; // 去皮重量 } Scale_t; Scale_t scale = {0, 1.0f, 0.0f}; // 采集 N 次取平均,降低随机噪声 long HX711_ReadAverage(uint8_t times) { long sum = 0; for (uint8_t i = 0; i < times; i++) sum += HX711_ReadRaw(); return sum / times; } // 校准:空载时调用,记录零点 void Scale_CalibrateZero(void) { scale.zero_offset = HX711_ReadAverage(20); } // 校准:放已知砝码后调用,weight_g 为砝码克数 void Scale_CalibrateWeight(float weight_g) { long raw = HX711_ReadAverage(20); scale.scale = (float)(raw - scale.zero_offset) / weight_g; } // 读取重量(克) float Scale_GetWeight(void) { long raw = HX711_ReadAverage(10); return (float)(raw - scale.zero_offset) / scale.scale - scale.tare; }

参数上,ReadAverage的次数不是越多越好。20 次平均能把随机噪声压下去,但响应变慢;10 次是响应和稳定的折中。校准砝码建议选量程的 50% 到 80%,比如 5kg 量程用 2kg 或 3kg 砝码,太轻的相对误差大,太重的接近满量程线性度会变差。scale系数算出来一般是几百到几千码值每克,如果算出来是负数,说明传感器 A+/A- 接反了。

3.2 去皮、滑动滤波与显示刷新节奏

去皮就是把当前重量记成tare,之后所有读数都减掉它。但去皮有个坑:如果去皮时秤上还有残留振动,tare会带误差,后面所有读数都偏。常见做法是去皮前先连续读几次,等波动小于阈值再记录。

滤波方面,一阶滑动平均够用,但遇到“放上去要等两三秒才稳”的问题,往往是平均次数太多加上没有做变化检测。更实用的做法是:稳定时用大平均次数保证精度,检测到重量突变时切小平均次数快速响应。

#define FILTER_LEN 8 static float buf[FILTER_LEN] = {0}; static uint8_t idx = 0; float Scale_GetFiltered(void) { buf[idx] = Scale_GetWeight(); idx = (idx + 1) % FILTER_LEN; float sum = 0; for (uint8_t i = 0; i < FILTER_LEN; i++) sum += buf[i]; float avg = sum / FILTER_LEN; // 突变检测:与均值差超过 50g 认为重量在快速变化 float diff = Scale_GetWeight() - avg; if (diff > 50.0f || diff < -50.0f) return Scale_GetWeight(); // 快速响应,不滤波 return avg; }

显示刷新不要跟着采样走。HX711 默认 10Hz 或 80Hz 输出速率,10Hz 时每 100ms 一个数据,显示每 200 到 300ms 刷一次就够,刷太快数字跳得人心慌。串口打印同理,建议 500ms 一次,方便观察趋势又不刷屏。

4. 避坑与排查:电子秤读数不稳、校准失败的五个真实原因

4.1 读数一直跳,最后一位停不下来

现象是空载时读数在正负几十克之间波动,放上砝码后小数点后两位一直变。原因通常有三个:供电纹波大、HX711 的 AVDD 和 DVDD 没做退耦、或者传感器信号线太长引入了工频干扰。解决上,先在 HX711 电源脚并 10uF 和 0.1uF 电容,信号线尽量短并远离电机和继电器,软件上把平均次数从 10 提到 20,基本能压到正负 2g 以内。

4.2 校准后放砝码读数差很多

现象是零点校准正常,放 1kg 砝码显示 800g 或 1200g。原因多半是校准砝码太轻,或者校准时秤体没放平。解决是换用量程 50% 以上的砝码重新校准,校准前用水平泡确认秤体水平,四个角受力均匀。如果还是差,检查scale系数是不是用了整数除法被截断,(raw - zero)要强制转 float 再除。

4.3 HX711 读出来全是 0 或全是 0xFFFFFF

现象是原始码值恒定不变,或者一直是最大值。原因是 DT 和 SCK 接反、HX711 没供电、或者传感器没接。排查顺序是先量 HX711 的 VCC 有没有电压,再用万用表测 DT 在空闲时是不是高电平,最后确认 SCK 有没有脉冲输出。接反的情况最常见,DT 和 SCK 对调后立刻正常。

4.4 去皮之后过一会儿零点自己漂了

现象是刚去皮显示 0,过几分钟变成 5g 或 -3g。原因是温漂,应变片和 HX711 都有温度系数,环境温度变化几度零点就会移。解决是开机预热 5 到 10 分钟再校准,软件上加一个“自动零点跟踪”:当读数在零点附近且持续稳定超过 5 秒,缓慢把零点往当前值靠。注意跟踪范围要限制在正负 10g 内,否则放小砝码会被当成漂移吃掉。

4.5 超过量程后读数不线性甚至卡死

现象是接近满量程时读数增长变慢,或者直接停在某个值。原因是传感器弹性体进入非线性区,或者 HX711 输入饱和。解决是选量程时留 20% 余量,5kg 传感器实际用到 4kg 以内;软件上加超限判断,超过量程 90% 就报警并停止更新显示,避免用户继续加载。

5. 进阶技巧:把电子秤做成能长期稳定跑的成品

5.1 用定时器中断做采样节拍,别在 while 里裸读

裸读的问题是采样间隔不均匀,主循环里其他任务一多,HX711 读取就被拉长,滤波效果变差。更稳的做法是用一个 10ms 或 100ms 的定时器中断置标志位,主循环检测标志再读,保证采样节拍固定。HX711 的 DT 引脚也可以配成外部中断,数据准备好自动触发,CPU 不用轮询。

volatile uint8_t sample_flag = 0; // 定时器中断里,每 100ms 置一次标志 void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); sample_flag = 1; } } // 主循环 while (1) { if (sample_flag) { sample_flag = 0; float w = Scale_GetFiltered(); if (w > OVERLOAD_THRESHOLD) { Buzzer_On(); LCD_ShowString(0, 0, "OVERLOAD"); } else { Buzzer_Off(); LCD_ShowFloat(0, 0, w); } } }

5.2 校准参数存 Flash,掉电不丢

每次上电重新校准不现实,把zero_offset和scale存到 STM32 内部 Flash 的最后一页,上电先读,读不到再用默认值。写 Flash 前要解锁,写完锁定,注意 Flash 写入前必须先擦除整页。常见做法是存一个结构体加一个校验字,校验不过就恢复默认,避免半写状态导致参数错乱。

参数建议存储位置说明
zero_offsetFlash 末页偏移 0空载零点,long 类型
scaleFlash 末页偏移 4每克码值,float 类型
tareFlash 末页偏移 8去皮重量,float 类型
checksumFlash 末页偏移 12前三项异或校验

5.3 验证方法:用标准砝码做三点复核

写完别急着收工,拿标准砝码做三点复核:空载、半量程、满量程各测三次,看误差是否在正负 0.5% 以内。如果半量程准、满量程偏小,是传感器非线性;如果三点都偏同一方向,是scale系数没校好。我一般还会在秤上放一个已知重量的手机或水杯做快速验证,比翻砝码方便。

从那以后我每次做完电子秤,都会先空载预热十分钟、再校准、再三点复核,最后连续跑两小时看零点漂移。这套流程走完,基本不会再出现“实验室能用、拿回去就飘”的翻车。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 14:07:50

大模型应用开发入门:从GPT-4 API到LangChain实战

简介&#xff1a;PDF版《大模型应用开发极简入门&#xff1a;基于 GPT-4 和 ChatGPT》是一本面向大模型应用开发入门读者的实用指南&#xff0c;适合希望系统掌握GPT-4和ChatGPT的开发者、产品经理与AI爱好者。内容从大语言模型的工作原理讲起&#xff0c;按章节依次覆盖GPT系列…

作者头像 李华
网站建设 2026/10/15 14:07:15

获取Chrome官方安装包:离线版安装风险避坑指南

获取Chrome安装包 与 离线版常见痛点与隐患 很多用户为了节省下载时间或在无网络环境下部署&#xff0c;倾向于搜索“Chrome离线安装包”。然而在实际使用中&#xff0c;非官方渠道获取的离线版本往往伴随一系列棘手问题。 事实上&#xff0c;对于绝大多数用户而言、最稳妥的…

作者头像 李华
网站建设 2026/10/15 14:07:04

单纯形法核心原理与工程实战:从线性规划建模到求解器调优

做优化的人&#xff0c;几乎都绕不开LP Simplex。LP是线性规划&#xff0c;Simplex是单纯形法&#xff0c;这两个词合在一起&#xff0c;就是在“若干线性约束下求解线性目标函数极值”的经典方法。很多教程一上来就推单纯形表、算检验数&#xff0c;读者学完只会机械操作&…

作者头像 李华
网站建设 2026/10/15 14:06:52

Linux弱口令检测实战:John the Ripper从原理到加固全指南

前几天帮某单位做例行安全巡检&#xff0c;登录测试服务器以后&#xff0c;我习惯性地收集了一波Linux账号口令哈希&#xff0c;丢给John the Ripper跑了一会儿&#xff0c;结果连五分钟都没用到&#xff0c;一个运维账号的弱口令就被直接撞了出来。这种场景在真实环境里几乎每…

作者头像 李华