对于一些频繁使用中断并且仅仅执行比较简单的任务的情况下,可以选用编码器接口的方式,一般都会设计一个硬件电路模块来自动完成。而编码器接口就是一个给编码器进行计次的电路。每个一段时间就去一次计数值,就能得到旋转的速度了。编码器测速一般都用于电机测速上,PWM驱动电机,编码器测速,PID算法进行闭环控制。一般电机的速度比较快,会用无接触方式的霍尔传感器或者光栅进行测速。为了模拟,就用人手扭动代替电机转动,编码器就代替霍尔传感器或者光栅(事实上都是一样效果)。

编码器接口的工作流程:

编码器有两个输出,分别输出两个相,当满足一定条件的时候,控制时基单元中的CNT进行自增或者自减,在一定时间后把得到的值读出来,然后CNT清零,再根据旋转方向进行自增自减。有点类似与侧频法的套路,但是这个比较高级,还能自减。 

定时器如果用了编码器的话,就不敢别的活了,考虑定时器够不够。

硬件资源以及软件资源是可以互补的,比如对于输出比较,可以设置一个中断,进入中断手动计数,自行把电平反转,对于输入捕获,也可以设置中断,手动把CNT放到CCR中。对于编码器接口,也可以用手动中断进入,然后进行自增自减少。

这就是硬件不够,软件来凑。优先使用硬件资源,软件资源可以去干更重要的事情。

图中最后一点,CH3CH4不能接编码器。

image.png

 正交编码器,输出的两个相,扭动正交编码器的时候,输出两个相位相差90度的波形,扭动越快输出的频率就越快,频率就相当于速度,测出频率就相当于速度啦。

根据机型的选择,规定正转A相提前B相提前90度,亦或是滞后90度,这只是一个极性的问题。

但是也可以不要B相,用一个普通IO口进行高低电平判断是正转还是反转,为什么用正交信号表示呢?

原因有2:

1.抗噪声,两个信号是交替跳变的,如果有毛刺,就是一个信号不变,另一个信号疯狂跳变,就是噪音干扰了,设置滤波电路就能去除噪声。

2.因为两个都能跳变,所以测频率的精确度提高了一倍。

工作原理:

根据上图右侧的表格,在编码器输出信号的歌边沿都进行计数,至于是自增还是自减,则看是正转还是反转,正转对应上面的表,计数器加一,反转对应下面的表,减。

电路结构:

image.png

 编码器接口的两个输入分别接输入捕获的前两个通道,CH1CH2,输入滤波以及边沿检测信号都都有在使用,但是后续的预分频器没有使用了。

编码器接口的输出功能是控制时基单元的CNT进行加减,这时候不用内部时钟以及计数模式了,因为计数器现在属于一种托编码器接口管的状态,加减由编码器接口决定。

image.png

 这里的ARR有用,利用补码的性质可以得到负数,比如一般把ARR设置成65535,CNT增加的时候,自然没问题,反转的时候,0就到65535了,这时候我们把这个数设置成有符号的数值,这个65535就对应-1,65534对应-2,以此类推!

编码器三种工作模式:

image.png

 其中TI1FP1就是A相,2就是B相,第二列就是另一相对应的高低电平。在TI1计数的意思就是之计算A的边沿,2就是B,在TI1和TI2就是对应两个相的边沿都可以计数。跟下图的逻辑是一致的。一般选用第三种模式,计数次数多的话,精度会更高一些。

image.png

示例:

image.png

 毛刺部分,根据第一个表的原理,会让它来回回摆动,增加减少增加减少,最后回归到毛刺之前的状态,这样就达到了滤除干扰的目的。

image.png

 两张图的TI1以及TI2都没变,为什么会计数不一样呢?因为一个均不反相一个TI1反相了,什么是反相,就是高电平变成低电平,低电平变成高电平,也就是经过一个非门。这个就跟极性选择有关系了,编码器接口的极性选择不是选择上升沿触发还是下降沿触发,因为上下沿都会触发,极性选择选择的是要不要经过非门,进过则信号的电平反转,不经过则跟以前一样。

上面两个图有什么用呢?

就是如果自己编码器接口发现技术方向反了,就可以改变一个相的极性改变计数方向,或者改变两者的接线颠换,就行啦。

代码部分:

编码器接口的接线只接受CH1跟CH2,所以要看清楚引脚的接线图对应的引脚,不要接错啦

第一步:开启RCC时钟,配置GPIO以及定时器的时钟

第二步,配置GPIO,把PA6以及PA7配置成输入模式,配置GPIO的时候,选择输入模式要根据外部的装置的默认输出状态来选择,默认高电平就上拉,低电平就下拉,不确定就浮空,但是浮空容易受干扰。

image.png

三,配置时基单元,ARR=65535,不分频,只执行CNT计数就行了

四,配置输入捕获单元,只有滤波器以及极性选择有用

    TIM_ICInitTypeDef TIM_ICInitStructure;

    TIM_ICStructInit(&TIM_ICInitStructure);

    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;

    TIM_ICInitStructure.TIM_ICFilter = 0xF;

    TIM_ICInit(TIM3, &TIM_ICInitStructure);

    TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;

    TIM_ICInitStructure.TIM_ICFilter = 0xF;

    TIM_ICInit(TIM3, &TIM_ICInitStructure);

虽然只需要这两个参数,但是其他也要配置,一般使用    TIM_ICStructInit(&TIM_ICInitStructure);富裕一个默认值,具体是什么,跳转看看就行,然后再把需要的修改修改,TIM_ICPolarity_Rising      表示高电平,也是不反相      

 TIM_ICPolarity_Falling  反相。但是在编码器模式配置的时候还会出现,所以这里就不配置了,重复啦。就是下面这个。有两个通道就配置两次咯,因为不是交叉,所以不能用PWMIconfig函数,

五,配置编码器接口模式,

void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,

                                uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);

第一个参数是定时器,二计数模式,第几个相计数,三四分别是通道一跟二的极性。

因为上面ICInit配置有一个极性配置,删掉那个配置的话其实就是默认值,所以一定要把这个函数放在ICInit下面,保证能够把ICInit配置的极性覆盖。

六,timcmd开启定时器

这样配置的话,在0往反方向旋转之后,就会变成65535,要想输出负数,需要强制类中转换,在获取值函数中,修改返回值的定义类型改成有符号类型,或者在OLED显示的时候,把显示函数改为有符号输出。符号是自带的,不用你管,C语言中可以验证。

int16_t Encoder_Get(void)

{

    int16_t Temp;

    Temp = TIM_GetCounter(TIM3);

    TIM_SetCounter(TIM3, 0);

    return Temp;

}

 OLED_ShowSignedNum(1, 7, Speed, 5);

测速,就是要隔一段时间读取CNT的值,输出CNT的值,然后把CNT的值清零,以CNT代替速度。

int16_t Encoder_Get(void)

{

    int16_t Temp;

    Temp = TIM_GetCounter(TIM3);

    TIM_SetCounter(TIM3, 0);

    return Temp;

}

隔一段时间是多少呢?在主函数中读取函数之后进入一段延时函数,但是不建议这么做,因为在主程序中设定一个比较长的延时函数,会影响主程序的进程,可以配置一个定时器,按照配置的花四溅进入中断,然后再中断中读取CNT的值并且传回主函数,用OLED的函数显示就行啦!

————————————————

版权声明:本文为CSDN博主「笔下觅封侯」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/m0_63148816/article/details/130453494