RS485 通訊協定是工業自動化領域最廣泛使用的有線通訊標準之一。無論是 PLC、變頻器、感測器陣列還是 SCADA 系統,RS485 幾乎無所不在。它的最大優勢——長距離(最長 1200m)、多站(最多 256 個裝置)、差動訊號抗干擾——讓它成為工廠環境的首選。本文從 MAX485 教學開始,帶你實作 ESP32 RS485 和 STM32 的通訊。
這篇文章會從 RS485 的硬體原理開始,帶你一步步看懂接線、選擇收發器晶片,並在 ESP32(Arduino)和 STM32(HAL) 上實作 RS485 通訊。
什麼是 RS485?
RS485(也稱 TIA-485)是由 Electronic Industries Alliance(EIA) 定義的差動串列通訊標準。它使用兩條線(A 線 / D+ 和 B 線 / D-)傳送差動訊號,而不是像 UART 那樣用單端訊號對地參考。
差動訊號的核心優點:
- 抗共模干擾:外界雜訊同時耦合到 A、B 兩線,電壓差不受影響
- 長距離傳輸:1200m 仍可保持 10Mbps 以上速率(距離越短速度越快)
- 多站匯流排:一條匯流排最多可接 256 個收發器(標準 RS485 驅動器為 32 單位負載)
- 雙向半雙工:同一時間只能一個裝置發送,但所有裝置都能接收
RS485 vs RS232 vs TTL UART
| 特性 | RS485 | RS232 | TTL UART |
|---|---|---|---|
| 訊號方式 | 差動(平衡) | 單端(不平衡) | 單端 |
| 電壓範圍 | ±1.5V ~ ±6V | ±3V ~ ±15V | 0V ~ 3.3V/5V |
| 最大距離 | 1200m | 15m | 1~3m |
| 最大速率 | 50Mbps(短距) | 115.2kbps | 取決於 MCU |
| 多站能力 | 最多 256 個 | 點對點 | 點對點 |
| 共模電壓 | -7V ~ +12V | N/A | N/A |
| 收發器 IC | MAX485 / MAX3485 | MAX232 | 內建於 MCU |
| 典型應用 | 工業網路、Modbus | 舊式 PC COM Port | 晶片間通訊 |
簡單總結:RS232 是 PC 時代的遺產,TTL UART 是晶片內部用的,而 RS485 才是工業現場的主力。
📖 延伸閱讀:SPI 通訊教學 · MQTT 實戰教學 · I2C 通訊教學
RS485 硬體接線
A/B 線(D+/D-)
RS485 使用兩條絞線(Twisted Pair):
- A 線(D+ / Non-inverting):當 A 電壓高於 B 時,邏輯為 1(Mark)
- B 線(D- / Inverting):當 B 電壓高於 A 時,邏輯為 0(Space)
注意不同廠商的標示方式可能不同——有些標 A/B,有些標 D+/D-,有些標 P/N(Positive/Negative)。接線前一定要確認規格書。
終端電阻
在匯流排最遠的兩端各接一顆 120Ω 電阻(A 到 B 之間):
- 防止訊號反射造成資料錯誤
- 確保空閒時差動電壓在負載狀態
- 只在最遠的兩個節點加,中間節點不需要
偏壓電阻
在某些系統中,為了確保匯流排空閒(所有發送器均為高阻抗)時的電位穩定,會在 主站端 加入偏壓電阻:
- A 線接到 Vcc(通常 470Ω ~ 1kΩ)
- B 線接到 GND(通常 470Ω ~ 1kΩ)
收發器晶片:MAX485 與 MAX3485
MCU 的 UART 腳位輸出的是 TTL 電平(0-3.3V 或 0-5V),不能直接接到 RS485 匯流排。你需要一顆 RS485 收發器:
| 晶片 | 電壓 | 速率 | 腳位 |
|---|---|---|---|
| MAX485 | 5V | 2.5Mbps | 8-pin DIP/SOIC |
| MAX3485 | 3.3V | 12Mbps | 8-pin DIP/SOIC |
| MAX1487 | 5V | 2.5Mbps | 8-pin(高 ESD) |
| SN65HVD3082E | 5V | 20Mbps | 8-pin(低功耗) |
對於 ESP32(3.3V 系統),MAX3485 是首選——不需要電平轉換。對於 STM32(5V 容忍腳位),MAX485 也可以直接驅動。
MAX485/MAX3485 腳位
| 腳位 | 名稱 | 說明 |
|---|---|---|
| 1 | RO | Receiver Output → MCU RX |
| 2 | RE | Receiver Enable (Low = 啟用接收) |
| 3 | DE | Driver Enable (High = 啟用發送) |
| 4 | DI | Driver Input ← MCU TX |
| 5 | GND | 接地 |
| 6 | A | Non-inverting output/input (D+) |
| 7 | B | Inverting output/input (D-) |
| 8 | Vcc | 電源(MAX485: 5V / MAX3485: 3.3V) |
關鍵控制腳:DE 和 RE。RS485 是半雙工,發送和接收不能同時進行。通常把 DE 和 RE 接在一起,用一個 GPIO 控制:
- GPIO = HIGH:DE 啟用 → 發送模式(RE 同時被拉高,接收關閉)
- GPIO = LOW:RE 啟用 → 接收模式(DE 同時被拉低,發送關閉)
ESP32 + MAX3485 實作(Arduino)
接線方式
| ESP32 | MAX3485 |
|---|---|
| GPIO17 (TX2) | DI (Pin 4) |
| GPIO16 (RX2) | RO (Pin 1) |
| GPIO4 | DE + RE (Pin 2&3) |
| 3.3V | Vcc (Pin 8) |
| GND | GND (Pin 5) |
A 和 B 腳接到 RS485 匯流排。
程式碼
#include <HardwareSerial.h>
#define TX2_PIN 17
#define RX2_PIN 16
#define RS485_CTRL 4 // DE/RE 控制腳
HardwareSerial rs485(2); // UART2
void setup() {
Serial.begin(115200);
pinMode(RS485_CTRL, OUTPUT);
digitalWrite(RS485_CTRL, LOW); // 預設接收模式
rs485.begin(9600, SERIAL_8N1, RX2_PIN, TX2_PIN);
}
void rs485_send(const uint8_t* data, size_t len) {
digitalWrite(RS485_CTRL, HIGH); // 切換到發送模式
delay(1); // 等待電晶體穩定
rs485.write(data, len);
rs485.flush(); // 等待資料發送完畢
delay(1);
digitalWrite(RS485_CTRL, LOW); // 回到接收模式
}
void loop() {
if (rs485.available()) {
uint8_t buf[64];
size_t len = rs485.readBytes(buf, 64);
Serial.print("收到 ");
Serial.print(len);
Serial.println(" bytes:");
for (size_t i = 0; i < len; i++) {
Serial.printf("0x%02X ", buf[i]);
}
Serial.println();
// 回傳相同資料(Loopback 測試)
rs485_send(buf, len);
}
}
STM32 + MAX485 實作(HAL)
CubeMX 設定
- USART2:非同步模式,Baud Rate 9600,8N1
- PA0:GPIO_Output(DE/RE 控制)
- PA2 (TX)、PA3 (RX) 連接到 MAX485
程式碼
#define RS485_CTRL_PORT GPIOA
#define RS485_CTRL_PIN GPIO_PIN_0
#define RS485_TX_MODE() HAL_GPIO_WritePin(RS485_CTRL_PORT, RS485_CTRL_PIN, GPIO_PIN_SET)
#define RS485_RX_MODE() HAL_GPIO_WritePin(RS485_CTRL_PORT, RS485_CTRL_PIN, GPIO_PIN_RESET)
void RS485_Send(uint8_t* data, uint16_t len) {
RS485_TX_MODE();
HAL_Delay(1);
HAL_UART_Transmit(&huart2, data, len, HAL_MAX_DELAY);
HAL_Delay(1);
RS485_RX_MODE();
}
void RS485_Receive_IT(void) {
RS485_RX_MODE();
static uint8_t rx_byte;
HAL_UART_Receive_IT(&huart2, &rx_byte, 1);
}
// 在回呼函數中處理收到的資料
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART2) {
static uint8_t rx_buf[64];
static uint16_t idx = 0;
rx_buf[idx++] = rx_byte;
// 遇到換行或緩衝區滿,處理一筆完整訊息
if (rx_byte == '
' || idx >= 64) {
RS485_Send(rx_buf, idx); // 回傳(Loopback)
idx = 0;
}
HAL_UART_Receive_IT(&huart2, &rx_byte, 1);
}
}
DE/RE 控制時序圖
RS485 的正確操作離不開 DE/RE 的精準控制。下圖展示了 ESP32 發送一筆資料的全過程:
關鍵時間點:
- t₁:DE 拉高 — 啟用驅動器,MAX485 開始驅動 A/B 線
- t₂:開始發送資料 — A/B 線產生差動電壓
- t₃:資料發送完成 — TX Buffer 已清空
- t₄:DE 拉低 — 關閉驅動器,回到接收模式
注意 t₁→t₂ 和 t₃→t₄ 之間需要 1~2ms 的延遲,確保匯流排穩定。延遲過短會導致資料尾端被截斷,這是 RS485 開發中最常見的 bug。
RS485 常見問題與排錯
1. 資料亂碼或接收不到
- 檢查 Baud Rate:確認所有裝置的鮑率一致
- 檢查 A/B 線是否接反:A 接 A、B 接 B,不要在途中交叉
- 檢查終端電阻:沒有 120Ω 終端電阻時,長線會發生訊號反射
- 檢查 DE/RE 延遲:切換模式後留足夠時間讓收發器穩定
2. 最後一兩個 byte 遺失
這是 DE 切換時序問題的典型症狀。解決方案:
- 在
rs485.flush()或HAL_UART_Transmit()返回後,務必延遲 2ms 以上再將 DE 拉低 - 考慮使用硬體自動方向控制(如 MAX13487 或 ADM2483)
3. 超過 32 個裝置時通訊不穩
標準 RS485 驅動器定義為 1 單位負載(UL),最多驅動 32 UL。超過 32 個裝置時:
- 使用 1/4 UL 收發器(如 MAX1487),可接 128 個
- 使用 1/8 UL 收發器(如 SN65HVD3082E),可接 256 個
- 加入 RS485 中繼器 擴充網路
4. 接地問題
RS485 雖然使用差動訊號,但 共模電壓範圍有限(-7V ~ +12V)。當節點間距離很遠時,地電位差可能超出範圍。解決方案:
- 在匯流排上加一條 第三條接地線
- 使用 隔離型 RS485 收發器(如 ADM2483、ISO3082)
- 使用 RS485 隔離模組(如 B0505S 隔離電源 + 光耦合)
總結
RS485 是工業通訊的基石。它的差動訊號設計讓它能在惡劣的工廠環境中穩定傳輸,而半雙工的特性要求開發者精準掌握 DE/RE 控制時序。
學會 RS485 之後,下一步可以學習 Modbus RTU——這是建立在 RS485 實體層之上的應用層協定,廣泛用於 PLC、感測器和工業儀表。到時候你在 ESP32 和 STM32 上的 RS485 底層程式碼可以直接沿用。
如果你在實作中遇到問題,歡迎留言討論。
文章評論