STM32定时器捕获功能实战精准读取编码电机转速与方向控制在工业自动化、机器人控制和智能小车开发中精确测量电机转速并实现闭环控制是核心需求。传统的光电编码器或霍尔传感器方案往往需要复杂的信号处理电路而现代编码电机通过内置正交编码器配合STM32强大的定时器硬件接口可以实现零额外电路的高精度转速测量。本文将深入讲解如何利用STM32的定时器输入捕获功能配合TB6612驱动模块构建完整的电机转速检测与控制系统。1. 硬件系统架构设计一套完整的编码电机控制系统需要三个核心组件STM32微控制器作为大脑、TB6612作为功率驱动、编码电机作为执行与反馈单元。我们先从硬件连接开始构建系统基础。TB6612驱动模块引脚功能速查表引脚名称类型电压范围功能描述VM电源输入4.5V-15V电机驱动主电源VCC逻辑电源2.7V-5.5V芯片逻辑供电PWMA/BPWM输入0-VCC电机速度控制信号AIN1/AIN2方向控制0-VCC电机转向控制参见真值表AO1/AO2功率输出0-VM电机A相输出BO1/BO2功率输出0-VM电机B相输出STBY使能控制0-VCC高电平工作低电平待机编码电机6线接口定义电机电源连接TB6612的AO1/AO2或BO1/BO2电机电源-连接TB6612的AO2/AO1或BO2/BO1编码器GND必须与STM32共地编码器VCC通常接3.3V或5V编码器A相接STM32定时器通道1编码器B相接STM32定时器通道2关键提示编码器电源与STM32必须共地否则无法正确捕获信号。建议在A/B相线上串联100Ω电阻并添加0.1μF电容滤波可有效抑制高频干扰。2. 定时器输入捕获原理深度解析STM32的定时器单元具有强大的编码器接口模式能够自动处理正交编码信号。我们以TIM2为例详解配置过程编码器模式工作原理当A相上升沿时根据B相电平决定计数方向B相为低电平向上计数B相为高电平向下计数每个正交周期A/B各变化一次计数4次实现4倍频// TIM2编码器模式初始化代码 void TIM2_Encoder_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 时基配置 TIM_TimeBaseStructure.TIM_Prescaler 0; // 不分频 TIM_TimeBaseStructure.TIM_Period 0xFFFF; // 16位最大值 TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); // 输入捕获配置 TIM_ICStructInit(TIM_ICInitStructure); TIM_ICInitStructure.TIM_Channel TIM_Channel_1; // A相 TIM_ICInitStructure.TIM_ICPolarity TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter 0x0F; // 高级滤波 TIM_ICInit(TIM2, TIM_ICInitStructure); TIM_ICInitStructure.TIM_Channel TIM_Channel_2; // B相 TIM_ICInit(TIM2, TIM_ICInitStructure); // 设置为编码器模式3双沿计数 TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); TIM_Cmd(TIM2, ENABLE); }关键参数解析TIM_ICFilter设置输入滤波系数有效抑制信号抖动TIM_EncoderMode模式3表示在TI1和TI2的所有边沿计数TIM_Period根据编码器线数和最大转速计算避免溢出3. 转速计算算法与方向判断获得脉冲计数后需要通过定时采样转换为实际转速。我们采用固定周期采样法避免使用阻塞式delay函数。转速计算步骤在定时中断中定期读取计数器值CNT计算差值ΔCNT 当前CNT - 上次CNT处理计数器溢出情况16位无符号数转换为转速RPM// 每100ms计算一次转速 void TIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3, TIM_IT_Update) ! RESET) { static int16_t last_cnt 0; int16_t current_cnt TIM_GetCounter(TIM2); int16_t delta current_cnt - last_cnt; // 处理计数器溢出 if(delta 0x7FFF) delta - 0xFFFF; else if(delta -0x7FFF) delta 0xFFFF; // 计算RPMdelta/(4*编码器线数)*600(100ms→min) float rpm (delta * 60.0f) / (4 * ENCODER_LINES * 0.1f); last_cnt current_cnt; TIM_ClearITPendingBit(TIM3, TIM_IT_Update); } }方向判断逻辑bool is_forward (TIM2-CR1 TIM_CR1_DIR) 0; // 检查计数方向性能优化技巧对于高速电机可缩短采样周期低速时则应延长周期以提高分辨率。动态调整采样率可兼顾全速度范围的测量精度。4. 抗干扰处理与系统校准实际应用中电机产生的电磁干扰和机械振动会导致编码信号出现毛刺。我们采用硬件滤波结合软件处理的综合方案硬件滤波配置在TIM_ICInitStructure中设置TIM_ICFilter参数典型值对应采样频率和事件数滤波器值采样频率事件数0x00无滤波10x01fCK_INT2.........0x0FfCK_INT/328软件容错机制速度突变检测限制最大加速度无效状态过滤A/B相同时为高或低移动平均滤波存储最近N个采样值#define FILTER_WINDOW 5 float speed_filter[FILTER_WINDOW] {0}; uint8_t filter_index 0; float apply_speed_filter(float new_speed) { speed_filter[filter_index] new_speed; filter_index (filter_index 1) % FILTER_WINDOW; float sum 0; for(int i0; iFILTER_WINDOW; i) { sum speed_filter[i]; } return sum / FILTER_WINDOW; }系统校准流程电机静止时记录计数器波动范围作为零点误差已知转速下测量脉冲数计算实际每转脉冲数正反转测试方向判断逻辑极限转速测试调整采样频率5. 闭环控制实践PID速度调节基于测得的速度反馈我们可以实现闭环控制。以下是一个简易PID实现typedef struct { float Kp, Ki, Kd; float integral; float prev_error; } PID_Controller; float PID_Update(PID_Controller* pid, float setpoint, float measurement) { float error setpoint - measurement; pid-integral error; if(pid-integral 1000) pid-integral 1000; else if(pid-integral -1000) pid-integral -1000; float derivative error - pid-prev_error; pid-prev_error error; return pid-Kp * error pid-Ki * pid-integral pid-Kd * derivative; } // 使用示例 PID_Controller speed_pid {0.5, 0.01, 0.05, 0, 0}; float target_rpm 300.0f; void control_loop() { float current_rpm get_filtered_speed(); float pwm PID_Update(speed_pid, target_rpm, current_rpm); set_motor_pwm(constrain(pwm, -1000, 1000)); }PID参数整定技巧先设KiKd0增大Kp直到出现轻微振荡然后加入Ki消除稳态误差最后加入Kd抑制超调不同转速段可能需要不同的参数组6. 高级应用位置控制与轨迹规划除了速度控制编码器还能提供位置反馈。通过累计脉冲数我们可以实现精确的位置控制int32_t total_counts 0; // 全局位置变量 void update_position() { int16_t current_cnt TIM_GetCounter(TIM2); static int16_t last_cnt 0; int16_t delta current_cnt - last_cnt; // 处理溢出 if(delta 0x7FFF) delta - 0xFFFF; else if(delta -0x7FFF) delta 0xFFFF; total_counts delta; last_cnt current_cnt; }梯形轨迹规划算法加速阶段线性增加速度设定值匀速阶段保持恒定速度减速阶段线性减小速度至零void trapezoidal_plan(float target_pos, float max_speed, float accel) { float distance target_pos - current_pos; float accel_dist (max_speed * max_speed) / (2 * accel); if(fabs(distance) 2 * accel_dist) { // 三角形轨迹 float peak_speed sqrt(fabs(distance) * accel); // ...分段计算 } else { // 梯形轨迹 // ...分段计算 } }在实际机器人控制中我经常发现电机在启动瞬间容易失步。通过增加加速度限制和加入前馈控制可以显著改善动态性能。另一个实用技巧是在位置接近目标时切换为比例控制避免超调和振荡。