我爱编程

[Arduino] 旋转编码器

2017-07-10  本文已影响889人  Cocoonshu

有用过酷炫的旋钮吗,怎么拧都拧不到底的那种?


旋钮

旋钮的实现方式有两种,一种是两头可以拧到底的电位器(也称滑动变阻器),另一种是可以一直拧的旋转编码器。电位器利用金属触点在电阻轨迹上滑动的距离来产生不同的电阻,从而使电路读取模拟电压或者电流值来改变某个功能,最常见的就是音响调节音量大小的旋钮。旋转编码器则是在旋转的过程中使处于旋钮盘上呈一定夹角的两个采样点或者多个采样点采集不同的状态,从而形成不同旋转方向和角度的电平编码数据,供电路或单片机做处理。所以电位器属于模拟电子的元器件(当然数字电路也可以通过ADC(模拟转数字)处理后使用),旋转编码器属于数字电路元器件(模拟电路要使用也不是不可以,原理OK就行)。

旋转编码器有各种各样的,按工作原理分有光电式、磁电式、触点式,按输出编码分有相对式(增量式)和绝对式。总之好多好多好多,本文不是用来介绍旋转编码器的,所以后面我要拉回主题了,有关旋转编码器的具体介绍,请参见百度百科。

电路连接

我们使用最便宜的触点式增量旋转编码器来实现本文所述的功能。它的电路原理如下:


旋转编码器电路原理图

旋转旋钮时,是安装下图所示原理来采样出A、B脚的电平的:


旋转编码器原理

在中间旋转的圆盘叫编码盘,它随着旋钮一起转动。可以把编码盘看做是导电金属盘,A、B引脚都分别接入单片机的两个GPIO脚,且同时拉高这两个引脚,C引脚就直接接地。当编码盘旋转时,A引脚在编码盘上的触点如果接触到编码盘,则与C引脚在编码盘上的触点接通,因为C引脚是接地的,那么就相当于此时把A引脚接地了,因此连接在A引脚上的GPIO脚则被拉低。B引脚同理。

触点式增量旋转编码器因为它那简单暴力的实现方式,会很容易出现电平抖动干扰,这种东西在[Ardunio] Button & Switch中也有提过。所以我们要对旋转编码器的电路添加一下抗干扰的外围,然后再在Ardunio或Android Things上使用它。电路图如下:

抗抖动外围

电路连接完成后,就是编写代码了

编写代码

这里的实现思路都是根据连接于旋转编码器A、B脚上的GPIO引脚的电平变化来判断旋钮旋转的方向及分度值的。在旋转编码器原理中,每一个脉冲周期计为两个分度值,每逆时针旋转一个分度值,则在单片机中对其状态值减少一个单位,每顺时针旋转一个分度值,则在单片机中对其状态值增加一个单位。
假设我们的旋钮是用来设置温度的,则示例代码可以这样写。

#define PINA 2
#define PINB 3

volatile int     mRotaryEncoderPulse        = 0;
volatile uint8_t mLastestRotaryEncoderPinAB = 0; // last last pin value of A and B
volatile uint8_t mLastRotaryEncoderPinAB    = 0; // last pin value of A and B

void onRotaryEncoderChanged(int value) {
    Serial.print("RotaryEncoder: ");Serial.println(value);
}

void onPinABInterrupted() {
    uint8_t currentPin = digitalRead(PINA) * 10 + digitalRead(PINB);
    if (currentPin == mLastRotaryEncoderPinAB) {
      return;
    } else {
        // (A<=>B) 表示旋转编码器的A脚和B脚的电平值
        // ---------------------------------------------------------------------------------------------------
        // (1<=>1) 是旋转编码的选择一个刻度的起始点
        // (1<=>0) 是旋转编码顺时针旋转的起始标识, (0<=>1) 是旋转编码逆时针旋转的起始标识
        // (0<=>0) 是旋转编码旋转一个刻度的中位点
        // (0<=>1) 是旋转编码顺时针旋转的终止标识, (0<=>1) 是旋转编码逆时针旋转的终止标识
        // (1<=>1) 是旋转编码的选择一个刻度的终止点,也是下一个刻度的起始点
        if (mLastRotaryEncoderPinAB == 00) {
          if (mLastestRotaryEncoderPinAB == 10 && currentPin == 01) mRotaryEncoderPulse++;
          else if (mLastestRotaryEncoderPinAB == 01 && currentPin == 10) mRotaryEncoderPulse--;
        }

        mLastestRotaryEncoderPinAB = mLastRotaryEncoderPinAB;
        mLastRotaryEncoderPinAB = currentPin;
    }

    if (lastPulse != mRotaryEncoderPulse) {
        onRotaryEncoderChanged(mRotaryEncoderPulse);
    }
}

void setup() {
    // put your setup code here, to run once:
    Serial.begin(115200);
    Serial.println("RotaryEncoder setup");

    // setup rotary encoder interruption
    setupRotaryEncoder();
}

void loop() {
    // put your main code here, to run repeatedly:
}
浅浅画图猝死惹,所以我自己来画~
上一篇 下一篇

猜你喜欢

热点阅读