资讯动态

手把手教你用STM32F103的编码器模式,给DIY小车的电机测速(附避坑指南)

发布时间:2026/8/27 13:18:02 来源:尧图企业网站定制
STM32F103编码器模式实战从电机测速到智能小车闭环控制在智能小车和机器人项目中电机转速的精确测量是实现运动控制的基础。我曾在一个校园智能车竞赛中因为编码器读数不稳定导致小车频繁偏离赛道经过反复调试才发现是滤波参数设置不当。本文将分享如何利用STM32F103的编码器模式构建可靠的测速系统以及如何避开那些教科书上不会告诉你的实践陷阱。1. 硬件搭建与信号处理1.1 编码器选型与安装常见的增量式编码器有光电式和霍尔式两种在DIY项目中推荐选择200-600PPR分辨率平衡精度与处理负担ABZ三相输出Z相用于零位校准非必需5V供电兼容3.3V直接连接STM32无需电平转换安装时要注意电机轴与编码器的同心度偏心会导致脉冲波形畸变。我曾用热熔胶固定编码器结果电机发热后胶体软化导致测量失效改用3D打印支架后问题解决。1.2 电路连接方案以TIM4通道为例的推荐接线方式信号线STM32引脚保护电路A相PB61kΩ电阻串联B相PB7100nF电容对地滤波VCC3.3V二极管防反接GNDGND磁珠隔离提示长距离传输时建议使用双绞线能有效抑制电机干扰遇到信号毛刺严重时可以调整定时器的输入滤波器参数TIM_ICInitStructure.TIM_ICFilter 0x0F; // 最大滤波值2. 定时器配置核心要点2.1 初始化代码深度解析以下配置使用TIM4实现四倍频计数提高分辨率void Encoder_Init_TIM4(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct {0}; TIM_ICInitTypeDef TIM_ICInitStruct {0}; // 时钟使能 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // GPIO配置 GPIO_InitStruct.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode GPIO_Mode_IPU; // 上拉输入 GPIO_Init(GPIOB, GPIO_InitStruct); // 定时器基础设置 TIM_TimeBaseInitStruct.TIM_Period 0xFFFF; // 16位计数器 TIM_TimeBaseInitStruct.TIM_Prescaler 0; TIM_TimeBaseInitStruct.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM4, TIM_TimeBaseInitStruct); // 编码器接口配置 TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); TIM_ICStructInit(TIM_ICInitStruct); TIM_ICInitStruct.TIM_ICFilter 6; // 适中滤波值 TIM_ICInit(TIM4, TIM_ICInitStruct); TIM_Cmd(TIM4, ENABLE); }2.2 三种编码器模式对比模式计数方式分辨率抗干扰性TIM_EncoderMode_TI1仅A相边沿触发1X低TIM_EncoderMode_TI2仅B相边沿触发1X低TIM_EncoderMode_TI12双相四边沿触发4X高在智能小车项目中选择TI12模式可以获得更高的测速精度但要注意电机高速运转时可能超过定时器最大计数频率需要根据车轮周长和减速比计算合适的采样周期3. 速度计算算法优化3.1 方向判断与转速换算改进版的读数函数处理正反转和计数器溢出int16_t Read_Encoder(void) { static uint16_t last_cnt 0; int32_t delta (int32_t)TIM4-CNT - last_cnt; // 处理计数器溢出 if(delta 0x7FFF) delta - 0xFFFF; else if(delta -0x7FFF) delta 0xFFFF; last_cnt TIM4-CNT; return (int16_t)delta; } float Get_RPM(uint16_t sample_period_ms) { int16_t pulses Read_Encoder(); float rpm (pulses * 60.0f) / (ENCODER_PPR * 4 * sample_period_ms / 1000.0f); return rpm; }3.2 采样周期选择策略根据电机特性推荐采样间隔低速电机100RPM200-500ms中速电机100-3000RPM50-100ms高速电机3000RPM10-20ms在平衡车项目中我发现20ms的采样周期既能捕捉快速变化又不会给MCU带来太大负担。4. 典型问题排查指南4.1 常见故障现象与解决方案计数方向与实际相反交换A/B相接线修改代码中的极性参数TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);高速时计数丢失降低输入滤波值检查编码器供电是否稳定改用更高频率的定时器如TIM1静止时有微小波动增加死区处理if(abs(pulses) NOISE_THRESHOLD) return 0;4.2 调试技巧使用逻辑分析仪捕获AB相信号时重点关注脉冲上升/下降时间应1μs两相之间的相位差理想为90°最大频率是否超出GPIO采样能力我曾遇到一个诡异现象电机反转时计数不准确最终发现是编码器供电线阻抗过大导致波形畸变改用独立电源后问题消失。5. 进阶应用闭环速度控制将编码器数据融入PID控制器void Motor_PID_Update(MotorCtrl* motor) { // 获取实际速度 float actual_rpm Get_RPM(PID_SAMPLE_TIME); // PID计算 motor-error motor-target_rpm - actual_rpm; motor-integral motor-error * PID_SAMPLE_TIME; motor-derivative (motor-error - motor-last_error) / PID_SAMPLE_TIME; // 抗积分饱和 if(motor-integral INTEGRAL_LIMIT) motor-integral INTEGRAL_LIMIT; else if(motor-integral -INTEGRAL_LIMIT) motor-integral -INTEGRAL_LIMIT; // 输出PWM float output motor-Kp * motor-error motor-Ki * motor-integral motor-Kd * motor-derivative; Set_PWM_Value(LIMIT(output, -100, 100)); motor-last_error motor-error; }关键参数整定经验先调P直到出现轻微振荡然后加D抑制振荡最后加I消除静差对于直流减速电机典型值为Kp0.8, Ki0.05, Kd0.2在调试智能小车时发现不同电池电压下PID参数需要微调后来增加了电压补偿系数使控制更加稳定。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价