TIM编码器接口

之前我们处理旋转编码器,是转一下中断一次,挺消耗资源的。

我们可以利用TIM的编码器功能,隔一段时间取一下旋转器值使得cnt++或–,以此判断旋转位置以及计算速度,相比中断节约资源。相当于外接了一个有方向的外部时钟。

编码器可以接收正交编码器信号控制cnt自增自减。

STM32 学习笔记_9 定时器中断:编码器接口模式-LMLPHP

通过判断其中一相上升/下降沿时,另一相是高or低电平判断转动方向。

STM32 学习笔记_9 定时器中断:编码器接口模式-LMLPHP

STM32 学习笔记_9 定时器中断:编码器接口模式-LMLPHP

编码器接口判断是正反转,控制cnt++或–。arr设置为65535,这样cnt=0时一自减就变为65535,转为有符号数就是-1.

STM32 学习笔记_9 定时器中断:编码器接口模式-LMLPHP

常用最后一个,精度高。

这里面的内容就是对应AB相的判断。比如图中FP1上升 FP2高电平,若1为A相2为B相,即A在上升时B在高电平,对应反转,因此向下计数。

STM32 学习笔记_9 定时器中断:编码器接口模式-LMLPHP

如果一个信号跳变,另一个信号状态和上次跳变一样说明是毛刺。计数值来回摆动过滤噪声。

代码:基于定时器的可测速编码器

大多数还是基于IC的代码。新函数:TIM_EncoderInterfaceConfig,配置编码器接口。

#include "stm32f10x.h"

void Encoder_Init(){
    //rcc, gpio, time base, ic(只配置滤波器和极性), TIM_Cmd 启动
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//滤波器采样,频率越低,采样点数越多,滤波效果越好。不过延迟也越大。采样频率就是内部时钟和这个分频参数共同作用的结果
    TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
    //比如我们想定1s中断一次,CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1),也就是72M / (PSC + 1) / (ARR + 1) = 1
    //所以两者赋值可以是10000-1和7200-1,只要两者都在65535以内就行,赋值不唯一
    TIM_TimeBaseInitStructure.TIM_Period=65536-1;
    TIM_TimeBaseInitStructure.TIM_Prescaler=1-1;//预分频器值,不分频
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器值,高级计数器才用
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
    
    //PA6 7对应定时器3,oc channel1 channel2.
    TIM_ICInitTypeDef TIM_ICInitStructure;
    TIM_ICStructInit(&TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;//PA6对应定时器3 ch1
    //TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//下面一行配置的极性会覆盖上面的配置,所以这里不用写
    TIM_ICInitStructure.TIM_ICFilter=0xF;//滤波消除毛刺,越大效果越好,看需求
    TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_Channel=TIM_Channel_2;//PA7对应定时器3 ch2
    //TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
    TIM_ICInitStructure.TIM_ICFilter=0xF;//滤波消除毛刺,越大效果越好,看需求
    TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);
    
    TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);
    
    TIM_Cmd(TIM3, ENABLE);
}

uint16_t Encoder_Get(){
    return TIM_GetCounter(TIM3);
}

因为转动一格,AB各出现一个下降和一个上升沿,因此±4.

如果想测速,可以在get函数里获取后手动清零,且在main中加一个延时获取,比如1000ms获取一次。

while(1){
        OLED_ShowNum(4,1,Encoder_Get(),5);
        Delay_ms(1000);
}

不过在主函数中这样写延时会影响其他部分程序的运行,因此最好是写一个中断函数。

void TIM2_IRQHandler(void){
    if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){
        cnt=Encoder_Get();
        TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
    }
}
05-16 16:12