将旋转编码器与AVR Micro控制器一起使用
我无法使旋转编码器与AVR微型控制器一起正常工作。该编码器是机械ALPS编码器,我正在使用Atmega168.
澄清度
我尝试使用外部中断来监听引脚,但似乎太慢了。当引脚A变为高电平时,中断过程开始,然后检查引脚B是否为高电平。这个想法是,如果在引脚A变为高电平的那一刻,引脚B为高电平,则它沿逆时针方向旋转。如果引脚B为低电平,则它沿顺时针方向旋转。但是看来AVR检查B针的时间太长了,所以它总是读得很高。
我还尝试创建一个仅在Pin B或者Pin A更改之前一直阻塞的程序。但是旋转编码器时可能会有太多的噪音,因为这也不起作用。我的最后一次尝试是使用一个计时器,将最后8个值存储在缓冲区中,并检查它是否从低变高。这也不起作用。
我尝试对编码器进行范围设定,从第一个Pin更改到另一个Pin更改,似乎要使用2到4ms。
解决方案
回答
我们到底有什么问题?我假设我们已经能够按照我们提供的Farnell页面上链接的技术规范将编码器的引脚连接到PIC,那么读取数据是否有问题?我们没有从编码器获取任何数据吗?我们不知道如何解释我们返回的数据吗?
回答
我有一个关于旋转编码器以及如何使用它们的网页,我们可能会觉得有用。
不幸的是,如果没有更多信息,我将无法解决特定问题。
哪些微控制器引脚连接到编码器,并且当前用于解码脉冲的代码是什么?
好的,我们要处理几个不同的问题,第一个问题是这是一个机械编码器,因此我们必须处理开关噪声(弹跳,颤动)。数据表表明,器件停止跳动并产生虚假输出可能需要3ms的时间。
我们需要创建一个防抖动例程。最简单的方法是连续检查A是否变高。如果是这样,请启动一个计时器,然后在3毫秒内再次检查它。如果它仍然很高,则可以检查B是否不高,然后忽略杂散脉冲并继续寻找A高。选中B时,我们将对其进行查看,启动一个计时器3毫秒,然后再次查看B。如果两次都相同,则可以使用该值(如果它在3毫秒内更改),则必须再次进行操作(读取B,等待3毫秒,然后再次读取并查看它是否匹配)。
atmega足够快,因此我们不必担心这些检查进展缓慢,除非我们也以较低的时钟速度运行。
一旦处理了机械噪声,便要查看适当的格雷码例程,除非遵循的算法将不起作用,除非我们还降低了当B变低时A变高的情况。通常,人们存储两个输入的最后一个值,然后将其与两个输入的新值进行比较,并使用一个小函数基于该值递增或者递减。 (请查看我在上面提到的网站上的"高分辨率阅读"标题表)。我将两个读数合并为一个四位数字,并使用一个简单的数组告诉我是增加还是减少计数器,但是有些解决方案甚至更高级,并且可以优化代码大小,速度或者简化代码维护。
回答
速度应该不是问题。大多数情况下,所有机械开关都需要防抖例程。如果要通过中断来执行此操作,请在触发时关闭该中断,请启动一个计时器,该计时器将在几毫秒后将其重新打开。将使程序免轮询> :)
回答
/* into 0 service rutine */ if(CHB) { if(flagB) Count++; FlagB=0; } else { if(FlagB) count--: FlagB=0: } /* into 1 service rutine */ FlagB=1; /* make this give to you a windows time of 1/4 of T of the encoder resolution that is in angle term: 360/ (4*resolution) */
回答
添加模拟低通滤波器可大大改善信号。使用低通滤波器,AVR上的代码非常简单。
_________ | | | Encoder | |_________| | | | | | | 100n | O | 100n GND O-||-+ GND +-||-O GND | | \ / 3K3 / \ 3K3 \ / | | VCC O-/\/-+ +-\/\-O VCC 15K | | 15K | | O O A B
啊,ASCII艺术的奇迹:p
这是AVR上的程序。将A和B连接到AVR上的输入PORTB:
#include <avr/io.h> #define PIN_A (PINB&1) #define PIN_B ((PINB>>1)&1) int main(void){ uint8_t st0 = 0; uint8_t st1 = 0; uint8_t dir = 0; uint8_t temp = 0; uint8_t counter = 0; DDRD = 0xFF; DDRB = 0; while(1){ if(dir == 0){ if(PIN_A & (!PIN_B)){ dir = 2; }else if(PIN_B & (!PIN_A)){ dir = 4; }else{ dir = 0; } }else if(dir == 2){ if(PIN_A & (!PIN_B)){ dir = 2; }else if((!PIN_A) & (!PIN_B)){ counter--; dir = 0; }else{ dir = 0; } }else if(dir == 4){ if(PIN_B & (!PIN_A)){ dir = 4; }else if((!PIN_A) & (!PIN_B)){ counter++; dir = 0; }else{ dir = 0; } }else if(PIN_B & PIN_A){ dir = 0; } PORTD = ~counter; } return 0; }
该代码有效,除非我们真正快速旋转编码器。然后可能会错过一到两步,但这并不重要,因为使用编码器的人不会知道他们已经转动了多少步。