Encoder tương đối: cấu tạo, tín hiệu A B Z, cách chế tạo và đếm xung bằng PCNT của ESP32

Encoder tương đối hoạt động thế nào: đĩa mã, mặt nạ, kênh A B lệch 90°, vạch Z, đếm x4, ngõ ra NPN, HTL, RS-422, cách chế tạo đĩa mã và đếm encoder bằng PCNT của ESP32.

Encoder là cảm biến biến chuyển động quay (hoặc thẳng) thành xung điện. Mỗi khi trục quay qua một góc nhỏ, encoder phát ra một xung; đếm xung là biết trục đã quay bao nhiêu, đếm trong một khoảng thời gian là biết tốc độ. Encoder tương đối (incremental) chỉ báo phần thay đổi so với lúc bắt đầu đếm, khác với encoder tuyệt đối luôn báo đúng vị trí ngay khi cấp nguồn (bài tiếp theo của loạt). Bài viết giải thích cấu tạo encoder quang, hai kênh A, B lệch pha 90°, vạch Z, các kiểu ngõ ra, cách chế tạo đĩa mã và cách đếm xung encoder bằng bộ đếm phần cứng PCNT của ESP32.

1. Cấu tạo encoder quang

Cấu tạo encoder quang loại tương đối: đèn LED chiếu qua đĩa mã có rãnh và mặt nạ tới ba đi-ốt quang A, B, Z, mạch so sánh biến tín hiệu thành ba dãy xung vuông; bên phải là đĩa mã nhìn chính diện với vòng rãnh và vùng đọc
Đường đi của ánh sáng trong encoder quang và đĩa mã nhìn chính diện (hình tự vẽ).
  • Đĩa mã: gắn trên trục, có một vòng rãnh trong suốt và vạch tối xen kẽ đều nhau. Số cặp rãnh và vạch trên một vòng là độ phân giải, ghi là P/R hay PPR (xung mỗi vòng), ví dụ 100, 360, 600, 1024, 2500.
  • Nguồn sáng và đi-ốt quang: LED hồng ngoại ở một bên, các đi-ốt quang ở bên kia. Rãnh đi qua thì đi-ốt nhận sáng, vạch đi qua thì bị che.
  • Mặt nạ: tấm đứng yên có rãnh cùng bước với đĩa, đặt ngay trước đi-ốt quang để chỉ cho ánh sáng đi qua một vùng hẹp, giúp xung sắc nét. Hai cửa sổ A và B được đặt lệch nhau một phần tư bước.
  • Vạch Z (index): một rãnh riêng ở vòng trong, cho đúng một xung mỗi vòng, dùng làm điểm gốc.
  • Mạch so sánh: biến tín hiệu gần hình sin từ đi-ốt quang thành xung vuông sạch, rồi đưa ra tầng ngõ ra.
Encoder quay công nghiệp vỏ kim loại màu đen, mặt bích nhôm có lỗ bắt vít, trục đặc có rãnh then và đầu cáp bên hông
Encoder quay công nghiệp loại trục đặc, mặt bích bắt vít và đầu cáp bên hông. Ảnh: Joao Paulo Chagas, CC BY 4.0, Wikimedia Commons, đã thu nhỏ.

2. Hai kênh A, B lệch pha 90° và cách đếm x4

Giản đồ hai kênh A và B của encoder lệch nhau 90 độ, trạng thái A B theo từng nửa chu kỳ, các điểm đếm x4 ở mọi sườn của A và B, điểm đếm x1 chỉ ở sườn lên của A, bảng thứ tự trạng thái khi quay thuận và quay nghịch
Tín hiệu cầu phương của kênh A, B và các cách đếm (hình tự vẽ).

Nếu chỉ có một kênh A, ta biết trục đã quay bao nhiêu nhưng không biết quay chiều nào. Kênh B có cùng số xung nhưng lệch một phần tư chu kỳ (90°). Hai tín hiệu này gọi là tín hiệu cầu phương:

  • Quay thuận: A đổi trạng thái trước B, thứ tự trạng thái (A, B) là 00 → 10 → 11 → 01 → 00.
  • Quay nghịch: B đổi trước, thứ tự đảo lại 00 → 01 → 11 → 10 → 00.
  • Đếm x1: chỉ đếm sườn lên của A. Đếm x2: cả sườn lên và xuống của A. Đếm x4: mọi sườn của cả A và B.
  • Encoder 600 xung/vòng đếm x4 cho 2400 số đếm mỗi vòng, tức 0,15° mỗi số đếm. Nhà sản xuất đôi khi ghi con số sau khi nhân 4 (CPR, đếm mỗi vòng), nên đọc kỹ nhãn.

Đếm x4 cũng có lợi khi trục rung qua lại quanh một sườn xung: mỗi lần tiến là +1, mỗi lần lùi là −1, nên số đếm không bị trôi.

Mô hình encoder quang: đĩa nhôm tròn có nhiều rãnh ở mép, một cảm biến quang chữ U ôm lấy mép đĩa
Mô hình encoder quang đơn giản: đĩa có rãnh ở mép quay qua khe của cảm biến quang chữ U. Ảnh: Rrudzik, CC BY-SA 4.0, Wikimedia Commons, đã thu nhỏ.

3. Các kiểu encoder và kiểu ngõ ra

Ba kiểu encoder: quang học, từ tính, cơ khí tiếp điểm; ba kiểu ngõ ra: NPN cực thu hở với điện trở kéo lên 3V3, đẩy kéo HTL hoặc TTL, vi sai RS-422 với hai dây A và A đảo
Kiểu tạo xung và kiểu mạch ngõ ra của encoder (hình tự vẽ).
KiểuNguyên lýDùng ở đâu
Quang họcĐĩa có rãnh, LED và đi-ốt quangĐộ phân giải cao (tới hàng chục nghìn xung/vòng), máy CNC, servo; sợ bụi, dầu và rung mạnh
Từ tínhVòng nam châm nhiều cực hoặc bánh răng thép, cảm biến Hall hoặc từ trởĐộng cơ giảm tốc có encoder cho robot, môi trường bẩn, nhiều rung
Cơ khíLá đồng trượt trên đĩa có vạch dẫn điệnNúm xoay chỉnh âm lượng, menu; rẻ nhưng tiếp điểm dội, mòn dần
Encoder cơ khí tháo ra từ một con chuột máy tính cũ: đĩa nhựa màu đỏ có các vạch đồng dẫn điện, ba lá đồng tiếp điểm trên đế nhựa đen gắn vào mạch in
Encoder cơ khí tháo từ một con chuột cũ: lá đồng trượt trên đĩa có vạch đồng tạo ra hai kênh A, B. Ảnh: Daniel beardsmore, phạm vi công cộng, Wikimedia Commons, đã thu nhỏ.

Về ngõ ra điện:

  • NPN cực thu hở: transistor bên trong chỉ kéo dây xuống 0 V; mức cao do điện trở kéo lên ở bên nhận. Rất tiện cho ESP32 vì kéo lên 3,3 V là an toàn, bất kể encoder chạy nguồn 5 V hay 24 V. Tần số cao thì dùng điện trở kéo lên nhỏ hơn (1 đến 2,2 kΩ) để sườn xung dốc.
  • Đẩy kéo (HTL, TTL): ngõ ra lên thẳng bằng điện áp nguồn của encoder; loại 10–30 V phải qua mạch hạ mức hoặc opto trước khi vào ESP32.
  • Vi sai RS-422 (line driver): mỗi kênh hai dây ngược pha (A và A đảo); bên nhận dùng IC thu vi sai. Chống nhiễu tốt, dây đi được hàng chục mét trong tủ điện có biến tần.

4. Encoder được chế tạo như thế nào

Sáu bước chế tạo encoder: làm đĩa mã bằng thủy tinh phủ crôm quang khắc hoặc lá kim loại khắc axit, làm mặt nạ và đầu đọc với rãnh lệch một phần tư bước, lắp trục và vòng bi đồng tâm, căn chỉnh pha trên máy hiện sóng, từ hóa vòng nam châm cho loại từ tính, kiểm tra cuối
Các bước chính chế tạo encoder quang và encoder từ (hình tự vẽ).
  1. Đĩa mã: loại độ phân giải cao dùng đĩa thủy tinh phủ lớp crôm mỏng, quang khắc như làm mạch in để tạo vạch cỡ vài micromet. Loại vừa dùng lá thép không gỉ khắc axit; loại rẻ dùng đĩa nhựa in vạch.
  2. Mặt nạ và đầu đọc: mặt nạ có rãnh cùng bước, cửa sổ B lệch cửa sổ A đúng 1/4 bước. Encoder hiện đại thường dùng một chip có sẵn mảng đi-ốt quang được bố trí để tự tạo hai tín hiệu lệch 90°.
  3. Trục và vòng bi: đĩa phải đồng tâm với trục, khe hở giữa đĩa và mặt nạ đều suốt một vòng, nếu không xung sẽ lúc rộng lúc hẹp.
  4. Căn chỉnh: cho trục quay đều, quan sát A, B trên máy hiện sóng, xoay nhẹ đầu đọc tới khi hai kênh lệch đúng 90° và mỗi xung có tỉ lệ cao thấp gần 50 %.
  5. Encoder từ: một đầu từ hóa ghi lên vòng nam châm hàng chục đến hàng trăm cặp cực xen kẽ; chip Hall hoặc từ trở đọc và nội suy ra nhiều xung hơn.
  6. Kiểm tra: đếm đủ số xung mỗi vòng, vị trí vạch Z, sai số chia vạch, thử rung, nhiệt độ và độ kín.

Tự chế một encoder đơn giản: in hoặc cắt một đĩa có rãnh đều (in 3D, cắt mica), gắn lên trục, dùng hai cảm biến quang chữ U đặt lệch nhau một phần tư bước rãnh. Độ phân giải thấp nhưng đủ để đo tốc độ bánh xe robot.

5. Thực hành: đếm encoder bằng PCNT của ESP32

Encoder 600 xung/vòng quay 3000 vòng/phút cho 30 kHz mỗi kênh. Đếm bằng ngắt GPIO sẽ chiếm nhiều thời gian của CPU và dễ sót xung. ESP32 có khối PCNT (bộ đếm xung) đếm cầu phương hoàn toàn bằng phần cứng; chương trình chỉ cần đọc số đếm khi cần.

Linh kiện

Sơ đồ nối

Sơ đồ nối encoder ngõ ra NPN với ESP32: dây nâu nguồn vào VIN 5 V, dây xanh dương 0 V vào GND, dây đen kênh A vào GPIO25, dây trắng kênh B vào GPIO26, dây cam kênh Z vào GPIO27, mỗi dây tín hiệu có điện trở 4,7 kilôôm kéo lên 3V3
Sơ đồ nối encoder NPN với ESP32 (hình tự vẽ; màu dây là ví dụ).
Dây encoderNối tới ESP32
Nguồn (5–24 V)VIN (5 V khi cắm USB)
0 VGND
A, B, ZGPIO25, GPIO26, GPIO27; mỗi chân thêm điện trở 4,7 kΩ lên 3V3

Chương trình

Chương trình dùng trình điều khiển driver/pulse_cnt.h có sẵn trong gói esp32 3.x, không cần thư viện ngoài. Sửa XUNG_MOI_VONG theo nhãn encoder.

/*
  PYWORLD.VN - Encoder tương đối (A, B, Z) với ESP32: đếm cầu phương x4 bằng bộ đếm xung phần cứng PCNT
  Kênh A vào GPIO25, kênh B vào GPIO26, vạch Z vào GPIO27; encoder ngõ ra cực thu hở NPN, kéo lên 3V3 bằng điện trở 4,7 kΩ
  In số đếm, số vòng, góc, tốc độ (vòng/phút) và chiều quay; báo mỗi lần qua vạch Z
*/

#include "driver/pulse_cnt.h"

#define CHAN_A 25
#define CHAN_B 26
#define CHAN_Z 27

const int XUNG_MOI_VONG = 600;                   // Số xung của mỗi kênh trong một vòng (ghi trên nhãn: 600 P/R)
const int DEM_MOI_VONG = XUNG_MOI_VONG * 4;      // Đếm cả sườn lên và xuống của A và B: x4
const int CHU_KY_MS = 200;

pcnt_unit_handle_t boDem = NULL;
volatile bool quaZ = false;

void IRAM_ATTR ngatZ() {
  quaZ = true;
}

void caiDatBoDem() {
  pcnt_unit_config_t cauHinh = {};
  cauHinh.low_limit = -30000;
  cauHinh.high_limit = 30000;
  cauHinh.flags.accum_count = 1;                 // Cộng dồn khi chạm giới hạn, số đếm không bị quay về 0
  pcnt_new_unit(&cauHinh, &boDem);

  pcnt_glitch_filter_config_t locNhieu = {};
  locNhieu.max_glitch_ns = 1000;                 // Bỏ qua xung nhiễu ngắn hơn 1 µs
  pcnt_unit_set_glitch_filter(boDem, &locNhieu);

  // Kênh 1: đếm theo sườn của A, chiều đếm do mức của B quyết định
  pcnt_chan_config_t kenhA = {};
  kenhA.edge_gpio_num = CHAN_A;
  kenhA.level_gpio_num = CHAN_B;
  pcnt_channel_handle_t hA = NULL;
  pcnt_new_channel(boDem, &kenhA, &hA);
  pcnt_channel_set_edge_action(hA, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE);
  pcnt_channel_set_level_action(hA, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE);

  // Kênh 2: đếm theo sườn của B, chiều đếm do mức của A quyết định
  pcnt_chan_config_t kenhB = {};
  kenhB.edge_gpio_num = CHAN_B;
  kenhB.level_gpio_num = CHAN_A;
  pcnt_channel_handle_t hB = NULL;
  pcnt_new_channel(boDem, &kenhB, &hB);
  pcnt_channel_set_edge_action(hB, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_DECREASE);
  pcnt_channel_set_level_action(hB, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE);

  // Cần điểm theo dõi ở hai giới hạn thì chế độ cộng dồn mới hoạt động
  pcnt_unit_add_watch_point(boDem, cauHinh.low_limit);
  pcnt_unit_add_watch_point(boDem, cauHinh.high_limit);

  pcnt_unit_enable(boDem);
  pcnt_unit_clear_count(boDem);
  pcnt_unit_start(boDem);
}

void setup() {
  Serial.begin(115200);
  caiDatBoDem();
  pinMode(CHAN_Z, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(CHAN_Z), ngatZ, FALLING);
  Serial.printf("\nEncoder %d xung/vong, dem x4 = %d dem/vong\n", XUNG_MOI_VONG, DEM_MOI_VONG);
}

void loop() {
  static int demTruoc = 0;
  static uint32_t mocTruoc = millis();
  if (millis() - mocTruoc < CHU_KY_MS) {
    return;
  }
  uint32_t bayGio = millis();
  int dem = 0;
  pcnt_unit_get_count(boDem, &dem);

  float giay = (bayGio - mocTruoc) / 1000.0f;
  int thayDoi = dem - demTruoc;
  float vongPhut = thayDoi / (float)DEM_MOI_VONG / giay * 60.0f;
  float soVong = dem / (float)DEM_MOI_VONG;
  float goc = (dem % DEM_MOI_VONG) * 360.0f / DEM_MOI_VONG;
  if (goc < 0) {
    goc += 360.0f;
  }
  const char *chieu = thayDoi > 0 ? "thuan" : (thayDoi < 0 ? "nghich" : "dung yen");
  Serial.printf("Dem %d | %.3f vong | Goc %.1f do | %.1f vong/phut | %s\n", dem, soVong, goc, vongPhut, chieu);

  if (quaZ) {
    quaZ = false;
    Serial.printf("Da qua vach Z (so dem hien tai %d)\n", dem);
  }
  demTruoc = dem;
  mocTruoc = bayGio;
}

Giải thích

  • Một đơn vị PCNT có hai kênh: kênh 1 đếm theo sườn của A và lấy mức của B để quyết định cộng hay trừ; kênh 2 làm ngược lại. Kết quả là đếm x4 có dấu.
  • Bộ lọc 1 µs bỏ qua gai nhiễu ngắn. Giới hạn ±30000 kèm chế độ cộng dồn giúp số đếm tiếp tục tăng khi vượt giới hạn phần cứng.
  • Mỗi 200 ms chương trình đọc số đếm, tính số vòng, góc trong vòng (0 đến 360°), tốc độ từ phần thay đổi và chiều quay từ dấu của phần thay đổi.
  • Vạch Z được báo bằng ngắt GPIO; có thể dùng để đặt lại số đếm về 0 sau khi tìm gốc.

Kết quả mong đợi

Hình minh họa Serial Monitor đếm encoder bằng PCNT của ESP32: số đếm, số vòng, góc theo độ, tốc độ vòng phút và chiều quay thuận hoặc nghịch, kèm thông báo khi qua vạch Z
Kết quả mong đợi trên Serial Monitor (hình minh họa; số đếm là ví dụ).

Encoder tương đối mất vị trí khi mất điện. Máy CNC, máy in 3D dùng encoder tương đối hoặc động cơ bước luôn chạy về công tắc gốc (và vạch Z) khi khởi động. Nếu cần biết vị trí ngay khi bật máy, dùng encoder tuyệt đối.

6. Xử lý lỗi thường gặp

Hiện tượngNguyên nhân thường gặpCách xử lý
Số đếm không đổiThiếu điện trở kéo lên; encoder không có nguồnThêm điện trở 4,7 kΩ lên 3V3; đo điện áp nguồn encoder
Đếm ngược chiều mong muốnĐổi chỗ A và BĐổi hai dây A, B hoặc đổi số chân trong chương trình
Số đếm mỗi vòng ít hơn 2400Quay quá nhanh so với điện trở kéo lên; bộ lọc quá lớnGiảm điện trở kéo lên còn 1–2,2 kΩ; giảm max_glitch_ns
Số đếm nhảy khi động cơ chạyNhiễu từ dây động lực, biến tầnDùng cáp có chống nhiễu, đi riêng dây; dùng encoder ngõ ra vi sai
Núm xoay cơ khí nhảy hai, ba số mỗi nấcTiếp điểm dộiThêm tụ 10 nF ở mỗi kênh, tăng bộ lọc lên vài µs, chia số đếm cho số sườn mỗi nấc

7. Xem thêm


Tin tức liên quan

Jack Type-C tích hợp mạch sạc pin 1S: cấu tạo, thông số và khi nào nên dùng
Jack Type-C tích hợp mạch sạc pin 1S: cấu tạo, thông số và khi nào nên dùng

Jack Type-C tích hợp mạch sạc pin 1S nhận 5 V, sạc pin Li-ion hoặc Li-po một cell đến 4,2 V ở dòng 1 A, có đèn báo đỏ và xanh. Bài nêu cấu tạo, thông số, điểm khác với jack Type-C thường và các trường hợp nên dùng.

Đo biến dạng bằng cảm biến strain gauge
Đo biến dạng bằng cảm biến strain gauge

Giải thích strain, hệ số gauge, cầu Wheatstone, cấu hình quarter/half/full bridge và các bước thiết lập phép đo biến dạng tin cậy.

Cảm biến gia tốc: cấu tạo MEMS răng lược, áp điện, cách chế tạo và đo góc nghiêng bằng ESP32
Cảm biến gia tốc: cấu tạo MEMS răng lược, áp điện, cách chế tạo và đo góc nghiêng bằng ESP32

Cảm biến gia tốc hoạt động thế nào: mô hình khối lượng – lò xo, MEMS răng lược điện dung vi sai, chế tạo vi cơ bề mặt, áp điện, servo, đo góc nghiêng và đọc MPU-6050 bằng ESP32.

Cấp nguồn ESP32 bằng pin 18650 sạc qua jack Type-C tích hợp mạch sạc
Cấp nguồn ESP32 bằng pin 18650 sạc qua jack Type-C tích hợp mạch sạc

Sơ đồ nguồn cho ESP32 chạy pin 18650, sạc qua jack Type-C trên vỏ hộp: vì sao cần mạch đổi áp, chọn tăng áp 5 V hay ổn áp 3,3 V, ước tính thời gian chạy pin, đo mức pin và các lỗi nguồn thường gặp.

Vali Nhựa Chuyên Dụng – Gia Công Mút Theo Biên Dạng Thiết Bị
Vali Nhựa Chuyên Dụng – Gia Công Mút Theo Biên Dạng Thiết Bị

Dịch vụ chọn vali và gia công mút theo biên dạng thiết bị; vật liệu, kích thước và mức bảo vệ được xác nhận theo từng mẫu cụ thể.

Modbus RTU (phần 1): khung tin, mã hàm, CRC và ESP32 làm thiết bị chủ
Modbus RTU (phần 1): khung tin, mã hàm, CRC và ESP32 làm thiết bị chủ

Giải thích Modbus RTU ở mức từng byte: khung tin, bốn vùng dữ liệu, mã hàm đọc ghi, khung báo lỗi. Kèm chương trình ESP32 làm thiết bị chủ đọc thanh ghi và ghi cuộn qua RS-485, không dùng thư viện.

Điều khiển hồng ngoại hoạt động thế nào: sóng mang 38 kHz, giao thức NEC và ESP32
Điều khiển hồng ngoại hoạt động thế nào: sóng mang 38 kHz, giao thức NEC và ESP32

Giải thích điều khiển hồng ngoại: sóng mang 38 kHz, mắt thu và giao thức NEC với khung 32 bit, mã lặp. Hướng dẫn tự giải mã phím điều khiển và tự phát lệnh bằng ESP32, không dùng thư viện ngoài.

I2C là gì: START, STOP, địa chỉ, ACK và cách đọc ghi thanh ghi trên ESP32
I2C là gì: START, STOP, địa chỉ, ACK và cách đọc ghi thanh ghi trên ESP32

Giải thích giao tiếp I2C ở mức từng bit: hai dây SDA, SCL, điện trở kéo lên, START, địa chỉ, ACK, STOP. Kèm hai chương trình ESP32: tự tạo tín hiệu I2C bằng GPIO và đọc ghi thanh ghi bằng Wire.


Bình luận
Đã thêm vào giỏ hàng