STM32编码器模式实战:从硬件滤波到高精度测速
1. 编码器模式基础与硬件连接我第一次接触STM32编码器模式是在一个电机控制项目中当时用外部中断采集编码器信号结果CPU占用率直接飙到80%后来改用硬件编码器模式才发现原来STM32早就帮我们准备好了解决方案。正交编码器通常输出A、B两相脉冲信号和Z相零点信号。A、B两相信号相位差90度通过判断哪个信号相位超前可以确定旋转方向。Z相每转一圈输出一个脉冲用于位置校准。以常见的1000线编码器为例旋转一圈会产生1000个A相脉冲和1000个B相脉冲。硬件连接时需要注意A、B相建议接TIMx_CH1和TIMx_CH2如TIM3的PC6/PC7如果编码器没有内置上拉电阻需要启用GPIO内部上拉信号线建议使用双绞线长距离传输时可考虑加74HC14整形// 典型编码器接口配置 GPIO_InitStruct.Pin GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Pull GPIO_PULLUP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate GPIO_AF2_TIM3; HAL_GPIO_Init(GPIOC, GPIO_InitStruct);2. 定时器编码器模式配置详解STM32的编码器模式本质上是定时器的特殊工作方式F1系列中TIM1-TIM5支持F4系列更多定时器支持。配置时有几个关键参数需要注意滤波设置TIM_ICInitTypeDef中的ICFilter参数特别重要。我的经验值是当编码器转速低于1000RPM时设为6高速场合设为3。这个值太小会导致噪声干扰太大会丢失脉冲。倍频模式TIM_ENCODERMODE_TI1仅A相边沿计数2倍频TIM_ENCODERMODE_TI2仅B相边沿计数2倍频TIM_ENCODERMODE_TI12双相边沿计数4倍频TIM_Encoder_InitTypeDef sConfig {0}; sConfig.EncoderMode TIM_ENCODERMODE_TI12; sConfig.IC1Polarity TIM_ICPOLARITY_RISING; sConfig.IC1Selection TIM_ICSELECTION_DIRECTTI; sConfig.IC1Prescaler TIM_ICPSC_DIV1; sConfig.IC1Filter 6; // 关键滤波参数 // 通道2配置相同... HAL_TIM_Encoder_Init(htim3, sConfig); HAL_TIM_Encoder_Start(htim3, TIM_CHANNEL_ALL);实测发现使用4倍频模式时1000线编码器实际可获得4000个计数/转。曾经有个项目因为没注意这个倍频关系导致位置计算错误电机来回震荡。3. 高精度测速算法实现单纯的脉冲计数只能得到位置信息要获得转速还需要定时采样。常用三种方法M法频率测量法// 每100ms采样一次 int32_t current_count __HAL_TIM_GET_COUNTER(htim3); float speed_rpm (current_count - last_count) * 600 / (4000 * 0.1); last_count current_count;适合高速测量低速时误差大。我曾用M法测300RPM电机误差小于0.5%但测30RPM时误差达10%。T法周期测量法// 测量两个脉冲间隔时间 uint32_t period HAL_TIM_ReadCapturedValue(htim3, TIM_CHANNEL_1); float speed_rpm 60.0f / (period * 1e-6) / 4000;适合低速测量但需要高频时基通常用另一个定时器。M/T法混合测量// 同时测量脉冲数和时间 uint32_t pulse_count current_count - last_count; uint32_t time_ticks HAL_GetTick() - last_time; float speed_rpm pulse_count * 60000.0f / (time_ticks * 4000);这是我在实际项目中最常用的方法在宽转速范围内都能保持1%以内的精度。关键是要处理好定时器溢出情况// 处理32位计数值 int32_t get_total_count() { static uint16_t last_cnt 0; static int32_t total 0; uint16_t curr_cnt TIM3-CNT; int16_t diff curr_cnt - last_cnt; total diff; last_cnt curr_cnt; return total; }4. 抗干扰与误差处理在工业现场遇到最头疼的就是信号干扰问题分享几个实战经验硬件滤波在编码器信号线上并联100pF电容信号线套磁环使用差分线路驱动器如AM26LS31软件容错#define FILTER_WINDOW 5 float speed_filter_buf[FILTER_WINDOW]; float median_filter(float new_val) { // 滑动窗口滤波 static uint8_t idx 0; speed_filter_buf[idx] new_val; if(idx FILTER_WINDOW) idx 0; // 排序取中值 float temp[FILTER_WINDOW]; memcpy(temp, speed_filter_buf, sizeof(temp)); bubble_sort(temp); // 实现排序算法 return temp[FILTER_WINDOW/2]; }误差补偿定期用Z相信号校准位置对编码器非线性误差进行软件补偿温度补偿高端编码器需要曾经有个伺服系统出现0.1mm的位置漂移后来发现是编码器电缆受变频器干扰在电缆上加装磁环后问题解决。