Giao thức truyền thông trên STM32 (Phần 6): RS-485 – tín hiệu vi sai, kết cuối và điều khiển chiều DE
RS-485 trên STM32 từ nguyên lý tín hiệu vi sai, đi dây bus với điện trở kết cuối và bias (có bảng tính) đến điều khiển chiều truyền bằng chân DE/RE, bằng GPIO hoặc Driver Enable phần cứng. Phần 6 của series giao thức truyền thông trên STM32.

Ghi chú: sơ đồ trong bài do Capitlab vẽ để minh hoạ. Số liệu trong các bảng là giá trị tính theo công thức, cột "Đo thực tế" sẽ được cập nhật khi đo trên phần cứng. Mã nguồn soạn theo tài liệu HAL và datasheet, chưa chạy thử trên phần cứng, nên hãy kiểm tra lại trước khi dùng cho sản phẩm.
RS-485 không phải một giao thức hoàn chỉnh mà chỉ là chuẩn tầng vật lý: nó quy định mức điện áp và cách đi dây. Dữ liệu chạy bên trên thường vẫn là UART, và trên UART lại là giao thức ứng dụng như Modbus RTU (phần 7). RS-485 có mặt khắp nơi trong tự động hoá vì nó đi được xa, chịu nhiễu tốt, và cho nhiều thiết bị dùng chung một đôi dây.
Bài này nói về ba việc phải làm đúng khi đưa RS-485 lên STM32: đi dây với điện trở kết cuối và bias, nối transceiver, và chuyển chiều phát/nhận đúng lúc để không cắt mất byte cuối.
1. Nguyên lý
RS-232 đo điện áp so với GND, nên nhiễu và chênh lệch GND giữa hai thiết bị đi thẳng vào dữ liệu. RS-485 truyền hai tín hiệu ngược pha trên một cặp dây xoắn, và bên nhận chỉ nhìn hiệu giữa hai dây. Nhiễu bám vào hai dây gần như giống nhau (nhiễu chế độ chung), nên khi trừ cho nhau thì mất đi.
Hình 1. Nhiễu chung trên A và B bị triệt tiêu ở hiệu A − B. Bên nhận quyết định mức logic theo dấu của A − B, với ngưỡng khoảng ±200 mV.
Ở tốc độ thấp (tới khoảng 100 kbit/s), RS-485 đi được khoảng 1200 m. Tốc độ càng cao thì càng ngắn, ở 10 Mbit/s chỉ còn vài chục mét. Đây là con số kinh nghiệm hay được trích, giới hạn thật phụ thuộc cáp và transceiver.
Đặc tả gốc cho phép 32 tải đơn vị trên một bus. Transceiver tải nhỏ (1/4 hoặc 1/8 tải đơn vị) cho phép gắn nhiều node hơn.
Cấu hình hay gặp là bán song công: chỉ hai dây A và B, mỗi thời điểm chỉ một node được phát, và transceiver có chân DE (Driver Enable) và RE (Receiver Enable) để đổi chiều. Cấu hình song công với hai cặp dây cũng có nhưng ít gặp hơn.
Mỗi hãng đặt tên dây một kiểu: có hãng ghi D+ và D−, có hãng ghi A và B ngược với hãng khác. Nối đúng sơ đồ mà không nhận được gì thì thử đảo hai dây.
2. Đi dây
Hình 2. Bus RS-485 điển hình. Hai điện trở kết cuối ở hai đầu và một cặp điện trở bias ở một điểm (thường ở master).
Dùng cáp xoắn đôi có trở kháng đặc tính khoảng 120 Ω, đi nối tiếp từ node này sang node kia (daisy-chain). Tránh đi dây hình sao và tránh nhánh rẽ dài.
Đặt điện trở kết cuối 120 Ω ở hai đầu bus, và chỉ ở hai đầu. Khi tắt nguồn, đo giữa A và B phải được khoảng 60 Ω.
Lúc không node nào phát, bus thả nổi và bộ thu có thể đọc ra ký tự rác. Cặp điện trở bias (fail-safe) kéo A lên Vcc và B xuống GND để bus rảnh luôn ở mức "1", tức A − B lớn hơn +200 mV. Cả bus chỉ cần một cặp, thường đặt ở master. Nhiều transceiver đời mới có sẵn tính năng fail-safe bên trong.
Các node phải có GND tham chiếu chung. Trong môi trường công nghiệp, chênh lệch GND giữa các tủ có thể lớn, khi đó nên dùng transceiver cách ly.
Tính điện trở bias
Điện áp trên bus khi rảnh: VAB = Vcc × Rt / (2 × Rb + Rt), trong đó Rt là điện trở kết cuối tương đương (hai điện trở 120 Ω song song là 60 Ω) và Rb là điện trở bias mỗi bên. VAB cần lớn hơn ngưỡng +200 mV và còn dư. Bảng tính, chưa kể tải của các bộ thu vốn còn kéo VAB xuống thêm:
| Vcc | Rb = 390 Ω | Rb = 470 Ω | Rb = 560 Ω | Rb = 680 Ω |
|---|---|---|---|---|
| 5 V | 357 mV | 300 mV | 254 mV | 211 mV |
| 3,3 V | 236 mV | 198 mV | 168 mV | 139 mV |
Với Vcc = 3,3 V, Rb = 560 Ω hay 680 Ω không vượt được ngưỡng 200 mV. Cần Rb khoảng 390 Ω hoặc nhỏ hơn, tuỳ số node. Đổi lại, Rb càng nhỏ thì bus càng tốn dòng khi rảnh. Datasheet transceiver thường có khuyến nghị cụ thể, nên đọc trước khi chọn.
3. Nối với STM32
Transceiver (MAX485, SP3485, THVD1500, ADM2587E cách ly...) có bốn chân phía MCU:
| Chân | Tên đầy đủ | Nối với |
|---|---|---|
| RO | Receiver Output | RX của STM32 |
| DI | Driver Input | TX của STM32 |
| DE | Driver Enable | GPIO (cao là phát) |
| RE | Receiver Enable (tích cực thấp) | Thường nối chung với DE |
Khi DE và RE nối chung vào một GPIO, mức cao là phát (DE bật, RE tắt), mức thấp là nhận. Tiện thêm một điểm: lúc phát, bộ thu tắt nên node không nghe lại tiếng vọng của chính nó.
Transceiver 3,3 V (SP3485, THVD1500) nối thẳng với STM32. Transceiver 5 V như MAX485 xuất RO ở mức 5 V, cần xem chân RX của STM32 có chịu được không, thường thì thêm mạch chuyển mức hoặc cầu phân áp.
4. Mã nguồn
4.1. Điều khiển DE bằng GPIO
#define RS485_TX_EN() HAL_GPIO_WritePin(DE_GPIO_Port, DE_Pin, GPIO_PIN_SET)
#define RS485_RX_EN() HAL_GPIO_WritePin(DE_GPIO_Port, DE_Pin, GPIO_PIN_RESET)
void rs485_send(const uint8_t *p, uint16_t n)
{
RS485_TX_EN();
HAL_UART_Transmit_IT(&huart2, (uint8_t *)p, n);
}
// Chỉ trả về chế độ nhận khi byte cuối đã ra khỏi thanh ghi dịch (TC), không phải TXE
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == &huart2) RS485_RX_EN();
}
Hình 3. TXE xảy ra khi byte cuối vừa chuyển vào thanh ghi dịch, byte đó còn phải phát hết. TC xảy ra khi bit stop cuối đã ra khỏi chân. Hạ DE phải ở TC.
Khi dùng HAL_UART_Transmit_IT hoặc DMA, HAL gọi HAL_UART_TxCpltCallback ở ngắt TC (truyền xong hoàn toàn), nên hạ DE trong callback này là đúng. Nếu tự viết ngắt và hạ DE ngay khi thanh ghi dữ liệu trống (TXE), byte cuối còn nằm trong thanh ghi dịch sẽ bị cắt.
Bảng dưới là thời gian tính cho một byte 8N1. Ở tốc độ cao, chuyển chiều sớm chừng chục µs là đủ mất cả byte:
| Baudrate | Thời gian 1 byte (10 bit) |
|---|---|
| 9 600 | 1041,7 µs |
| 19 200 | 520,8 µs |
| 115 200 | 86,8 µs |
| 921 600 | 10,85 µs |
| 1 000 000 | 10,0 µs |
4.2. Nhận lại sau khi phát
Hạ DE xong là thiết bị phải nhận được ngay. Nếu dùng cách nhận bằng DMA và IDLE ở phần 1, cứ để phần nhận chạy liên tục, chỉ đổi chiều transceiver. Với DE và RE nối chung, bộ thu tắt trong lúc phát nên bộ đệm nhận không bị lẫn tiếng vọng.
4.3. Driver Enable phần cứng của USART
Nhiều dòng STM32 có chân USARTx_DE: phần cứng tự bật transceiver trong lúc phát, với thời gian đệm trước và sau khung cấu hình được. Trong CubeMX, bật RS485 Hardware Flow Control (hoặc khởi tạo bằng HAL_RS485Ex_Init), chọn cực tính DE và thời gian assertion/de-assertion, đơn vị 1/16 bit, từ 0 đến 31. Cách này chính xác tới từng bit và không phụ thuộc độ trễ ngắt, nên chip có hỗ trợ thì dùng.
// Khởi tạo thay cho HAL_UART_Init (ví dụ):
if (HAL_RS485Ex_Init(&huart2, UART_DE_POLARITY_HIGH, 8, 8) != HAL_OK) Error_Handler();
// Sau đó chỉ cần gửi bình thường, phần cứng tự bật tắt DE:
HAL_UART_Transmit(&huart2, buf, len, 100);
5. Lỗi thường gặp
| Triệu chứng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| Nhận được rác khi không ai phát | Thiếu điện trở bias, bus thả nổi | Thêm cặp bias hoặc dùng transceiver fail-safe |
| Mất byte cuối của gói | Hạ DE ở TXE thay vì TC | Hạ DE ở HAL_UART_TxCpltCallback hoặc dùng USART_DE phần cứng |
| Hai node phát cùng lúc, dữ liệu hỏng | RS-485 không có phân xử như CAN | Dùng giao thức hỏi đáp master-slave để chỉ một node phát |
| Lỗi khi tốc độ cao hoặc bus dài | Thiếu hoặc thừa điện trở kết cuối, nhánh rẽ quá dài | Đo A-B bằng oscilloscope, chỉ để điện trở ở hai đầu |
| Không nhận được dữ liệu dù nối đúng | A và B nối ngược do ký hiệu khác nhau giữa các hãng | Thử đảo A và B |
| Một node làm hỏng cả bus | Node lỗi giữ DE ở mức phát | Thêm cơ chế thời gian chờ (watchdog) hạ DE |
| Nhiễu mạnh, mất khung ngẫu nhiên | Cáp không xoắn, GND chênh lệch | Dùng cáp xoắn có bọc, transceiver cách ly |
6. Lưu ý khi thiết kế
Nhánh rẽ (stub) càng ngắn càng tốt, lý tưởng là bằng 0, tức mọi node nằm ngay trên đường bus.
Trong môi trường công nghiệp, chân A và B cần bảo vệ: điện trở nối tiếp, diode TVS dành cho RS-485, và ống phóng điện khí nếu có nguy cơ sét lan truyền.
Với bus dài và tốc độ cao, chọn transceiver có giới hạn tốc độ sườn (slew-rate limited) để giảm dội, và chỉ chạy tốc độ vừa đủ dùng.
RS-485 không có phân xử, nên giao thức bên trên phải quy định ai được nói và khi nào. Giao thức hỏi đáp như Modbus RTU còn yêu cầu khoảng lặng rõ ràng giữa các khung.
7. Kế hoạch đo thực tế
Cột "Đo thực tế" để trống cho đến khi chạy trên phần cứng.
| Thử nghiệm | Cách đo | Giá trị tính toán | Đo thực tế |
|---|---|---|---|
| Điện trở bus khi tắt nguồn | Đồng hồ vạn năng giữa A và B | Khoảng 60 Ω | (điền sau) |
| Điện áp vi sai A-B khi rảnh, có và không có bias | Oscilloscope vi sai hoặc hai kênh trừ nhau | Theo bảng ở mục 2 | (điền sau) |
| Dạng sóng ở 9600, 115200 và 1 Mbaud, có và không có điện trở kết cuối | Oscilloscope, quan sát dội | Dội rõ khi thiếu kết cuối | (điền sau) |
| Độ trễ chuyển chiều DE (GPIO so với phần cứng) | Logic analyzer: DE so với TX | Phần cứng: theo thời gian assertion/de-assertion đặt | (điền sau) |
| Khoảng cách tối đa ở mỗi tốc độ | Tăng dần chiều dài cáp, ghi nhận lúc lỗi bắt đầu | Theo quy tắc kinh nghiệm ở mục 1 | (điền sau) |
| Tỷ lệ lỗi khi có nhiễu (gần động cơ hoặc biến tần) | Gửi 1 triệu byte, đếm lỗi | Không lỗi nếu cáp tốt | (điền sau) |
8. Hỏi đáp
RS-485 khác RS-232 thế nào? RS-232 nối điểm-điểm, tín hiệu tính theo GND (±3 đến ±15 V), tầm ngắn. RS-485 vi sai, nhiều node, đi xa và chịu nhiễu tốt hơn nhiều. Bên trên cả hai thường vẫn là UART.
RS-485 và RS-422 khác gì nhau? RS-422 cũng vi sai nhưng chỉ một bộ phát với nhiều bộ thu, thường dùng hai cặp dây song công. RS-485 cho phép nhiều bộ phát, nên chạy được bán song công trên hai dây.
Bus ngắn, tốc độ thấp có cần kết cuối không? Vài mét ở 9600 baud thường vẫn chạy khi không có kết cuối, nhưng gắn vào vẫn an toàn hơn. Bus dài hoặc tốc độ cao thì bắt buộc.
Chạy Modbus trên RS-485 được không? Được, đây là cách dùng phổ biến nhất. Xem phần 7: Modbus.
Kết luận
Điện trở 120 Ω chỉ đặt ở hai đầu, cả bus một cặp bias. Hạ DE ở ngắt TC, hoặc dùng USART_DE phần cứng, để không cắt byte cuối. RS-485 không phân xử, nên giao thức bên trên phải lo chuyện ai được nói.
Phần 7 dựng Modbus RTU trên RS-485: khung dữ liệu, CRC-16 và khoảng lặng 3,5 ký tự.
Tài liệu tham khảo
- TIA/EIA-485, datasheet transceiver (MAX485, SP3485, THVD1500...)
- Tài liệu STM32 về USART Driver Enable (
HAL_RS485Ex_Init) và reference manual của dòng chip bạn dùng