yule 发表于 2007-2-23 23:42:11

发一个我的程序,旋转编码器解码,今天晚上写出来的

初步试验,可以用了,没有发现问题。



/*旋转编码器解码程序

       

        PC4,PC5为左右输入,内部上拉,公共脚接地。

        1ms调用一次

       

//2007.2.23   yule



//修改:



*/



#define Knob_In_PIN PINC

#define Knob_In_PORT PORTC

#define Knob_In_DDR DDRC



#define Knob_In_L PC4

#define Knob_In_R PC5



//输出结果:0不动作;1左转一格;2右转一格。

enum direct {STOP,LEFT,RIGHT}D_OUT;



const uint8_t direct_left PROGMEM ={3,2,0,1,3};

const uint8_t direct_right PROGMEM ={3,1,0,2,3};



//初始化。

void Knob_Init(void)

{

        Knob_In_DDR &= ~((1<<Knob_In_L)|(1<<Knob_In_R));        //输入

        Knob_In_PORT |=(1<<Knob_In_L)|(1<<Knob_In_R);                //上拉

}



//编码器解码

void knob_scan(void)

{

   static uint8_t knob_ary;

   static uint8_t knob_count=0;



   uint8_t knob_value;

   uint8_t update=0;

   uint8_t i;

   

   knob_value = Knob_In_PIN&((1<<Knob_In_L)|(1<<Knob_In_R)); //取端口C管脚信号

   knob_value =knob_value>>4;

   

   if(knob_value==knob_ary)

   {

                ++knob_count;

                if(knob_count==1)

                {

                        knob_ary=knob_ary;

                        knob_ary=knob_ary;

                        update=1;

                }

                if(knob_count==0xff)                //停留,不转动。

                {

                        knob_count=2;

                }

        }

        else

        {

                knob_ary=knob_value;

                knob_count=0;

        }

        //如果转动产生新数据,判断方向。

        //左转为 11 10 00 01 11         3 2 0 1 3

        //右转为 11 01 00 10 11         3 1 0 2 3

        if(update)

        {

                update=0;

               

                for(i=0;i<4;i++)

                {

                        if((knob_ary==pgm_read_byte(direct_left+i))

                                &&(knob_ary==pgm_read_byte(direct_left+i+1)))

                                {

                                        D_OUT=LEFT;

                                }

                        else if((knob_ary==pgm_read_byte(direct_right+i))

                                &&(knob_ary==pgm_read_byte(direct_right+i+1)))

                                {

                                        D_OUT=RIGHT;

                                }

                        else D_OUT=STOP;

                }

        }

}

yule 发表于 2007-2-23 23:44:26

大家瞧瞧,有没有需要修改的,多提些意见。

zlf667788 发表于 2007-2-24 09:58:58

用中断不是更好,简单一点的做法是使用一路信号的固定边沿触发中断,中断发生时读另一路信号来判断方向,复杂一点的做法是两路信号的两个边沿都触发中断

Gorgon_Meducer 发表于 2007-2-25 00:11:21

……

unsigned long DCounter = DCOUNTER_ZERO;

……



# define MotorDirection          (PINB & 0x10)

……



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

*函数说明:外中断初始化函数                              *

*说明:    使用外中断0和外中断1 下降沿触发               *

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

void ExternIntInit(void)

{

    MCUCR = 0x0A;

    GICR= 0xC0;

}





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

*函数说明:外中断0中断处理程序                           *

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

void Int0Isr(void)

{

    /*

    if (MotorDirection)

    {

      DCounter ++;

    }

    else if (DCounter)

    {

      DCounter --;

    }*/

               

                asm("push r20")

                asm("push r2");

                asm("push r3");

                asm("push r4");

                asm("push r5");

               

                asm("lds R2,_DCounter");        //Load direct from data space                                               

                asm("lds R3,_DCounter+1");//Load direct from data space       

                asm("lds R4,_DCounter+2");//Load direct from data space       

                asm("lds R5,_DCounter+3");//Load direct from data space       

                asm("ldi r20,1");

                if (MotorDirection)

                {

                   //DCounter ++;

                  asm("add r2,r20");   //Add without carry

                        asm("ldi r20,0");        

                        asm("adc r3,r20");          //Add with carry

                        asm("adc r4,r20");          //Add with carry

                        asm("adc r5,r20");          //Add with carry

                }

                else

                {

                  //DCounter --;

                        asm("sub r2,r20");   //Add without carry

                        asm("ldi r20,0");        

                        asm("sbc r3,r20");          //Add with carry

                        asm("sbc r4,r20");          //Add with carry

                        asm("sbc r5,r20");          //Add with carry

                }

                asm("sts _DCounter,R2");               

                asm("sts _DCounter+1,R3");

                asm("sts _DCounter+2,R4");

                asm("sts _DCounter+3,R5");

               

                asm("pop r5");

                asm("pop r4");

                asm("pop r3");

                asm("pop r2");

                asm("pop r20");

               

}

yangzq 发表于 2007-2-28 23:51:54

什么叫旋转编码器?

gphs 发表于 2007-3-2 10:12:53

中断不好使!



扫描好些!

zlf667788 发表于 2007-3-2 13:40:04

很多方法在于运用,运用得当才能发挥其效用,不知楼上是怎么用的中断而断定其不好用?

dewangyang 发表于 2007-3-2 13:58:02

int8 CodingsWitchPolling()//return 0=不变;-1=减;1=加//a先高=+

{

        static bool aold,bold;        //定义了两个变量用来储蓄上一次调用此方法是编码开关两引脚的电平

        static bool st;                        //定义了一个变量用来储蓄以前是否出现了两个引脚都为高电平的状态

        int8 tmp = 0;

        if(CodingsWitch_A&&CodingsWitch_B) st = 1;                //如果两个引脚都为高电平则把st置1

        if(st)                                        //如果st为1执行下面的步骤

        {

                if(CodingsWitch_A==0&&CodingsWitch_B==0)                //如果当前编码开关的两个引脚都为底电平执行下面的步骤

                {

                        if(bold)                //如果上一次调用时编码开关b引脚为高说明编码开关在向加大的方向转

                        {

                                st = 0;               

                                tmp++;                //设返回值为1

                        }

                        if(aold)                //如果上一次调用时编码开关a引脚为高说明编码开关在向减小的方向转

                        {

                                st = 0;

                                tmp--;                //设返回值为-1

                        }

                }

        }

        aold = CodingsWitch_A;                                //储蓄a到aold

        bold = CodingsWitch_B;                                //储蓄b到bold

        return tmp;                                //返回 0=不变;-1=减;1=加

}



我这个如何

xiaobendan 发表于 2007-3-3 09:57:28

我一直都用中断方式的啊

hellolicao 发表于 2007-10-30 10:04:43

我用dewangyang 水滴的程序作了实验,发现旋钮转动较快时有漏读现象.请问多长时间调用一次程序才能不漏判.

bluerain 发表于 2007-10-30 10:10:11

一般都是用中断+滤波

dvhome 发表于 2007-10-30 15:10:18

我习惯使用扫描方式

wqb202 发表于 2007-10-30 16:11:40

扫描?开玩笑吧!如果单片机要处理的事情多一点的话,看你怎么扫描,

hellolicao 发表于 2007-10-30 16:25:21

别人设计的硬件,用的是普通IO口我只能用扫描的方式.不知道怎么才能做的好点.

qingzhong 发表于 2009-3-14 11:36:44

mark

lningbd 发表于 2009-3-19 00:07:34

参考楼主的程序,编成了,谢谢。
我的这个编码器用在步进电机调速上面,0--700转/分.要求:转一下后,停一下再转,每格速度+1或者-1;连续迅速旋转编码器4圈以上时,每转一格+5或者-5,给人一种“加速”的感觉。请问这种加速的感觉如何利用编程实现呢?大家给个思路。谢谢

tangmix 发表于 2009-3-19 09:25:40

我一直用中断,很实时,不会丢失脉冲,单片机是MSP430

xiaobendan 发表于 2009-3-19 10:19:29

最近发现还是扫描比较好容易处理干扰。
中断方式不好弄,尤其是传输线路比较长,又没有屏蔽的情况,比如40米的普通电缆

motion_ctrl 发表于 2009-3-19 15:36:34

旋转编码器的信号输出有A相,B相,Z相,其中Z为零脉冲,计数模块中不使用。计数模块使用A,B来进行计数。编码器结构以及计数原理如下:
结构:玻璃码盘,上排为A,下排为B,灰色的不透光,白色的透光。
编码器转动后经过码盘和光电二极管的作用可以得到如下的波形:

http://cache.amobbs.com/bbs_upload782111/files_13/ourdev_426534.JPG
±à???÷???í (原文件名:???ü??.JPG)

上图为编码器的工作原理,图中A相超前B相90度,如果转动方向相反,则A相滞后B相90度。B_A为B&A的组合,若A相超前B相90度设定为正向旋转,则编码器正向转动一个码的距离,B_A变化过程为01-11-10-00。
根据这个原理计数模块根据B_A变化过程来计数:
若当前B_A为00状态,下一个状态为01则计数器加1。
若当前B_A为00状态,下一个状态为10则计数器减1。
其余状态不符合编码器的工作原理,计数器的值不变。
此时计数器的结果是编码器实际移动一个码距离的4倍。由于编码器加工精度的原因,这个结果与实际的位置相比,不是非常准确。
通常的增量式光电编码器产生的周期性的方波信号理论的占空比是1:1,即50%。但是由于码盘加工精度,安装精度等原因的影响,在编码器匀速转动时产生信号的占空比不是1:1,是40%-60%,即信号占空比的误差是10%。
但是其信号的周期误差,即高电平与低电平时间总和的误差比较小,为1%。所以通常用编码器信号测量速度应该测量信号的整个周期来计算,这样测量的误差才较小。
只有把计数结果右移两位(除以4),得到的结果才是准确的编码器移动一个码的距离。

motion_ctrl 发表于 2009-3-19 15:43:24

我通常用FPGA来实现编码器的计数,以前也用过单片机IO查询方式处理过,需要评估查询速度是否满足编码器的最大频率变化。

motion_ctrl 发表于 2009-3-19 16:06:55

//编码器计数程序
void encoder_cnt(void)
{
        uchar temp;
        temp = PIND; //取端口D管脚信号
        couch_clr = (temp & 0x08); //取编码器清零信号
        if(couch_clr != false) //有编码器清零信号
        {
                couch_num = 0; //水平床码清零
        }
        else
        {
                if(encoder_cnt_en == false) //编码器计数模块没有启动
                {
                        pr_couch_ba = temp & 0x03; //取编码器A、B相电平信号
                }
                else
                {
                        couch_ba = temp & 0x03; //取编码器A、B相电平信号
                        if(pr_couch_ba == 0x00)
                        {
                                if(couch_ba == 0x01)
                                {
                                        couch_num++; //水平床码加1
                                }
                                else if(couch_ba == 0x02)
                                {
                                        couch_num--; //水平床码减1
                                }
                        }
                        else if(pr_couch_ba == 0x01)
                        {
                                if(couch_ba == 0x03)
                                {
                                        couch_num++; //水平床码加1
                                }
                                else if(couch_ba == 0x00)
                                {
                                        couch_num--; //水平床码减1
                                }
                        }
                        else if(pr_couch_ba == 0x02)
                        {
                                if(couch_ba == 0x00)
                                {
                                        couch_num++; //水平床码加1
                                }
                                else if(couch_ba == 0x03)
                                {
                                        couch_num--; //水平床码减1
                                }
                        }
                        else if(pr_couch_ba == 0x03)
                        {
                                if(couch_ba == 0x02)
                                {
                                        couch_num++; //水平床码加1
                                }
                                else if(couch_ba == 0x01)
                                {
                                        couch_num--; //水平床码减1
                                }
                        }
                }
                pr_couch_ba = couch_ba;
        }
        if(couch_num >= 0) //床码值大于等于0
        {
                couch_num_minus = 0; //床码值正标志
                if(couch_num < 20000)
                {
                        abs_couch_num = couch_num; //取绝对床码
                }
        }
        else
        {
                couch_num_minus = 1; //床码值负标志
                if(couch_num > -20000)
                {
                        abs_couch_num = abs(couch_num); //取绝对床码
                }
        }
        abs_couch_num = abs_couch_num >> 2;//床码计数结果除4
}
这时以前我写的IO查询方式的编码器计数函数。我个人认为还是扫描好些,若用中断需要AB相双沿触发中断。

ljgvictory 发表于 2009-3-19 16:56:48

MARK

KANGYD 发表于 2009-3-19 23:03:39

这几天正要做闭环控制,谢谢!

armfans 发表于 2009-3-20 00:20:04

学习了

wsl16805 发表于 2009-3-20 15:29:33

记号.

tkdr2001 发表于 2009-3-30 13:31:26

进来学习了,最近买了100只编码器:)

tkdr2001 发表于 2009-3-30 13:50:48

主楼上面的   pgm_read_byte这个函数好像没有提供出来,应该是读状态需要动到的,否则,后面的for循环不知道怎么动作了

tkdr2001 发表于 2009-3-30 14:10:12

分析了这3个,感觉还是7楼的好用些啊

zdq823 发表于 2009-3-30 15:13:52

不错,有原理,有程序,学习了

sunyouyuan 发表于 2009-3-30 17:24:19

旋转编码器是什么?

ml07077 发表于 2009-3-30 21:15:46

好,学习!

wanghengquan 发表于 2009-3-30 21:47:51

是光编码器吧   我也用的JANCO的   一个好几万

xinyang666 发表于 2009-3-31 17:10:32

MARK 学习了

phone 发表于 2009-3-31 21:38:49

使用中断来作是比较实时,但要处理干扰。

kinggao 发表于 2009-4-2 16:05:27

MARK 学习了

waiman 发表于 2009-4-18 22:23:21

MARK 标记一下

wang_xm 发表于 2009-6-14 15:09:53

试验的如何了啊?关注

tyblly 发表于 2009-6-14 16:38:00

mark

sanhope 发表于 2009-6-14 17:20:06

支持23楼:我就是这么做的

coody 发表于 2009-6-14 20:01:44

感觉太复杂了,偶多年以来,一直是100us查询一次并两次采样相等才有效,相当于去抖,在A的下降沿时检测B是高还是低来决定左右,很可靠,用C的话,就几句语句就OK了

vi51 发表于 2009-7-10 23:36:44

有用汇编做的编码开关程序吗,想学习使用。

shaoyidong 发表于 2009-7-11 06:29:16

mark

zhq448 发表于 2009-7-11 06:37:44

旋转编码器应该有A B 相加一组Z的啊

wxfhw 发表于 2009-7-11 07:37:53

记号,谢谢
正要使用旋转编码器

sange 发表于 2009-7-11 15:19:27

mark

vv3g 发表于 2009-7-13 12:50:37

打地基

wang_xm 发表于 2009-7-29 03:36:44

测量速度不怕丢失一个2个 测量位置就有跟随问题了 尤其是精度高,速度高的场合。没有外部逻辑配合 仅靠AVR ,速度还是不够。

chen1986sl 发表于 2009-7-29 03:56:37

用过一次,效果不理想, 今天也做回mark 党

chen1986sl 发表于 2009-7-29 03:57:42

【17楼】 qingzhong
积分:154
派别:
等级:------
来自:深圳
mark
 
2009-03-14,11:36:44   遇到个挖坟党。。。。。。

chengtina 发表于 2009-7-29 09:01:50

mark

CCAO_75 发表于 2009-7-29 09:12:17

hao

wenming 发表于 2009-7-29 09:16:24

最近用ALPS的大约10块左右,EC11系列的。

ideality0214 发表于 2009-9-9 10:11:04

请问楼主旋转编码器的解码用的是那款芯片?

piccode 发表于 2009-9-9 10:27:17

程序太长了。 7句就可以搞定。 X在3-100范围内加减。ALPS的EC11系列


///////////////////////RB中断入口RB4-RB7//////////////////////////////////////
#int_RB
void RB_isr(){

         if (input(PIN_B4)!=input(PIN_B5)){//这里很重要,不然的话会加一个数又减一个数
            rb_buf^=portb;      //相同为0.相异为1
            if(plus) if(x<101) x++; //断rb_buf的4和5来检测是RB4还是RB5中断.
            if(sub)if(x>3) x--;   //
               }
                  
          while((input(PIN_B4)!=input(PIN_B5)));   //等待B4电平=B5电平
         rb_buf=input_b();                  //读RB口,电平锁存退出中断程序.
         CLEAR_INTERRUPT(int_RB);
}
////////////////////////////end/////////////////////////////////////////////////

luyongganglyg 发表于 2009-10-15 21:18:51

受教!有个欧姆龙的正好玩玩

cana11225 发表于 2009-10-16 10:59:30

MARK

xinfa190 发表于 2009-10-16 12:54:54

【42楼】 coody
积分:400
派别:
等级:------
来自:
感觉太复杂了,偶多年以来,一直是100us查询一次并两次采样相等才有效,相当于去抖,在A的下降沿时检测B是高还是低来决定左右,很可靠,用C的话,就几句语句就OK了
===================================================================================================================
这种方法我用在300块钱一个的编码器上好用,用在10块钱一个的上面很不好用,这几天正在搞

LiAsO 发表于 2009-10-16 15:25:23

先用硬件消除颤动,去抖。或者加个滤波(?) 或者加个四分频电路(?)加触发器判别方向(?)
不然用带qei模块的mcu或者dsp试一试?

to楼上,上一个公司里用的是6块的(200pcs),taiwan产。烂。最后凑合凑合,加强了电路,改进了程序,总算能用了。
加速旋转时候的代码,好像不一样的。

xytzc 发表于 2009-10-16 16:47:30

mark

Heavin 发表于 2009-10-16 22:47:04

扫描,感觉还好,停在中间会自动退出,基本不会误判。

void Decode(void)

{
if((!Flag_End_Decode)&&Flag_Decode_End_Process)
    {
   if(In_Decode_Pin_L == In_Decode_Pin_R)
       {
    if(!Flag_End_Detected)
      {
      if(In_Decode_Pin_L)
          {
            Flag_Decode_Level_L = 0;
            Flag_End_Detected = 1;

             }
      if(!In_Decode_Pin_L)
         {
             Flag_Decode_Level_L = 1;
             Flag_End_Detected = 1;
             }      
      }
      }
    else
      {
         if(Flag_Decode_Level_L)
            {
            if(In_Decode_Pin_L)
                {
                Flag_Decode_Add = 1;
                Flag_Decode_Dec = 0;
                Flag_End_Decode = 1;
                Flag_Decode_End_Process = 0;
                }
            else if(In_Decode_Pin_R)
                {
                Flag_Decode_Add = 0;
                Flag_Decode_Dec = 1;
                Flag_End_Decode = 1;
                Flag_Decode_End_Process = 0;
                }
             }
         else
            {
            if(!In_Decode_Pin_L)
                {
                Flag_Decode_Add = 1;
                Flag_Decode_Dec = 0;
                Flag_End_Decode = 1;
                Flag_Decode_End_Process = 0;
                }
            else if(!In_Decode_Pin_R)
                {
                Flag_Decode_Add = 0;
                Flag_Decode_Dec = 1;
                Flag_End_Decode = 1;
                Flag_Decode_End_Process = 0;
                }
               }
         }

      }   
}

Heavin 发表于 2009-10-16 22:50:41

void Update_Vaule()
{

if(Flag_End_Decode)
    {
      Flag_End_Decode = 0;
      Flag_End_Detected = 0;
      if(Flag_Decode_Add)
      {
          Flag_Decode_Add = 0;

               if(run_time < 60 )
                {
               run_time++;
                }
            
          }
      
      if(Flag_Decode_Dec)
      {
          Flag_Decode_Dec = 0;
             if(run_time > 1)
            {
               run_time--;
            }
            
          }
      
      }
      else
    {   
    if(In_Decode_Pin_L == In_Decode_Pin_R)
      {
      Flag_Decode_End_Process = 1;
      }
   }



}

连着处理哈。。

mcu_M3 发表于 2009-10-17 09:10:38

mark

gdmfq 发表于 2009-10-18 00:54:55

发一个 机械式旋转编码器 检测程序 用51写的
#define WA= PX.X
#define WB =Py.y

void encoder(void) interrupt 3
{
    unsigned char i,j;
    static unsigned char ta,tb;
    TR1 = 0;
    i = WA;
    j = WB;
   
    if((i^j)&&(ta&tb))
    {
      if((i == 1)&&(j ==0))
      {
            count++;
      }
      else if((i == 0)&&(j == 1))
      {
            count--;
      }
    }

       

      ta = WA;
      tb = WB;

    count0++;
    TH1 = (65536 - 2000)/256;
    TL1 = (65536 - 2000)%256;
   
    TR1 = 1;
}

psocfans 发表于 2009-10-19 08:24:34

mark

eric_wang 发表于 2009-10-19 17:44:52

mark

Phil_Zhong 发表于 2010-3-5 10:31:22

楼主:未提供 pgm_read_byte 定义,程序有BUG,请补充 pgm_read_byte 的定义!

dory 发表于 2010-3-5 12:16:28

mark !!!

zyw19987 发表于 2010-10-22 20:26:45

这贴受教了
mark !!!

jimjimzixi 发表于 2010-10-22 21:24:15

好贴

tangwei039 发表于 2010-10-22 21:55:05

受教

xoqu 发表于 2010-10-30 23:54:47

俺是菜鸟,能不能给一个C的增量旋转编码器控制步进的,也就是输出PU和DR,谢谢!

ifus 发表于 2010-10-31 22:28:47

MARK,有意思

uc-can-m3 发表于 2010-10-31 23:52:30

mark

liumaojun_cn 发表于 2010-11-1 08:50:57

mark,用的时候再研究

jetbo 发表于 2010-11-1 10:03:59

mark

lbhj310 发表于 2010-11-1 10:21:59

mark

xpxp 发表于 2010-11-1 11:49:02

自己编的ec11编码器程序快速转动有反弹现象,进来学习一下。

au730 发表于 2010-11-1 23:35:38

mark
赶紧淘几个玩下

rocyang 发表于 2010-11-2 00:51:15

收藏

JnzGoto 发表于 2010-11-15 21:14:01

Mark

yunlong 发表于 2010-11-18 22:22:37

学习

chkmsfc 发表于 2010-11-19 14:00:57

hao

wayhe 发表于 2010-11-19 17:31:15

mark

8irmb 发表于 2010-11-19 18:43:23

马克。

gdmfq 发表于 2010-11-19 20:27:16

current = GET_WHEEL();      //Get the MCU's logic
      if ( current == WHEEL_ANB )
      {
            lock = 0x01;
      }
      if ( lock )               //Get half resolvtion
      {   
            if ( current == WHEEL_B )//org:0x10
            {
                wheel_count_out += 2;
                if ( wheel_count_out >= WHEEL_END )
                {
                  wheel_count_out = WHEEL_END;
                }
                lock = 0x00;
                return;
            }
            
            if ( current == WHEEL_A )//org:0x20
            {
                wheel_count_out -= 2;
                if ( wheel_count_out <= WHEEL_BEGIN )
                {
                  wheel_count_out = WHEEL_BEGIN;
                }
                lock = 0x00;
            }

chaobaocai 发表于 2010-11-28 21:26:05

回复【楼主位】yule
-----------------------------------------------------------------------

菜鸟 问题   你好 你能 告诉一下你使用什么软件下写的程序吗chaobaocai@163.com

yzlyear 发表于 2010-11-28 21:37:15

mark

yl604922959 发表于 2010-11-29 09:41:13

mark..

lcgforward 发表于 2010-11-29 11:04:23

编码器用的很多了,但都是用PLC来实现的。

qinhya 发表于 2010-11-29 14:25:06

mark

steven_sd 发表于 2010-11-29 15:13:47

mark

huashenglaji 发表于 2010-12-16 13:22:20

有空要好好看看,呵呵

wwwdege 发表于 2010-12-16 15:00:25

mark

mcu2007 发表于 2010-12-16 15:07:23

标题让我吓一跳,以为我的编码器用错了。

Yoran 发表于 2010-12-16 16:12:20

记号

tonyone 发表于 2010-12-16 17:12:23

mark

rocyang 发表于 2010-12-29 01:30:50

mark

millwood0 发表于 2010-12-29 01:42:12

can any of you make it more complicated?

myworkmail 发表于 2010-12-29 09:02:18

mark

xiaohu123 发表于 2010-12-29 09:10:59

mark
页: [1] 2
查看完整版本: 发一个我的程序,旋转编码器解码,今天晚上写出来的