Arduino教程——旋轉編碼器


 

參考資料:https://www.arduino.cn/thread-2423-1-1.html

 

1. 所需的材料

  • 旋轉編碼器(KY-040)
  • Arduino UNO開發板
  • 字符型圖形點陣液晶1602
  • 電位器10k
  • 面包板

2. 連接導線旋轉編碼器是如何工作的?

旋轉編碼器是一種機電換能器,意味着它將機械運動轉換為電子脈沖。它由旋鈕組成,當旋轉時,旋鈕將逐步移動並產生一系列脈沖序列,每個步驟具有預定義的寬度。有許多類型的編碼器,每個編碼器都有自己的工作機制,稍后我們將了解這些類型,但現在讓我們只關注KY040增量編碼器,因為我們將它用於我們的教程。

編碼器的內部機械結構如下所示。它基本上由圓盤(灰色)和放置在該圓盤頂部的導電墊(銅色)組成。這些導電焊盤以相同的距離放置,如下所示。輸出引腳固定在該圓盤的頂部,這樣當旋鈕旋轉時,導電墊與輸出引腳接觸。這里有兩個輸出引腳,輸出A和輸出B,如下圖所示。
在這里插入圖片描述

輸出引腳A和輸出B產生的輸出波形分別以藍色和綠色顯示。當導電焊盤直接位於引腳下方時,它會變高,導致導通時間,當導電焊盤移開時,引腳變低,導致上面所示波形的關閉時間。現在,如果我們計算脈沖數,我們將能夠確定編碼器移動了多少步。

現在可能會出現這樣的問題:當一個脈沖信號足以計算旋轉旋鈕時所采取的步數時,為什么我們需要兩個脈沖信號。這是因為我們需要確定旋鈕旋轉的方向。如果您看一下這兩個脈沖,您會注意到它們都是90°異相。因此,當順時針旋轉旋鈕時,輸出A將首先變高,當旋鈕逆時針旋轉時,輸出B將首先變高。

3. 旋轉編碼器的類型

市場上有很多種類型的旋轉編碼器,設計工程師可以根據自己的應用選擇一種。最常見的類型如下所示

  • 增量編碼器
  • 絕對值編碼器
  • 磁編碼器
  • 光學編碼器
  • 激光編碼器

這些編碼器基於輸出信號和傳感技術進行分類,增量編碼器和絕對值編碼器基於輸出信號進行分類,磁、光和激光編碼器基於傳感技術進行分類。這里使用的編碼器是增量型編碼器。

4. KY-040旋轉編碼器引腳和說明

KY-040增量式旋轉編碼器的引腳分布如下所示

 


前兩個引腳(接地和Vcc)用於為編碼器供電,通常使用+ 5V電源。除了以順時針方向和逆時針方向旋轉旋鈕外,編碼器還有一個開關(低電平有效),按下內部的旋鈕可以按下該開關。來自此開關的信號通過引腳3(Switch)獲得。最后它有兩個輸出引腳,產生如上所述的波形。

正傳波形

 

反轉波形

 

 

 

5. Arduino與旋轉編碼器的連接電路圖

旋轉編碼器與Arduino連接的完整電路圖如下圖所示

 


旋轉編碼器有5個引腳,順序如上圖的標簽所示。前兩個引腳是接地和Vcc,它連接到Arduino的地和+ 5V引腳。編碼器的開關連接到數字引腳D8,並通過1k電阻拉高。兩個輸出引腳分別連接到D9和D8。

要顯示通過旋轉Rotary編碼器增加或減少的變量值,我們需要一個顯示模塊。這里使用的是常用的字符型圖形點陣液晶1602。我們已將連接的顯示屏設置成4位工作模式,並使用Arduino的+ 5V引腳為其供電。電位計用於調整LCD顯示屏的對比度。完整的電路可以在面包板上進行搭建,一旦完成所有的連接后,效果看起來類似下圖。

 


說明,采用單片機內部的上拉輸入可能帶來較大功耗,因為單片機內部電阻較大,arduino實現的基本原理為已知每圈脈沖數為2500,則當計數大道2500時,計數一圈,若為stm32則進行溢出中斷。通過A、B相的高低電平關系,判斷AB相的先后關系,進而判斷轉向。
為了區分正反轉及檢測零點,通常包括三個部分:A相,B相和Z相,A相與B相相差1/4周期(相位差90度),可以用來區分正轉還是反轉;Z相為單圈脈沖,碼盤轉一圈產生一次,可以用作編碼器的參考零位,如下圖:

 

6. 編寫用於旋轉編碼器的Arduino程序

如果您了解旋轉編碼器的工作原理,那么編程Arduino開發板以便將旋轉編碼器連接到它是相當容易的。我們只需要讀取脈沖數來確定編碼器的轉動數,以及首先檢查哪個脈沖高,以找到編碼器旋轉的方向。在本篇文章中,我們將在LCD第一行上顯示增加或減少的數字以及在第二行上顯示編碼器的方向。

由於我們使用了一個LCD顯示屏,因此包含了Arduino IDE中默認存在的液晶庫。然后我們定義用於連接LCD和Arduino的引腳。最后,我們初始化這些引腳上的LCD顯示屏。

#include <LiquidCrystal.h> //Default Arduino LCD Library is included
const int rs = 7, en = 6, d4 = 5, d5 = 4, d6 = 3, d7 = 2; //Mention the pin number for LCD connection
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
lcd.begin(16, 2); //Initialise 16*2 LCD

接下來在setup函數中,我們在LCD屏幕上顯示介紹消息,然后等待2秒鍾,以便用戶可以讀完該消息。這是為了確保LCD能夠正常工作。

#include <LiquidCrystal.h> //Default Arduino LCD Library is included
const int rs = 7, en = 6, d4 = 5, d5 = 4, d6 = 3, d7 = 2; //Mention the pin number for LCD connection
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
lcd.begin(16, 2); //Initialise 16*2 LCD

 

Rotary編碼器有三個輸出引腳,對於Arduino來說,它們都是INPUT引腳。這三個引腳分別是開關(Switch)、輸出A(Output A)和輸出B(Output B)。這些引腳使用pinMode函數聲明為Input,如下所示。

//pin Mode declaration
  pinMode (Encoder_OuputA, INPUT);
  pinMode (Encoder_OuputB, INPUT);
  pinMode (Encoder_Switch, INPUT);

  

在void setup()函數中,我們讀取輸出A引腳的狀態以檢查引腳的最后狀態。然后,我們將使用此信息與新值進行比較,以檢查哪個引腳(輸出A或輸出B)變高。

Previous_Output = digitalRead(Encoder_OuputA); //Read the inital value of Output A

最后在loop函數內,我們必須將輸出A和輸出B的值與先前輸出進行比較,以檢查哪一個先變高。這可以通過簡單地將A和B的當前輸出值與先前輸出進行比較來完成,如下所示。

if (digitalRead(Encoder_OuputA) != Previous_Output)
   {
     if (digitalRead(Encoder_OuputB) != Previous_Output)
     {
       Encoder_Count ++;
       lcd.clear(); 
       lcd.print(Encoder_Count);
       lcd.setCursor(0, 1); 
       lcd.print("Clockwise");
     }

 

在上面的代碼中,如果輸出B已從先前的輸出改變,則執行第二個if條件。在這種情況下,編碼器變量的值遞增,LCD顯示編碼器以順時針方向旋轉。類似地,如果if條件不符合,則在隨后的其他條件中,我們遞減變量並顯示編碼器沿逆時針方向旋轉。代碼如下所示。

  else
     {
       Encoder_Count--;
       lcd.clear(); 
       lcd.print(Encoder_Count);
       lcd.setCursor(0, 1); 
       lcd.print("Anti - Clockwise");
     }
   }

  

最后,在loop函數結束時,我們必須使用當前輸出值更新先前的輸出值,以便可以使用相同的邏輯重復循環。

Previous_Output = digitalRead(Encoder_OuputA);

  

另一個可選方法是檢查編碼器上的開關是否被按下。這可以通過檢查旋轉編碼器上的開關銷來監控。該引腳是低電平有效引腳,意味着按下該按鈕時它將變為低電平。如果沒有按下引腳保持高電平,我們也使用了一個上拉電阻,以確保在未按下開關時保持高電平,從而避免浮空狀態。

if (digitalRead(Encoder_Switch) == 0)
   {
     lcd.clear(); 
     lcd.setCursor(0, 1); 
     lcd.print("Switch pressed");
   }

  

 

Arduino控制旋轉編碼器的工作過程

一旦硬件和代碼准備就緒,只需將代碼上傳到Arduino開發板板,然后向Arduino供電。您可以通過USB電纜為其供電,也可以使用12V適配器。上電時,LCD應顯示介紹消息,然后變為空白。現在旋轉旋轉編碼器,您應該看到值根據旋轉方向遞增或遞減。第二行將顯示編碼器是以順時針方向還是逆時針方向旋轉。下圖顯示了實際的工作過程:

 

此外,當按下按鈕時,第二行將顯示按下按鈕。這只是一個旋轉編碼器與Arduino連接的示例程序,檢查它是否按預期工作。現在,您應該可以將編碼器用於任何項目並進行相應的編程。

7.3 完整代碼

/*
   Interfacing Rotary Encoder with Arduino

   Power LCD and Rotary encoder from the +5V pin of Arduino
   LCD RS -> pin 7
   LCD EN -> pin 6
   LCD D4 -> pin 5
   LCD D5 -> pin 4
   LCD D6 -> pin 3
   LCD D7 -> pin 2
   Encoder Switch -> pin 10
   Encoder Output A -> pin 9
   Encoder Output B -> pin 8
*/

int Encoder_OuputA  = 9;
int Encoder_OuputB  = 8;
int Encoder_Switch = 10;

int Previous_Output;
int Encoder_Count;



#include <LiquidCrystal.h>  //Default Arduino LCD Librarey is included 

const int rs = 7, en = 6, d4 = 5, d5 = 4, d6 = 3, d7 = 2; //Mention the pin number for LCD connection
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() {
  lcd.begin(16, 2); //Initialise 16*2 LCD

  lcd.print(" Rotary Encoder "); //Intro Message line 1
  lcd.setCursor(0, 1);
  lcd.print("  With Arduino  "); //Intro Message line 2

  delay(2000);
  lcd.clear();

  //pin Mode declaration
  pinMode (Encoder_OuputA, INPUT);
  pinMode (Encoder_OuputB, INPUT);
  pinMode (Encoder_Switch, INPUT);

  Previous_Output = digitalRead(Encoder_OuputA); //Read the inital value of Output A
}

void loop() {
  //aVal = digitalRead(pinA);

  if (digitalRead(Encoder_OuputA) != Previous_Output)
  {
    if (digitalRead(Encoder_OuputB) != Previous_Output)
    {
      Encoder_Count ++;
      lcd.clear();
      lcd.print(Encoder_Count);
      lcd.setCursor(0, 1);
      lcd.print("Clockwise");
    }
    else
    {
      Encoder_Count--;
      lcd.clear();
      lcd.print(Encoder_Count);
      lcd.setCursor(0, 1);
      lcd.print("Anti - Clockwise");
    }
  }

  Previous_Output = digitalRead(Encoder_OuputA);

  if (digitalRead(Encoder_Switch) == 0)
  {
    lcd.clear();
    lcd.setCursor(0, 1);
    lcd.print("Switch pressed");
  }
}

  

8. 補充其他的網友的代碼

#define PinA 2 //外部中斷0
#define PinZ 3 //外部中斷1
#define PinB 8 //編碼器的OUT_B信號連接到數字端口8

//變量初始化
unsigned long time1 = 0; // 時間標記
float time_cw;
float time_ccw;
long count = 0;
const float d = 75.7 / 1000; //輪子的直徑
const float pi = 3.141592654;//圓周率
int num = 0;//圈數
double t;//一圈的運動時間
float velocity;
double time3;//外部中斷1產生時的時間,即捕捉到Z相的置零信號時,用於在loop循環內進行一圈時間長短的計算

void setup()
{
  pinMode(PinA, INPUT_PULLUP);//因為編碼器信號為歐姆龍E6B2-CWZ6C,為開漏輸出,因此需要上拉電阻,此處采用arduino的內部上拉輸入模式,置高
  pinMode(PinB, INPUT_PULLUP);//同上
  pinMode(PinZ, INPUT_PULLUP);//同上
  attachInterrupt(0, Encode, FALLING);//脈沖中斷函數:捕捉A相信號,並判斷A、B相先后順序
  attachInterrupt(1, Set_state , FALLING);//用於在捕捉到Z的零信號時,進行狀態置零

  Serial.begin (9600);
}

void loop()
{
  double distance;
  //正轉
  if (count == 2500)
  {
    //      Serial.println("ok");//調試用
    num = num + 1;
    time_cw = millis();
    t = time_cw - time3;
    t = t / 1000;
    distance = num * d * pi;
    velocity = d * pi / t;
    Serial.print("The wheel has run ");
    Serial.print(distance);
    Serial.println("m.");
    Serial.print("The cw_speed is ");
    Serial.print(velocity);
    Serial.println("m/s.");
    Serial.print("The time is ");
    Serial.print(t);
    Serial.println("s.");
  }
  //反轉
  if (count == -2500)
  {
    //      Serial.println("ok");//調試用
    num = num + 1;
    time_ccw = millis();
    t = time_ccw - time3;
    t = t / 1000;
    distance = num * d * pi;
    velocity = d * pi / t;
    Serial.print("The wheel has run ");
    Serial.print(distance);
    Serial.println("m.");
    Serial.print("The ccw_speed is ");
    Serial.print(velocity);
    Serial.println("m/s.");
    Serial.print("The time is ");
    Serial.print(t);
    Serial.println("s.");
  }
}

// 編碼器計數中斷子程序

void Encode()
{
  //為了不計入噪音干擾脈沖,
  //當2次中斷之間的時間大於5ms時,計一次有效計數
  if ((millis() - time1) > 5)
  {
    //當編碼器碼盤的OUTA脈沖信號下跳沿每中斷一次,
    if ((digitalRead(PinA) == LOW) && (digitalRead(PinB) == HIGH))
    {
      count--;
    }
    else
    {
      count++;
    }
  }
  time1 == millis();
}
void Set_state() {
  count = 0;
  time3 = millis();//發生中斷時的時間
}

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM