【STM32】STM32编码器接口实战:从零搭建电机测速系统

发布时间:2026/7/14 13:49:13
【STM32】STM32编码器接口实战:从零搭建电机测速系统 1. 为什么需要编码器接口测速我第一次接触电机测速是在大四的机器人比赛上。当时团队的小车总是跑不直调试了半天才发现是左右轮速不一致导致的。后来给电机加装了编码器用STM32的编码器接口实时监测转速问题才迎刃而解。这种经历让我深刻理解到精确的速度反馈是运动控制的基础。编码器就像电机的眼睛它能将机械运动转化为电信号。常见的增量式编码器会输出A、B两相脉冲信号通过STM32的编码器接口可以精确捕获这些脉冲。相比普通IO口检测硬件编码器接口有三个明显优势不占用CPU资源脉冲计数由定时器硬件完成抗干扰能力强内置输入滤波电路支持四倍频通过AB相相位差识别正反转在实际项目中无论是工业传送带的速度控制还是AGV小车的轨迹校正都离不开可靠的测速系统。下面我就从硬件选型开始带你一步步搭建完整的测速方案。2. 硬件选型与电路设计2.1 编码器选型要点去年给工厂做自动化改造时我测试过市面上七八种编码器总结出几个关键参数分辨率每转脉冲数(PPR)常见有100-5000线输出类型推挽/开漏输出供电电压5V/12V/24V防护等级IP54以上适合工业环境对于直流减速电机我推荐这款100线的增量式编码器型号E6B2-CWZ6C 分辨率100 PPR 供电电压5-24V DC 输出相位A/B/Z三通道 价格约35元/个2.2 接口电路设计很多新手会忽略信号调理电路结果在电机启停时经常出现误计数。我的经验是必须加上这三个部分电源滤波0.1μF陶瓷电容并联10μF电解电容信号整形74HC14施密特触发器限流保护220Ω串联电阻具体接线示意图如下编码器A相 → 74HC14 → PA6(TIM3_CH1) 编码器B相 → 74HC14 → PA7(TIM3_CH2) 编码器GND → 共地注意长距离传输时要使用双绞线能有效抑制电磁干扰3. STM32编码器模式配置3.1 定时器初始化去年给客户调试时发现不同STM32系列的配置略有差异。以STM32F103为例关键配置步骤如下void Encoder_Init(void) { // 1. 使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 2. GPIO配置上拉输入 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_Init(GPIOA, GPIO_InitStructure); // 3. 时基初始化 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_Period 65535; // 16位最大值 TIM_TimeBaseInitStructure.TIM_Prescaler 0; // 不分频 TIM_TimeBaseInit(TIM3, TIM_TimeBaseInitStructure); // 4. 编码器接口配置 TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); // 5. 使能定时器 TIM_Cmd(TIM3, ENABLE); }这里有个坑要注意TIM_EncoderMode_TI12模式会同时使用CH1和CH2的边沿触发实现四倍频计数。如果只接单通道要改用TIM_EncoderMode_TI1或TI2。3.2 滤波参数优化电机运行时会产生高频干扰我在实验室用示波器观察到的噪声脉冲宽度约200ns。根据这个实测数据设置滤波器参数TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICInitStructure.TIM_ICFilter 0x6; // 约680ns滤波 TIM_ICInit(TIM3, TIM_ICInitStructure);滤波时间计算公式t_filter (TIM_ICFilter 1) * 100ns4. 转速计算算法4.1 脉冲计数处理在智能车比赛中我发现直接读取计数器值会有两个问题16位计数器溢出电机反转时数值跳变改进后的读取函数如下int32_t Encoder_Get(void) { static uint16_t last_cnt 0; uint16_t curr_cnt TIM_GetCounter(TIM3); int32_t delta (int16_t)(curr_cnt - last_cnt); last_cnt curr_cnt; return delta; }4.2 RPM计算公式通过实际标定我总结出这个实用公式RPM (ΔCount * 60) / (PPR * 4 * T_sample)其中ΔCount采样周期内的脉冲变化量PPR编码器每转脉冲数4四倍频系数T_sample采样周期(秒)示例代码#define PPR 100 #define SAMPLE_TIME 0.1f // 100ms void TIM2_IRQHandler(void) { if(TIM_GetITStatus(TIM2, TIM_IT_Update)){ int32_t pulse Encoder_Get(); float rpm (pulse * 60) / (PPR * 4 * SAMPLE_TIME); // 显示或处理rpm值 } }5. 调试技巧与常见问题5.1 示波器诊断法当测速异常时我通常用这个排查流程检查编码器供电电压实测应在4.8-5.2V用示波器观察A/B相信号波形确认脉冲频率与转速匹配检查STM32计数器值变化5.2 典型问题解决问题1转速显示为0检查编码器接线是否松动确认TIM3时钟已使能测量PA6/PA7引脚电平变化问题2转速值跳动大增加采样周期如从50ms改为100ms调大输入滤波器参数检查电机机械振动问题3反转时数值异常确认使用TI12编码模式检查A/B相序是否接反改用int32_t存储累计值6. 进阶优化方向6.1 速度滤波算法原始数据会有毛刺我常用的二阶滤波算法float Speed_Filter(float new_val) { static float buf[2] {0}; buf[1] buf[0]; buf[0] new_val; return (buf[0] buf[1]*0.8) / 1.8f; }6.2 多电机同步方案在机械臂项目中我用这个结构体管理多个编码器typedef struct { TIM_TypeDef* TIMx; uint16_t PPR; float rpm; uint32_t last_time; } Encoder_TypeDef; Encoder_TypeDef motor[3];最近在调试带编码器的伺服电机时发现STM32的编码器接口配合DMA可以大幅降低CPU负载。具体做法是将计数器值通过DMA传输到内存配合定时器触发实现全硬件速度采集。这个方案在四轴飞行器的电调控制上效果特别好下次可以专门聊聊这个高级用法。