STM32F103C8T6实战:用CubeMX HAL库搞定编码器电机测速与OLED显示(附完整代码)

STM32F103C8T6实战:用CubeMX HAL库搞定编码器电机测速与OLED显示(附完整代码) STM32F103C8T6实战CubeMX HAL库驱动编码器电机与OLED显示系统开发指南1. 项目背景与硬件架构在嵌入式控制系统中电机速度检测与实时显示是工业自动化、机器人控制等领域的核心需求。本项目基于STM32F103C8T6Blue Pill开发板构建了一套完整的电机测速解决方案主要硬件组成包括主控单元STM32F103C8T6 Cortex-M3微控制器运动检测增量式编码器电机200线/转驱动模块TB6612FNG电机驱动芯片显示单元0.96寸OLEDSSD1306驱动I2C接口开发环境STM32CubeMX HAL库关键性能参数对比模块参数数值编码器分辨率200脉冲/转定时器计数范围0-65535OLED刷新率≥30fpsPWM频率1kHz采样周期中断间隔10ms2. CubeMX工程配置详解2.1 时钟树配置系统时钟设置为72MHz通过以下步骤实现选择HSE8MHz外部晶振作为时钟源PLL倍频设置为x9APB1分频设置为/236MHzAPB2保持72MHz注意TIM1/8挂在APB2其他定时器在APB1时钟配置影响PWM频率精度2.2 定时器模块配置2.2.1 TIM3 - 编码器接口模式// 编码器模式参数 htim3.Instance TIM3; htim3.Init.Prescaler 0; htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 65535; htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim3.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; // 编码器接口配置 sConfig.EncoderMode TIM_ENCODERMODE_TI12; sConfig.IC1Polarity TIM_ICPOLARITY_RISING; sConfig.IC1Selection TIM_ICSELECTION_DIRECTTI; sConfig.IC1Prescaler TIM_ICPSC_DIV1; sConfig.IC1Filter 0;2.2.2 TIM4 - PWM生成// PWM通道配置PB6 htim4.Instance TIM4; htim4.Init.Prescaler 71; // 1MHz计数频率 htim4.Init.CounterMode TIM_COUNTERMODE_UP; htim4.Init.Period 999; // 1kHz PWM htim4.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; // PWM占空比设置函数 void PWM_SetDuty(uint16_t duty) { __HAL_TIM_SET_COMPARE(htim4, TIM_CHANNEL_1, duty); }2.3 I2C接口配置OLED使用I2C1PB8-SCL, PB9-SDAhi2c1.Instance I2C1; hi2c1.Init.ClockSpeed 400000; // 400kHz Fast Mode hi2c1.Init.DutyCycle I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 0; hi2c1.Init.AddressingMode I2C_ADDRESSINGMODE_7BIT;3. 核心算法实现3.1 速度计算模型编码器测速基于脉冲计数法计算公式为速度(rad/s) (脉冲数 × 2π) / (编码器线数 × 采样周期)具体实现代码#define ENCODER_LINES 200 // 编码器线数 #define SAMPLE_TIME 0.01f // 10ms采样周期 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim htim1) { int16_t counts (int16_t)__HAL_TIM_GET_COUNTER(htim3); __HAL_TIM_SET_COUNTER(htim3, 0); // 计算角速度rad/s float speed (counts * 2 * 3.14159f) / (ENCODER_LINES * 4 * SAMPLE_TIME); // 更新全局变量 current_speed speed; pulse_counts counts; } }3.2 抗干扰处理针对电机抖动带来的脉冲干扰采用数字滤波硬件滤波在编码器输入引脚配置0.1μF电容软件滤波移动平均算法#define FILTER_WINDOW 5 float speed_buffer[FILTER_WINDOW]; uint8_t buf_index 0; float filtered_speed(float new_speed) { speed_buffer[buf_index] new_speed; buf_index (buf_index 1) % FILTER_WINDOW; float sum 0; for(int i0; iFILTER_WINDOW; i) { sum speed_buffer[i]; } return sum / FILTER_WINDOW; }4. OLED显示优化4.1 显示帧率控制避免频繁刷新导致的闪烁问题void Update_Display(void) { static uint32_t last_update 0; if(HAL_GetTick() - last_update 50) return; // 20fps OLED_ClearBuffer(); // 显示速度值保留2位小数 char buf[16]; snprintf(buf, sizeof(buf), Speed: %.2f rad/s, current_speed); OLED_ShowString(0, 2, buf, 12, 0); // 显示脉冲计数 snprintf(buf, sizeof(buf), Pulses: %d, pulse_counts); OLED_ShowString(0, 4, buf, 12, 0); OLED_Refresh(); last_update HAL_GetTick(); }4.2 图形化显示实现添加速度趋势图显示#define GRAPH_WIDTH 128 #define GRAPH_HEIGHT 30 uint8_t graph_data[GRAPH_WIDTH]; void Draw_Speed_Graph(void) { // 移位历史数据 for(int i0; iGRAPH_WIDTH-1; i) { graph_data[i] graph_data[i1]; } // 添加新数据点限幅到0-100 rad/s graph_data[GRAPH_WIDTH-1] (uint8_t)(fabs(current_speed) * GRAPH_HEIGHT / 100); if(graph_data[GRAPH_WIDTH-1] GRAPH_HEIGHT) graph_data[GRAPH_WIDTH-1] GRAPH_HEIGHT; // 绘制曲线 for(int x0; xGRAPH_WIDTH; x) { for(int y0; yGRAPH_HEIGHT; y) { if(y GRAPH_HEIGHT - graph_data[x]) { OLED_DrawPixel(x, y20, 1); } } } }5. 系统调试技巧5.1 常见问题排查表现象可能原因解决方案电机不转PWM输出异常检查TIM4配置和GPIO复用速度显示为0编码器接线错误交换A/B相或检查上拉电阻OLED白屏I2C地址不匹配尝试0x3C或0x3D地址数值跳动大电源干扰增加电机电源滤波电容5.2 性能优化建议中断优化将OLED刷新移出中断上下文使用DMA传输I2C数据资源节省技巧// 使用位带操作快速控制GPIO #define MOTOR_DIR_SET(dir) do { \ AIN1_GPIO_Port-BSRR dir ? AIN1_Pin : (AIN1_Pin 16); \ AIN2_GPIO_Port-BSRR dir ? (AIN2_Pin 16) : AIN2_Pin; \ } while(0)低功耗设计在空闲时切换定时器为低功耗模式动态调整OLED刷新率本方案实测在12V供电环境下速度检测精度可达±0.5rad/s满足大多数闭环控制需求。实际开发中建议先单独测试各模块功能再逐步集成。