Linux 序列埠程式設計核心在於配置 struct termios,透過 tcgetattr/tcsetattr 系統呼叫設定波特率、資料位、停止位、校驗、流控等參數。本文提供完整配置流程、旗標對照表、常用函式封裝與錯誤處理模式。
/dev/ttyS0、/dev/ttyS1 ... (原生 UART,ISA/PCI)。/dev/ttyUSB0、/dev/ttyUSB1 ... (USB 轉序列埠)。/dev/ttyACM0 ... (USB CDC ACM,如 Arduino)。int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0) { perror("open"); exit(1); }
// 設為阻塞模式 (預設)
fcntl(fd, F_SETFL, 0);
| 成員 | 常用旗標 | 說明 |
|---|---|---|
c_cflag (控制模式) | CBAUD (波特率)、CSIZE (資料位)、CSTOPB (停止位)、PARENB (啟用校驗)、PARODD (奇校驗)、HUPCL (關閉時掛斷)、CLOCAL (忽略載波)、CREAD (啟用接收)、CRTSCTS (硬體流控 RTS/CTS) | 核心通訊參數 |
c_iflag (輸入模式) | IGNBRK、BRKINT、IGNPAR、PARMRK、INPCK (啟用輸入校驗)、ISTRIP (去除第 8 位)、IXON/IXOFF/IXANY (軟體流控 XON/XOFF) | 輸入處理 |
c_oflag (輸出模式) | OPOST (啟用輸出處理)、ONLCR (NL→CR-NL) | 通常清零 (raw output) |
c_lflag (本地模式) | ICANON (規範模式)、ECHO、ECHOE、ISIG (信號字元) | 序列埠通常關閉 (raw mode) |
c_cc[NCCS] (控制字元) | VMIN (最少讀取字元)、VTIME (讀取逾時 0.1s 單位) | 非規範模式讀取行為 |
struct termios oldtio, newtio;
/* 1. 保存原配置 */
if (tcgetattr(fd, &oldtio) != 0) { perror("tcgetattr"); exit(1); }
/* 2. 清零新配置 */
memset(&newtio, 0, sizeof(newtio));
/* 3. 啟用接收與本地連線 */
newtio.c_cflag |= CLOCAL | CREAD;
/* 4. 設定波特率 (輸入/輸出同速) */
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
/* 5. 設定資料位 8 位 */
newtio.c_cflag &= ~CSIZE; // 清除資料位遮罩
newtio.c_cflag |= CS8;
/* 6. 設定無奇偶校驗 (8N1) */
newtio.c_cflag &= ~PARENB; // 關閉校驗
newtio.c_iflag &= ~INPCK; // 關閉輸入校驗
/* 7. 設定停止位 1 位 */
newtio.c_cflag &= ~CSTOPB;
/* 8. 關閉硬體流控 */
newtio.c_cflag &= ~CRTSCTS;
/* 9. 關閉軟體流控 */
newtio.c_iflag &= ~(IXON | IXOFF | IXANY);
/* 10. 原始模式 (關閉規範、回顯、信號) */
newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/* 11. 原始輸出 */
newtio.c_oflag &= ~OPOST;
/* 12. 讀取行為: 非阻塞輪詢 (VMIN=0, VTIME=0) 或阻塞等待 (VMIN=1, VTIME=0) */
newtio.c_cc[VMIN] = 0;
newtio.c_cc[VTIME] = 0; // 0.1s * 0 = 0, 非阻塞回傳
/* 13. 清空輸入緩衝區並套用 */
tcflush(fd, TCIFLUSH);
if (tcsetattr(fd, TCSANOW, &newtio) != 0) { perror("tcsetattr"); exit(1); }
int serial_set_baud(int fd, speed_t baud) {
struct termios t;
if (tcgetattr(fd, &t)) return -1;
cfsetispeed(&t, baud);
cfsetospeed(&t, baud);
return tcsetattr(fd, TCSANOW, &t);
}
int serial_set_parity(int fd, int parity) { // 0=N, 1=O, 2=E
struct termios t;
if (tcgetattr(fd, &t)) return -1;
t.c_cflag &= ~(PARENB | PARODD);
t.c_iflag &= ~INPCK;
if (parity == 1) { t.c_cflag |= PARENB | PARODD; t.c_iflag |= INPCK; }
else if (parity == 2) { t.c_cflag |= PARENB; t.c_iflag |= INPCK; }
return tcsetattr(fd, TCSANOW, &t);
}
ssize_t serial_read(int fd, void *buf, size_t count, int timeout_ms) {
struct termios t;
tcgetattr(fd, &t);
t.c_cc[VMIN] = 0;
t.c_cc[VTIME] = timeout_ms / 100; // 0.1s 單位
tcsetattr(fd, TCSANOW, &t);
return read(fd, buf, count);
}
// 寫入
write(fd, "AT\r\n", 4);
// 讀取 (阻塞直到有資料或逾時)
char buf[256];
int n = read(fd, buf, sizeof(buf)-1);
if (n > 0) { buf[n] = '\0'; printf("收到: %s", buf); }
tcsetattr(fd, TCSANOW, &oldtio); // 還原原配置
close(fd);
read() 立即回傳 0VMIN=0, VTIME=0 非阻塞模式;改 VMIN=1, VTIME=0 阻塞等待。dialout 群組:usermod -aG dialout $USER,重新登入。/dev/serial/by-id/usb-... 符號連結 (固定唯一 ID)。掌握 termios 旗標與標準配置流程,即可在 Linux 上穩健開發序列埠通訊應用 (Modbus RTU、GPS、傳感器、PLC 等)。