STM32F103C8T6 + HX711 + 电子秤模块:从调零到稳定显示的完整代码与避坑指南
2026/6/14 5:02:08 网站建设 项目流程

STM32F103C8T6与HX711电子秤开发实战:从硬件对接到精准测量

1. 项目概述与硬件选型

在嵌入式开发领域,重量测量是一个经典而实用的应用场景。使用STM32F103C8T6搭配HX711芯片构建电子秤系统,不仅成本低廉,还能深入理解模拟信号采集与数字滤波的核心技术。这个蓝色药丸大小的STM32芯片,虽然只有64KB Flash和20KB RAM,但完全能够胜任电子秤的数据处理任务。

核心硬件组件

  • STM32F103C8T6最小系统板(俗称"蓝药丸")
  • HX711 24位ADC模块
  • 应变式称重传感器(常见规格5kg/10kg)
  • 0.96寸OLED显示屏(可选,用于本地显示)
  • 精密可调电阻(用于校准)

提示:购买称重传感器时注意额定载荷,过小的量程会导致传感器损坏,过大的量程则影响测量精度。一般DIY项目选择5kg规格比较合适。

硬件连接示意图:

信号线STM32引脚HX711引脚备注
VCC3.3VVCC也可接5V,但需电平匹配
GNDGNDGND共地至关重要
DT (数据)PA1DOUT配置为上拉输入
SCK (时钟)PA2PD_SCK配置为推挽输出

2. HX711驱动开发与底层配置

HX711作为专为电子秤设计的ADC芯片,其驱动时序是开发的关键。与常规SPI/I2C器件不同,HX711采用独特的同步串行协议,需要精确控制时钟边沿。

驱动开发要点

  1. 初始化GPIO:
void HX711_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; // SCK引脚配置为推挽输出 GPIO_InitStruct.Pin = GPIO_PIN_2; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // DT引脚配置为上拉输入 GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); // 初始时钟低电平 }
  1. 数据读取函数优化版(带超时检测):
int32_t HX711_ReadData(void) { uint32_t timeout = 100000; while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET) { if (--timeout == 0) return 0; // 超时返回0 } int32_t value = 0; for (uint8_t i = 0; i < 24; i++) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); delay_us(1); value <<= 1; HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); delay_us(1); if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET) { value++; } } // 第25个脉冲选择通道和增益 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); delay_us(1); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); return value ^ 0x800000; // 补码转换 }

常见问题排查表:

现象可能原因解决方案
读取值始终为0接线错误或接触不良检查DT/SCK连接,确认共地
数值跳动幅度大电源噪声或机械振动增加滤波电容,稳定安装结构
测量值随温度漂移传感器温度补偿不足采用温度传感器进行补偿
响应速度慢滤波参数过于保守调整卡尔曼滤波的Q/R参数

3. 传感器校准与滤波算法

校准是电子秤准确度的关键。我们需要分两步进行:硬件零点和满量程校准。

校准流程

  1. 零点校准(无负载时):
void Calibrate_Tare(void) { uint32_t sum = 0; for (int i = 0; i < 10; i++) { sum += HX711_ReadData(); HAL_Delay(100); } config.tare_value = sum / 10; // 存储零点值 Save_Config(); // 保存到EEPROM }
  1. 满量程校准(施加已知重量):
void Calibrate_Scale(float known_weight) { uint32_t sum = 0; for (int i = 0; i < 10; i++) { sum += HX711_ReadData(); HAL_Delay(100); } config.scale = (sum / 10 - config.tare_value) / known_weight; Save_Config(); // 保存到EEPROM }

改进型卡尔曼滤波实现

typedef struct { float q; // 过程噪声协方差 float r; // 测量噪声协方差 float p; // 估计误差协方差 float k; // 卡尔曼增益 float x; // 估计值 } KalmanFilter; float Kalman_Update(KalmanFilter* kf, float measurement) { // 预测 kf->p = kf->p + kf->q; // 更新 kf->k = kf->p / (kf->p + kf->r); kf->x = kf->x + kf->k * (measurement - kf->x); kf->p = (1 - kf->k) * kf->p; return kf->x; } // 初始化示例 KalmanFilter kf = { .q = 0.001f, .r = 0.01f, .p = 1.0f, .x = 0.0f };

滤波参数调优建议:

  • 快速响应场景(如动态称重):

    • Q = 0.1, R = 1.0
    • 牺牲平滑性换取速度
  • 高精度静态测量

    • Q = 0.001, R = 0.01
    • 响应慢但数据稳定

4. 系统集成与性能优化

将各个模块整合为完整系统时,需要考虑任务调度、显示刷新和用户交互的协调。

主程序架构

void main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); HX711_Init(); OLED_Init(); Load_Config(); // 从EEPROM加载校准参数 KalmanFilter kf = { .q = 0.003f, .r = 0.03f, .p = 1.0f }; while (1) { int32_t raw = HX711_ReadData(); float filtered = Kalman_Update(&kf, (raw - config.tare_value) / config.scale); // 显示刷新(每秒4次) static uint32_t last_disp = 0; if (HAL_GetTick() - last_disp > 250) { OLED_ShowWeight(filtered); last_disp = HAL_GetTick(); } // 按键处理 if (Read_Key() == KEY_TARE) { Calibrate_Tare(); } HAL_Delay(10); } }

电源管理技巧

  1. 在VCC和GND之间添加100μF电解电容和0.1μF陶瓷电容
  2. 使用LDO稳压器(如AMS1117-3.3)而非开关电源
  3. 传感器供电线路单独走线,避免数字噪声干扰

机械安装注意事项

  • 称重平台与传感器之间采用刚性连接
  • 保证受力方向与传感器敏感轴一致
  • 避免侧向力影响测量精度
  • 使用橡胶垫隔离环境振动

5. 高级功能扩展

基础功能实现后,可以考虑添加以下增强功能:

多传感器并联配置

float Read_MultiSensors(void) { float sum = 0; for (int i = 0; i < SENSOR_COUNT; i++) { Select_Sensor(i); // 切换多路复用器 sum += (HX711_ReadData() - config.tare[i]) / config.scale[i]; } return sum / SENSOR_COUNT; }

蓝牙APP监控(基于HC-05模块)

void Send_To_App(float weight) { char buf[32]; snprintf(buf, sizeof(buf), "{\"weight\":%.2f}\r\n", weight); HAL_UART_Transmit(&huart1, (uint8_t*)buf, strlen(buf), HAL_MAX_DELAY); }

自动休眠与唤醒

void Check_Sleep(void) { static float last_weight = 0; static uint32_t stable_time = 0; if (fabs(current_weight - last_weight) < 0.5f) { stable_time += 100; if (stable_time > 10000) { // 10秒无变化 Enter_LowPowerMode(); } } else { stable_time = 0; } last_weight = current_weight; }

实际项目中,我发现称重传感器的预热时间对精度影响很大。冷启动后前10分钟的读数会漂移约0.5%,解决方案是在首次校准时等待足够的热机时间,或者采用温度补偿算法。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询