1. HX711压力传感器与STM32开发基础
第一次接触HX711压力传感器时,我被它的小巧体积和强大功能惊艳到了。这个只有指甲盖大小的芯片,居然能实现24位精度的模数转换,完全能满足厨房秤、工业称重等场景的需求。HX711本质上是一个带放大功能的ADC芯片,专门为应变片式称重传感器设计。
硬件连接其实非常简单,你只需要准备:
- 一块STM32开发板(推荐STM32F103C8T6,便宜又好用)
- HX711模块(淘宝10块钱左右)
- 压力传感器(通常随HX711一起购买)
接线时要注意:HX711的VCC接3.3V或5V,GND接地,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(i=0; i<24; 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打印了一个传感器支架,发现支架本身的重量也会影响测量,所以必须在安装好所有机械结构后再进行校准。