HX711数据不稳定问题
根本原因:PC14/PC15 是 STM32F1 的 OSC32 晶振引脚,即使不启用 LSE,这两个引脚也受备份域保护,有以下严重限制:
最大输出频率仅 3MHz(无法可靠驱动 SCK)
驱动能力极弱(最大灌电流仅 3mA)
GPIO 读取不稳定,导致位错位 → RAW 值大幅跳变
#include "hx711.h" /* 全局变量 */ static uint8_t g_gain = HX711_GAIN_128; // 增益设置 static int32_t g_offset = 0; // 零点偏移 static float g_scale = 1.0f; // 比例系数 /* 微秒级延时 */ static void HX711_Delay_us(uint32_t us) { uint32_t i; // 72MHz系统时钟,调整系数以获得更准确的延时 for(i = 0; i < us * 18; i++) { __NOP(); } } /** * @brief HX711初始化 * @note DT: PA3 (输入) * SCK: PA4 (输出) */ void HX711_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* 使能GPIOA时钟 */ __HAL_RCC_GPIOA_CLK_ENABLE(); /* 配置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); /* 配置DT为上拉输入 */ GPIO_InitStruct.Pin = HX711_DT_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(HX711_DT_PORT, &GPIO_InitStruct); /* 初始化SCK为低电平 */ HX711_SCK_LOW(); /* 设置默认增益 */ HX711_SetGain(HX711_GAIN_128); /* 等待HX711稳定 */ HAL_Delay(100); /* 等待HX711稳定后再去皮 */ HAL_Delay(500); // 增加稳定时间 /* 自动去皮(清零) */ HX711_Tare(10); } /** * @brief 检查HX711是否准备好 * @return 1: 准备好, 0: 未准备好 */ uint8_t HX711_IsReady(void) { return (HX711_DT_READ() == GPIO_PIN_RESET) ? 1 : 0; } /** * @brief 读取HX711原始数据 * @return 24位AD值 */ uint32_t HX711_Read(void) { uint32_t data = 0; uint8_t i; /* 等待HX711准备好,超时保护 */ uint32_t timeout = 200000; while(!HX711_IsReady() && timeout--) { HX711_Delay_us(1); } if(timeout == 0) { HX711_SCK_LOW(); // 确保SCK低电平,避免进入掉电模式 return 0; } /* 关中断:防止时序被打断导致位错位 */ __disable_irq(); /* 读取24位数据 - HX711规格书时序:SCK上升沿后在高电平期间读DT */ data = 0; for(i = 0; i < 24; i++) { HX711_SCK_HIGH(); HX711_Delay_us(1); // 在SCK高电平期间读取DT数据 data = data << 1; if(HX711_DT_READ()) { data |= 0x01; } HX711_SCK_LOW(); HX711_Delay_us(1); } /* 设置增益(额外的时钟脉冲) */ for(i = 0; i < g_gain; i++) { HX711_SCK_HIGH(); HX711_Delay_us(1); HX711_SCK_LOW(); HX711_Delay_us(1); } /* 开中断 */ __enable_irq(); /* 将24位数据转换为有符号32位数据 */ if(data & 0x800000) { data |= 0xFF000000; } return data; } /** * @brief 读取HX711平均值 * @param times: 采样次数 * @return 平均AD值 */ uint32_t HX711_ReadAverage(uint8_t times) { int64_t sum = 0; // 使用int64_t防止溢出 uint8_t i; for(i = 0; i < times; i++) { sum += (int32_t)HX711_Read(); // 转换为有符号数 HAL_Delay(10); } return (uint32_t)(sum / times); } /** * @brief 设置HX711增益 * @param gain: 增益选择 * HX711_GAIN_128: 通道A,增益128 * HX711_GAIN_32: 通道B,增益32 * HX711_GAIN_64: 通道A,增益64 */ void HX711_SetGain(uint8_t gain) { g_gain = gain; HX711_Read(); // 读取一次以应用新的增益设置 } /** * @brief 去皮(设置零点) * @param times: 采样次数 */ void HX711_Tare(uint8_t times) { int64_t sum = 0; uint8_t i; /* 丢弃前2次预热读数,等待ADC内部稳定 */ for(i = 0; i < 2; i++) { HX711_Read(); HAL_Delay(10); } for(i = 0; i < times; i++) { sum += (int32_t)HX711_Read(); HAL_Delay(10); } g_offset = (int32_t)(sum / times); } /** * @brief 获取重量值 * @return 重量值(单位由比例系数决定) */ int32_t HX711_GetWeight(void) { #define HX711_SAMPLE_NUM 5 int32_t buf[HX711_SAMPLE_NUM]; int32_t tmp; uint8_t i, j; int64_t sum = 0; float weight; /* 采样5次 */ for(i = 0; i < HX711_SAMPLE_NUM; i++) { buf[i] = (int32_t)HX711_Read(); HAL_Delay(10); } /* 冒泡排序(升序) */ for(i = 0; i < HX711_SAMPLE_NUM - 1; i++) { for(j = 0; j < HX711_SAMPLE_NUM - 1 - i; j++) { if(buf[j] > buf[j + 1]) { tmp = buf[j]; buf[j] = buf[j + 1]; buf[j + 1] = tmp; } } } /* 去掉最大值和最小值,取中间3次均值 */ for(i = 1; i < HX711_SAMPLE_NUM - 1; i++) { sum += buf[i]; } /* 重量计算:(均值 - 零点值) / 比例系数 */ weight = (float)((int32_t)(sum / (HX711_SAMPLE_NUM - 2)) - g_offset) / g_scale; /* 小于0视为空载噪声,返回0 */ if(weight < 0.0f) { return 0; } return (int32_t)weight; #undef HX711_SAMPLE_NUM } /** * @brief 设置比例系数 * @param scale: 比例系数(AD值/重量单位) * @note 需要通过校准获得此值 * 例如: 放置100g砂码,读取AD差值为1000,则scale = 1000/100 = 10 * 注意:如果数值为负,说明传感器接线反了,可传入负值校正 */ void HX711_SetScale(float scale) { if(scale == 0.0f) scale = 1.0f; // 防止除零错误 g_scale = scale; } /** * @brief 设置零点偏移 * @param offset: 零点偏移值 */ void HX711_SetOffset(int32_t offset) { g_offset = offset; } /** * @brief HX711进入掉电模式 */ void HX711_PowerDown(void) { HX711_SCK_LOW(); HX711_SCK_HIGH(); HAL_Delay(1); } /** * @brief HX711退出掉电模式 */ void HX711_PowerUp(void) { HX711_SCK_LOW(); }#ifndef __HX711_H #define __HX711_H #include "main.h" /* HX711引脚定义 */ #define HX711_DT_PORT GPIOA #define HX711_DT_PIN GPIO_PIN_3 #define HX711_SCK_PORT GPIOA #define HX711_SCK_PIN GPIO_PIN_4 /* HX711控制宏 */ #define HX711_SCK_HIGH() HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET) #define HX711_SCK_LOW() HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET) #define HX711_DT_READ() HAL_GPIO_ReadPin(HX711_DT_PORT, HX711_DT_PIN) /* HX711增益选择 */ #define HX711_GAIN_128 1 // 通道A,增益128 #define HX711_GAIN_32 2 // 通道B,增益32 #define HX711_GAIN_64 3 // 通道A,增益64 /* 函数声明 */ void HX711_Init(void); uint8_t HX711_IsReady(void); uint32_t HX711_Read(void); uint32_t HX711_ReadAverage(uint8_t times); void HX711_SetGain(uint8_t gain); void HX711_Tare(uint8_t times); int32_t HX711_GetWeight(void); void HX711_SetScale(float scale); void HX711_SetOffset(int32_t offset); void HX711_PowerDown(void); void HX711_PowerUp(void); #endif /* __HX711_H */