Cách thức hoạt động của Rotary Encoder với Arduino
Bạn biết gì về Rotary Encoder, đây là một module sử dụng khá phổ biến trong các ứng dụng như cánh tay Robot 4 bậc, máy in 3D và gần nhất sản phẩm được mình chế tạo là máy rót rượu tự động. Thông qua bài hướng dẫn này, mình sẽ giải thích rõ hơn về cách thức hoạt động cũng như là cấu tạo bên trong của Rotary Encoder Arduino.
Linh kiện cần thiết cho dự án
TÊN LINH KIỆN | SỐ LƯỢNG | NƠI BÁN |
Arduino Uno R3 | 1 | Shopee | Cytron |
Module Volume xoay Rotary Encoder | 1 | Shopee | Cytron |
Breadboard | 1 | Shopee | Cytron |
Servo SG90 | 1 | Shopee | Cytron |
Dây cắm (Đực – Đực) | 10 – 20 | Shopee | Cytron |
Nguyên lý hoạt động của Rotary Encoder
Rotary Encoder là một thiết bị cơ học được sử dụng để ghi nhận và theo dõi sự thay đổi vị trí của một trục xoay. Nó hoạt động dựa trên nguyên lý của hai tín hiệu đầu vào: một tín hiệu A và một tín hiệu B.
Khi trục xoay của Rotary Encoder Arduino được quay, các đĩa có đặc tính quang học hoặc điện tử bên trong Encoder sẽ tạo ra các xung tín hiệu A và B. Các xung tín hiệu A và B có pha chênh lệch 90 độ với nhau. Khi quay thuận (theo chiều kim đồng hồ), tín hiệu A sẽ chạy trước tín hiệu B. Khi quay ngược (ngược chiều kim đồng hồ), tín hiệu B sẽ chạy trước tín hiệu A.
Nguyên lý hoạt động của Rotary Encoder dựa trên việc theo dõi sự thay đổi pha của các tín hiệu A và B để xác định hướng và tốc độ quay của trục xoay. Bằng cách theo dõi sự thay đổi pha và thứ tự của các xung tín hiệu, Encoder có thể xác định cả hướng và số lượng bước quay của trục xoay. Sự thay đổi vị trí được đọc thông qua các xung tín hiệu và được chuyển đổi thành các giá trị số để sử dụng trong việc kiểm soát hoặc hiển thị.
Các bạn có thể quan sát trạng thái của B khi trạng thái của A thay đổi:
- Nếu B! = A, thì núm xoay theo chiều kim đồng hồ.
- Nếu B = A thì xoay núm ngược chiều kim đồng hồ.
Sơ đồ chân Rotary Encoder
- GND: Chân nối đất
- VCC: Nguồn cấp (+) cho Rotary Encoder (Điện áp khuyến cáo từ 3.3 -5V)
- SW: Là chân nút nhấn (hoạt động ở mức thấp).
- DT: Chân dữ liệu, được sử dụng để đọc giá trị của vòng quay (Pha A)
- CLK: Chân xung clock, được sử dụng để đọc giá trị của vòng quay (Pha B)
Sơ đồ đấu nối Rotary Encoder với Arduino
Arduino Uno R3 | Rotary Encoder |
5V | + |
GND | GND |
2 | CLK |
3 | DT |
4 | SW |
Code 1: Cách đọc Rotary Encoder
// Rotary Encoder Inputs #define CLK 2 #define DT 3 #define SW 4 int counter = 0; int currentStateCLK; int lastStateCLK; String currentDir =""; unsigned long lastButtonPress = 0; void setup() { // Set encoder pins as inputs pinMode(CLK,INPUT); pinMode(DT,INPUT); pinMode(SW, INPUT_PULLUP); // Setup Serial Monitor Serial.begin(9600); // Read the initial state of CLK lastStateCLK = digitalRead(CLK); } void loop() { // Read the current state of CLK currentStateCLK = digitalRead(CLK); // If last and current state of CLK are different, then pulse occurred // React to only 1 state change to avoid double count if (currentStateCLK != lastStateCLK && currentStateCLK == 1){ // If the DT state is different than the CLK state then // the encoder is rotating CCW so decrement if (digitalRead(DT) != currentStateCLK) { counter --; currentDir ="CCW"; } else { // Encoder is rotating CW so increment counter ++; currentDir ="CW"; } Serial.print("Direction: "); Serial.print(currentDir); Serial.print(" | Counter: "); Serial.println(counter); } // Remember last CLK state lastStateCLK = currentStateCLK; // Read the button state int btnState = digitalRead(SW); //If we detect LOW signal, button is pressed if (btnState == LOW) { //if 50ms have passed since last LOW pulse, it means that the //button has been pressed, released and pressed again if (millis() - lastButtonPress > 50) { Serial.println("Button pressed!"); } // Remember last button press event lastButtonPress = millis(); } // Put in a slight delay to help debounce the reading delay(1); }
Dưới đây là kết quả, được xuất ra Serial Monitor.
Giải thích code
#define CLK 2 #define DT 3 #define SW 4
Khai báo ba chân CLK, DT, SW kết nối với Arduino.
int counter = 0; int currentStateCLK; int lastStateCLK; String currentDir =""; unsigned long lastButtonPress = 0;
- Biến
counter
được khởi tạo với giá trị ban đầu là 0. Biến này được sử dụng để theo dõi số lần quay của Rotary Encoder.
- Biến
currentStateCLK
được sử dụng để lưu trữ trạng thái hiện tại của chân CLK.
- Biến
lastStateCLK
được sử dụng để lưu trữ trạng thái trước đó của chân CLK.
- Biến
currentDir
là một đối tượng kiểu String được khởi tạo với giá trị rỗng. Biến này sẽ chứa thông tin về hướng quay của Rotary Encoder (CW – Clockwise hoặc CCW – Counter Clockwise).
- Biến
lastButtonPress
là một số nguyên không dấu (unsigned long) và được khởi tạo với giá trị 0. Biến này được sử dụng để lưu thời điểm lần nhấn nút cuối cùng trên Rotary Encoder Arduino.
pinMode(CLK,INPUT); pinMode(DT,INPUT); pinMode(SW, INPUT_PULLUP); Serial.begin(9600); lastStateCLK = digitalRead(CLK);
Đặt chân các chân CLK, DT, SW của Rotary Encoder là chế độ đầu vào (INPUT), Pull-up resistor được kích hoạt (INPUT_PULLUP) trên chân SW.
Khởi động giao tiếp Serial với tốc độ baud rate là 9600.
lastStateCLK = digitalRead(CLK);
Đọc trạng thái hiện tại của chân CLK và lưu trữ vào biến lastStateCLK. Điều này giúp ghi nhớ trạng thái ban đầu của chân CLK để so sánh với trạng thái hiện tại trong quá trình xử lý dữ liệu từ Rotary Encoder.
currentStateCLK = digitalRead(CLK); if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
currentStateCLK = digitalRead(CLK);
Đọc trạng thái hiện tại của chân CLK và lưu trữ vào biến currentStateCLK. Điều này cho phép biết trạng thái hiện tại của chân CLK (HIGH hoặc LOW).
if (currentStateCLK != lastStateCLK && currentStateCLK == 1)
Kiểm tra xem trạng thái hiện tại của chân CLK có khác với trạng thái trước đó (lastStateCLK) và trạng thái hiện tại của chân CLK là HIGH (1) hay không. Điều kiện này chỉ đúng khi có sự thay đổi trạng thái từ LOW sang HIGH trên chân CLK, cho biết rằng đã xảy ra một xung từ Rotary Encoder.
if (digitalRead(DT) != currentStateCLK) { counter --; currentDir ="CCW"; } else { counter ++; currentDir ="CW"; }
digitalRead(DT) != currentStateCLK
Kiểm tra trạng thái của chân DT (data) có khác với trạng thái hiện tại của chân CLK (clock) hay không. Điều này giúp xác định hướng quay của Rotary Encoder.
- Nếu trạng thái của chân DT khác với trạng thái hiện tại của chân CLK, có nghĩa là Rotary Encoder đang quay ngược chiều kim đồng hồ (Counter-Clockwise – CCW). Trong trường hợp này, giảm giá trị của biến counter đi 1 và gán giá trị “CCW” cho biến currentDir.
- Ngược lại, nếu trạng thái của chân DT giống với trạng thái hiện tại của chân CLK, có nghĩa là Rotary Encoder Arduino đang quay theo chiều kim đồng hồ (Clockwise – CW). Trong trường hợp này, tăng giá trị của biến counter lên 1 và gán giá trị “CW” cho biến currentDir.
Serial.print("Direction: "); Serial.print(currentDir); Serial.print(" | Counter: "); Serial.println(counter);
Hiển thị thông tin về hướng quay và giá trị đếm của Rotary Encoder trên Serial Monitor.
lastStateCLK = currentStateCLK;
lastStateCLK
được gán giá trị của currentStateCLK
để lưu trữ trạng thái trước đó của tín hiệu đầu vào của chân CLK.
int btnState = digitalRead(SW); if (btnState == LOW) { if (millis() - lastButtonPress > 50) { Serial.println("Button pressed!"); } lastButtonPress = millis(); }
Đoạn code trên, dùng để đọc trạng thái của nút bấm (SW) bằng cách sử dụng hàm digitalRead(SW) và lưu giá trị đó vào biến btnState.
Sau đó, kiểm tra xem giá trị của btnState có bằng LOW hay không. Nếu giá trị này bằng LOW, điều đó có nghĩa là nút bấm đã được nhấn.
Trong điều kiện này, kiểm tra thời gian từ lần nhấn trước đó của nút bấm (lastButtonPress) đến thời điểm hiện tại (millis()) để kiểm tra xem đã trôi qua ít nhất 50ms kể từ lần nhấn trước đó hay chưa. Nếu điều kiện này được đáp ứng, in ra thông báo “Button pressed!” sử dụng hàm Serial.println().
Cuối cùng, cập nhật thời điểm lần nhấn cuối cùng của nút bấm bằng cách gán giá trị của millis() cho biến lastButtonPress. Điều này giúp tính toán thời gian từ lần nhấn trước đó cho lần nhấn tiếp theo.
Code 2: Điều khiển động cơ Servo với Rotary Encoder
Sơ đồ đấu nối
Arduino Uno R3 | Rotary Encoder | Động cơ Servo SG90 |
5V | + | |
GND | GND | Nâu |
2 | CLK | |
3 | DT | |
4 | SW | |
9 | Vàng |
Code
// Include the Servo Library #include <Servo.h> // Rotary Encoder Inputs #define CLK 2 #define DT 3 Servo servo; int counter = 0; int currentStateCLK; int lastStateCLK; void setup() { // Set encoder pins as inputs pinMode(CLK,INPUT); pinMode(DT,INPUT); // Setup Serial Monitor Serial.begin(9600); // Attach servo on pin 9 to the servo object servo.attach(9); servo.write(counter); // Read the initial state of CLK lastStateCLK = digitalRead(CLK); } void loop() { // Read the current state of CLK currentStateCLK = digitalRead(CLK); // If last and current state of CLK are different, then pulse occurred // React to only 1 state change to avoid double count if (currentStateCLK != lastStateCLK && currentStateCLK == 1){ // If the DT state is different than the CLK state then // the encoder is rotating CCW so decrement if (digitalRead(DT) != currentStateCLK) { counter --; if (counter<0) counter=0; } else { // Encoder is rotating CW so increment counter ++; if (counter>179) counter=179; } // Move the servo servo.write(counter); Serial.print("Position: "); Serial.println(counter); } // Remember last CLK state lastStateCLK = currentStateCLK; }