| 協議 | 特點 | 典型應用 |
|---|---|---|
| RS232 | 全雙工、點對點、短距離 (15m)、單端信號 | Modem、PLC、儀器儀表、舊式設備 |
| RS485 | 半雙工、多點總線、長距離 (1200m)、差分信號 | 工業控制、多設備串聯 |
| RS422 | 全雙工、差分、長距離、點對點 | 高速數據傳輸 |
| USB | 熱插拔、高速、複雜協議棧 | 現代外設、虛擬串口 (CDC/ACM) |
本文聚焦RS232,Java 透過 javax.comm (Java Communications API) 操作。
| 腳位 | 名稱 | 方向 | 功能 |
|---|---|---|---|
| 2 | RXD | 輸入 | 接收資料 |
| 3 | TXD | 輸出 | 傳送資料 |
| 5 | GND | — | 訊號地 |
| 7 | RTS | 輸出 | 請求傳送 (硬體流控) |
| 8 | CTS | 輸入 | 清除傳送 (硬體流控) |
設備 A (DTE) 設備 B (DTE)
TXD (3) ──────────→ RXD (2)
RXD (2) ←────────── TXD (3)
GND (5) ────────── GND (5)
注意:若設備內部已交叉 TX/RX,則 2-2, 3-3, 5-5 直連即可
萬用表測量:TXD 對 GND 通常有 -3V 至 -15V 負電壓(空閒態標記)。
comm.jar → JRE/lib/ext/ (擴展庫)win32com.dll → 應用程式工作目錄 (user.dir) 或 JRE/bin/javax.comm.properties → JRE/lib/ (驅動配置:Driver=com.sun.comm.Win32Driver)官方不提供 Linux 版,可用 RXTX 或 jSerialComm (現代、跨平台、無需安裝)。
Enumeration<CommPortIdentifier> ports = CommPortIdentifier.getPortIdentifiers();
while (ports.hasMoreElements()) {
CommPortIdentifier portId = ports.nextElement();
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
System.out.println("發現串口: " + portId.getName()); // COM1, COM2, /dev/ttyS0
}
}
CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier("COM1");
CommPort commPort = portId.open("MyApp", 2000); // 2秒超時
SerialPort serialPort = (SerialPort) commPort;
// 參數配置:9600, 8N1
serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,
SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
// 獲取流
InputStream in = serialPort.getInputStream();
OutputStream out = serialPort.getOutputStream();
| 模式 | 輪詢 | 事件驅動 |
|---|---|---|
| 原理 | 循環 in.read() | 實作 SerialPortEventListener |
| 執行緒 | 需獨立執行緒 | JVM 回調執行緒 |
| CPU 佔用 | 高 (需 sleep) | 低 |
| 即時性 | 依賴 sleep 間隔 | 即時 |
| 複雜度 | 低 | 中 |
serialPort.addEventListener(new SerialPortEventListener() {
@Override
public void serialEvent(SerialPortEvent event) {
if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
byte[] buffer = new byte[1024];
int len = in.read(buffer);
if (len > 0) {
String data = new String(buffer, 0, len, "UTF-8");
processData(data);
}
} catch (IOException e) { handleError(e); }
}
}
});
serialPort.notifyOnDataAvailable(true); // 開啟事件通知
String cmd = "AT\r\n";
out.write(cmd.getBytes("ASCII"));
out.flush();
// 正確順序
in.close();
out.close();
commPort.close(); // 釋放串口佔用權
PortInUseException → 確保關閉其他終端程式 (PuTTY, SecureCRT)new String(bytes, "GBK"),避免平台預設編碼差異serialPort.setInputBufferSize()、setOutputBufferSize()win32com.dll 版本不匹配 → 更新 JDK/Comm API 版本// Maven: com.fazecast:jSerialComm:2.10.4
SerialPort[] ports = SerialPort.getCommPorts();
SerialPort port = ports[0];
port.openPort();
port.setBaudRate(9600);
port.setNumDataBits(8);
port.setNumStopBits(1);
port.setParity(0);
port.addDataListener(new SerialPortDataListener() {
@Override
public int getListeningEvents() { return SerialPort.LISTENING_EVENT_DATA_AVAILABLE; }
@Override
public void serialEvent(SerialPortEvent event) {
byte[] buffer = new byte[event.getReceivedData().length];
int numRead = event.getReceivedData(buffer);
System.out.println("收到: " + new String(buffer, 0, numRead));
}
});
優勢:純 Java、零原生庫、跨平台、Maven 直接依賴、API 現代化。
Java 串列通訊核心在於:正確安裝 Comm API/RXTX/jSerialComm → 參數雙端一致 → 選擇輪詢/事件模式 → 處理編碼/流控/緩衝。現代專案首選 jSerialComm,避開原生庫安裝地獄。理解 RS232 硬體層特性,是排查通訊異常的關鍵。