ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32+HX711+OLED称重模块完整项目:原理、接线、代码与校准排障

STM32+HX711+OLED称重模块完整项目:原理、接线、代码与校准排障 在嵌入式项目里称重模块算是一个非常经典的应用组合传感器把重量变成微弱的电信号HX711 负责放大和数字化STM32 负责读数和换算OLED 再把结果显示出来。我做称重相关的东西有好几年了从最简单的厨房秤到带数据记录的工业小料斗回头看看这个组合几乎是“零失败”的起步方案但很多朋友卡在了奇怪的地方比如读出来的数值一直在跳、OLED怎么点都不亮、校准完放几天又漂了。这篇就围绕 STM32 HX711 称重模块 OLED 显示这个完整项目把原理、接线、代码、校准、排障全部过一遍适合正在做毕业设计、电子竞赛或者想系统学习数据采集和显示链路的人收藏阅读。1. 项目拆解与方案选型1.1 称重项目的真实需求先别急着写代码拿一个真实的称重需求来拆一下。假设现在要做一个“桌面电子秤”面板上有 OLED 显示屏、两个按键量程 5kg精度要求“小数点后一位能稳定”也就是显示到 0.1kg 或者按克显示到 1g。这个需求听起来简单但拆开以后会牵扯出几个问题第一个问题是传感器信号太小。市面上常见的称重传感器其实是“应变片式”的满量程输出只有 2mV/V 左右。给它 5V 激励电压满量程 5kg 时也才输出 10mV 的差分信号。这种微伏到毫伏级别的信号普通 MCU 的 ADC 直接采基本等于在噪声里捞针。第二个问题是实时性和稳定性的矛盾。OLED 要刷新HX711 要采样MCU 还要处理按键和去皮逻辑如果程序结构写得不好显示出来的重量就会一会跳一下。解决这个问题不能光靠“加大滤波”而是要先想清楚整个数据流的每个环节。第三个问题是校准。所有称重传感器都有离散性同一批出厂的传感器零点和灵敏度都可能差不少。如果程序不校准读数毫无意义。所以这个项目的本质不是“驱动一个芯片”而是“搭建一条完整的模拟小信号采集和显示链路”。把这个链路打通了以后做温度采集、压力采集、光照采集思路都是同一套。1.2 为什么偏偏选 STM32 HX711 OLED先说 MCU。STM32尤其是 F103C8T6 这种“小蓝板”在国产开发板上几乎人手一块资料多、CubeMX 配置方便、价格也便宜。称重这种低功耗场景对主频没有要求48MHz 也好、72MHz 也罢都能跑但 STM32 的 GPIO 翻转速度够快延时可控这对 HX711 的时序读取很关键。然后是 HX711。这颗芯片是专为称重传感器设计的 24 位高精度 ADC内置可编程增益放大器。人家问“STM32 内置 ADC 能不能直接采传感器”答案是可以采但效果会很差。下面这个表格把两者的差异列出来看完就明白为什么专门加一颗 HX711对比项HX711 外置方案STM32 内置 ADC 方案分辨率24 位实际有效精度受噪声影响约 21~22 位12 位信号放大内置 PGA增益 32/64/128 可选无内置仪表放大器需外搭运放参考电压比率式测量跟随传感器激励电压变化参考电压固定激励电压波动会直接变成误差抗干扰设计差分输入 内部滤波单端输入为主噪声敏感软件复杂度串行时序读取简单稳定需要自己处理放大、偏置、滤波再提一嘴“比率式测量”。HX711 的 ADC 参考电压直接取自传感器激励电压 AVDD也就是说如果电源电压从 5V 掉到 4.8V传感器输出变小了但 ADC 的参考电压也跟着变小读数几乎不受影响。这是称重系统里非常实用的特性自己做锂电池供电的便携秤时尤其重要。OLED 就不用多说了I2C 接口只要两根线128x64 分辨率足够显示重量和单位没有背光功耗低SSD1306 驱动几乎成了 I2C 屏幕的事实标准。相比之下LCD1602 需要 8 根数据线还得调对比度视野一偏就看不清楚实在不优雅。2. HX711 与称重传感器的底层原理2.1 称重传感器惠斯通电桥与 mV 级信号称重传感器内部是一个惠斯通电桥四个应变片电阻组成桥臂。不承受力量时四个电阻平衡输出为零受力后其中两个电阻被拉长阻值变大另外两个被压缩阻值变小电桥失去平衡在输出端产生一个很小的差分电压。这个差分电压有多小呢以常见的 1kg 传感器为例灵敏度 1.0mV/V 到 2.0mV/V。如果给 5V 激励满量程输出也就是 5mV 到 10mV。STM32 内置 ADC 在 3.3V 参考下最低的一位对应 3.3V / 4096 ≈ 0.8mV也就是说满量程 10mV 信号只占到大约 12 个 LSB这还没算噪声和温漂。所以放大和 24 位数字化是必须的。传感器的四根引线通常用颜色区分红色接 E激励正、黑色接 E-激励负、白色接 A信号正、绿色接 A-信号负。不同厂家的线序可能会变接之前最好用万用表量一下E 和 E- 之间的阻值通常是 400Ω 左右A 和 A- 之间也是 400Ω 左右这才是健康的电桥。如果量出来是开路或者短路传感器基本可以判定已经坏了。2.2 HX711 芯片手册要点HX711 是海芯科技出的 24 位高精度 ADC双通道本项目中只用通道 A。它的核心特性有四个内置 PGA。通道 A 的可编程增益为 128 或 64。增益 128 时满量程差分输入约 ±20mV正好匹配 2mV/V 传感器在 5V 激励下的信号范围。增益 64 适合灵敏度更高或者激励电压更大的传感器。24 位无符号/有符号输出。芯片手册上写的是二进制补码输出实际读回来的 24 位 raw 值需要自己判断正负。空载时通常在一个较大的正数附近加上重量之后数值增大如果是负向受力raw 会变成负数。内部振荡器与采样率。RATE 引脚接了高电平跑 80Hz接低电平跑 10Hz。大部分模块上会留一个焊盘或者短路电阻让你选。题目热搜里就有人问“10Hz 还是 80Hz”后文专门讲。串行接口。HX711 用 DOUT数据输出和 SCK时钟输入两根线和 MCU 通信。注意它跟 I2C、SPI 都不一样是芯片主动通知、MCU 被动读的一种“类 SPI 半双工”时序。HX711 模块的引脚一般是 VCC、GND、SCK、DOUT少数模块还会引出 RATE、DVDD 之类。买模块的时候认准“HX711 称重模块”几块钱一片板上通常已经做好了去耦电容和传感器接线端子省去自己画板的麻烦。2.3 串行时序与 10Hz/80Hz 采样率之争HX711 的读取时序看起来简单但很多人第一次写容易出错我详细拆一下。正常情况下HX711 转换完成后会把 DOUT 引脚拉低表示“数据准备好了”。MCU 看到 DOUT 变低就开始产生 SCK 脉冲。在每一个 SCK 的上升沿DOUT 引脚会输出一位数据先高位后低位一共 24 位。读完第 24 位之后如果要让下一次转换继续按增益 128 工作需要再多发一个 SCK 脉冲也就是第 25 个脉冲这个脉冲同时会触发 HX711 内部把通道 A 的增益配置为 128。如果第 25 个 SCK 之后就停了通道增益锁在 128。如果你发了 26 个脉冲增益就变成 64发了 27 个就变成 32。这是很多人忽略的点——读一次重量发多少个 SCK 是有讲究的不能随手写个 for 循环发满 28 个。DOUT 会一直保持低电平直到最后一个 SCK 脉冲结束然后 DOUT 变高芯片开始下一次转换。所以程序里读完一次之后要等 DOUT 再次被拉低才能读下一次。采样率的问题在这里就很实际了。RATE 接低电平内部振荡器输出 10Hz也就是每 100ms 完成一次转换RATE 接高电平80Hz每 12.5ms 完成一次转换。那到底选哪个采样率优点缺点适用场景10Hz噪声低、数据稳定、功耗低响应慢最快每秒更新 10 次桌面秤、计价秤、静态称重80Hz响应快适合流水线动态称重单次读数噪声明显更大分拣设备、动态检重秤大部分新手直接把模块上 RATE 悬空或接地默认 10Hz建议就这么用。如果你发现“每秒重量跳了十几次”其实属于正常现象因为 HX711 本来就在以 10Hz 刷新。要更平滑就做软件滤波。如果想用 80HzRATE 接高电平软件里多做几次滑动平均也能得到平滑结果代价是功耗稍微高一点。2.4 硬件接线与电源处理接线表先放在这里照着接就行HX711 模块引脚STM32 引脚说明VCC5V给 HX711 模拟和数字供电GNDGND共地SCKPA0推挽输出接 HX711 时钟DOUTPA1输入模式HX711 数据输出E / E-传感器红/黑线激励电压输出A / A-传感器白/绿线差分信号输入电源是称重项目最容易翻车的地方。HX711 的模拟电源和数字电源如果处理不干净读出来的数据会像股市曲线一样乱跳。我的经验是给 HX711 模块的 VCC 和 GND 之间并一个 10uF 电解电容和一个 0.1uF 陶瓷电容放在模块供电脚附近。传感器激励线和信号线如果比较长用双绞线或者屏蔽线。屏蔽层单端接地不要形成地环路。如果系统里还有电机、继电器、舵机这类大电流设备STM32 的板和 HX711 模块要尽量远离它们或者在总电源入口加 π 型滤波。DOUT 引脚在 STM32 侧配置成浮空输入或带上拉输入都可以但不要开推挽输出否则会打架。有些廉价模块板上会把 AVDD 和 VCC 直接连在一起这时 HX711 的参考电压就等于电源电压。如果电源有纹波读数就会有周期性的波动。碰到这种情况最直接的办法是换一片做工更好的 HX711 模块而不是在软件里疯狂滤波。软件滤波只能平滑随机噪声对于固定频率的电源纹波方向错了反而会把真实重量变化也削掉。3. OLED 显示集成从初始化到显示汉字3.1 SSD1306 驱动思路OLED 屏本身就是一个带控制器的显示面板。0.96 寸 128x64 的 I2C OLED 模块控制芯片绝大多数是 SSD1306。SSD1306 内部有 128x64 位的显存把某一位写成 1对应的像素就点亮。MCU 要做的就是通过 I2C 往这块显存里写数据。SSD1306 的 I2C 地址常见有两个0x3C 和 0x3D。模块背面如果有地址选择电阻可以通过焊接改变地址。淘宝上买的模块默认地址一般写在商品详情里最常见的还是 0x3C。如果你的代码初始化了屏幕却没反应先检查地址是不是 0x3D。I2C 写 SSD1306 时控制字节区分命令和数据控制字 0x00 表示后面跟的是命令0x40 表示后面是显存数据。简单理解就是往 OLED 的寄存器写命令来配置模式往显存地址写数据来显示内容。3.2 手写驱动与初始命令序列网上能搜到很多 SSD1306 驱动库但把最核心的初始化命令搞清楚自己对驱动也就有底了。初始化序列其实是一串固定命令下面的代码可以直接用HAL 库环境下同样适用static void OLED_WriteCmd(uint8_t cmd) { uint8_t buf[2] {0x00, cmd}; // 第一个字节是控制字 0x00表示命令 HAL_I2C_Master_Transmit(hi2c1, OLED_ADDR 1, buf, 2, 100); } static void OLED_WriteData(uint8_t data) { uint8_t buf[2] {0x40, data}; // 控制字 0x40表示数据 HAL_I2C_Master_Transmit(hi2c1, OLED_ADDR 1, buf, 2, 100); } void OLED_Init(void) { OLED_WriteCmd(0xAE); // 关闭显示 OLED_WriteCmd(0xD5); // 设置时钟分频 OLED_WriteCmd(0x80); OLED_WriteCmd(0xA8); // 设置 multiplex ratio OLED_WriteCmd(0x3F); // 128x64 OLED_WriteCmd(0xD3); // 设置显示偏移 OLED_WriteCmd(0x00); OLED_WriteCmd(0x40); // 起始行 0 OLED_WriteCmd(0x8D); // 电荷泵设置 OLED_WriteCmd(0x14); // 开启电荷泵 OLED_WriteCmd(0x20); // 内存寻址模式 OLED_WriteCmd(0x00); // 水平模式 OLED_WriteCmd(0xA1); // 段重映射 OLED_WriteCmd(0xC8); // COM 扫描方向 OLED_WriteCmd(0xDA); // COM 引脚配置 OLED_WriteCmd(0x12); OLED_WriteCmd(0x81); // 对比度 OLED_WriteCmd(0xCF); OLED_WriteCmd(0xD9); // 预充电周期 OLED_WriteCmd(0xF1); OLED_WriteCmd(0xDB); // VCOMH 失配调节 OLED_WriteCmd(0x40); OLED_WriteCmd(0xA4); // 全局显示开启 OLED_WriteCmd(0xA6); // 正常显示非反色 OLED_WriteCmd(0xAF); // 开启显示 }这段代码里最容易被忽视的是0x8D / 0x14电荷泵命令。有些 3.3V 供电的 OLED 如果不开电荷泵屏幕就是一片漆黑很多“OLED 不亮”的案例最后查出来是这里没写。SSD1306 显存按页组织128x64 被分成 8 页每页 8 行像素每页 128 列。水平寻址模式下写数据会自动往右移动写满一页后跳到下一页。写一个 16x16 的汉字其实就是按这个内存排布把字形数据填充进去。3.3 汉字和数字的显示方法OLED 显示汉字本质上是把“字”变成“点阵数据”。16x16 汉字点阵每个字占 32 字节16x8 的 ASCII 字符每个字符占 8 字节。这些字节通常用取模软件生成比如 PCtoLCD2002。取模的时候要注意设置取模方式。常见的设置是逐行式、低位在前、阴码1 表示点亮。很多人显示汉字乱码一多半是取模方向和内存写入顺序对不上。举个最简单的 8x16 字符显示函数例子把字符点阵按列写入void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr) { uint8_t i; for (i 0; i 8; i) { OLED_SetCursor(x i, y); OLED_WriteData(OLED_F8x16[chr - ][i]); OLED_WriteData(OLED_F8x16[chr - ][i 8]); } }OLED_SetCursor的本质是设置页地址0xB0 页和列地址低 4 位和高 4 位然后写显存。对初学者来说手写一个基础驱动完全够用而且比套用大而全的 u8g2 库更能理解 I2C 屏幕的工作原理。3.4 要不要用 U8g2 库热搜词里有“玩转 u8g2 oled 库”这里也聊一下。U8g2 是一个很成熟的开源 OLED 驱动库内置非常丰富的字体包括中文字体还支持画线、画框、图片显示。用起来确实方便比如u8g2_ClearBuffer(u8g2); u8g2_SetFont(u8g2, u8g2_font_ncenB08_tr); u8g2_DrawStr(u8g2, 0, 10, Weight: 100g); u8g2_SendBuffer(u8g2);U8g2 的坑在于它默认的缓冲策略比较吃内存。F103C8T6 只有 20KB RAM如果开全屏缓冲128x64 8KB再加上系统其他变量很容易警急。解决办法是使用 U8g2 的“页缓冲模式”也就是每次只刷新一条横向的屏幕区域。不过这样代码结构会复杂一点对毕设项目来说手写驱动的成本更低。我的建议是如果只显示固定文本和数字手写驱动就够了如果需要绘制波形、动态图表、或者要花哨字体再上 U8g2。这个项目主要显示重量值手写驱动完全能胜任。4. 完整代码实现从上电到显示重量4.1 CubeMX 工程配置我用的是 STM32CubeMX HAL 库芯片选 STM32F103C8T6。需要配置的资源有RCC外部晶振 HSE时钟树到 72MHzSYSDebug 选 Serial Wire方便用 ST-Link 调试I2C1标准模式 100kHz或者快速模式 400kHz 都行地址检测关闭GPIO PA0推挽输出速度 HighGPIO PA1浮空输入其他外设如果后续加按键把 PB0/PB1 配成输入。要注意的是DOUT 如果配置成上拉输入而 HX711 的 DOUT 是开漏结构上拉会让电路增加一点不必要的电流浮空输入最稳。SCK 配置成推挽输出因为 HX711 需要确定的高低电平。4.2 HX711 驱动代码驱动代码的核心是单片读取和多次平均两块。下面这套代码我在多个项目里实测过时序稳定直接用没问题。/* hx711.h */ #ifndef __HX711_H #define __HX711_H #include main.h #define HX711_SCK_PIN GPIO_PIN_0 #define HX711_SCK_PORT GPIOA #define HX711_DOUT_PIN GPIO_PIN_1 #define HX711_DOUT_PORT GPIOA void HX711_Init(void); int32_t HX711_Read(void); int32_t HX711_Read_Avg(uint8_t times); #endif/* hx711.c */ #include hx711.h static void Delay_us(uint16_t us) { volatile uint16_t i; for (i 0; i us * 10; i) { __NOP(); } } void HX711_Init(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin HX711_SCK_PIN; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(HX711_SCK_PORT, GPIO_InitStruct); GPIO_InitStruct.Pin HX711_DOUT_PIN; GPIO_InitStruct.Mode GPIO_MODE_INPUT; GPIO_InitStruct.Pull GPIO_NOPULL; HAL_GPIO_Init(HX711_DOUT_PORT, GPIO_InitStruct); HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET); } int32_t HX711_Read(void) { uint32_t value 0; uint8_t i; /* 等待 DOUT 变低表示一次转换完成 */ while (HAL_GPIO_ReadPin(HX711_DOUT_PORT, HX711_DOUT_PIN) GPIO_PIN_SET); for (i 0; i 24; i) { HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET); Delay_us(1); HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET); Delay_us(1); /* 在 SCK 低电平时读 DOUT避免边沿抖动影响 */ value (value 1) | (HAL_GPIO_ReadPin(HX711_DOUT_PORT, HX711_DOUT_PIN) GPIO_PIN_SET ? 1 : 0); } /* 第 25 个脉冲保持通道 A 增益 128 */ HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET); Delay_us(1); HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET); Delay_us(1); /* 24 位有符号扩展 */ if (value 0x800000) { value | 0xFF000000; } return (int32_t)value; } int32_t HX711_Read_Avg(uint8_t times) { int32_t sum 0; uint8_t i; for (i 0; i times; i) { sum HX711_Read(); } return sum / times; }有几个点需要特别说明。Delay_us我故意用了一个粗略的延时函数。HX711 的时序要求其实很宽松SCK 高电平和低电平至少要保持微秒级空循环完全够用。如果较真去用定时器反而引入复杂度和额外中断干扰。关键是每次读完后 SCK 要恢复低电平否则芯片会认为你要继续发脉冲。读取时机的选择标准做法是“等待 DOUT 拉低”。如果还没等 DOUT 变低就开始发 SCK读回来的数据可能还是上一帧或者半帧数据表现为读数跳变。4.3 数据滤波方案对比HX711 单次读数有一些随机噪声尤其在 80Hz 模式下更明显。软件滤波可以大幅提升稳定性常见的方案有三种滤波方式原理优点缺点多次算术平均连续读 N 次求平均简单直接能抑制随机噪声响应变慢去极值平均去掉最大最小再平均能剔除偶然毛刺需要排序计算略多一阶低通滤波新值 α*新采样 (1-α)*旧值平滑度高代码量少有滞后需要调 α称重这种静态场景我最喜欢的是“去极值平均”。因为称重数据偶尔会出现一个很大的尖峰干扰可能是震动、电磁干扰或者按键触摸造成的普通平均值会把尖峰一起平均进去导致读数跳一下。去极值平均则能把这些毛刺自动剔除。下面是一个简单实现int32_t HX711_Read_Filter(uint8_t times) { int32_t buf[8]; int32_t sum 0; uint8_t i, max_i 0, min_i 0; for (i 0; i times; i) { buf[i] HX711_Read(); if (buf[i] buf[max_i]) max_i i; if (buf[i] buf[min_i]) min_i i; } for (i 0; i times; i) { if (i max_i || i min_i) continue; sum buf[i]; } return sum / (times - 2); }这个函数传 8 次采样去头去尾对中间 6 次求平均。对 10Hz 采样率来说一次滤波耗时约 0.8 秒刷新频率确实不高但用来做静态桌面秤绰绰有余。如果需要更快的响应把采样次数降到 5 次。4.4 校准公式与重量换算HX711 读回来的是一个无量纲的 ADC 原始值要变成“克”必须通过校准得到两个参数零点偏移offset和比例系数scale。重量换算公式是weight(g) (raw - offset) / scale其中offset是空载时的读数scale表示每 1g 重量对应的 ADC 读数变化量。用一个已知重量的标准砝码 W比如 500g测出加载后的读数raw_load那么scale (raw_load - offset) / W代码实现如下typedef struct { int32_t offset; float scale; } Weight_Handle; Weight_Handle g_weight {0, 0.0f}; /* 两次采样校准空载 已知砝码 */ void Weight_Calibrate(uint16_t known_grams) { int32_t raw_empty HX711_Read_Avg(30); int32_t raw_known HX711_Read_Avg(30); g_weight.offset raw_empty; g_weight.scale (float)(raw_known - raw_empty) / (float)known_grams; } /* 实时获取重量单位 g */ float Weight_GetGram(void) { int32_t raw HX711_Read_Filter(8); return ((float)(raw - g_weight.offset)) / g_weight.scale; }关于scale的数值量级可以做一个小估算。5kg 传感器增益 128满量程大约 5V 激励下输出 10mV在 HX711 内部被放大 128 倍后约 1.28V对应 24 位 ADC 输出大约在数百万量级。也就是说scale大约在一千到几千之间具体值要看传感器和激励电压。这里有个常见的错觉scale越大代表越灵敏不对。scale是“每克对应的 ADC 数值”它由传感器灵敏度和增益共同决定。同一颗传感器RATE 接 10Hz 还是 80Hz对坐标刻度没有影响影响的是噪声分布。所以不要因为读数跳就试图修改scale那会让校准完全失效。4.5 主程序逻辑整个主程序的结构不复杂核心是一个带“去皮”状态的主循环int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_I2C1_Init(); OLED_Init(); HX711_Init(); OLED_ShowString(0, 0, Starting...); HAL_Delay(500); Weight_Calibrate(500); // 用 500g 砝码校准 uint8_t tare_pressed 0; float weight_gram 0.0f; while (1) { /* 去皮按键检测按下后把当前重量记为 0 */ if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) GPIO_PIN_RESET) { g_weight.offset HX711_Read_Avg(20); // 重新取零点 tare_pressed 1; } weight_gram Weight_GetGram(); /* OLED 显示 */ OLED_ShowString(0, 0, Weight:); OLED_ShowSignedNum(2, 48, (int16_t)weight_gram); // 整数部分 OLED_ShowChar(2, 56, .); OLED_ShowNum(2, 64, (uint16_t)((weight_gram - (int16_t)weight_gram) * 10)); // 小数位 HAL_Delay(100); } }这个代码里用了HAL_Delay(100)控制整体刷新周期。因为 HX711 10Hz 采样率下数据本身就 100ms 变一次主循环 100ms 刷新正合适。如果你的滤波函数一次要 8 次采样那一次循环可能耗时接近 0.8 秒OLED 刷新自然变慢这不是 bug而是滤波和刷新速度的取舍。想要刷新快就减少滤波采样次数。去皮功能的实现也非常直白offset直接赋成当前读数。注意去皮之后scale不变因为每克对应的 ADC 增量是物理特性和零点无关。这个细节很多人会搞错以为去皮要重新校准scale。5. 现场校准与精度调优5.1 校准的必要性每一个称重传感器的零点输出和灵敏度都不完全一样即使是同一批次也可能有微小的差异。如果代码里写死“offset 800000scale 1500”换了传感器或者换了供电电压显示重量就会完全不对。校准就是把传感器的个体差异“告诉”程序的过程。校准还有个容易忽略的作用消除结构应力。传感器安装到结构架上之后螺丝拧紧的松紧、秤盘的重量、连接线的拉扯都会改变零点。所以装机之后、首次使用之前必须做一次完整校准。结构有变动时要重新校准。5.2 三步校准法校准其实很简单三个步骤保证秤台上没有东西静置 5 秒让传感器进入稳定状态然后执行一次offset采样建议多次平均。放上一个标准砝码。这里我建议放量程的 50% 到 80% 左右的砝码比如量程 5kg 就放 2kg 或 3kg。放太轻的砝码校准比例系数的误差会被成倍放大放太接近满量程又容易碰到传感器的非线性区。采样加载读数代入公式计算scale。校准完成之后再加几组不同重量的砝码验证。比如空载显示 0g放 500g 显示 500g放 2kg 显示 2001g 之类。如果等比例偏差在 2g 以内说明校准成功如果误差随重量线性变大说明scale没算准如果误差在某个区间明显变大可能是结构或者传感器本身非线性。5.3 精度的进一步提升如果要做高精度称重有几个实操技巧预热。HX711 和传感器上电后前 10 分钟温漂最明显高精度场景先预热再校准校完再测。多次校准取平均。我通常会连续校准三次取scale的平均值能抵消偶然误差。程序里区分“动态”和“稳定”状态。重量变化超过一定阈值时显示变化中的重量重量连续 N 次变化很小时锁定显示并清除波动。这个策略可以极大改善用户体验。AT 指令或者串口输出原始数据。调试阶段把 raw 值实时发到串口用串口助手观察数据分布比你盯着 OLED 判断“稳不稳”要直观得多。我调这个项目时手里一定挂着串口输出随时看 raw 的趋势。关于线性度多说一句。普通传感器的非线性误差大约在满量程的 0.03% 到 0.1% 之间。对 5kg 量程来说0.1% 就是 5g。如果你要做到 0.1g 精度两点校准远远不够需要做多点分段校准甚至用多个权值砝码拟合一个校正曲线。这些属于进阶玩法毕设和常规项目用两点校准完全足够。6. 常见问题与排查技巧6.1 HX711 读不出数据现象是程序卡死在while (HAL_GPIO_ReadPin(DOUT) SET);或者串口打印出来的 raw 一直是同一个固定值。排查顺序建议这样第一检查 DOUT 是不是真的接到了单片机。用万用表量 DOUT 引脚电压HX711 空载正常工作时 DOUT 应该是在高低电平之间跳动的如果一直高或者一直低就说明芯片没在正常转换。第二检查 SCK 和 DOUT 有没有接反。很多新手把 SCK 接到输入、DOUT 接到输出时序自然就错乱了。按照模块上的丝印SCK 是单片机输出的时钟DOUT 是芯片输出的数据。第三检查 HX711 模块供电电压。VCC 不能低于 2.6V 也不能超过 5.5V很多模块直接接 5V但如果你用 3.3V 给 HX711 供电虽然芯片能工作传感器的激励电压也变成 3.3V输出信号会更弱在增益 128 下可能刚好够用但余量变小。电源纹波大的话问题会更明显。第四确认模块和传感器的接线没有虚焊。HX711 模块的螺丝端子很容易松动传感器线插进去没拧紧会表现为读数飘忽甚至频繁掉回零。我至少遇到过三次“读不出来”最后发现是端子上螺丝没拧到底。6.2 数据跳字严重跳字是称重项目里最让新人心态爆炸的问题。分两种情况判断如果跳字是“有规律的固定频率”比如每秒跳一下那优先怀疑电源。用示波器看 VCC 波形或者干脆换成 USB 供电 / 锂电池独立供电试试。如果是“不规则随机跳动”优先怀疑传感器信号线受到干扰把线材换成双绞屏蔽线并且让信号线远离电源线和大电流走线。如果跳字是“数值不断变大或者变小”那很可能不是噪声而是程序逻辑问题。比如你没有等待 DOUT 拉低就开始读读到的可能是残留在移位寄存器里的旧数据或者你 SCK 的脉冲数发错了导致增益漂移。用逻辑分析仪抓一抓 DOUT 和 SCK 的波形看看 241 个脉冲是否工整答案立刻就出来了。还有一个小技巧把 HX711 的采样率从 80Hz 切回 10Hz对降低跳字立竿见影。RATE 引脚接低电平后数据会稳定不少。6.3 OLED 不亮或花屏OLED 不亮先从物理查起。I2C 的 SDA/SCL 有没有接对模块的 VCC 和 GND 有没有接反这些都是低级错误但最容易发生。模块上 OLED 不亮还有一种可能是 I2C 总线上没有接上拉电阻很多模块板载了上拉但也有便宜的模块没做这时你在 SDA 和 SCL 上各接一个 4.7kΩ 到 3.3V 的上拉电阻。花屏问题有两类。一类是“开机一瞬间花屏过一两秒正常”这是上电时序问题复位做到不干净OLED 内部状态不对。解决办法是在初始化之前对 OLED 的 RES 引脚做一次完整的低电平复位脉冲如果模块没有引出 RES就先上电等 100ms 再初始化。另一类是“一直花屏显示乱码”。这种情况优先检查代码里内存寻址模式和显存写入顺序是否匹配。比如初始化命令里设了页寻址模式但写数据函数用的却是水平寻址或者取模方向和OLED_SetCursor的页/列方向不一致就会显示成乱码。0.9 寸的 OLED 偶见兼容性问题有的 0.9 寸屏用的不是 SSD1306 而是 SSD1315寄存器不完全兼容初始化可能要多写几条兼容命令或者干脆选 0.96 寸的屏别在屏型号上给自己挖坑。还有一个经常被忽略的原因I2C 速率太高。如果 OLED 模块的走线比较长、或者屏幕端电容偏大400kHz 快速模式可能不稳定出现偶尔花屏。把 I2C 降到 100kHz 标准模式试试很多疑难杂症会直接消失。6.4 称重漂移与零点变化放一个 1kg 砝码在秤上读数却慢慢从 1000g 爬到 1003g这种“漂移”很常见。它可能来自三个方面第一是传感器本身的蠕变。应变片受力后会有一个微小的持续变形过程尤其是廉价的铝制传感器蠕变效应更明显。解决办法是称重前先让传感器受力稳定一段时间或者程序里做“稳定判定”读数连续几次变化小于阈值才更新显示。第二是温漂。HX711 自身温漂和传感器温漂都会导致零点变化。如果环境温度变化大最有效的办法是定期自动去皮比如在秤台空载且重量稳定时每隔一段时间自动把offset更新一次。第三是激励电压变化。虽然 HX711 是比率式测量但如果传感器激励端到芯片供电端之间有额外压降导致电桥实际激励电压变化同样会有漂移。解决方法是把传感器激励线尽量粗、尽量短避免长距离走线。温度漂移这块我自己的体会是硬件上的隔离比软件补偿靠谱得多。把 HX711 放在远离散热风口和功率器件的地方PCB 上模拟地尽量单独铺铜比程序里写一堆温度补偿要简单有效。7. 这个项目还能怎么往深里做称重项目本身是个很好的“母项目”做完之后能衍生出不少方向。比如加一个蓝牙模块把重量数据通过 BLE 发给手机 App做一个智能体重秤或厨房秤数据记录工具。加一个 RS485 接口把称重数据传到上位机就变成一套简单的工业称重终端。增加一个步进电机或者舵机做自动称重分拣的演示装置。换用不同的传感器量程比如换 100g 的高精度传感器再配合更严格的屏蔽和滤波可以做实验室级别的微量称重。如果想把数据稳定性做得更强可以研究一下 HX711 的兄弟芯片 ADS1232、AD7793 这类 Sigma-Delta ADC接口思路完全相通。学会了 HX711 的时序读取和校准逻辑迁移到其他 ADC只需要改改寄存器配置就行。我个人在实际项目里的一个习惯是每做一个传感器项目都会把原始 ADC 值、校准参数、转换后的物理量一起打印到串口保存下来。这样即使 OLED 显示出了问题也能通过串口日志回查是软件问题还是硬件显示问题。这种调试习惯比多会一个库有用得多。再分享一个小技巧HX711 校准用的砝码不一定非要买贵的标准砝码。去超市买一袋精确计量过的食盐或者大米包装上标着净含量比如 500g也能凑合当校准重物。当然要求高的时候还是得买正规的 M1 级砝码但临时验证程序流程生活里的标称重量物品足够用了。这个称重项目做到最后你会发现它真正教给你的不是“怎么驱动 HX711”而是一整套“从微弱信号到可靠读数”的方法论选硬件要看信噪比写代码要处理时序上系统要解决干扰交付前要做校准。这条链路走通之后再去碰其他传感器项目心里就有底了。
返回列表