单片机软件解析编码器数据

使用电机时通常会遇到编码器的使用,编码器通过码盘和相关传感器可以测量电机的转动速度,因此读取编码器的数据是十分重要的。

stm32集成了硬件编码器模式,因此只需要配置相关的时钟就可以使用编码器。但是在没有stm32的时候,我们就需要用软件来实现编码器数据的解析。

编码器输出为AB两个电平信号,当电机正转时,A滞后B90度相位,正向计数;当电机反转时,A超前B90度相位,反向计数。此外还有抖动的情况,抖动意味着A会出现一个单独的脉冲或者B会出现一个单独的脉冲。如下图所示:

单片机软件解析编码器数据_第1张图片
一般来说我们在AB的每个边沿都计数,这样可以获得最大的精度。stm32提供了三种计数方法,我们在软件实现的时候也可以实现这三种不同的方法。

单片机软件解析编码器数据_第2张图片
现在我们考虑一下软件解析编码器的思路应该是怎么样的(在AB的每个边沿都计数),我们要解决下面三个问题(AB相差90度,一般的抖动长度不会超过这么长):

  1. 正向计数:电机正转意味着会出现这样的波形:A↓-B↑-A↑-B↓-…,按照这样的顺序每次计数加一就行了;
  2. 反向计数:电机反转意味着会出现这样的波形:B↑-A↓-B↓-A↑-…,按照这样的顺序每次计数减一就行了;
  3. 抖动时不计数:对于抖动而言必然是:A(或B)出现了一个上升沿(或下降沿)必然会接着出现一个下降沿(或上升沿),对于这样的抖动,我们只需要保证在两个边沿过后计数为零就可以了。

下面是程序的流程图,我们使用有限状态机,使用四个状态,在每个边沿触发状态的改变,结果如下:

单片机软件解析编码器数据_第3张图片

下面是摘自msp430论坛上的一段代码,实现了上述功能:

#include "includes.h"

void init(void)
{
        P2IES |= BIT4 + BIT6;              //P1.4、P1.6设为下降沿中断
        P2IES &= ~(BIT5 + BIT7);           //P1.5、P1.7设为上升沿中断
        P2IE |= BIT4 + BIT5 + BIT6 + BIT7; //允许P1.4567中断
        P2IFG = 0;                         //避免第一次误动作
        timerB_Init();
        _EINT(); //总中断允许
}
unsigned int EncoderCnt = 2001; //旋转角度计数值,全局变量,供其他程序访问
uchar EncoderStatus = 1;        //旋转时序状态变量

#pragma vector = PORT2_VECTOR //P1口中断源
__interrupt void P2_ISR(void) //声明一个中断服务程序,名为P1_ISR();
{
        _BIC_SR(SCG0);    //如果从LPM3唤醒,恢复时钟准确性
        if (P2IFG & BIT4) //-------------------A 下降中断(P1.4中断入口)-------------//
        {
                if (EncoderStatus == 1)
                {
                        EncoderStatus = 2;
                        EncoderCnt++;
                } //A 下沿,1->2
                if (EncoderStatus == 4)
                {
                        EncoderStatus = 3;
                        EncoderCnt--;
                } //A 下沿,4->3
        }
        if (P2IFG & BIT5) //-------------------A 上升中断(P1.5中断入口)-------------//
        {
                if (EncoderStatus == 3)
                {
                        EncoderStatus = 4;
                        EncoderCnt++;
                } //A 上沿,3->4
                if (EncoderStatus == 2)
                {
                        EncoderStatus = 1;
                        EncoderCnt--;
                } //A 上沿,2->1
        }
        if (P2IFG & BIT6) //-------------------B 下降中断(P1.6中断入口)-------------//
        {
                if (EncoderStatus == 4)
                {
                        EncoderStatus = 1;
                        EncoderCnt++;
                } //B 下沿,4->1
                if (EncoderStatus == 3)
                {
                        EncoderStatus = 2;
                        EncoderCnt--;
                } //B 下沿,3->2
        }
        if (P2IFG & BIT7) //-------------------B 上升中断(P1.7中断入口)-------------//
        {
                if (EncoderStatus == 2)
                {
                        EncoderStatus = 3;
                        EncoderCnt++;
                } //B 上沿,2->3
                if (EncoderStatus == 1)
                {
                        EncoderStatus = 4;
                        EncoderCnt--;
                } //B 上沿,1->4
        }
        //if(P2IFG&BIT1)  EncoderCnt=2200;//转一圈,清零标志
        P2IFG = 0; //清楚P1口中断标志位
        if (EncoderCnt <= 1)
                EncoderCnt = 1;
        LPM3_EXIT; //退出中断后退出低功耗模式。若退出中断后要保留低功耗模式,将本句屏蔽
}


· 关注公众号【技术斋】,发现更多精彩!

你可能感兴趣的:(常用模块)