HX711压力传感器实战:基于STM32 HAL库的精准称重系统设计与校准 1. HX711压力传感器与STM32开发基础第一次接触HX711压力传感器时我被它的小巧体积和强大功能惊艳到了。这个只有指甲盖大小的芯片居然能实现24位精度的模数转换完全能满足厨房秤、工业称重等场景的需求。HX711本质上是一个带放大功能的ADC芯片专门为应变片式称重传感器设计。硬件连接其实非常简单你只需要准备一块STM32开发板推荐STM32F103C8T6便宜又好用HX711模块淘宝10块钱左右压力传感器通常随HX711一起购买接线时要注意HX711的VCC接3.3V或5VGND接地DOUT接STM32的GPIO输入引脚SCK接GPIO输出引脚。我用的是PB0和PB1你完全可以根据自己开发板的空闲引脚调整。2. HAL库环境搭建与GPIO配置使用STM32CubeMX可以快速生成HAL库工程。新建工程选择你的芯片型号后在Pinout界面配置两个GPIO将PB0设置为GPIO_Output推挽输出高速模式将PB1设置为GPIO_Input上拉输入生成代码后在main.c中添加HX711的初始化函数void Init_HX711pin(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; __HAL_RCC_GPIOB_CLK_ENABLE(); // SCK引脚配置 GPIO_InitStruct.Pin GPIO_PIN_0; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, GPIO_InitStruct); // DOUT引脚配置 GPIO_InitStruct.Pin GPIO_PIN_1; GPIO_InitStruct.Mode GPIO_MODE_INPUT; GPIO_InitStruct.Pull GPIO_PULLUP; HAL_GPIO_Init(GPIOB, GPIO_InitStruct); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); }实测发现HAL库的GPIO操作比标准库稍慢所以在时序要求严格的场合可以直接操作寄存器#define HX711_SCK_SET() (GPIOB-BSRR GPIO_PIN_0) #define HX711_SCK_RESET() (GPIOB-BRR GPIO_PIN_0) #define HX711_DOUT_READ() (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1))3. HX711数据读取与增益设置HX711的数据读取需要严格遵循时序。芯片支持三种增益设置128/64/32对应不同的应用场景。我以最常用的128倍增益为例uint32_t HX711_Read(void) { uint32_t count 0; uint8_t i; HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) GPIO_PIN_SET); for(i0; i24; i) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); count 1; HAL_Delay(1); // 实际使用中应该用更精确的延时 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) GPIO_PIN_SET) count; HAL_Delay(1); } // 第25个脉冲选择增益 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); count ^ 0x800000; // 转换补码 HAL_Delay(1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); return count; }几个关键点需要注意必须先等待DOUT变低才开始读取每个时钟脉冲后需要至少1us的延时第25个脉冲决定下次转换的增益返回的数据是补码形式需要异或0x800000转换4. 称重算法实现与去皮功能实际称重时需要先获取空载时的基准值去皮然后计算实际重量uint32_t tare_weight 0; // 去皮重量 float calibration_factor 106.5f; // 校准系数 float Get_Weight(void) { uint32_t raw_data HX711_Read(); if(raw_data tare_weight) { float weight (raw_data - tare_weight) / calibration_factor; return weight 0 ? weight : 0; } return 0; } void Tare(void) { tare_weight HX711_Read(); }校准系数需要根据实际传感器调整。我的经验是先用标准砝码测试空载时调用Tare()去皮放上已知重量的砝码如500g记录此时的raw_data值计算calibration_factor (raw_data - tare_weight) / 砝码重量5. 卡尔曼滤波实现数据平滑原始数据会有波动加入卡尔曼滤波后效果明显改善。我在项目中使用的简化版卡尔曼滤波器typedef struct { float q; // 过程噪声协方差 float r; // 测量噪声协方差 float x; // 估计值 float p; // 估计误差协方差 float k; // 卡尔曼增益 } KalmanFilter; void Kalman_Init(KalmanFilter* kf, float q, float r) { kf-q q; kf-r r; kf-p 1; kf-x 0; } float Kalman_Update(KalmanFilter* kf, float measurement) { // 预测 kf-p kf-q; // 更新 kf-k kf-p / (kf-p kf-r); kf-x kf-k * (measurement - kf-x); kf-p * (1 - kf-k); return kf-x; }使用时先初始化KalmanFilter kf; Kalman_Init(kf, 0.01, 0.5); // 参数需要根据实际情况调整然后在获取重量后滤波float raw_weight Get_Weight(); float filtered_weight Kalman_Update(kf, raw_weight);6. 线性回归校准提升精度为了进一步提高精度我采用了线性回归校准。具体步骤准备多个标准砝码如100g、200g、500g、1000g记录每个砝码对应的AD值使用Excel或数学工具计算斜率和截距应用校准公式typedef struct { float slope; float intercept; } LinearCalibration; LinearCalibration calib {0.0023, -12.4}; // 示例值 float Calibrated_Weight(uint32_t raw) { return calib.slope * (raw - tare_weight) calib.intercept; }实测发现经过线性校准后误差可以控制在0.5%以内完全满足商业电子秤的要求。7. 完整工程实现与优化建议将上述模块整合后主程序逻辑如下int main(void) { HAL_Init(); SystemClock_Config(); Init_HX711pin(); KalmanFilter kf; Kalman_Init(kf, 0.01, 0.5); Tare(); // 去皮 while(1) { float weight Kalman_Update(kf, Get_Weight()); printf(Weight: %.1fg\r\n, weight); HAL_Delay(100); } }几个优化建议使用定时器中断定期采样避免主循环阻塞添加温度补偿环境温度变化会影响传感器精度实现自动休眠功能降低功耗对于快速变化的重量如流水线称重可以适当降低滤波强度8. 常见问题排查与解决在实际开发中遇到过几个典型问题问题1读数不稳定检查电源是否稳定HX711对电源噪声敏感确保传感器安装牢固没有机械振动适当增加卡尔曼滤波的Q值问题2重量显示为负数确认去皮操作正确检查传感器接线是否正确A和A-不要接反可能是线性校准的截距设置过大问题3响应速度慢降低滤波强度减小R值检查延时函数是否过长尝试使用DMA或中断方式读取数据问题4不同重量段误差不一致分段线性校准对不同重量范围使用不同参数检查传感器量程是否合适超量程使用会降低精度记得每次硬件改动后都要重新校准。我用3D打印了一个传感器支架发现支架本身的重量也会影响测量所以必须在安装好所有机械结构后再进行校准。