RS-485 là gì: cặp dây vi sai, điện trở đầu cuối và cách dùng module MAX485 với ESP32
Giải thích chuẩn RS-485: tín hiệu vi sai trên hai dây A, B, điện trở đầu cuối 120 Ω, truyền bán song công với chân DE, RE. Kèm sơ đồ nối module MAX485 và chương trình hỏi đáp giữa hai mạch ESP32.
RS-485 là chuẩn truyền dữ liệu phổ biến nhất trong công nghiệp: biến tần, đồng hồ điện, bộ điều khiển nhiệt độ, cảm biến đặt xa hàng trăm mét phần lớn đều có cổng RS-485. Chuẩn này chỉ quy định phần dây dẫn và mức điện; dữ liệu đi trên đó vẫn là các byte của UART, và giao thức thường gặp nhất chạy trên nó là Modbus RTU. Bài viết giải thích cặp dây vi sai, điện trở đầu cuối, cách nhiều thiết bị chia nhau một cặp dây, rồi dùng hai mạch ESP32 với module MAX485 để truyền hỏi đáp qua RS-485.
1. RS-485 khác UART thường ở đâu?
- UART mức 3,3 V hoặc 5 V chỉ nối được hai thiết bị, dây dài vài mét là bắt đầu lỗi.
- RS-485 đổi mỗi bit thành hiệu điện áp giữa hai dây A và B. Nhiễu tác động lên hai dây như nhau nên bị loại bỏ khi lấy hiệu. Nhờ đó đường dây đi được tới khoảng 1200 m ở tốc độ thấp.
- Nhiều thiết bị mắc song song trên cùng một cặp dây: chuẩn gốc cho phép 32 tải, các IC đời mới cho phép nhiều hơn.
- RS-485 không quy định nội dung dữ liệu, không có địa chỉ hay kiểm tra lỗi. Những việc đó thuộc về giao thức ở lớp trên như Modbus RTU.
2. Đường dây: dây xoắn, điện trở đầu cuối, GND
- Cặp dây xoắn: hai dây A, B phải xoắn vào nhau, tốt nhất là cáp có lớp chống nhiễu. Hai dây đi rời nhau làm mất phần lớn khả năng chống nhiễu.
- Đi dây nối tiếp qua từng nút: đường dây chính đi lần lượt từ nút này sang nút kia. Không đấu hình sao (nhiều nhánh dài tỏa ra từ một điểm).
- Điện trở đầu cuối 120 Ω: đặt giữa A và B ở hai nút đầu và cuối đường dây, để tín hiệu không dội lại ở đầu dây. Với dây ngắn vài mét và tốc độ thấp, thiếu điện trở này mạch vẫn thường chạy; dây dài thì bắt buộc.
- Dây GND: bộ thu chịu được chênh lệch điện áp đất trong khoảng -7 đến +12 V. Nên có thêm dây thứ ba nối chung GND giữa các nút để chênh lệch này không vượt giới hạn.
- Tên dây: các hãng đặt tên A, B không thống nhất; có hãng ghi D+, D-. Nếu nối đúng mà không nhận được gì, thử đổi chéo hai dây.
3. Tín hiệu trên hai dây
- A cao hơn B ít nhất 0,2 V: bộ thu đọc ra mức 1. A thấp hơn B ít nhất 0,2 V: mức 0.
- Khi không nút nào phát, hai dây thả nổi. Hai điện trở phân cực (kéo A lên, kéo B xuống) giữ cho đường dây ở trạng thái mức 1, đúng với trạng thái nghỉ của UART. Module MAX485 thông dụng đã có sẵn các điện trở này.
4. Bán song công: mỗi lúc một nút phát
- Vì mọi nút dùng chung một cặp dây, tại một thời điểm chỉ được một nút phát. Kiểu truyền này gọi là bán song công.
- IC thu phát có hai chân điều khiển: DE (mức cao thì bật bộ phát) và RE (mức thấp thì bật bộ thu). Nối chung hai chân vào một chân GPIO: mức cao là phát, mức thấp là nghe.
- Trình tự gửi: nâng DE, gửi dữ liệu, chờ đến khi bit cuối cùng ra khỏi chân TX, rồi hạ DE ngay. Trong Arduino, hàm
Serial2.flush()làm việc chờ này. - Để các nút không phát đè lên nhau, cần quy ước ai được phát lúc nào. Cách đơn giản và phổ biến nhất là một nút chủ hỏi, các nút tớ chỉ trả lời khi được hỏi.
- Bộ UART của ESP32 còn có chế độ RS-485 tự điều khiển chân DE bằng tín hiệu RTS (
setMode(UART_MODE_RS485_HALF_DUPLEX)). Bài này điều khiển bằng tay để thấy rõ nguyên lý.
5. Chuẩn bị
- Hai mạch ESP32 DevKit V1 30 chân (hoặc bản dùng chip CH340).
- Hai module chuyển đổi RS-485 dùng IC MAX485 (loại có các chân RO, RE, DE, DI và cọc A, B).
- Điện trở 2,2 kΩ và 3,3 kΩ cho mạch chia áp ở chân RO, mỗi mạch một cặp.
- Một đoạn dây đôi xoắn để nối A với A, B với B.
- Breadboard và dây cắm.
- Arduino IDE 2.x với gói esp32 by Espressif Systems (bài dùng phiên bản 3.3.10). Không cần cài thêm thư viện.
6. Sơ đồ nối
- Mức điện áp: MAX485 cần nguồn 5 V nên chân RO phát ra mức 5 V. Chân này phải qua mạch chia áp 2,2 kΩ và 3,3 kΩ trước khi vào RX2 của ESP32. Chân DI, DE, RE nhận mức 3,3 V của ESP32 nên nối thẳng.
- Nếu dùng module chạy 3,3 V (IC MAX3485 hoặc SP3485) thì cấp 3V3 và nối thẳng RO vào RX2.
- Mỗi module MAX485 thông dụng có sẵn một điện trở 120 Ω giữa A và B. Với hai module thì đúng bằng hai điện trở đầu cuối cần có. Khi mắc ba module trở lên, tháo điện trở này ở các module nằm giữa.
- Nối thêm GND giữa hai mạch nếu chúng dùng nguồn khác nhau.
7. Chương trình hỏi đáp qua RS-485
Nạp cùng một chương trình cho hai mạch, chỉ khác hằng LA_NUT_CHU: một mạch đặt true, mạch kia đặt false. Nút chủ mỗi giây gửi một dòng DOC n rồi chờ trả lời tối đa 300 ms. Nút tớ chỉ phát khi nhận được câu hỏi.
/*
PYWORLD.VN - RS-485 với ESP32 và module MAX485: hai nút hỏi đáp trên một cặp dây, tự điều khiển chiều truyền
Nạp chương trình cho hai mạch: một mạch đặt LA_NUT_CHU = true (nút hỏi), mạch kia đặt false (nút trả lời)
DI nối TX2 (GPIO17), RO nối RX2 (GPIO16), DE và RE nối chung vào D4; A nối A, B nối B giữa hai module
*/
#define RX2_PIN 16
#define TX2_PIN 17
#define DE_RE_PIN 4 // Mức cao: phát lên đường dây; mức thấp: nghe
const bool LA_NUT_CHU = true; // Đổi thành false khi nạp cho mạch thứ hai
const uint32_t TOC_DO_BAUD = 9600;
const unsigned long THOI_GIAN_CHO_TRA_LOI_MS = 300;
const size_t DO_DAI_TOI_DA = 60;
// Gửi một dòng: chiếm đường dây, gửi, chờ gửi hết rồi nhả đường dây ngay
void guiDong(const String &dong) {
digitalWrite(DE_RE_PIN, HIGH);
Serial2.print(dong);
Serial2.print('\n');
Serial2.flush(); // Chờ đến khi bit cuối cùng rời khỏi chân TX
digitalWrite(DE_RE_PIN, LOW);
}
// Chờ một dòng kết thúc bằng ký tự xuống dòng; trả về false nếu quá thời gian
bool nhanDong(String &dong, unsigned long thoiGianChoMs) {
dong = "";
unsigned long batDau = millis();
while (millis() - batDau < thoiGianChoMs) {
while (Serial2.available()) {
char kyTu = Serial2.read();
if (kyTu == '\n') {
dong.trim();
return true;
}
if (dong.length() < DO_DAI_TOI_DA) {
dong += kyTu;
}
}
delay(1);
}
return false;
}
void setup() {
Serial.begin(115200);
pinMode(DE_RE_PIN, OUTPUT);
digitalWrite(DE_RE_PIN, LOW); // Mặc định ở chế độ nghe để không chiếm đường dây
Serial2.begin(TOC_DO_BAUD, SERIAL_8N1, RX2_PIN, TX2_PIN);
Serial.printf("\nRS-485 %u baud, vai tro: %s\n", (unsigned)TOC_DO_BAUD, LA_NUT_CHU ? "nut chu (hoi)" : "nut to (tra loi)");
}
void loop() {
static uint32_t soLan = 0;
String dong;
if (LA_NUT_CHU) {
// Nút chủ: hỏi rồi chờ trả lời trong thời gian giới hạn
while (Serial2.available()) {
Serial2.read(); // Bỏ dữ liệu cũ còn sót trong bộ đệm
}
soLan++;
guiDong("DOC " + String(soLan));
if (nhanDong(dong, THOI_GIAN_CHO_TRA_LOI_MS)) {
Serial.printf("Hoi lan %u, tra loi: %s\n", (unsigned)soLan, dong.c_str());
} else {
Serial.printf("Hoi lan %u: khong co tra loi\n", (unsigned)soLan);
}
delay(1000);
} else {
// Nút tớ: chỉ lên tiếng khi được hỏi
if (nhanDong(dong, 1000) && dong.startsWith("DOC")) {
delay(5); // Chờ nút chủ nhả đường dây rồi mới trả lời
guiDong("OK " + dong.substring(4) + " " + String(millis() / 1000) + "s");
Serial.printf("Nhan \"%s\", da tra loi\n", dong.c_str());
}
}
}
8. Kết quả mong đợi
- Mở Serial Monitor của hai mạch ở 115200 baud. Nút chủ in câu trả lời của từng lần hỏi; nút tớ in câu hỏi đã nhận.
- Rút một dây A hoặc B: nút chủ báo
khong co tra loi. Cắm lại, việc hỏi đáp tiếp tục bình thường. - Thay đoạn dây ngắn bằng một cuộn dây đôi xoắn dài vài chục mét: kết quả không đổi. Đây là điều UART mức 3,3 V không làm được.
9. Xử lý lỗi thường gặp
| Hiện tượng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| Không bao giờ có trả lời | Nối ngược A với B, sai tốc độ baud, hoặc cả hai mạch cùng đặt là nút chủ | Đổi chéo hai dây; kiểm tra hằng LA_NUT_CHU và tốc độ baud |
| Mất byte cuối của mỗi dòng | Hạ chân DE trước khi gửi xong | Gọi Serial2.flush() trước khi hạ DE |
| Câu trả lời bị hỏng phần đầu | Nút tớ trả lời khi nút chủ chưa kịp nhả đường dây | Giữ khoảng chờ vài mili giây trước khi nút tớ trả lời |
| Nhận được byte lạ khi không ai phát | Thiếu điện trở phân cực nên đường dây thả nổi | Dùng module có sẵn điện trở phân cực, hoặc thêm điện trở kéo A lên nguồn và kéo B xuống GND |
| Chạy với dây ngắn, lỗi với dây dài | Thiếu điện trở đầu cuối, dây không xoắn, tốc độ quá cao | Đặt 120 Ω ở hai đầu, dùng dây đôi xoắn, giảm tốc độ baud |
| ESP32 nóng hoặc chân RX2 hỏng | Chân RO mức 5 V nối thẳng vào ESP32 | Thêm mạch chia áp, hoặc dùng module RS-485 chạy 3,3 V |
10. Xem thêm
- ESP32 điều khiển module relay 4 kênh an toàn: phần đầu ra thường đi cùng một nút RS-485.
- Mạch hạ áp LM2596 và MP1584: lấy 5 V từ nguồn 12 V hoặc 24 V của tủ điện.
- Các mạch ESP32 tại PYWORLD: danh mục ESP32.
Xem thêm