资讯详情

STM32+HX711称重模块+OLED完整称重项目讲解

📅 2026/9/27 23:22:43 | 华诺云谱 👁 阅读
STM32+HX711称重模块+OLED完整称重项目讲解
摘要本篇博文讲解基于STM32F103C8T6、HX711称重采集模块、电阻应变式称重传感器以及OLED显示屏实现的电子秤项目。从硬件接线、传感器原理、HX711芯片工作逻辑、芯片串行读取时序到STM32程序去皮、标定算法、实物演示、故障现象全部讲解适合单片机课程实验、电子秤入门学习。整体工作逻辑称重传感器把重量转换成微小电压变化HX711把微弱电压转换成数字计数STM32经过去皮、砝码标定将原始计数换算成实际重量最后OLED屏幕显示重量数据。视频讲解STM32HX711称重模块OLED完整称重项目讲解一、项目演示目标本项目实现一套10kg量程简易电子秤。 完整信号流转物体施加重量 → 称重传感器弹性体形变应变片电阻改变 → 惠斯通电桥输出毫伏级差分电压 → HX711内部放大24位ADC模数转换输出原始计数 → STM32读取计数完成去皮零点补偿、砝码标定换算为克单位 → OLED屏幕输出重量数值异常情况输出故障提示。⚠️注意本项目不使用STM32片内ADC模数转换全部由HX711芯片完成。二、硬件接线说明主控芯片STM32F103C8T6最小系统板 外设HX711模块、10kg电阻应变式称重传感器、0.96寸I2C OLED显示屏HX711与STM32接线模块引脚STM32引脚功能说明VCC5VHX711模块供电SCKPB3STM32输出时钟对应芯片PD_SCKDTPB4HX711数据输出DOUT数据就绪信号GNDGND必须和主控共地补充说明PB3、PB4引脚上电默认复用JTAG调试功能代码初始化函数HX711_Init()内部会关闭JTAG保留SWD下载功能再把引脚配置为普通GPIO口。更换HX711模块时需要确认DT引脚输出电平是否和STM32兼容不能只看模块供电是5V。OLED与STM32接线软件I2C模块引脚STM32引脚功能说明VCC5VOLED供电SCLPB8I2C时钟线SDAPB9I2C数据线GNDGND共地称重传感器接HX711模块传感器接到HX711模块E、E‑是惠斯通电桥激励电源端A、A‑为A通道差分信号输入端。传感器的导线颜色没有统一标准接线严格按照模块丝印标识不要依靠导线颜色判断。三、电阻应变式称重传感器工作原理我们使用的是电阻应变式传感器。当物体压在传感器上金属弹性体发生微小弹性形变粘贴在弹性体上的应变片同步伸长或者压缩应变片电阻发生极其微小变化。四个应变片组成惠斯通电桥把电阻变化转化为差分电压输出。电桥激励端口E、E‑差分信号输出端口A、A‑输出差分电压V差 V(A) - V(A‑)输出电压属于毫伏级别微弱信号STM32单片机无法直接采集因此必须依靠HX711做信号放大和AD转换。小知识点加载重物之后原始计数变大还是变小取决于A、A‑接线以及传感器受力方向。代码中标定系数支持负值适配正负计数情况。 ⚠️10kg只是传感器物理额定量程不是固定换算参数。即使同样是10kg传感器零点、每千克对应的计数值都不一样每一套硬件都需要重新标定。四、HX711芯片工作原理HX711是专门针对称重传感器设计的24位ADC芯片。称重传感器输出的差分电压送入芯片内部可编程增益放大器本项目固定使用A通道增益128倍放大之后的模拟信号送入24位ADC输出24位补码格式原始计数通过DT、SCK两根信号线串行把计数传输给STM32单片机。重点提醒24位只是输出数字位数不等于实际称重精度就可以达到2的24次方分之一。实际精度会受机械安装、电源噪声、温度、标定误差、振动干扰影响。HX711转换输出速率由硬件RATE引脚决定可以为10次/秒或者80次/秒本项目没有软件控制RATE引脚采样速率由购买的HX711模块硬件电路决定。五、STM32读取HX711时序逻辑两根信号线DT(DOUT)数据就绪引脚高电平代表AD转换未完成DT拉低代表数据就绪可以读取。SCK(PD_SCK)单片机输出时钟信号。单次读取完整流程SCK保持低电平循环等待DT引脚变为低电平程序设置最大等待时间200ms防止断线卡死。STM32输出24个时钟脉冲逐位读取24bit数据高位优先。额外发送第25个时钟脉冲用来配置下一次采集选择A通道、128倍增益。将读到的24位补码转换为32位整型int32_t原始计数值超时就返回超时错误码。补充第25/26/27脉冲分别对应不同通道增益A通道128倍、B通道32倍、A通道64倍本项目固定使用25脉冲。 SCK引脚高电平维持时间不能超过60μs否则HX711会进入掉电休眠驱动代码读取期间临时关闭中断读完再恢复中断。六、去皮零点、标定换算算法读到的原始计数不能直接作为重量必须获取零点和标定系数两个关键参数。零点R0秤台空载的时候多次采样取平均得到空载计数也就是去皮。项目中HX711_Tare(8)代表采集8次取平均作为零点。标定系数K先完成去皮放上已知重量砝码。调用HX711_Calibrate(砝码克数,8)读取8次采样做平均计算每千克对应的计数值。计算公式K计数/千克 (砝码原始计数 - R0) × 1000 / M砝码克数 实际物体质量(克) (当前原始计数 - R0) ×1000 / K举个例子方便理解 空载零点R0120000放1000g砝码读到计数920000 K(920000‑120000)*1000 /1000 800000 计数每千克 当采集原始计数等于520000重量(520000‑120000)*1000 / 800000 500g。程序上电之后等待2s自动执行8次采样去皮上电时刻秤盘上面必须清空不能放物体。 当前代码只有去皮、标定的时候做8次平均主循环显示读数没有滤波如果想要抑制数字跳动可以自己增加滑动平均滤波但会牺牲刷新速度。七、源代码下面包含HX711、main代码7.1 工程文件说明文件函数/宏功能HARDWARE/HX711/HX711.CHX711_Init()初始化PB3 PB4关闭JTAG保留SWD调试HX711.CHX711_ReadRaw()读取HX711原始24位计数带超时保护HX711.CHX711_Tare()空载多次采样记录零点R0HX711.CHX711_Calibrate()砝码标定计算每千克计数值KHX711.CHX711_ReadWeightG()原始计数换算得到重量单位gHARDWARE/HX711/HX711.HHX711_DEFAULT_COUNTS_PER_KG默认标定系数宏定义USER/main.cDemo_ShowWeight()重量数据、报错信息绘制输出到OLED屏幕main.c代码/* STM32F103C8T6 HX711 10 kg load cell teaching demonstration. * HX711: PD_SCK PB3, DOUT PB4. OLED: SCL PB8, SDA PB9. * Keep the platform empty during startup tare. The displayed grams are * estimates until HX711_DEFAULT_COUNTS_PER_KG is calibrated for this cell. */ #include delay.h #include sys.h #include oled.h #include HX711.H static void Demo_ShowText(const char *message, const char *detail) { OLED_Clear(); OLED_ShowString(0u, 0u, (u8 *)HX711 10KG SCALE, 12u); OLED_ShowString(0u, 13u, (u8 *)PB3 SCK PB4 DT, 12u); OLED_ShowString(0u, 28u, (u8 *)message, 12u); OLED_ShowString(0u, 52u, (u8 *)detail, 12u); OLED_Refresh(); } static void Demo_ShowWeight(int32_t weight_g) { uint32_t magnitude; if ((weight_g (int32_t)HX711_CAPACITY_G) || (weight_g -(int32_t)HX711_CAPACITY_G)) { Demo_ShowText(OVER 10KG RANGE, REMOVE LOAD); return; } magnitude (weight_g 0) ? (uint32_t)(-weight_g) : (uint32_t)weight_g; OLED_Clear(); OLED_ShowString(0u, 0u, (u8 *)HX711 10KG SCALE, 12u); OLED_ShowString(0u, 13u, (u8 *)WEIGHT (EST):, 12u); OLED_ShowChar(0u, 29u, (weight_g 0) ? - : , 16u); OLED_ShowNum(8u, 29u, magnitude, 5u, 16u); OLED_ShowChar(48u, 29u, g, 16u); OLED_ShowString(0u, 52u, (u8 *)CALIBRATE FOR GRAMS, 12u); OLED_Refresh(); } int main(void) { int32_t weight_g; HX711_Status status; delay_init(); NVIC_Configuration(); OLED_Init(); OLED_ColorTurn(0u); OLED_DisplayTurn(0u); HX711_Init(); Demo_ShowText(REMOVE ALL LOAD, TARING IN 2 SEC); delay_ms(1000u); delay_ms(1000u); while (HX711_Tare(8u) ! HX711_OK) { Demo_ShowText(HX711 NOT READY, CHECK PB3 / PB4); delay_ms(500u); } while (1) { status HX711_ReadWeightG(weight_g); if (status HX711_OK) { Demo_ShowWeight(weight_g); } else { Demo_ShowText(READ TIMEOUT, CHECK HX711 WIRING); } delay_ms(100u); } }HX711.C代码#include HX711.H #include delay.h #include stm32f10x.h #include stm32f10x_gpio.h #include stm32f10x_rcc.h /* HX711 使用 GPIO 模拟时序不占用硬件 SPI 外设。 */ #define HX711_PORT GPIOB #define HX711_SCK_PIN GPIO_Pin_3 #define HX711_DOUT_PIN GPIO_Pin_4 static int32_t hx711_offset; /* 空载零点单位为原始计数 */ static int32_t hx711_counts_per_kg HX711_DEFAULT_COUNTS_PER_KG; static uint8_t hx711_tared; /* 置 1 后才允许读取重量或进行砝码标定 */ /* 配置时钟输出和数据输入PB3/PB4 复用为 GPIO 后仍可使用 SWD。 */ void HX711_Init(void) { GPIO_InitTypeDef gpio; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); /* SCK 持续高电平超过 60 us 会使 HX711 进入掉电状态因此先置低。 */ GPIO_ResetBits(HX711_PORT, HX711_SCK_PIN); gpio.GPIO_Pin HX711_SCK_PIN; gpio.GPIO_Mode GPIO_Mode_Out_PP; gpio.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(HX711_PORT, gpio); gpio.GPIO_Pin HX711_DOUT_PIN; gpio.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(HX711_PORT, gpio); hx711_offset 0; hx711_tared 0u; hx711_counts_per_kg HX711_DEFAULT_COUNTS_PER_KG; } /* 读取一次原始数据等待就绪、读取 24 位再发送增益选择脉冲。 */ HX711_Status HX711_ReadRaw(int32_t *raw, uint16_t timeout_ms) { uint32_t bits 0u; uint32_t primask; uint8_t i; if (raw 0) { return HX711_BAD_ARGUMENT; } /* 转换完成后 DT 变低。逐毫秒等待断线时按设定时间返回超时。 */ while (GPIO_ReadInputDataBit(HX711_PORT, HX711_DOUT_PIN) ! Bit_RESET) { if (timeout_ms 0u) { return HX711_TIMEOUT; } delay_ms(1u); --timeout_ms; } /* 时钟高电平期间若被中断打断可能超过 60 us 并触发掉电。 * 短暂关闭中断完成传输结束后恢复调用前的中断状态。 */ primask __get_PRIMASK(); __disable_irq(); for (i 0u; i 24u; i) { GPIO_SetBits(HX711_PORT, HX711_SCK_PIN); delay_us(1u); bits (bits 1) | (GPIO_ReadInputDataBit(HX711_PORT, HX711_DOUT_PIN) Bit_SET ? 1u : 0u); GPIO_ResetBits(HX711_PORT, HX711_SCK_PIN); delay_us(1u); } /* 第 25 个脉冲选择下一次转换使用 A 通道、128 倍增益。 */ GPIO_SetBits(HX711_PORT, HX711_SCK_PIN); delay_us(1u); GPIO_ResetBits(HX711_PORT, HX711_SCK_PIN); if (primask 0u) { __enable_irq(); } /* 将 24 位补码符号扩展为 int32_t保留负值。 */ *raw (int32_t)bits; if ((bits 0x800000u) ! 0u) { *raw - 0x1000000L; } return HX711_OK; } /* 多次采样求平均使用 64 位累加器防止原始计数求和溢出。 */ static HX711_Status HX711_ReadAverage(uint8_t samples, int32_t *average) { int64_t sum 0; int32_t raw; uint8_t i; HX711_Status status; if ((samples 0u) || (average 0)) { return HX711_BAD_ARGUMENT; } for (i 0u; i samples; i) { status HX711_ReadRaw(raw, HX711_READ_TIMEOUT_MS); if (status ! HX711_OK) { return status; } sum raw; } *average (int32_t)(sum / samples); return HX711_OK; } /* 空载采样并记录零点失败时保留之前的零点。 */ HX711_Status HX711_Tare(uint8_t samples) { int32_t offset; HX711_Status status HX711_ReadAverage(samples, offset); if (status HX711_OK) { hx711_offset offset; hx711_tared 1u; } return status; } /* 已知质量标定系数 (加载计数 - 空载计数) × 1000 / 砝码克数。 */ HX711_Status HX711_Calibrate(uint32_t known_mass_g, uint8_t samples) { int32_t loaded_raw; int64_t counts_per_kg; HX711_Status status; if ((known_mass_g 0u) || (known_mass_g HX711_CAPACITY_G)) { return HX711_BAD_ARGUMENT; } if (hx711_tared 0u) { return HX711_NOT_TARED; } status HX711_ReadAverage(samples, loaded_raw); if (status ! HX711_OK) { return status; } counts_per_kg ((int64_t)loaded_raw - hx711_offset) * 1000 / known_mass_g; if ((counts_per_kg 0) || (counts_per_kg 0x7FFFFFFFL) || (counts_per_kg (-0x7FFFFFFFL - 1L))) { return HX711_BAD_CALIBRATION; } hx711_counts_per_kg (int32_t)counts_per_kg; return HX711_OK; } /* 手动载入标定系数可用于恢复预先测得的设备系数。 */ HX711_Status HX711_SetCountsPerKg(int32_t counts_per_kg) { if (counts_per_kg 0) { return HX711_BAD_CALIBRATION; } hx711_counts_per_kg counts_per_kg; return HX711_OK; } /* 返回当前的计数/千克系数便于记录标定结果。 */ int32_t HX711_GetCountsPerKg(void) { return hx711_counts_per_kg; } /* 读取重量先减去空载零点再按当前系数换算为克。 */ HX711_Status HX711_ReadWeightG(int32_t *weight_g) { int32_t raw; int64_t grams; HX711_Status status; if (weight_g 0) { return HX711_BAD_ARGUMENT; } if (hx711_tared 0u) { return HX711_NOT_TARED; } status HX711_ReadRaw(raw, HX711_READ_TIMEOUT_MS); if (status ! HX711_OK) { return status; } grams (((int64_t)raw - hx711_offset) * 1000) / hx711_counts_per_kg; if ((grams 0x7FFFFFFFL) || (grams (-0x7FFFFFFFL - 1L))) { return HX711_BAD_CALIBRATION; } *weight_g (int32_t)grams; return HX711_OK; }HX711.H代码#ifndef HX711_H #define HX711_H #include stdint.h #ifdef __cplusplus extern C { #endif /* 接线PB3 接 HX711 的 SCKPB4 接 DTDOUT。 * 驱动固定读取 A 通道、128 倍增益每次读数共发送 25 个时钟脉冲。 * PB3、PB4 原为 JTAG 引脚初始化时关闭 JTAG保留 SWD 调试功能。 * 使用前先调用 delay_init()再调用 HX711_Init()。 * 10 kg 仅表示传感器量程不能由量程推算标定系数。 * 默认系数 755000 来自参考代码的估算准确称重必须先空载去皮 * 再使用已知质量的砝码标定并保存得到的系数。 */ #define HX711_CAPACITY_G 10000u #define HX711_DEFAULT_COUNTS_PER_KG 755000L #define HX711_READ_TIMEOUT_MS 200u /* 各接口的返回状态通信和标定失败时不会更新调用者的重量数据。 */ typedef enum { HX711_OK 0, /* 操作成功 */ HX711_TIMEOUT, /* 等待 DT 变低超时 */ HX711_BAD_ARGUMENT, /* 空指针、采样次数为零或砝码质量无效 */ HX711_NOT_TARED, /* 尚未完成空载去皮 */ HX711_BAD_CALIBRATION /* 系数为零或计算结果超出有效范围 */ } HX711_Status; /* 初始化 PB3/PB4关闭 JTAG 并保留 SWD再次调用会清除去皮值和运行时标定值。 */ void HX711_Init(void); /* 等待一次转换完成并读取 24 位有符号原始值。 * raw输出原始计数timeout_ms等待时间上限单位毫秒。 */ HX711_Status HX711_ReadRaw(int32_t *raw, uint16_t timeout_ms); /* 空载去皮对 samples 次原始读数取平均记录为零点samples 不可为零。 */ HX711_Status HX711_Tare(uint8_t samples); /* 已知质量标定必须先去皮再放上 known_mass_g 克砝码并取 samples 次平均。 * 系数单位为“计数/千克”断电后不会自动保存。 */ HX711_Status HX711_Calibrate(uint32_t known_mass_g, uint8_t samples); /* 直接设置或读取标定系数负系数可用于加重后原始计数下降的接线方向。 */ HX711_Status HX711_SetCountsPerKg(int32_t counts_per_kg); int32_t HX711_GetCountsPerKg(void); /* 读取一次重量单位为克必须先去皮结果会使用当前标定系数换算。 */ HX711_Status HX711_ReadWeightG(int32_t *weight_g); #ifdef __cplusplus } #endif #endif7.2 OLED屏幕显示内容说明正常称重显示效果HX711 10KG SCALE WEIGHT (EST): 00500g CALIBRATE FOR GRAMSEST标识代表使用默认标定系数必须实物标定后读数才准确。重量超过10000g屏幕提示OVER 10KG RANGE⚠️软件超限提示不能当做机械过载保护。初始化读取超时屏幕打印HX711 NOT READY运行过程读取超时READ TIMEOUT八、实物演示章节本章节适合实验实操按照步骤操作可以复现整个电子秤功能同时复现故障现象。实验准备物品STM32F103C8T6最小系统板、下载器HX711称重模块、10kg应变式称重传感器0.96寸I2C OLED显示屏杜邦线若干、已知重量砝码推荐1kg砝码秤盘、固定支架固定称重传感器正常标定演示步骤按照上面表格完成全部接线所有模块GND共地仔细核对PB3 PB4 PB8 PB9引脚不要接错。秤盘上面清空不放置任何重物上电单片机自动执行上电去皮操作。如果需要重新去皮把秤清空按下复位按键。将1000g砝码平稳放置在秤盘上代码中调用HX711_Calibrate(1000,8)执行标定。注意原版main.c不会自动执行标定需要自己添加代码触发标定动作。标定完成调用HX711_GetCountsPerKg()读取实际硬件得到的K值把这个数值替换到头文件HX711_DEFAULT_COUNTS_PER_KG重新编译烧录系数掉电保存。也可以运行时调用Set函数设置但是掉电丢失。拿其他已知重量物体放到秤盘观察OLED读数对比实际重量校验准确度。读数偏差大优先检查接线、传感器固定、零点是否清空。现象复现演示故障模拟断线故障演示断电拔掉HX711的DT信号线重新上电OLED屏幕输出HX711 NOT READY说明代码超时保护生效。实验结束恢复接线恢复接线前务必断电防止烧IO。空载数字轻微跳动空载的时候看到数值小幅正负跳变属于常见现象。原因来自机械振动、桌面抖动、电源噪声、零点温漂。解决思路传感器牢固固定、增加软件滑动平均滤波、使用纹波更小电源。没有标定直接运行直接烧录程序不放砝码标定屏幕虽然会输出克数但是数值是错的只能够观察重量变化趋势不能读取真实质量。实验小结HX711的24位AD只是硬件能力一套好用电子秤离不开良好机械结构、电源、正确标定。九、常见问题总结Q为什么硬件是10kg传感器代码读数不准 A10kg只是最大量程每只传感器输出灵敏度不一样必须实物砝码标定不能直接使用默认系数。QDT、SCK接好但是一直报HX711 NOT READY A①没有共地②PB3 PB4引脚没有关闭JTAG③模块供电异常④传感器EE‑接线断开。Q读数不停跳动 A检查传感器固定不要悬空晃动电源增加滤波电容代码增加多次采样平均滤波。Q上电忘记拿开秤盘重物 A零点R0会错误去皮失败所有重量全部读数错误上电秤盘必须空载。十、最后总结称重传感器依靠惠斯通电桥将重量转化为微弱差分电压HX711完成信号放大和24位AD转换输出原始计数STM32单片机通过去皮记录零点砝码标定获取换算系数最终把原始数字转化为真实重量通过OLED展示。整个项目重点不在于AD位数零点校准和实物标定才是电子秤可以准确测量的关键。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑