增量式旋转编码器(见图1)是一种控制系统中常见的转角测量装置,常见的转角测量装置,常被安装在各种旋转轴末端,以测量轴转角。它输出如图2所示的两路相位差90°的脉冲(以下简称A路、B路)指示位置。旋转编码器每转过一定角度,就输出一个脉冲。当旋转编码器顺时钟旋转时,A路脉冲超前B路脉冲90°;当旋转编码器逆时针旋转时,A路脉冲滞后B路脉冲90°。计算脉冲个数可以获知转角,根据相位差可以知道旋转方向,从而计算出旋转轴相对角度的变化。
旋转编码器内部一般由极细的光栅条纹与光电检测装置构成。通常将一圈内光栅条纹数目称为“线”来表示分辨率,例如“1000线”的编码器,旋转一圈能输出1000个脉冲。受到光线衍射效应的影响,光栅条纹上限一般是每2500条左右。这限制了旋转编码器的分辨率,很难超过2500线。
为了提高编码器的分辨率,普通采用电子4细分方法。两路脉冲相差90°,一个周期内两路信号共会出现两次上升沿与两次下降沿,4个沿均匀分布在一个周期内。取4个沿作为旋转检测判据,可以将分辨率提高4倍。在2500线的旋转编码器上,能够实现1/10000圈的角度检测分辨率。图3是对编码器输出波形进行细分的原理,编码器顺时针旋转,输出两个脉冲,再逆时针旋转,输出两个脉冲。4细分后能检测出编码器顺时针旋转8步,再逆时针转8步。
早期控制系统中,4细分常用硬件电路来实现。为了节省成本与电路板面积,本例要求在MSP430单片机上用软件来实现旋转编码器4细分逻辑,并将转角值保存在全局变量内。
这是典型的时序逻辑问题,可以很方便地用状态机建模。先分析状态。一个脉冲周期内,脉冲A与脉冲B的电平总共有4种组合:“A=1 B=0”、“A=0 B=0”、“A=0 B=1”、“A=1 B=1”。将这4种组合分别称为状态1、2、3、4(见图4)。编码器旋转时,依次在这4种状态之间跳变。引发状态转移共有4种事件:A下降沿、A上升沿、B下降沿、B上升沿。根据时序图,先用语言描述状态变化规律:
在状态1时,遇到A下降沿,变为状态2,同时角度计数加1
在状态2时,遇到B上升沿,变为状态3,同时角度计数加1
在状态3时,遇到A上升沿,变为状态4,同时角度计数加1
在状态4时,遇到B下降沿,变为状态1,同时角度计数加1
在状态4时,遇到A下降沿,变为状态3,同时角度计数减1
在状态3时,遇到B下降沿,变为状态2,同时角度计数减1
在状态2时,遇到A 上升沿,变为状态1,同时角度计数减1
在状态1时,遇到B上升沿,变为状态4,同时角度计数减1
再根据语言描述,画出状态转移图(见图4),可以发现当编码器顺时钟旋转时,状态也按照顺时针方向转移,每次状态转移时让角度变量增加。反之逆时针转移时,让转角变量递减。
MSP430单片机的I/O口能设置为上升沿或下降沿中断,用两个I/O口(P1.4、P1.5)检测A脉冲,用两个I/O口(P1.6、P1.7)检测B脉冲。如图5所示。其中P1.4与P1.6设为下降沿中断、P1.5与P1.7设为上升沿中断。首先初始化4个I/O口:
P1IES |= BIT4+BIT5 ; //P1.4、P1.6设为下降沿中断
P1IES &=~(BIT4+BIT5); //P1.5、P1.7设为上升沿中断
P1IE |=BIT4+BIT5+BIT6+BIT7; //允许P1.4567中断
P1IFG = 0; //避免第一次误动作
_EINT(); //总中断允许
在4个中断内进行状态转移处理与计数即可实现4细分程序。利用中断触发也同时保证了检测的实时性:
Int EncoderCnt= 0; //旋转角度计数值,全局变量,供其他程序访问
Unsigned charEncoderStatus = 1; //旋转时序状态变量
Pragma vector =PORT1_VECTOR //P1口中断源
__interruptvoid P1_ISR( void ) //声明一个中断服务程序,名为P1_ISR();
{
_BIC_SR(SCG0); //如果从LPM3唤醒,恢复时钟准确性
If(P1IFG&BIT4) //-------------------A 下降中断(P1.4中断入口)-------------//
{
If (EncoderStatus ==1) { EncoderStatus = 2; EncoderCnt++ ;} //A 下沿,1->2
If (EncoderStatus == 4) { EncoderStatus= 3; EncoderCnt-- ;} //A 下沿,4->3
}
If(P1IFG&BIT5) //-------------------A 上升中断(P1.5中断入口)-------------//
{
If (EncoderStatus ==3) { EncoderStatus = 4; EncoderCnt++ ;} //A 上沿,3->4
If (EncoderStatus == 2) { EncoderStatus= 1; EncoderCnt-- ;} //A 上沿,2->1
}
If(P1IFG&BIT6) //-------------------B 下降中断(P1.6中断入口)-------------//
{
If (EncoderStatus ==4) { EncoderStatus = 1; EncoderCnt++ ;} //B 下沿,4->1
If (EncoderStatus == 3) { EncoderStatus= 2; EncoderCnt-- ;} //B 下沿,3->2
}
If(P1IFG&BIT7) //-------------------B 上升中断(P1.7中断入口)-------------//
{
If (EncoderStatus ==2) { EncoderStatus = 3; EncoderCnt++ ;} //B 上沿,2->3
If (EncoderStatus == 1) { EncoderStatus= 4; EncoderCnt-- ;} //B 上沿,1->4
}
P1IFG = 0; //清楚P1口中断标志位
__low_power_mode_odd_on_exit(); //退出唤醒CPU(如果主程序需要唤醒)
}
在每个中断内,根据当前状态处理状态转移,并改变EncoderCnt变量的值。在软件的其他任何位置,只要访问EncoderCnt变量即可获知被测量转轴的当前转角。