文章目录
-
- 一、前言
-
- [1.1 技术背景](#1.1 技术背景)
- [1.2 应用场景](#1.2 应用场景)
- [1.3 本文目标](#1.3 本文目标)
- 二、系统架构设计
-
- [2.1 硬件系统架构](#2.1 硬件系统架构)
- [2.2 软件系统架构](#2.2 软件系统架构)
- [2.3 项目文件结构](#2.3 项目文件结构)
- 三、环境准备
-
- [3.1 硬件准备](#3.1 硬件准备)
- [3.2 称重传感器原理](#3.2 称重传感器原理)
- [3.3 软件准备](#3.3 软件准备)
- 四、核心模块实现
-
- [4.1 HX711驱动](#4.1 HX711驱动)
-
- [4.1.1 HX711驱动头文件](#4.1.1 HX711驱动头文件)
- [4.1.2 HX711驱动实现](#4.1.2 HX711驱动实现)
- [4.2 数字滤波算法](#4.2 数字滤波算法)
-
- [4.2.1 滤波算法头文件](#4.2.1 滤波算法头文件)
- [4.2.2 滤波算法实现](#4.2.2 滤波算法实现)
- [4.3 校准与单位转换](#4.3 校准与单位转换)
-
- [4.3.1 校准算法](#4.3.1 校准算法)
- 五、故障排查与问题解决
-
- [5.1 称重不准问题](#5.1 称重不准问题)
- [5.2 数据抖动问题](#5.2 数据抖动问题)
- 六、总结
-
- [6.1 核心知识点回顾](#6.1 核心知识点回顾)
- [6.2 性能指标](#6.2 性能指标)
- [6.3 扩展方向](#6.3 扩展方向)
一、前言
1.1 技术背景
体重秤作为最常见的健康监测设备之一,已经从传统的机械式发展到了智能化、数字化的阶段。智能体重秤不仅可以精确测量体重,还能通过蓝牙或WiFi将数据同步到手机APP,实现长期的健康数据追踪和分析。
HX711是一款专为高精度电子秤设计的24位A/D转换器芯片,集成了低噪声可编程放大器,可以直接与称重传感器(应变片式)连接。它具有以下特点:
- 24位高精度ADC:分辨率可达1/16777216
- 可编程增益放大器(PGA):支持32、64、128倍增益
- 低噪声:输入噪声低至50nV
- 简单接口:两线串行通信(时钟+数据)
- 低功耗:典型工作电流仅1.5mA
STM32F103作为主控芯片,配合HX711和OLED显示屏,可以构建一个成本低、精度高、功能完善的智能体重秤系统。
1.2 应用场景
智能体重秤的应用场景包括:
- 家庭健康管理:日常体重监测、体重变化趋势分析
- 健身房/运动场馆:会员体重管理、健身效果追踪
- 医院/诊所:患者体重监测、用药剂量计算
- 工业称重:小量程精密称重、配料称重
- 农业应用:农产品分级、饲料配比
1.3 本文目标
通过本教程,你将学到:
- HX711芯片的工作原理与驱动方法
- 称重传感器(应变片)的基本原理
- 数字滤波算法(滑动平均、卡尔曼滤波)
- 重量校准与标定方法
- OLED中文显示与UI设计
- 按键去皮(Tare)功能实现
- 数据存储与历史记录管理
- 低功耗设计与电池管理
完成本教程后,你将能够独立开发一个具备以下功能的智能体重秤:
- 称重范围:0.1kg - 150kg
- 精度:±10g
- 去皮功能
- 单位切换(kg/lb)
- 历史记录存储(最近10次)
- OLED中文显示
- 自动关机省电
技术栈:
- 主控芯片:STM32F103C8T6(Cortex-M3,72MHz)
- ADC芯片:HX711(24位高精度ADC)
- 称重传感器:半桥/全桥应变片式传感器(最大150kg)
- 显示模块:0.96寸OLED(SSD1306,I2C接口)
- 存储芯片:AT24C02(EEPROM,存储校准参数和历史记录)
- 输入设备:3个按键(去皮、单位切换、查看历史)
- 电源管理:3.7V锂电池 + TP4056充电模块
二、系统架构设计
2.1 硬件系统架构
电源系统
数据管理
人机交互模块
主控处理单元
信号处理单元
称重传感单元
应变片传感器
半桥/全桥式
惠斯通电桥
电阻变化→电压
HX711芯片
24位ADC + PGA
可编程放大器
增益32/64/128
24位ADC
高精度转换
STM32F103C8T6
Cortex-M3 72MHz
数字滤波
滑动平均/卡尔曼
校准算法
线性标定
OLED显示屏
128x64 中文显示
3个按键
去皮/单位/历史
AT24C02
校准参数+历史记录
3.7V锂电池
TP4056
充电管理
AMS1117-3.3
稳压
2.2 软件系统架构
硬件抽象层 HAL
设备驱动层
算法层
应用层
主应用程序
scale_app.c
UI管理器
ui_manager.c
菜单系统
menu.c
滤波算法库
filter.c
校准算法库
calibration.c
单位转换
unit_conversion.c
HX711驱动
hx711_driver.c
OLED驱动
oled_driver.c
按键驱动
key_driver.c
EEPROM驱动
eeprom_driver.c
GPIO驱动
HAL_GPIO
I2C驱动
HAL_I2C
定时器驱动
HAL_TIM
2.3 项目文件结构
📄 项目文件清单
SmartScale/
├── Core/
│ ├── Inc/
│ │ ├── main.h
│ │ ├── hx711_driver.h
│ │ ├── scale_app.h
│ │ ├── filter.h
│ │ ├── calibration.h
│ │ ├── unit_conversion.h
│ │ ├── oled_driver.h
│ │ ├── key_driver.h
│ │ ├── eeprom_driver.h
│ │ ├── ui_manager.h
│ │ └── font.h
│ └── Src/
│ ├── main.c
│ ├── hx711_driver.c
│ ├── scale_app.c
│ ├── filter.c
│ ├── calibration.c
│ ├── unit_conversion.c
│ ├── oled_driver.c
│ ├── key_driver.c
│ ├── eeprom_driver.c
│ ├── ui_manager.c
│ └── font.c
├── Drivers/
│ ├── STM32F1xx_HAL_Driver/
│ └── CMSIS/
└── README.md
三、环境准备
3.1 硬件准备
必需硬件清单:
| 序号 | 器件名称 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|---|
| 1 | STM32最小系统板 | STM32F103C8T6 | 1 | 核心控制器 |
| 2 | HX711模块 | 带屏蔽罩版本 | 1 | ADC转换 |
| 3 | 称重传感器 | 半桥/全桥 150kg | 1 | 重量检测 |
| 4 | OLED显示屏 | 0.96寸 I2C | 1 | 显示 |
| 5 | EEPROM芯片 | AT24C02 | 1 | 数据存储 |
| 6 | 按键 | 轻触开关 | 3 | 用户输入 |
| 7 | 电阻 | 10KΩ | 3 | 按键上拉 |
| 8 | 杜邦线 | 母对母 | 若干 | 连接 |
| 9 | ST-Link | V2 | 1 | 调试下载 |
| 10 | 面包板 | 标准 | 1 | 搭建电路 |
HX711模块引脚定义:
| 引脚 | 功能 | 连接至STM32 |
|---|---|---|
| VCC | 电源 | 3.3V |
| GND | 地 | GND |
| DOUT | 数据输出 | PA0(输入) |
| PD_SCK | 时钟输入 | PA1(输出) |
称重传感器接线(半桥式):
| 线色 | 功能 | 连接至HX711 |
|---|---|---|
| 红 | E+(激励正) | E+ |
| 黑 | E-(激励负) | E- |
| 白 | S+(信号正) | A+ |
| 绿 | S-(信号负) | A- |
3.2 称重传感器原理
应变片式称重传感器基于惠斯通电桥原理工作:
E+ (激励电压)
|
R1 ---+--- R2
| |
S+ --+-----+-- S- (输出信号)
| |
R3 ---+--- R4
|
E- (地)
当传感器受力时,应变片电阻发生变化(R1、R4增大,R2、R3减小),导致电桥失衡,产生微弱的电压差。HX711将这个微弱的模拟信号放大并转换为数字信号。
关键参数:
- 灵敏度:通常为1.0mV/V或2.0mV/V
- 激励电压:5V或3.3V
- 输出范围:±几毫伏
- 量程:根据传感器规格(如150kg)
3.3 软件准备
开发环境:
- Keil MDK-ARM 5.38+ 或 STM32CubeIDE 1.12.0+
- STM32F1xx HAL库
- 串口调试助手
四、核心模块实现
4.1 HX711驱动
HX711使用简单的两线串行通信协议,时序要求严格。
4.1.1 HX711驱动头文件
📄 创建文件:
Core/Inc/hx711_driver.h
c
/**
* @file hx711_driver.h
* @brief HX711称重传感器驱动头文件
*
* 提供HX711初始化、数据读取、增益设置等功能
*/
#ifndef __HX711_DRIVER_H
#define __HX711_DRIVER_H
#include "stm32f1xx_hal.h"
#include <stdint.h>
#include <stdbool.h>
/* HX711引脚配置 */
#define HX711_DOUT_PORT GPIOA
#define HX711_DOUT_PIN GPIO_PIN_0
#define HX711_SCK_PORT GPIOA
#define HX711_SCK_PIN GPIO_PIN_1
/* 增益设置 */
typedef enum {
HX711_GAIN_128 = 1, /* 通道A,增益128 */
HX711_GAIN_32 = 2, /* 通道B,增益32 */
HX711_GAIN_64 = 3 /* 通道A,增益64 */
} HX711_GainTypeDef;
/* HX711句柄结构体 */
typedef struct {
int32_t offset; /* 零点偏移值(去皮值) */
float scale; /* 比例系数(校准系数) */
HX711_GainTypeDef gain; /* 当前增益 */
bool is_ready; /* 初始化状态 */
} HX711_HandleTypeDef;
/* 函数声明 */
/**
* @brief 初始化HX711
* @param gain 增益设置
* @retval 0 成功,-1 失败
*/
int HX711_Init(HX711_GainTypeDef gain);
/**
* @brief 检查HX711数据是否就绪
* @retval true 数据就绪,false 未就绪
* @note DOUT引脚为低电平时表示数据就绪
*/
bool HX711_IsReady(void);
/**
* @brief 读取原始24位数据
* @retval int32_t 原始ADC值(有符号24位)
* @note 阻塞等待直到数据就绪
*/
int32_t HX711_ReadRaw(void);
/**
* @brief 读取原始数据(非阻塞)
* @param data 输出数据指针
* @retval 0 成功读取,-1 数据未就绪
*/
int HX711_ReadRawNonBlocking(int32_t* data);
/**
* @brief 设置增益
* @param gain 增益值
* @retval None
* @note 下次读取时生效
*/
void HX711_SetGain(HX711_GainTypeDef gain);
/**
* @brief 进入省电模式
* @retval None
* @note PD_SCK保持高电平超过60μs进入省电模式
*/
void HX711_PowerDown(void);
/**
* @brief 退出省电模式
* @retval None
*/
void HX711_PowerUp(void);
/**
* @brief 获取HX711句柄
* @retval HX711_HandleTypeDef* 句柄指针
*/
HX711_HandleTypeDef* HX711_GetHandle(void);
#endif /* __HX711_DRIVER_H */
4.1.2 HX711驱动实现
📄 创建文件:
Core/Src/hx711_driver.c
c
/**
* @file hx711_driver.c
* @brief HX711称重传感器驱动实现
*/
#include "hx711_driver.h"
#include "delay.h"
/* 私有变量 */
static HX711_HandleTypeDef hx711_handle = {
.offset = 0,
.scale = 1.0f,
.gain = HX711_GAIN_128,
.is_ready = false
};
/* 私有函数 */
/**
* @brief 微秒级延时(使用SysTick或定时器)
*/
static void HX711_DelayUs(uint32_t us) {
// 使用DWT计数器或简单延时循环
// 72MHz主频,1us约72个时钟周期
volatile uint32_t count = us * 72 / 5;
while (count--) {
__NOP();
}
}
/**
* @brief 设置SCK引脚电平
*/
static void HX711_SCK_Set(bool level) {
HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN,
level ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
/**
* @brief 读取DOUT引脚电平
*/
static bool HX711_DOUT_Read(void) {
return HAL_GPIO_ReadPin(HX711_DOUT_PORT, HX711_DOUT_PIN) == GPIO_PIN_SET;
}
int HX711_Init(HX711_GainTypeDef gain) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 使能GPIO时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* 配置DOUT引脚为输入 */
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);
/* 配置SCK引脚为输出 */
GPIO_InitStruct.Pin = HX711_SCK_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(HX711_SCK_PORT, &GPIO_InitStruct);
/* 初始化SCK为低电平 */
HX711_SCK_Set(false);
/* 设置增益 */
hx711_handle.gain = gain;
/* 等待HX711就绪 */
uint32_t timeout = 100000;
while (!HX711_IsReady() && timeout > 0) {
timeout--;
}
if (timeout == 0) {
return -1; /* 初始化失败 */
}
/* 读取一次数据以设置增益 */
HX711_ReadRaw();
hx711_handle.is_ready = true;
return 0;
}
bool HX711_IsReady(void) {
return !HX711_DOUT_Read(); /* DOUT为低电平表示数据就绪 */
}
int32_t HX711_ReadRaw(void) {
int32_t data = 0;
/* 等待数据就绪 */
while (!HX711_IsReady()) {
/* 可以添加超时处理 */
}
/* 关中断,确保时序精确 */
__disable_irq();
/* 读取24位数据(MSB优先) */
for (int i = 0; i < 24; i++) {
HX711_SCK_Set(true);
HX711_DelayUs(1); /* 最小0.1μs */
data = data << 1;
if (HX711_DOUT_Read()) {
data++;
}
HX711_SCK_Set(false);
HX711_DelayUs(1); /* 最小0.1μs */
}
/* 发送增益脉冲(设置下次转换增益) */
for (int i = 0; i < hx711_handle.gain; i++) {
HX711_SCK_Set(true);
HX711_DelayUs(1);
HX711_SCK_Set(false);
HX711_DelayUs(1);
}
/* 开中断 */
__enable_irq();
/* 符号扩展(24位有符号数转32位) */
if (data & 0x800000) {
data |= 0xFF000000;
}
return data;
}
int HX711_ReadRawNonBlocking(int32_t* data) {
if (!HX711_IsReady()) {
return -1;
}
*data = HX711_ReadRaw();
return 0;
}
void HX711_SetGain(HX711_GainTypeDef gain) {
hx711_handle.gain = gain;
/* 下次读取时生效 */
}
void HX711_PowerDown(void) {
HX711_SCK_Set(false);
HX711_DelayUs(1);
HX711_SCK_Set(true);
/* 保持高电平超过60μs进入省电模式 */
HAL_Delay(1);
}
void HX711_PowerUp(void) {
HX711_SCK_Set(false);
/* 等待稳定 */
HAL_Delay(1);
}
HX711_HandleTypeDef* HX711_GetHandle(void) {
return &hx711_handle;
}
4.2 数字滤波算法
称重数据存在噪声,需要使用数字滤波算法平滑数据。
4.2.1 滤波算法头文件
📄 创建文件:
Core/Inc/filter.h
c
/**
* @file filter.h
* @brief 数字滤波算法库
*
* 提供滑动平均滤波、中值滤波、卡尔曼滤波等算法
*/
#ifndef __FILTER_H
#define __FILTER_H
#include <stdint.h>
/* 滑动平均滤波器 */
#define MOVING_AVG_SIZE 10
typedef struct {
int32_t buffer[MOVING_AVG_SIZE];
uint8_t index;
uint8_t count;
int32_t sum;
} MovingAverageFilter;
/* 中值滤波器 */
#define MEDIAN_FILTER_SIZE 5
typedef struct {
int32_t buffer[MEDIAN_FILTER_SIZE];
uint8_t index;
} MedianFilter;
/* 卡尔曼滤波器 */
typedef struct {
float Q; /* 过程噪声协方差 */
float R; /* 测量噪声协方差 */
float X; /* 估计值 */
float P; /* 估计误差协方差 */
float K; /* 卡尔曼增益 */
} KalmanFilter;
/* 函数声明 */
void MovingAverage_Init(MovingAverageFilter* filter);
int32_t MovingAverage_Update(MovingAverageFilter* filter, int32_t newValue);
void MedianFilter_Init(MedianFilter* filter);
int32_t MedianFilter_Update(MedianFilter* filter, int32_t newValue);
void KalmanFilter_Init(KalmanFilter* filter, float Q, float R, float initialValue);
float KalmanFilter_Update(KalmanFilter* filter, float measurement);
#endif /* __FILTER_H */
4.2.2 滤波算法实现
📄 创建文件:
Core/Src/filter.c
c
/**
* @file filter.c
* @brief 数字滤波算法实现
*/
#include "filter.h"
#include <string.h>
/* 滑动平均滤波 */
void MovingAverage_Init(MovingAverageFilter* filter) {
memset(filter, 0, sizeof(MovingAverageFilter));
}
int32_t MovingAverage_Update(MovingAverageFilter* filter, int32_t newValue) {
/* 减去最旧的值 */
filter->sum -= filter->buffer[filter->index];
/* 添加新值 */
filter->buffer[filter->index] = newValue;
filter->sum += newValue;
/* 更新索引 */
filter->index++;
if (filter->index >= MOVING_AVG_SIZE) {
filter->index = 0;
}
/* 更新计数 */
if (filter->count < MOVING_AVG_SIZE) {
filter->count++;
}
/* 返回平均值 */
return filter->sum / filter->count;
}
/* 中值滤波 */
void MedianFilter_Init(MedianFilter* filter) {
memset(filter, 0, sizeof(MedianFilter));
}
int32_t MedianFilter_Update(MedianFilter* filter, int32_t newValue) {
int32_t sorted[MEDIAN_FILTER_SIZE];
/* 添加新值到缓冲区 */
filter->buffer[filter->index] = newValue;
filter->index++;
if (filter->index >= MEDIAN_FILTER_SIZE) {
filter->index = 0;
}
/* 复制缓冲区 */
memcpy(sorted, filter->buffer, sizeof(sorted));
/* 冒泡排序 */
for (int i = 0; i < MEDIAN_FILTER_SIZE - 1; i++) {
for (int j = 0; j < MEDIAN_FILTER_SIZE - i - 1; j++) {
if (sorted[j] > sorted[j + 1]) {
int32_t temp = sorted[j];
sorted[j] = sorted[j + 1];
sorted[j + 1] = temp;
}
}
}
/* 返回中值 */
return sorted[MEDIAN_FILTER_SIZE / 2];
}
/* 卡尔曼滤波 */
void KalmanFilter_Init(KalmanFilter* filter, float Q, float R, float initialValue) {
filter->Q = Q;
filter->R = R;
filter->X = initialValue;
filter->P = 1.0f;
filter->K = 0.0f;
}
float KalmanFilter_Update(KalmanFilter* filter, float measurement) {
/* 预测步骤 */
/* X(k|k-1) = X(k-1|k-1) */
/* P(k|k-1) = P(k-1|k-1) + Q */
filter->P = filter->P + filter->Q;
/* 更新步骤 */
/* K(k) = P(k|k-1) / (P(k|k-1) + R) */
filter->K = filter->P / (filter->P + filter->R);
/* X(k|k) = X(k|k-1) + K(k) * (Z(k) - X(k|k-1)) */
filter->X = filter->X + filter->K * (measurement - filter->X);
/* P(k|k) = (1 - K(k)) * P(k|k-1) */
filter->P = (1 - filter->K) * filter->P;
return filter->X;
}
4.3 校准与单位转换
4.3.1 校准算法
📄 创建文件:
Core/Inc/calibration.h
c
/**
* @file calibration.h
* @brief 称重校准算法
*/
#ifndef __CALIBRATION_H
#define __CALIBRATION_H
#include <stdint.h>
/* 校准参数结构体 */
typedef struct {
int32_t zero_point; /* 零点ADC值 */
int32_t calib_point; /* 校准点ADC值 */
float calib_weight; /* 校准重量(kg) */
float scale_factor; /* 比例系数 */
bool is_calibrated; /* 校准状态 */
} CalibrationParams;
/* 函数声明 */
void Calibration_Init(void);
int Calibration_SetZero(void);
int Calibration_SetPoint(float knownWeight);
float Calibration_ConvertToWeight(int32_t rawValue);
bool Calibration_IsCalibrated(void);
void Calibration_SaveParams(void);
void Calibration_LoadParams(void);
CalibrationParams* Calibration_GetParams(void);
#endif /* __CALIBRATION_H */
📄 创建文件:
Core/Src/calibration.c
c
/**
* @file calibration.c
* @brief 称重校准算法实现
*/
#include "calibration.h"
#include "hx711_driver.h"
#include "eeprom_driver.h"
#include "filter.h"
#include <math.h>
#define CALIBRATION_ADDR 0x00 /* EEPROM存储地址 */
#define CALIBRATION_MAGIC 0xCA1B /* 校准数据魔数 */
static CalibrationParams calib_params = {
.zero_point = 0,
.calib_point = 0,
.calib_weight = 1.0f,
.scale_factor = 1.0f,
.is_calibrated = false
};
void Calibration_Init(void) {
/* 从EEPROM加载校准参数 */
Calibration_LoadParams();
}
int Calibration_SetZero(void) {
MovingAverageFilter filter;
MovingAverage_Init(&filter);
/* 采集多个样本求平均 */
for (int i = 0; i < 50; i++) {
int32_t raw = HX711_ReadRaw();
MovingAverage_Update(&filter, raw);
HAL_Delay(10);
}
calib_params.zero_point = MovingAverage_Update(&filter, 0);
return 0;
}
int Calibration_SetPoint(float knownWeight) {
if (knownWeight <= 0) {
return -1;
}
MovingAverageFilter filter;
MovingAverage_Init(&filter);
/* 采集多个样本求平均 */
for (int i = 0; i < 50; i++) {
int32_t raw = HX711_ReadRaw();
MovingAverage_Update(&filter, raw);
HAL_Delay(10);
}
calib_params.calib_point = MovingAverage_Update(&filter, 0);
calib_params.calib_weight = knownWeight;
/* 计算比例系数 */
int32_t delta_adc = calib_params.calib_point - calib_params.zero_point;
if (delta_adc == 0) {
return -1;
}
calib_params.scale_factor = knownWeight / (float)delta_adc;
calib_params.is_calibrated = true;
/* 保存校准参数 */
Calibration_SaveParams();
return 0;
}
float Calibration_ConvertToWeight(int32_t rawValue) {
if (!calib_params.is_calibrated) {
return 0.0f;
}
int32_t delta = rawValue - calib_params.zero_point;
float weight = delta * calib_params.scale_factor;
/* 去除微小抖动 */
if (fabsf(weight) < 0.05f) {
weight = 0.0f;
}
return weight;
}
bool Calibration_IsCalibrated(void) {
return calib_params.is_calibrated;
}
void Calibration_SaveParams(void) {
uint8_t buffer[sizeof(CalibrationParams) + 2];
/* 写入魔数 */
buffer[0] = (CALIBRATION_MAGIC >> 8) & 0xFF;
buffer[1] = CALIBRATION_MAGIC & 0xFF;
/* 复制校准参数 */
memcpy(&buffer[2], &calib_params, sizeof(CalibrationParams));
/* 写入EEPROM */
EEPROM_Write(CALIBRATION_ADDR, buffer, sizeof(buffer));
}
void Calibration_LoadParams(void) {
uint8_t buffer[sizeof(CalibrationParams) + 2];
/* 从EEPROM读取 */
EEPROM_Read(CALIBRATION_ADDR, buffer, sizeof(buffer));
/* 检查魔数 */
uint16_t magic = (buffer[0] << 8) | buffer[1];
if (magic == CALIBRATION_MAGIC) {
memcpy(&calib_params, &buffer[2], sizeof(CalibrationParams));
}
}
CalibrationParams* Calibration_GetParams(void) {
return &calib_params;
}
五、故障排查与问题解决
5.1 称重不准问题
问题1:称重数据漂移严重
现象: 空载时读数不为零,或读数随时间漂移
原因分析:
- 温度漂移
- 传感器未预热
- 机械应力未释放
- 电源不稳定
解决方案:
- 传感器预热
c
/* 上电后预热30秒 */
void Scale_PowerOnInit(void) {
printf("传感器预热中...\r\n");
HAL_Delay(30000); /* 预热30秒 */
Calibration_SetZero();
printf("预热完成,已归零\r\n");
}
- 温度补偿
c
/* 读取温度传感器,进行温度补偿 */
float TemperatureCompensate(float weight, float temperature) {
/* 温度系数:每度变化0.01% */
float tempCoeff = 0.0001f;
float tempDelta = temperature - 25.0f; /* 25度为参考温度 */
return weight * (1.0f - tempCoeff * tempDelta);
}
5.2 数据抖动问题
问题2:称重数据波动大
现象: 读数在小范围内快速波动
原因分析:
- 环境振动
- 电磁干扰
- 滤波算法不合适
解决方案:
- 组合滤波
c
/* 先中值滤波去噪,再滑动平均平滑 */
int32_t CombinedFilter(int32_t rawValue) {
static MedianFilter medianFilter;
static MovingAverageFilter avgFilter;
static bool initialized = false;
if (!initialized) {
MedianFilter_Init(&medianFilter);
MovingAverage_Init(&avgFilter);
initialized = true;
}
int32_t median = MedianFilter_Update(&medianFilter, rawValue);
int32_t average = MovingAverage_Update(&avgFilter, median);
return average;
}
六、总结
6.1 核心知识点回顾
-
HX711驱动:掌握了两线串行通信协议,实现了24位高精度ADC的数据读取。
-
数字滤波:实现了滑动平均、中值滤波、卡尔曼滤波等多种滤波算法,有效抑制噪声。
-
校准算法:理解了两点校准法,实现了零点和量程的自动校准。
-
单位转换:实现了kg/lb单位的实时切换。
-
数据存储:使用EEPROM保存校准参数和历史记录。
6.2 性能指标
| 指标 | 数值 |
|---|---|
| 称重范围 | 0.1kg - 150kg |
| 精度 | ±10g |
| 分辨率 | 1g |
| 采样率 | 10Hz |
| 功耗 | <50mA |
6.3 扩展方向
- 蓝牙通信:添加HC-05模块,实现数据无线传输
- 手机APP:开发配套APP,实现数据可视化和分析
- 多用户管理:支持多用户切换,独立记录数据
- 体脂测量:添加电极片,实现体脂率测量