对于一些频繁使用中断并且仅仅执行比较简单的任务的情况下,可以选用编码器接口的方式,一般都会设计一个硬件电路模块来自动完成。而编码器接口就是一个给编码器进行计次的电路。每个一段时间就去一次计数值,就能得到旋转的速度了。编码器测速一般都用于电机测速上,PWM驱动电机,编码器测速,PID算法进行闭环控制。一般电机的速度比较快,会用无接触方式的霍尔传感器或者光栅进行测速。为了模拟,就用人手扭动代替电机转动,编码器就代替霍尔传感器或者光栅(事实上都是一样效果)。
编码器接口的工作流程:
编码器有两个输出,分别输出两个相,当满足一定条件的时候,控制时基单元中的CNT进行自增或者自减,在一定时间后把得到的值读出来,然后CNT清零,再根据旋转方向进行自增自减。有点类似与侧频法的套路,但是这个比较高级,还能自减。
定时器如果用了编码器的话,就不敢别的活了,考虑定时器够不够。
硬件资源以及软件资源是可以互补的,比如对于输出比较,可以设置一个中断,进入中断手动计数,自行把电平反转,对于输入捕获,也可以设置中断,手动把CNT放到CCR中。对于编码器接口,也可以用手动中断进入,然后进行自增自减少。
这就是硬件不够,软件来凑。优先使用硬件资源,软件资源可以去干更重要的事情。
图中最后一点,CH3CH4不能接编码器。
正交编码器,输出的两个相,扭动正交编码器的时候,输出两个相位相差90度的波形,扭动越快输出的频率就越快,频率就相当于速度,测出频率就相当于速度啦。
根据机型的选择,规定正转A相提前B相提前90度,亦或是滞后90度,这只是一个极性的问题。
但是也可以不要B相,用一个普通IO口进行高低电平判断是正转还是反转,为什么用正交信号表示呢?
原因有2:
1.抗噪声,两个信号是交替跳变的,如果有毛刺,就是一个信号不变,另一个信号疯狂跳变,就是噪音干扰了,设置滤波电路就能去除噪声。
2.因为两个都能跳变,所以测频率的精确度提高了一倍。
工作原理:
根据上图右侧的表格,在编码器输出信号的歌边沿都进行计数,至于是自增还是自减,则看是正转还是反转,正转对应上面的表,计数器加一,反转对应下面的表,减。
电路结构:
编码器接口的两个输入分别接输入捕获的前两个通道,CH1CH2,输入滤波以及边沿检测信号都都有在使用,但是后续的预分频器没有使用了。
编码器接口的输出功能是控制时基单元的CNT进行加减,这时候不用内部时钟以及计数模式了,因为计数器现在属于一种托编码器接口管的状态,加减由编码器接口决定。
两张图的TI1以及TI2都没变,为什么会计数不一样呢?因为一个均不反相一个TI1反相了,什么是反相,就是高电平变成低电平,低电平变成高电平,也就是经过一个非门。这个就跟极性选择有关系了,编码器接口的极性选择不是选择上升沿触发还是下降沿触发,因为上下沿都会触发,极性选择选择的是要不要经过非门,进过则信号的电平反转,不经过则跟以前一样。
上面两个图有什么用呢?
就是如果自己编码器接口发现技术方向反了,就可以改变一个相的极性改变计数方向,或者改变两者的接线颠换,就行啦。
代码部分:
编码器接口的接线只接受CH1跟CH2,所以要看清楚引脚的接线图对应的引脚,不要接错啦
第一步:开启RCC时钟,配置GPIO以及定时器的时钟
第二步,配置GPIO,把PA6以及PA7配置成输入模式,配置GPIO的时候,选择输入模式要根据外部的装置的默认输出状态来选择,默认高电平就上拉,低电平就下拉,不确定就浮空,但是浮空容易受干扰。
三,配置时基单元,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