使用带有 AVR 微控制器的旋转编码器

发布于 2024-07-04 03:11:07 字数 653 浏览 9 评论 0原文

我无法让旋转编码器与 AVR 微控制器正常工作。 编码器是机械ALPS编码器,并且我'我使用 Atmega168

澄清

我曾尝试使用外部中断来监听引脚,但似乎速度太慢。 当引脚 A 变高时,中断程序启动,然后检查引脚 B 是否为高电平。 这个想法是,如果引脚 B 在引脚 A 变高时也处于高电平,那么它就会逆时针旋转。 如果引脚 B 为低电平,则它顺时针旋转。 但似乎 AVR 检查引脚 B 的时间太长,所以它总是读为高。

我还尝试创建一个程序,该程序会一直阻塞,直到引脚 B 或引脚 A 发生变化。 但可能是编码器旋转时噪音太大,因为这也不起作用。 我的最后一次尝试是使用一个计时器,将最后 8 个值存储在缓冲区中,并检查它是否从低到高。 这也不起作用。

我尝试过确定编码器的范围,从第一个 Pin 更改到其他 Pin 更改似乎需要 2 到 4 毫秒。

I'm having trouble getting a rotary encoder to work properly with AVR micro controllers. The encoder is a mechanical ALPS encoder, and I'm using Atmega168.

Clarification

I have tried using an External Interrupt to listen to the pins, but it seems like it is too slow. When Pin A goes high, the interrupt procedure starts and then checks if Pin B is high. The idea is that if Pin B is high the moment Pin A went high, then it is rotating counter clock-wise. If Pin B is low, then it is rotating clock-wise. But it seems like the AVR takes too long to check Pin B, so it is always read as high.

I've also tried to create a program that simply blocks until Pin B or Pin A changes. But it might be that there is too much noise when the encoder is rotated, because this does not work either. My last attempt was to have a timer which stores the last 8 values in a buffer and checks if it is going from low to high. This did not work either.

I have tried scoping the encoder, and it seems to use between 2 and 4ms from the first Pin changes till the other Pin changes.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

往日 2024-07-11 03:11:07

我有一个关于旋转编码器以及如何使用它们的网页,其中你可能会发现有用。

不幸的是,如果没有更多信息,我无法解决您的特定问题。

哪些微控制器引脚连接到编码器,您当前用于解码脉冲的代码是什么?

好吧,您正在处理一些不同的问题,第一个问题是这是一个机械编码器,因此您必须处理开关噪音(弹跳、颤振)。 数据表表明部件可能需要长达 3 毫秒的时间才能停止弹跳,并且创建错误的输出。

您需要创建一个去抖例程。 其中最简单的是不断检查 A 是否变高。 如果是,则启动计时器并在 3 毫秒后再次检查。 如果它仍然很高,那么您可以检查 B - 如果它不高,那么您忽略杂散脉冲并继续寻找 A 高点。 当您检查 B 时,您会查看它,启动 3 毫秒的计时器,然后再次查看 B。 如果两次都相同,那么您可以使用该值 - 如果它在 3 毫秒内发生变化,那么您必须再次执行此操作(读取 B,等待 3 毫秒,然后再次读取它并查看是否匹配)。

atmega 的速度足够快,您不必担心这些检查速度缓慢,除非您的时钟速度也很慢。

一旦处理了机械噪音,您就需要查看正确的格雷码例程 - 您所遵循的算法将无法工作,除非您在 B 变低时 A 为高时也递减。 通常,人们存储两个输入的最后一个值,然后将其与两个输入的新值进行比较,并使用一个小函数基于该值进行递增或递减。 (查看我上面提到的表格网站上的“高分辨率阅读”标题)。 我将两个读数组合成一个四位数字,并使用一个简单的数组来告诉我是递增还是递减计数器,但有一些更先进的解决方案,并针对代码大小、速度或代码维护的简易性进行了优化。

I have a webpage about rotary encoders and how to use them, which you might find useful.

Unfortunately without more information I can't troubleshoot your particular problem.

Which microcontroller pins are connected to the encoder, and what is the code you're currently using to decode the pulses?

Ok, you're dealing with a few different issues, the first issue is that this is a mechanical encoder, so you have to deal with switch noise (bounce, chatter). The data sheet indicates that it may take up to 3mS for the parts to stop bouncing and creating false outputs.

You need to create a debounce routine. The simplest of which is to continuously check to see if A goes high. If it does, start a timer and check it again in 3 ms. If it's still high, then you can check B - if it's not high then you ignore the spurious pulse and continue looking for A high. When you check B, you look at it, start a timer for 3 ms, and then look at B again. If it was the same both times, then you can use that value - if it changes within 3 ms then you have to do it again (read B, wait 3 ms, then read it again and see if it matches).

The atmega is fast enough that you shouldn't have to worry about these checks going slowly, unless you're also running a slow clock speed.

Once you deal with the mechanical noise, then you want to look at a proper gray code routine - the algorithm you're following won't work unless you also decrement if A is high when B goes low. Generally people store the last value of the two inputs, and then compare it to the new value of the two inputs and use a small function to increment or decrement based on that. (Check out the heading "high resolution reading" on the website I mentioned above for the table). I combine the two readings into a four bit number and use a simple array to tell me whether I increment or decrement the counter, but there are solutions that are even more advanced, and optimize for code size, speed, or ease of code maintenance.

浪推晚风 2024-07-11 03:11:07
/* 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)
 */
/* 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)
 */
听,心雨的声音 2024-07-11 03:11:07

速度应该不是问题。 大多数机械开关都需要防抖例程。 如果你想用中断来做到这一点,请在中断触发时关闭它,启动一个计时器,在几毫秒后将其重新打开。 将使您的程序保持无轮询>:)

Speed should not be a problem. Mostly all mechanical switches need debounce routines. If you wanna do this with interrupts turn off the interrupt when it triggers, start a timer that will turn it back on after a couple of ms. Will keep your program polling-free >:)

临风闻羌笛 2024-07-11 03:11:07

您究竟遇到了什么问题? 我假设您已经能够按照您提供的 Farnell 页面上链接的技术规格将编码器的引脚连接到您的 PIC,那么读取数据有问题吗? 您没有从编码器获取任何数据吗? 您不知道如何解释您返回的数据吗?

What exactly are you having problems with? I assume you've been able to hook the pins of the encoder to your PIC as per the technical specifications linked on the Farnell page you gave, so is the problem with reading the data? Do you not get any data from the encoder? Do you not know how to interpret the data you're getting back?

方觉久 2024-07-11 03:11:07

添加模拟低通滤波器可以极大地改善信号。 有了低通滤波器,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;
}

除非您非常快地旋转编码器,否则此代码将有效。 然后它可能会错过一两步,但这并不重要,因为使用编码器的人不会知道他们已经转动了多少步。

Adding an analog lowpass filter greatly improves the signal. With the lowpass filter, the code on the AVR was really simple.

       _________
        |         |
        | Encoder |
        |_________|
          |  |  |
          |  |  |
     100n |  O  | 100n  
 GND O-||-+ GND +-||-O GND
          |     | 
          \     /
      3K3 /     \ 3K3
          \     /
          |     |    
VCC O-/\/-+     +-\/\-O VCC
     15K  |     |  15K
          |     |
          O     O
          A     B

Ah, the wonders of ASCII art :p

Here is the program on the AVR. Connect A and B to input PORTB on the avr:

#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;
}

This code works unless you rotate the encoder really fast. Then it might miss a step or two, but that is not important, as the person using the encoder won't know how many steps they have turned it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文