微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 用STM32对编码开关实现精确计数

用STM32对编码开关实现精确计数

时间:11-13 来源:互联网 点击:
机械触点式编码开关,依靠 A 相和 B 相信号的相位差来判断旋转方向,脉冲数计数,输出的理想波形如下图:

实际使用时,其输出的脉冲边沿可能会包含很多毛刺,如果简单的使用上升沿或者下降沿触发,在边沿处会可能产生很多误触发,使用延时等手段虽能缓解,但从方法到结果都有蒙混过关之嫌,不能令人满意。下图是我用示波器随意抓了一个编码开关的下降沿:

为了能够可靠计数,必须避开上升沿和下降沿附近的毛刺,在编码开关输出脉冲信号的脉冲中段取值,如下图所示:

1、A 相和 B 相信号分别送入具有上升/下降沿触发能力的IO,开启上升沿/下降沿触发中断;

2、在 A 相信号的上升沿/下降沿触发中断中判断 B 相的状态,如上图所示的 T0 时刻,得到 B 相当前的值为 0;

3、在 B 相信号的上升沿/下降沿触发中断中判断 A 相的状态,如上图所示的 T1 时刻,得到 A 相当前的值为 1;

4、建立一个保存 A 相 和 B 相状态的数组,至少可保存 3 组状态值,若得到的状态值和数组中保存的上一个状态值相等,说明该次触发为毛刺,则不更新状态数组,例如 T1 时刻由 B 相信号触发的中断程序中,判断 A 相信号为 1 状态,与 T0 时刻中的 A:0; B:0 状态不同,则更新数组;若 T1 时刻后 B 相的上升沿产生毛刺进入中断程序,则因为读取到的 A 相值仍然为 1,因为和上一个状态相同,不更新状态数组;

5、如此利用 A 相的边沿触发中断读取 B 相的值,B 相的边沿触发读取 A 相的值,就可以保证取样点位于没相信号的中间时刻;

6、在每次状态数组有更新时判断编码开关的旋转方向,如上图蓝色框内的数值组合出现时,可以判断编码器 +1,反之,如下图紫色框内的数值组合出现时,可判断编码器值 -1;

7、当编码器出现中途改变方向的情况时,其输出的脉冲如下图所示:

可以看到,T1、T2、T3时刻编码器产生 +1 的计数脉冲,T5、T6 因为采集到的 A 相和 B 相状态值与 T3 状态没有变化,故不更新状态数组也不做任何处理,到 T7 时刻以后开始更新,则 T3、T7、T8 组合产生 -1 的状态组合。整个过程中都不会发生丢计数的情况,另一种编码器改变旋转方向的情况如下:

类似上面的分析,也不会发生丢计数的情况。下面给出STM32外部触发程序作为参考:

/* Encoder.c */

signed char Phase_A, Phase_B; //按bit位来储存A/B相的状态,bit7作为缓存,bit6为最新状态 //值,然后依次是bit5、bit4,需要更新时,使用右移操作,之 //所以使用有符号数,是为了右移时保留bit7的值

void EXTI0_IRQHandler(void)

{

EXTI->PR &= EXTI_PR_PR0; //Clear interrup pending

if(COMP->CSR & COMP_CSR_COMP2OUT) //若编码器直接连入IO,此处改为判断IO状态即可,我使 //用的是比较器输出的值来驱动IO产生外部触发,故为此

{

Phase_B |= 0x80; //Phase_B = 1xxx xxxx

}

else

{

Phase_B &= 0x7F; //Phase_B = 0xxx xxxx

}

if(((Phase_B < 1)^Phase_B) & 0x80) //判断缓存中的bit7位和bit6位是否相等,若不等,更新缓存

{

Phase_B >>= 1;

Phase_A >>= 1;

if( ((Phase_A & 0x70) == 0x30) && ((Phase_B & 0x70) == 0x60) ) //A:0 1 1, B:1 1 0 序列

{

FlashData_Copy.encoder_count += 1;

}

if( ((Phase_A & 0x70) == 0x60) && ((Phase_B & 0x70) == 0x30) ) //A:1 1 0, B:0 1 1 序列

{

FlashData_Copy.encoder_count -= 1;

}

}

}

void EXTI2_TSC_IRQHandler(void)

{

EXTI->PR &= EXTI_PR_PR2; //Clear interrupt pending

if(COMP->CSR & COMP_CSR_COMP1OUT)

{

Phase_A |= 0x80; //Phase_A = 1xxx xxxx

}

else

{

Phase_A &= 0x7F; //Phase_A = 0xxx xxxx

}

if(((Phase_A < 1)^Phase_A) & 0x80)

{

Phase_A >>= 1;

Phase_B >>= 1;

if( ((Phase_A & 0x70) == 0x30) && ((Phase_B & 0x70) == 0x60) )

{

FlashData_Copy.encoder_count += 1;

}

if( ((Phase_A & 0x70) == 0x60) && ((Phase_B & 0x70) == 0x30) )

{

FlashData_Copy.encoder_count -= 1;

}

}

}

/******************************************************************************

EXTI0 and EXTI2 interrupt setup

******************************************************************************/

void setupEXTI0_2(void)

{

/* EXTI0 config */

SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI0_PA;

EXTI->IMR |= EXTI_IMR_MR0;

EXTI->RTSR |= EXTI_RTSR_TR0; //Rising trigger

EXTI->FTSR |= EXTI_FTSR_TR0; //Falling trigger

/* Enable the Selected IRQ Channels --------------------------------------*/

NVIC->ISER[EXTI0_IRQn >> NVIC->I

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top