# Máy, thiết bị và sản phẩm khác > Điện - Điện tử - Tự động hóa > Arduino >  Arduino - Quadrature Encoder reader

## CKD

*Arduino - Quadrature Encoder reader -------- Đọc encoder bằng Arduino.*
Lâu quá không dụng tới mấy cái board Arduino, sợ mấy cái board buồn nên hôm nay phải lôi ra làm cái thí nghiệm nhỏ nhỏ  :Smile: 
Cũng để tiếp nối chủ đề Encoder là gì. Hôm nay CKD làm một bài tập nho nhỏ.. ứng dụng Arduino để đọc Encoder.

Để có thể đọc Encoder.. CKD tiến hành làm thêm một mạch đệm, làm trung gian giữa encoder & Arduino. Mạch đệm này có công dụng "chuẩn" tín hiệu từ encoder gởi về Arduino, do đó góp phần chống nhiễu.

Để mọi việc đơn giản và nhanh chóng, CKD chế tạo một prototype board bằng Arduino Proto Shield. Đây gần giống như board đụt lỗ, giúp việc gắn kết các linh kiện được dễ dàng & nhanh chóng. Được thiết kế với các pinout tương thích với các board Arduino.
Board đệm sử dụng IC 74HC14, là loại Schmitt Triger Inverter cùng vài con tụ & trở.

Sơ đồ cơ bản thế này


Kết nối với board Arduino như sau:
- 5V+ để lấy nguồn nuôi.
- Gnd
- Pin 3 -- tín hiệu enocder A
- Pin A4 - tín hiệu encoder B

Vài hình ảnh Arduino Proto Shield sau khi gắn thêm kinh kiện.












_* Trên Arduino Proto Shield ngoài chức năng đệm cho encoder thì còn đệm thêm cho vài tín hiệu khác.. dự phòng cho các thí nghiệm kế tiếp.
* Thêm vài con LED để biết trạng thái tín hiệu._

- Có thể mua Arduino Proto Shield ở TME với giá 60K/bo.
- Cũng có thể mua ở http://store.arduino.cc/product/A000077

----------

cnclaivung, Huudong, imechavn

----------


## CKD

Tiến hành cắm vào board Arduino UNO R3 thử xem sao..






Ấu dè.. may quá.. vừa khít  :Smile:  (nếu không vừa thì mang trả TME).

----------

anhcos, Huudong

----------


## Gamo

Cái câu may quá... lại vừa khít này là tội lỗi của ông Giang, mấy pa xạo quá là xạo  :Cool:

----------

anhcos, thuhanoi

----------


## CKD

Ngoài làm cái mạch thì phải chuẩn bị phần code cho Arduino nữa..
Phần code thực thi vài nhiệm vụ như sau.
- Khai báo các pinout, các thư viện có sử dụng.
- Phần xuất tín hiệu, điều khiển LCD.
- Phần nhận tín hiệu encoder & đếm.

*1. Khai báo sử dụng thư viện.*


```
 /*
 Use a 16x2 LCD display.  The LiquidCrystal library works with all LCD displays that are compatible with the 
 Hitachi HD44780 driver. There are many of them out there, and you  can usually tell them by the 16-pin interface.
 The circuit:
 * LCD RS pin to digital pin 8
 * LCD En pin to digital pin 9
 * LCD D4 pin to digital pin 4
 * LCD D5 pin to digital pin 5
 * LCD D6 pin to digital pin 6
 * LCD D7 pin to digital pin 7
 * LCD R/W pin to ground
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)
 */
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
//                RS  EN  D4  D5 D6 D7
LiquidCrystal lcd( 8,  9,  4,  5, 6, 7);
```

Sử dụng thư viện LCD của Arduino. Khai báo_#include <LiquidCrystal.h>_Khởi tạo biến cho LCD, sử dụng các pinout tương RS  EN  D4  D5 D6 D7 ứng với các pin 9 8 4 5 6 7._LiquidCrystal lcd( 8,  9,  4,  5, 6, 7);_
*2. Khai báo pinout*


```
void setup()
{
  // setup the LCD's number of columns and rows: 
  lcd.begin(16, 2);  
  // startup LOGO  
  lcd.setCursor(4,0);
  lcd.print("CNCProVN");
  lcd.setCursor(2,1);
  lcd.print("Pham Duy Anh");
  delay(1000);
  lcd.clear();
  // setup pin in/out
  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  
  attachInterrupt(1, doEncoder, RISING);   //trigger when the pin goes from low hight
}
```

Khởi tạo lcd 16x2 (16 ký tự, 2 dòng)lcd.begin(16, 2);Khai báo pinout. pinMode(pin, mode)
Với *pin* là số pin trên Arduino, *mode* là chế độ làm việc: INPUT, OUTPUT hoặc INPUT_PULLUP.
* *INPUT* là chế độ làm việc nhận tín hiệu.
* *INPUT_PULLUP* là chế độ làm việc nhận tín hiệu có điện trở kéo lên.
* *OUTPUT* là chế độ làm việc xuất tín hiệuKhai báo sử dụng ngắt ngoài.attachInterrupt(interrupt, function, mode)
Với *interrupt* là số ngắt (với arduino UNO thì có ngắt 0 & 1), *function* là chương trình con được gọi khi có ngắt, *mode* là chế độ hoạt động: LOW, CHANGE, RISING, FALLING
* *LOW* ngắt sẽ thực thi khi tín hiệu ở mức Low.
* *CHANGE* ngắt sẽ thực thi khi tín hiệu thay đổi trạng thái từ low->high hoặc hight->low
* *RISIN*G ngắt sẽ thực thi khi tín hiệu thay đổi trạng thái từ low->high
* *FALLING* ngắt sẽ thực thi khi tín hiệu thay đổi trạng thái từ hight->low
Xem thêm *Arduino - Mỗi ngày một ví dụ, từ dễ tới khó - VD001 - LED Blink*

----------

anhcos, Huudong

----------


## CKD

*3. Thân chương trình chính cũng là chức năng xuất tín hiệu cho LCD*



```
void loop()
{
  lcd.setCursor(0,0);
  lcd.print("Encoder value:");
  lcd.setCursor(1,1);
  lcd.print(enc);
  lcd.print("                ");
}
```

Di chuyển con trỏ đến vị trílcd.setCursor(col, row);
Trong đó *col* là vị trí ký tự trong dòng, *row* là số dòngXuất ký tự hoặc chuổi ký tự ra LCDlcd.print(string);

----------

anhcos, Huudong

----------


## CKD

*4. Chương trình con đếm encoder.*
Chương trình này sẽ được gọi mỗi khi kênh A chuyển trạng thái từ low sang high



```
void doEncoder()
{
  if (digitalRead(encoderPinB)) enc++;
  else                          enc--;  
}
```

Thực hiện phép so sánh, 
- nếu kênh encoder B có giá trị là hight thì cộng 1 giá trị enc++
- nếu kênh encoder B có giá trị là low thì trừ 1 giá trị enc--

Đây là cách đọc encoder 1X, với chương trình đơn giản nhất. Đương nhiên cùng với sự đơn giản nhất thì không được hiệu quả tốt nhất. Tuy nhiên trong giới hạn của bài tập thì chấp nhận được.

Xem thêm Encoder la gi..

File có đính kèm bên dưới......

----------

anhcos, Huudong, taih2

----------


## anhcos

Tks CKD, encoder thì chả biết gì, nhưng phần code LCD thì sẽ dùng đến.

----------


## CKD

Kết nối với enocder 500ppr của con DC servo. Tạm cứ test đã.. 500ppr mà quay tay thì nó nhảy lia lịa.

----------


## Ga con

Đọc kiểu này cho vui thôi anh  :Stick Out Tongue: .
Không biết e nhớ có nhầm kg, hình như con motor này 504 ppr chứ không phải 500. Có khi nhầm sang con khác.

Thanks.

----------


## Gamo

Ủa, em tò mò: bác Phúc đọc encoder cho driver DC Servo của bác bằng cách nào?

----------


## CKD

> Đọc kiểu này cho vui thôi anh .
> Không biết e nhớ có nhầm kg, hình như con motor này 504 ppr chứ không phải 500. Có khi nhầm sang con khác.


Cái này nội dung chính là bài tập nhầm làm quen với lập trình. Sách vở căn bản thì.. nó hơi nhàm chán nên lái các ví dụ sang các ứng dụng có liên quan đến cnc cho nó hứng thú hơn. Phải từ từ, đi từ đơn giản rồi từ từ mới phức tạp được. Mấy cái lập trình & điện tử này mình cứ nhấm hướng tây thiên mà đi.. khi nào tới được thì tới, không tới được thì vui là chính cũng được ạ. Đem khoe lên đây để xem có đồng minh hay không? Cũng như có gì các cụ trùm về lĩnh vực này còn biết mà góp ý sửa sai  :Confused: .

Về phương pháp đọc encoder thì thấy cũng nhiều, mỗi người làm mỗi kiểu. Đơn giản như mình làm cũng có, phức tạp hơn tí cũng có, rồi so sánh gray code cũng có. Đi sâu vào mà nếu dùng thuần arduino thì cũng khó giải quyết vì nó chạy nhiều thư viện, nên tốc độ xử lý khá chậm. Muốn tăng tốc thì phải xử lý trực tiếp trên bit.. mà cái này thì không rành  :Smile: .

Cái này mình đã ứng dụng vào mấy cái nút xoay (rotary encoder) 20ppr thấy dùng được. Chủ yếu để tăng giảm giá trị gì đó.. chưa thấy vấn đề gì lớn. Không biết ứng dụng vào mấy cái cần tốc độ cao thì thế nào  :Wink: .
Định bụng là sẽ phát triển để làm DRO, đọc mấy cái thước quang. Cụ Nam ròm có giao cho 1 cây thước quang ngắn, tra cứu thì nó là dạng ABZ, 1000pp_mm. Có hứa với cụ ấy là làm thử cái mạch đọc.. cho hiện số. Nhưng mãi mà chưa làm được  :Frown: 

Cái encoder của con DC này thì.. nhớ là đã có tháo ra xem, trên cái đĩa của nó có ghi 500, nên chắc là 500 đó ạ.

Bác có cao kiến gì thì giúp với ạ.
Thanks!

----------


## cuongmay

báo cáo bác đọc encoder kieu này nó sai bét nhè ạ . muốn chính xác phải dùng 2 ngắt chế độ any change dành cho 2 kênh AB .khi có ngắt được phục vụ thì load dữ liệu 2 kenh AB rồi dịch phải sau đó xor nó với dữ liệu kênh AB của chu kì trước , cuối cùng kiểm tra giá trị bit B sau khi xor sẽ ra chiều chuyển động .nếu đường truyền tốt ,tốc độ xử lý kịp ,code tối ưu thì sẽ tuyệt đối chính xác còn không đạt thì phải chuẩn hoá bằng xung index hay còn gọi là kênh C hoặc kênh Z . mình viết code đọc encoder luôn phải dùng asembly để tối ưu tốc độ .bác viết bằng adruno thì chắc chỉ chạy đưuợc encoder vai trăm xung .

----------

anhcos, CKD

----------


## Ga con

Chắc em nhầm con Pitman này sang con nhỏ nhỏ nào đó rồi. Hồi trước mua kiểu này để lấy enc nhiều.
Một con MCU mà làm từng này việc thì em e là chạy không nổi. Cái Adruino này cũng tiện nhỉ, mà giờ e không có thời gian vọc rồi  :Frown: .

@ bác Gamo: em có trao đổi với bác CKD rồi, em thì luôn đọc enc 1 xung ra 4 trạng thái gray code để lọc nhiễu. Nhiều người bảo để tăng độ phân giải nhưng thực chất không phải, mấy con nhiều xung quá thì càng oải. Nên em chỉ khoái loại 500<ppr<2000 thôi.

Thanks.

----------


## Ga con

> báo cáo bác đọc encoder kieu này nó sai bét nhè ạ . muốn chính xác phải dùng 2 ngắt chế độ any change dành cho 2 kênh AB .khi có ngắt được phục vụ thì load dữ liệu 2 kenh AB rồi dịch phải sau đó xor nó với dữ liệu kênh AB của chu kì trước , cuối cùng kiểm tra giá trị bit B sau khi xor sẽ ra chiều chuyển động .nếu đường truyền tốt ,tốc độ xử lý kịp ,code tối ưu thì sẽ tuyệt đối chính xác còn không đạt thì phải chuẩn hoá bằng xung index hay còn gọi là kênh C hoặc kênh Z . mình viết code đọc encoder luôn phải dùng asembly để tối ưu tốc độ .bác viết bằng adruno thì chắc chỉ chạy đưuợc encoder vai trăm xung .


Dạ theo em thì chả có gì đạt đến tuyệt đối chính xác ạ :Smile: . Ngắt pinchange bác CKD cũng làm rồi nhưng trao đổi với em thì bác ấy bảo cũng chưa đạt.

Cách đọc xung này sẽ có thể ra trường hợp đặt con motor trên bàn, gõ cái bàn, motor không chạy nhưng xung cứ lên ào ào.

Con của bác CKD dùng tốc độ không cao, xài con ATXMega thì chắc đủ.

Thanks.

----------


## nhatson

nếu muốn đọc encoder tốc độ cao ( cao cấp ) > mình ko cần tiết kiệm > có 2 pan em thấy khả thi
1. dùng riêng 1 con để đọc encoder rồi truyền nối tiếp
2. dùng ic logic/cpld/ chuyên dùng để counter

----------


## CKD

Mô hình này có nhiều giới hạn:
- Tốc độ thực thi các dòng lệnh bị giới hạn, do arduino muốn người dùng dễ đàng và tương thích với tất cả các phiên bản arduino nên tất cả các pinout đều được ánh xạ qua map. Khi thực thi thì phải mất thời gian suy từ Pin No sang Pin/Port. Cách khắc phục là truy xuất trực tiếp pin/port, nhưng với cách này thì người sử dụng phải nắm được core vxl của board. Ví dụ như arduino UNO R3 dùng trong ví dụ này là atmega328.
- Phần code đọc encoder như trên do không có so sánh trạng thái trước & sau, nên các xung nhiễu sẽ khiến cho encoder đọc sai. Vẫn sẽ đọc sai nếu encoder giao động (rung động) tại một trạng thái.
- Do giới hạn bỡi bố trí pin/port theo các shield nên khã năng tùy biến cũng giới hạn phần nào.

Ưu điểm của arduino & các shield là:
- Được chế tạo sẵn giúp người sử dụng tiếp cận dễ dàng. Có thể nhanh chóng làm các bài tập, lập trình ứng dụng đơn giản mà không cần phải biết rỏ về cấu trúc vi điều khiển. Cũng không cần biết nhiều về điện, điện tử. Có thể nói các dự án trên nền arduino phần lớn có dạng plug and play.
- Phần code như trên đã được test trong các điều kiện đơn giản, trong các ứng dụng không đòi hỏi phải chính xác tuyệt đối. Rất phù hợp cho các ứng dụng như
--- núm vặn encoder, như nút cuộn trong mấy con chuột. Những loại này thường có khất nên việc rung động tại 1 vị trí là không có  :Smile: .
--- trong các dự án kiểu robot thăng bằng, xe tự hành v.v... đều có thể ứng dụng.

----------


## itanium7000

Em thấy nếu bỏ qua Arduino Uno R3 cụ thể là con ATmega328 thì sử dụng con nào khác có bộ Timer tích hợp chế độ quadrature encoder là thuận tiện, hiệu quả. Nhưng đây lại là chủ đề Arduino Uno R3 thì phải đọc bằng phần mềm thôi.

----------


## CKD

> nếu muốn đọc encoder tốc độ cao ( cao cấp ) > mình ko cần tiết kiệm > có 2 pan em thấy khả thi
> 1. dùng riêng 1 con để đọc encoder rồi truyền nối tiếp
> 2. dùng ic logic/cpld/ chuyên dùng để counter


Nếu dùng 2 con thì truyền nối tiếp e mất nhiều thời gian. Phương án truyền song song cùng lúc 8bit hay hơn à. Vì các ứng dụng kiểu này.. yêu cầu pinout ít.. nên mấy con vxl còn dư nhiền pin/port lắm à. Dùng 1 port 8bit truyền cho nhanh  :Smile:

----------


## CKD

> Em thấy nếu bỏ qua Arduino Uno R3 cụ thể là con ATmega328 thì sử dụng con nào khác có bộ Timer tích hợp chế độ quadrature encoder là thuận tiện, hiệu quả. Nhưng đây lại là chủ đề Arduino Uno R3 thì phải đọc bằng phần mềm thôi.


Cái này tại chuối mũi vào mấy cái board arduino đời cũ nó mới vậy. Chơi arduino DUE thì cũng chạy trên nền SAM rồi ạ.
Vụ này thuần là tại vì ngoài mấy con AVR ra thì mình chẵng biết lập trình cho con nào nữa. Thấy mấy cái ứng dụng nhỏ nhỏ thì nó vẫn đáp ứng được.. nên ráng đu theo  :Smile: . Chứ về kinh tế thì.. mấy con ARM Cortex chạy nhanh như ngựa mà giá lại rất hạt giẻ  :Smile: 
Mà mấy cụ iu của CKD cứ xúi CKD lên đời.. nhưng mà mở cái datasheet lên xem thấy quán gà rồi nên thôi.

----------


## cuongmay

> Dạ theo em thì chả có gì đạt đến tuyệt đối chính xác ạ. Ngắt pinchange bác CKD cũng làm rồi nhưng trao đổi với em thì bác ấy bảo cũng chưa đạt.
> 
> Cách đọc xung này sẽ có thể ra trường hợp đặt con motor trên bàn, gõ cái bàn, motor không chạy nhưng xung cứ lên ào ào.
> 
> Con của bác CKD dùng tốc độ không cao, xài con ATXMega thì chắc đủ.
> 
> Thanks.


đó là do code  và mạch chưa tốt thôi bác ấy không chuyên điện mà. mình làm servo cho xác máy nhật với code như thế chạy tinh khuôn 1 đêm sáng ra kiểm tra không thấy sai % nào so với du xích.

----------


## CKD

```
boolean A, B, lastA, lastB;
attachInterrupt(1, doEncoder, CHANGE);
//2x Quadrature Encoder
void doEncoder()
{
  A = digitalRead(enocderPinA);
  B = digitalRead(encoderPinB);

  if ((A != lastA) & (B != lastB))
  {
    if (A ^ B) enc --;
    else       enc ++;
  }
  lastA = A;
  lastB = B;
}
```

Cái này thì trong quá trình test không thấy sai.. chỉ có điều tốc độ vẫn chậm hehe  :Smile:

----------


## CKD

Clip chạy với code ban đầu  :Wink:  đúng sai chưa rỏ vì chỉ nhìn bằng mắt.

----------

Ga con, nhatson

----------


## nhatson

truyền serial có nhiều kiều mờ anh CKD, I2C SPI , đảm bảo là ko chậm
nều dùng paraler thì dùng ic logic chuyển đổi thành up/down hoặc clock/dir rồi đưa vào IC đếm> MCU

----------


## Ga con

> đó là do code  và mạch chưa tốt thôi bác ấy không chuyên điện mà. mình làm servo cho xác máy nhật với code như thế chạy tinh khuôn 1 đêm sáng ra kiểm tra không thấy sai % nào so với du xích.


Dạ chạy một cái khuôn để kết luận thì cũng chưa đúng ạ.

Như máy em làm chạy gỗ, số dòng lệnh tính bằng triệu dòng, chạy liên tục hơn 2 ngày mới xong. Xong về home OK chả thấy sai vạch nào. Khi đã chạy ổn định là thế, nhưng đùng một phát có hôm điện nhà nhiễu loạn thế nào ấy, nó cũng loạn, qua hôm sau lại hết. Nên e mới nói là chả có gì là tuyệt đối.

Driver của em thì một số bác đã kiểm chứng rồi ạ. Có giai đoạn em cũng bị vật lê lết, mà nguyên nhân thì tùm lum, cả phần driver và phần còn lại. Giờ rút kinh nghiệm làm cho em xài thôi.

Thanks.

----------


## anhcos

Cái LCD 2004 cũng chỉ cần 4 pin data như cái 1602 phải không CKD, mình đọc datasheet thì thấy nó ghi 8 pin data lận.

----------


## CKD

Vụ LCD thì em cũng mới biết có vậy hà. Truyền 4pin hay 8pin đều được thì phải. Bản thân cái 16x2 cũng là 8pin mà. Ngoài ra giờ nó hay dùng kiểu spi, hoặc 1 wire thì phải.

----------


## cdt1995

Alo, bác CKD còn đó không cho em hỏi về vụ mạch với.

----------


## CKD

Mạch gì vậy bác?

----------


## cdt1995

> Mạch gì vậy bác?


Cái mạch bác nối trên con arduino á, cái điện trở là kéo lên, vậy tụ để làm gì vậy bác? Nếu dùng để lọc cao tần thì có thêm R mắc nối tiếp đúng không bác? Hay sao nhỉ?

----------

