摘要
本篇博文讲解基于STM32F103C8T6、HX711称重采集模块、电阻应变式称重传感器以及OLED显示屏实现的电子秤项目。从硬件接线、传感器原理、HX711芯片工作逻辑、芯片串行读取时序,到STM32程序去皮、标定算法、实物演示、故障现象全部讲解,适合单片机课程实验、电子秤入门学习。
整体工作逻辑:称重传感器把重量转换成微小电压变化,HX711把微弱电压转换成数字计数,STM32经过去皮、砝码标定,将原始计数换算成实际重量,最后OLED屏幕显示重量数据。
视频讲解:STM32+HX711称重模块+OLED完整称重项目讲解
一、项目演示目标
本项目实现一套10kg量程简易电子秤。 完整信号流转:物体施加重量 → 称重传感器弹性体形变,应变片电阻改变 → 惠斯通电桥输出毫伏级差分电压 → HX711内部放大+24位ADC模数转换输出原始计数 → STM32读取计数,完成去皮零点补偿、砝码标定换算为克单位 → OLED屏幕输出重量数值,异常情况输出故障提示。
⚠️注意:本项目不使用STM32片内ADC,模数转换全部由HX711芯片完成。

二、硬件接线说明
主控芯片:STM32F103C8T6最小系统板 外设:HX711模块、10kg电阻应变式称重传感器、0.96寸I2C OLED显示屏
HX711与STM32接线
| VCC | 5V | HX711模块供电 |
| SCK | PB3 | STM32输出时钟,对应芯片PD_SCK |
| DT | PB4 | HX711数据输出DOUT,数据就绪信号 |
| GND | GND | 必须和主控共地 |
补充说明:PB3、PB4引脚上电默认复用JTAG调试功能,代码初始化函数HX711_Init()内部会关闭JTAG,保留SWD下载功能,再把引脚配置为普通GPIO口。更换HX711模块时,需要确认DT引脚输出电平是否和STM32兼容,不能只看模块供电是5V。
OLED与STM32接线(软件I2C)
| VCC | 5V | OLED供电 |
| SCL | PB8 | I2C时钟线 |
| SDA | PB9 | I2C数据线 |
| GND | GND | 共地 |
称重传感器接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芯片。
重点提醒:24位只是输出数字位数,不等于实际称重精度就可以达到2的24次方分之一。实际精度会受机械安装、电源噪声、温度、标定误差、振动干扰影响。
HX711转换输出速率由硬件RATE引脚决定,可以为10次/秒或者80次/秒,本项目没有软件控制RATE引脚,采样速率由购买的HX711模块硬件电路决定。
五、STM32读取HX711时序逻辑
两根信号线:
- DT(DOUT):数据就绪引脚,高电平代表AD转换未完成;DT拉低,代表数据就绪,可以读取。
- SCK(PD_SCK):单片机输出时钟信号。
单次读取完整流程:
补充:第25/26/27脉冲分别对应不同通道增益:A通道128倍、B通道32倍、A通道64倍,本项目固定使用25脉冲。 SCK引脚高电平维持时间不能超过60μs,否则HX711会进入掉电休眠,驱动代码读取期间临时关闭中断,读完再恢复中断。
六、去皮零点、标定换算算法
读到的原始计数不能直接作为重量,必须获取零点和标定系数两个关键参数。
计算公式:
K(计数/千克) = (砝码原始计数 – R0) × 1000 / M(砝码克数)
实际物体质量(克) = (当前原始计数 – R0) ×1000 / K
举个例子方便理解: 空载零点R0=120000;放1000g砝码读到计数920000; K=(920000‑120000)*1000 /1000 =800000 计数每千克; 当采集原始计数等于520000,重量=(520000‑120000)*1000 / 800000 =500g。
程序上电之后等待2s自动执行8次采样去皮,上电时刻秤盘上面必须清空,不能放物体。 当前代码只有去皮、标定的时候做8次平均;主循环显示读数没有滤波,如果想要抑制数字跳动,可以自己增加滑动平均滤波,但会牺牲刷新速度。
七、源代码
下面包含HX711、main代码
7.1 工程文件说明
| HARDWARE/HX711/HX711.C | HX711_Init() | 初始化PB3 PB4,关闭JTAG保留SWD调试 |
| HX711.C | HX711_ReadRaw() | 读取HX711原始24位计数,带超时保护 |
| HX711.C | HX711_Tare() | 空载多次采样,记录零点R0 |
| HX711.C | HX711_Calibrate() | 砝码标定,计算每千克计数值K |
| HX711.C | HX711_ReadWeightG() | 原始计数换算得到重量(单位g) |
| HARDWARE/HX711/HX711.H | HX711_DEFAULT_COUNTS_PER_KG | 默认标定系数宏定义 |
| USER/main.c | Demo_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 的 SCK,PB4 接 DT(DOUT)。
* 驱动固定读取 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
#endif
7.2 OLED屏幕显示内容说明
正常称重显示效果:
HX711 10KG SCALE
WEIGHT (EST):
+00500g
CALIBRATE FOR GRAMS
- EST标识:代表使用默认标定系数,必须实物标定后读数才准确。
- 重量超过10000g,屏幕提示:OVER 10KG RANGE,⚠️软件超限提示不能当做机械过载保护。
- 初始化读取超时,屏幕打印:HX711 NOT READY
- 运行过程读取超时:READ TIMEOUT
八、实物演示章节
本章节适合实验实操,按照步骤操作,可以复现整个电子秤功能,同时复现故障现象。
📌实验准备物品
📌正常标定演示步骤
注意:原版main.c不会自动执行标定,需要自己添加代码触发标定动作。

📌现象复现演示(故障模拟)
实验小结:HX711的24位AD只是硬件能力,一套好用电子秤离不开良好机械结构、电源、正确标定。
九、常见问题总结
十、最后总结
称重传感器依靠惠斯通电桥,将重量转化为微弱差分电压;HX711完成信号放大和24位AD转换输出原始计数;STM32单片机通过去皮记录零点,砝码标定获取换算系数,最终把原始数字转化为真实重量,通过OLED展示。整个项目重点不在于AD位数,零点校准和实物标定才是电子秤可以准确测量的关键。
网硕互联帮助中心






评论前必须登录!
注册