1. 编码器接口模式基础概念旋转编码器是工业控制和机器人领域常用的位置/速度传感器它能将机械转角转换为电脉冲信号。STM32的定时器外设内置了硬件编码器接口可以直接处理这类信号大大减轻CPU负担。这个功能本质上是通过定时器的输入捕获单元实现的特殊工作模式。编码器输出的典型信号包含两路正交方波A相和B相相位差90度。正转时A相超前B相90度反转时则相反。STM32的编码器接口会自动识别这种相位关系实现双向计数。我实际测试发现当编码器转速较快时1000rpm硬件接口的稳定性远优于软件中断计数方案。编码器接口支持三种工作模式模式1仅在TI1边沿计数模式2仅在TI2边沿计数模式3在TI1和TI2的边沿都计数最常用以模式3为例每个脉冲周期会触发4次计数A相上升/下降沿B相上升/下降沿实现4倍频分辨率提升。例如100线编码器实际可获得400个计数/转。2. 硬件电路设计与配置要点2.1 典型硬件连接方案我用STM32F103C8T6核心板搭建测试电路关键连接如下编码器A相 → PA6TIM3_CH1编码器B相 → PA7TIM3_CH2OLED显示屏 → I2C接口特别注意编码器如果是集电极开路输出必须加上拉电阻通常4.7kΩ。我遇到过因忘记上拉导致计数丢失的问题后来在代码中额外开启了GPIO内部上拉GPIO_InitStruct.GPIO_Mode GPIO_Mode_IPU; // 上拉输入2.2 定时器基础配置编码器模式下时基单元配置有特殊要求TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; TIM_TimeBaseStruct.TIM_Prescaler 0; // 不分频 TIM_TimeBaseStruct.TIM_Period 65535; // 16位最大值 TIM_TimeBaseStruct.TIM_CounterMode TIM_CounterMode_Up; // 实际方向由编码器决定 TIM_TimeBaseInit(TIM3, TIM_TimeBaseStruct);这里有个坑点虽然配置了向上计数模式但在编码器接口模式下实际计数方向由硬件自动控制软件配置的CounterMode参数无效。我当初调试时曾误以为配置未生效白白浪费半天时间。3. 编码器模式详细配置步骤3.1 输入捕获通道设置两个通道都需要配置滤波器这对消除机械抖动至关重要。我的经验值是选择0xF16个时钟周期滤波TIM_ICInitTypeDef TIM_ICInitStruct; TIM_ICInitStruct.TIM_Channel TIM_Channel_1; TIM_ICInitStruct.TIM_ICFilter 0xF; TIM_ICInit(TIM3, TIM_ICInitStruct); TIM_ICInitStruct.TIM_Channel TIM_Channel_2; TIM_ICInit(TIM3, TIM_ICInitStruct);3.2 编码器接口模式激活关键配置函数如下建议使用TI12模式同时捕获两相TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);极性参数实测发现如果编码器输出信号质量较差如上升沿缓慢可以尝试设置为下降沿触发能提高计数稳定性。4. M法与T法测速原理对比4.1 M法频率法实现M法通过在固定时间窗口内统计脉冲数计算转速。例如1秒内测得400个脉冲对于100线编码器4倍频后400脉冲/转转速即为60rpm。代码实现要点int16_t get_speed_m(void) { int16_t pulse_count TIM_GetCounter(TIM3); TIM_SetCounter(TIM3, 0); // 清零计数器 return pulse_count * 60 / (ENCODER_PPR * 4); // 转换为rpm }优缺点分析优点高速测量精度高计算简单缺点低速时可能采样不到脉冲导致速度为零我测试当转速50rpm时误差明显增大4.2 T法周期法实现T法通过测量脉冲间隔时间计算转速。例如用定时器捕获两个脉冲间隔为10ms对于400脉冲/转的编码器转速60/(0.01*400)15rpm。实现时需要开启输入捕获中断记录相邻脉冲时间差计算转速float speed_rpm 60.0f / (time_diff * ENCODER_PPR * 4 * 1e-6);实测数据在100rpm以下T法误差2%但超过2000rpm时可能因中断处理不及时导致丢失脉冲5. 混合测速策略与优化方案5.1 动态切换策略根据实测数据我总结出以下切换阈值转速500rpm使用M法转速500rpm切换至T法中间过渡区取两种方法的加权平均值5.2 溢出处理技巧当编码器高速旋转时16位计数器可能溢出。我的解决方案是int32_t full_count (int32_t)overflow_count * 65536 TIM_GetCounter(TIM3);其中overflow_count通过定时器更新中断维护。6. 完整代码示例与调试技巧6.1 初始化代码整合void Encoder_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; TIM_ICInitTypeDef TIM_ICInitStruct; // 1. 时钟使能 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 2. GPIO配置 GPIO_InitStruct.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode GPIO_Mode_IPU; GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStruct); // 3. 时基配置 TIM_TimeBaseStruct.TIM_Prescaler 0; TIM_TimeBaseStruct.TIM_Period 65535; TIM_TimeBaseStruct.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStruct); // 4. 输入捕获配置 TIM_ICInitStruct.TIM_Channel TIM_Channel_1; TIM_ICInitStruct.TIM_ICFilter 0xF; TIM_ICInit(TIM3, TIM_ICInitStruct); TIM_ICInitStruct.TIM_Channel TIM_Channel_2; TIM_ICInit(TIM3, TIM_ICInitStruct); // 5. 编码器模式激活 TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); TIM_Cmd(TIM3, ENABLE); }6.2 常见问题排查计数方向相反检查编码器A/B相接线尝试交换TIM_ICPolarity参数计数不准确用示波器观察信号质量增大输入滤波器值检查电源稳定性我曾因电源噪声导致丢失脉冲低速时速度跳变改用T法测速增加软件滤波如移动平均