[技术问答] 增量式编码器学习笔记

[复制链接]
1645|13
 楼主| 西门扫雪 发表于 2015-12-17 20:51 | 显示全部楼层 |阅读模式
假设函数IS_PIN_A_HIGH()和IS_PIN_B_HIGH()是读取A,B两个引脚的状态
假设有两个外中断INT0和INT1都已经配置为双边沿触发模式,则解码如下:
  1. //! 编码计数器
  2. static volatile uint32_t s_wQDCounter = 0;
  3. ISR(INT0_vect)
  4. {
  5.     if (IS_PIN_A_HIGH() && IS_PIN_B_HIGH()) {
  6.         s_wQDCounter++;
  7.     } else {
  8.         s_wQDCounter--;
  9.     }
  10. }
  11. ISR(INT1_vect)
  12. {
  13.     if (IS_PIN_A_HIGH() && IS_PIN_B_HIGH()) {
  14.         s_wQDCounter--;
  15.     } else {
  16.         s_wQDCounter++;
  17.     }
  18. }


 楼主| 西门扫雪 发表于 2015-12-17 20:52 | 显示全部楼层
读取全局变量s_wQDCounter的时候别忘记加入中断保护。如果要追求效率,可以将计数器类型修改为uint16_t。

--------------------------
以上就是中断法,可以用引脚电平变化中断来做。上面的代码是4倍频。如果要2倍频,去掉任何一个中断处理程序即可。
如果要单倍频,选择任意一个外中断,并选择只对某个边沿触发即可。

多年测试,稳定可靠~
记住一句口诀:

在任意边沿触发模式下,A和B进行电平比较:
对A触发的中断:同加异减
对B触发的中断:同减异加
反之亦然

总结到最后,就是 同加异减,同减异加
 楼主| 西门扫雪 发表于 2015-12-17 20:53 | 显示全部楼层
附工程
  1. #include <avr/io.h>
  2. #include <avr/interrupt.h>
  3. unsigned int flag=0;
  4. unsigned char count=0;
  5. unsigned int num=0;
  6. unsigned int GrayData=0;
  7. unsigned int Hdata=0;
  8. unsigned int Ldata=0;
  9. #define set_bit(x,y) ((x)|=(1<<(y)))
  10. #define clr_bit(x,y) ((x)&=~(1<<(y)))
  11. #define PUL  PORTA ^= (1 << PA2)
  12. static volatile unsigned int s_wQDCounter = 0;
  13. // unsigned int s_wQDCounter = 0;
  14. /*unsigned int  GtoB(unsigned int num)
  15. {
  16.         num ^= num >> 16;
  17.     num ^= num >> 8;
  18.         num ^= num >> 4;
  19.         num ^= num >> 2;
  20.         //num ^= num >> 1;
  21.         return num^(num >> 1);
  22. }  */
 楼主| 西门扫雪 发表于 2015-12-17 20:53 | 显示全部楼层
  1. unsigned int GraytoDecimal(unsigned int x)
  2. {
  3.         unsigned int y = x;
  4.         while(x>>=1)
  5.         y ^= x;
  6.         return y;
  7. }

  8. unsigned int DecimaltoGray(unsigned int x)
  9. {
  10.         return x^(x>>1);
  11. }

  12. unsigned int BinToInt(char *bin)
  13. {
  14.         unsigned int Value=0;
  15.         while(*bin)
  16.         {
  17.         Value=Value<<1;
  18.         Value += (*bin)-'0';
  19.         bin++;
  20.         };
  21.         return Value;
  22. }
 楼主| 西门扫雪 发表于 2015-12-17 20:54 | 显示全部楼层
  1. //==================================================
  2. const unsigned char seg[]={        0xC0, // 0
  3.         0xF9, // 1
  4.         0xA4, // 2
  5.         0xB0, // 3
  6.         0x99, // 4
  7.         0x92, // 5
  8.         0x82, // 6
  9.         0xF8, // 7
  10.         0x80, // 8
  11.         0x90 // 9
  12. };


  13. //==================================================

  14. //IO端口初始化
  15. void PortInit(void)
  16. {
  17.         DDRA=0XFF;
  18.         PORTA=0XFF;
  19.         DDRB=0XFF;
  20.         PORTB=0XFF;
  21.         DDRC=0XFF;
  22.         PORTC=0XFF;
  23.     DDRD=0x00;
  24.         PORTD=0XFF;
  25.         //set_bit(PORTA,PA0);
  26.       
  27.       
  28. }

  29. //Timer0初始化
  30. void Timer0Init(void)
  31. {
  32. TCCR0 = 0x00; //stop
  33. TCNT0 = 0x06; //set count
  34. OCR0  = 0xFA;  //set compare
  35. TCCR0 = 0x03; //start timer
  36. }

  37. void int_init(void)//配置外部中断
  38. {
  39.         MCUCR |= 0x05;
  40.         MCUCSR|= 0x00;
  41.         GICR  |= 0xC0;
  42. }
 楼主| 西门扫雪 发表于 2015-12-17 20:56 | 显示全部楼层
  1. //==================================================

  2. //定时器0溢出中断服务程序
  3. ISR(TIMER0_OVF_vect) //4ms刷新显示一次
  4. {
  5.         TCNT0=0X06;
  6.         flag++;
  7.         count++;

  8.         switch(count)
  9.         {
  10.                 case 1:if(num/1000){PORTA=0X01;PORTB=seg[num/1000];}else PORTB=0XFF;break;
  11.                 case 2:if(((num/1000)+(num%1000/100))){PORTA=0X02;PORTB=seg[num%1000/100];}else PORTB=0XFF;break;
  12.                 case 3:if(((num/1000)+(num%1000/100)+(num%100/10))){PORTA=0X04;PORTB=seg[num%100/10];}else PORTB=0XFF;break;
  13.                 case 4:PORTA=0X08;PORTB=seg[num%10];break;
  14.                 case 5:count=0;break;
  15.                 default:break;
  16.         }
  17.         num=s_wQDCounter;
  18.       
  19. }
  20. unsigned char IS_PIN_A_HIGH(void)  //PD2 CHA //编码器A相
  21. {
  22. if ((PIND&(1<<PD2))==0)return 1;
  23. else return 0;

  24.       
  25. }
 楼主| 西门扫雪 发表于 2015-12-17 20:58 | 显示全部楼层
  1. unsigned char IS_PIN_B_HIGH(void) //PD3 CHB 编码器B相
  2. {
  3. if ((PIND&(1<<PD3))==0)return 1;
  4. else return 0;
  5.       
  6. }
  7. ISR(INT0_vect)
  8. {  
  9.         cli();
  10.     if (IS_PIN_A_HIGH() && IS_PIN_B_HIGH()) {
  11.         s_wQDCounter++;
  12.     } else {
  13.         s_wQDCounter--;
  14.     }
  15.         sei();
  16. }

  17. ISR(INT1_vect)
  18. {
  19.         cli();
  20.     if (IS_PIN_A_HIGH() && IS_PIN_B_HIGH()) {
  21.         s_wQDCounter--;
  22.     } else {
  23.         s_wQDCounter++;
  24.     }
  25.         sei();
  26. }
 楼主| 西门扫雪 发表于 2015-12-17 20:58 | 显示全部楼层
  1. //==================================================

  2. //主函数
  3. int main(void)
  4. {
  5.         cli();
  6.         TIMSK = 0x01; //timer interrupt sources
  7.         PortInit();
  8.         Timer0Init();
  9.         int_init();
  10.         sei();
  11.         while (1){;}
  12. }
  13.         
lishunde 发表于 2015-12-18 17:02 | 显示全部楼层
新唐没有象ST一样的硬件编码器接口有点遗憾
落叶行健ywm 发表于 2015-12-21 16:30 | 显示全部楼层
lishunde 发表于 2015-12-18 17:02
新唐没有象ST一样的硬件编码器接口有点遗憾

有的。472有
捉虫天师 发表于 2015-12-21 23:53 | 显示全部楼层
增量式编码器是将位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示位移的大小。
编码器是把角位移或直线位移转换成电信号的一种装置。前者称为码盘,后者称码尺.按照读出方式编码器可以分为接触式和非接触式两种.接触式采用电刷输出,一电刷接触导电区或绝缘区来表示代码的状态是“1”还是“0”;非接触式的接受敏感元件是光敏元件或磁敏元件,采用光敏元件时以透光区和不透光区来表示代码的状态是“1”还是“0”。
捉虫天师 发表于 2015-12-21 23:55 | 显示全部楼层
照工作原理编码器可分为增量式和绝对式两类。
增量式编码器是将位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示位移的大小。
绝对式编码器的每一个位置对应一个确定的数字码,因此它的示值只与测量的起始和终止位置有关,而与测量的中间过程无关。
旋转增量式编码器以转动时输出脉冲,通过计数设备来知道其位置,当编码器不动或停电时,依靠计数设备的内部**来记住位置。这样,当停电后,编码器不能有任何的移动,当来电工作时,编码器输出脉冲过程中,也不能有干扰而丢失脉冲,不然,计数设备**的零点就会偏移,而且这种偏移的量是无从知道的,只有错误的生产结果出现后才能知道。
解决的方法是增加参考点,编码器每经过参考点,将参考位置修正进计数设备的**位置。在参考点以前,是不能保证位置的准确性的。为此,在工控中就有每次操作先找参考点,开机找零等方法。
比如,打印机扫描仪的定位就是用的增量式编码器原理,每次开机,我们都能听到噼哩啪啦的一阵响,它在找参考零点,然后才工作。
643757107 发表于 2015-12-22 12:01 | 显示全部楼层
BEN增量编码器输出脉冲信号,集电极开路,长线驱动,推挽,互补,绝对值信号输出有:SSI、4-20MA、profibus-dp、DEVicenet、并行、二进制码、、BiSS、ISI、CANopen、Endat及Hiperface等
smy096 发表于 2020-5-5 19:10 | 显示全部楼层
学习了,好资料!!!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

33

主题

286

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部