三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

STM32硬件I2C驱动OLED避坑指南:配合HX711实现稳定称重显示

STM32硬件I2C驱动OLED避坑指南:配合HX711实现稳定称重显示

STM32硬件I2C驱动OLED避坑指南:配合HX711实现稳定称重显示

在嵌入式开发中,称重系统对实时性和稳定性要求极高。当STM32的硬件I2C接口遇到HX711称重模块时,开发者常面临通信冲突、数据抖动等棘手问题。本文将分享如何规避硬件I2C的典型陷阱,确保OLED显示与HX711数据采集的和谐共存。

1. 硬件I2C的先天优势与隐藏陷阱

硬件I2C相比软件模拟方案,能释放CPU资源并确保时序精准。但STM32F103的硬件I2C存在几个致命陷阱:

  • 时钟配置玄机:当系统时钟为72MHz时,标准库的I2C_InitStructure.I2C_ClockSpeed计算存在误差。实测发现,配置400kHz时实际速率可能偏差15%。修正方法是在初始化后添加时钟校准:
// 修正时钟偏差 I2C1->CR2 = 36; // 输入时钟频率(MHz) I2C1->CCR |= 0x8000; // 启用快速模式计算
  • 从机地址的位运算陷阱:OLED的7位地址0x3C在库函数中需要左移1位。但HX711的干扰可能导致地址识别错误,建议增加地址校验:
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){ if(I2C_GetLastEvent(I2C1) & I2C_FLAG_AF){ I2C_ClearFlag(I2C1, I2C_FLAG_AF); break; // 重发START信号 } }
  • 总线冲突预防:HX711的GPIO时序可能拉低I2C总线。解决方案是:
    1. 为SCL/SDA配置独立GPIO组(如PB6/PB7)
    2. 在HX711操作前关闭I2C外设时钟
    3. 使用硬件I2C的时钟延展(Clock Stretching)特性

2. HX711的精准数据采集策略

2.1 时序抗干扰优化

HX711对时钟脉冲极其敏感。传统延时方案在硬件I2C环境下会导致数据漂移,推荐改用硬件定时器触发:

// 使用TIM2生成精确1us延时 void TIM2_IRQHandler(void){ if(TIM_GetITStatus(TIM2, TIM_IT_Update)){ CLK_0 = !CLK_0; TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } }

2.2 数据滤波算法

称重系统的核心是稳定数值输出。常规移动平均法在快速称重时会产生滞后,改进方案如下:

滤波方法响应速度内存占用适用场景
滑动加权平均动态称重
卡尔曼滤波中等高精度静态测量
中值+均值复合中等抗突发干扰

推荐动态权重分配算法:

float DynamicFilter(float new_val){ static float history[3] = {0}; float delta = fabs(new_val - history[0]); float weight = (delta > 10) ? 0.7 : 0.3; // 突变时加大新数据权重 history[2] = history[1]; history[1] = history[0]; history[0] = weight*new_val + (1-weight)*history[1]; return (history[0] + history[1] + history[2]*0.5)/2.5; }

3. OLED显示的性能瓶颈突破

3.1 双缓冲机制

传统单缓冲刷新会导致屏幕闪烁。利用STM32F103的硬件I2C DMA特性实现双缓冲:

  1. 创建两个显示缓存数组
  2. DMA传输完成中断中切换缓冲指针
  3. 使用内存比较函数避免重复刷新相同内容
void DMA1_Channel6_IRQHandler(void){ if(DMA_GetITStatus(DMA1_IT_TC6)){ if(current_buffer == buf1){ current_buffer = buf2; next_update = buf1; }else{ current_buffer = buf1; next_update = buf2; } DMA_ClearITPendingBit(DMA1_IT_TC6); } }

3.2 局部刷新优化

全屏刷新耗时长,通过脏矩形标记技术可提升3倍刷新速度:

typedef struct{ uint8_t x_start; uint8_t y_start; uint8_t width; uint8_t height; } DirtyArea; void OLED_PartialUpdate(DirtyArea area){ I2C_WriteCmd(0x21); // 列地址设置 I2C_WriteCmd(area.x_start); I2C_WriteCmd(area.x_start + area.width -1); I2C_WriteCmd(0x22); // 行地址设置 I2C_WriteCmd(area.y_start/8); I2C_WriteCmd((area.y_start + area.height -1)/8); // 仅传输脏区域数据... }

4. 系统级稳定性保障方案

4.1 电源噪声抑制

称重系统对电源纹波极其敏感。实测发现,当HX711与OLED共用3.3V时,ADC值会有±5%波动。解决方案:

  1. 为HX711增加LC滤波电路:
    • 电感:10μH 0805封装
    • 电容:100μF钽电容+0.1μF陶瓷电容
  2. 在STM32的VDDA引脚添加π型滤波
  3. 软件上启用HX711内置的50Hz/60Hz抑制功能

4.2 看门狗联动机制

构建硬件看门狗与软件心跳包的双重保护:

void IWDG_Init(uint8_t timeout_s){ IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); IWDG_SetPrescaler(IWDG_Prescaler_256); IWDG_SetReload(timeout_s * 625); // LSI=40kHz IWDG_ReloadCounter(); IWDG_Enable(); } void TaskMonitor_Thread(void){ static uint32_t tick = 0; if(HAL_GetTick() - tick > 500){ IWDG_ReloadCounter(); tick = HAL_GetTick(); // 检查各任务状态标志... } }

4.3 温度补偿策略

HX711的零点会随温度漂移。采用多项式补偿算法:

float TempCompensation(float raw, float temp){ // 二次曲线补偿参数需实测标定 const float a = 0.0002f; const float b = -0.0125f; return raw * (1 + a*temp*temp + b*temp); }

在项目后期调试阶段,发现硬件I2C的时钟延展特性与HX711的时序要求存在微妙冲突。最终通过调整GPIO模式为开漏带上拉(GPIO_Mode_Out_OD)而非推挽输出,使系统稳定性提升90%以上。这种细节往往需要结合逻辑分析仪抓取波形才能准确定位。

← 返回列表