前言:termios——串口配置的「控制面板」
在 Linux/Unix 系統中,struct termios 是終端/串口屬性配置的核心數據結構。掌握其四大標志組 (c_iflag/oflag/cflag/lflag) 及控制字符數組 c_cc,是編寫健壯串口通信程序、Modbus 主站、GPS 解析器、調試終端的前提。本文系統梳理每個字段含義、常用宏定義、配置函數族 (tcgetattr/setattr 等)、波特率設置、cfmakeraw 原始模式一鍵配置。
一、 termios 結構體定義
struct termios {
tcflag_t c_iflag; // 輸入模式標志
tcflag_t c_oflag; // 輸出模式標志
tcflag_t c_cflag; // 控制模式標志 (波特率、數據位、校驗位、停止位)
tcflag_t c_lflag; // 本地模式標志 (規範/非規範、回顯、信號)
cc_t c_cc[NCCS]; // 控制字符數組 (VMIN, VTIME, VEOF, VINTR...)
};
二、 四大標志組詳細字段表
1. c_iflag:輸入處理模式
| 宏 | 功能 |
| IGNBRK | 忽略 BREAK 條件 |
| BRKINT | BREAK 產生 SIGINT (若未 IGNBRK) |
| IGNPAR | 忽略奇偶校驗錯誤 |
| PARMRK | 標記奇偶校驗錯誤 (前綴 0xFF 0x00) |
| INPCK | 啟用輸入奇偶校驗 |
| ISTRIP | 剝離第 8 位 (7 位數據模式) |
| INLCR | NL → CR 映射 |
| IGNCR | 忽略 CR |
| ICRNL | CR → NL 映射 (終端常用,Enter 發送 \r 轉 \n) |
| IXON | 啟用輸出軟件流控 (XON/XOFF) |
| IXOFF | 啟用輸入軟件流控 |
| IMAXBEL | 輸入緩衝區滿發 BEL |
2. c_oflag:輸出處理模式
| 宏 | 功能 |
| OPOST | 啟用輸出後處理 (其它標志生效前提) |
| ONLCR | NL → CR+NL (終端換行顯示標準) |
| OCRNL | CR → NL |
| TABDLY | 製表符輸出延遲 (TAB0~TAB3) |
3. c_cflag:硬件控制模式 (最關鍵)
| 宏 | 功能 |
| CBAUD / CBAUDEX | 波特率掩碼 (配合 cfsetispeed/ospeed) |
| CSIZE | 數據位掩碼:CS5/CS6/CS7/CS8 |
| CSTOPB | 2 停止位 (未置則 1 位) |
| CREAD | 啟用接收器 (必置) |
| PARENB | 啟用奇偶校驗 |
| PARODD | 奇校驗 (未置則偶校驗) |
| HUPCL | 最後關閉時掛斷調制解調器 |
| CLOCAL | 忽略調制解調器狀態線 (直連串口必置) |
| CRTSCTS | 硬件流控 (RTS/CTS) |
4. c_lflag:本地/行規範模式
| 宏 | 功能 |
| ISIG | 識別 INTR/QUIT/SUSP 產生信號 |
| ICANON | 規範模式 (行緩衝、編輯、EOF) |
| ECHO | 回顯輸入字符 |
| ECHOE | ERASE 刪除字符 / WERASE 刪除單詞 |
| ECHOK | KILL 刪除整行 |
| ECHONL | 即使無 ECHO 也回顯 NL |
| TOSTOP | 後台寫入發送 SIGTTOU |
三、 核心配置函數族
1. tcgetattr / tcsetattr:屬性讀寫
int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
optional_actions:TCSANOW 立即生效、TCSADRAIN 等輸出完成、TCSAFLUSH 等輸出完成並刷新輸入。
- **模式**:讀取 → 修改 → 寫回,保留未修改字段。
2. 流控與斷續函數
tcsendbreak(fd, duration):發送 BREAK 信號。
tcdrain(fd):等待輸出緩衝區排空。
tcflush(fd, queue_selector):TCIFLUSH 刷輸入、TCOFLUSH 刷輸出、TCIOFLUSH 雙刷。
tcflow(fd, action):TCOOFF 停止輸出、TCOON 恢復、TCIOFF 發 XOFF、TCION 發 XON。
3. 波特率便捷函數
speed_t cfgetospeed(const struct termios *);
int cfsetospeed(struct termios *, speed_t);
speed_t cfgetispeed(const struct termios *);
int cfsetispeed(struct termios *, speed_t);
- 常用常量:
B9600, B115200, B921600 等。
B0:掛斷 (降低 DTR/RTS)。
- 修改後必須
tcsetattr 才生效。
4. cfmakeraw:一鍵進入原始模式
// 等效手動配置
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
termios_p->c_cflag &= ~(CSIZE|PARENB);
termios_p->c_cflag |= CS8; // 8 數據位,無校驗,1 停止位
**原始模式特點**:字節流直通、無回顯、無行緩衝、無信號干擾、無流控,適合二進制協議 (Modbus RTU、自定義幀)。
四、 實戰配置模板:115200 8N1 原始模式
int setup_serial(int fd) {
struct termios opt;
if (tcgetattr(fd, &opt) < 0) return -1;
cfmakeraw(&opt); // 基礎原始模式
// 設置波特率
cfsetispeed(&opt, B115200);
cfsetospeed(&opt, B115200);
// 8N1 顯式確認
opt.c_cflag &= ~CSIZE;
opt.c_cflag |= CS8; // 8 數據位
opt.c_cflag &= ~PARENB; // 無校驗
opt.c_cflag &= ~CSTOPB; // 1 停止位
// 必置標志
opt.c_cflag |= (CLOCAL | CREAD); // 忽略 Modem 線、啟用接收
// 非規範讀取超時設置 (100ms 無數據返回)
opt.c_cc[VMIN] = 0;
opt.c_cc[VTIME] = 10; // 0.1s * 10 = 1s (實際單位 0.1s)
if (tcsetattr(fd, TCSANOW, &opt) < 0) return -1;
tcflush(fd, TCIOFLUSH); // 清空殘留數據
return 0;
}
五、 避坑指南
- 打開串口必用
O_RDWR | O_NOCTTY | O_NDELAY:非控制終端、非阻塞 (配合 fcntl 切換阻塞)。
- 波特率設置順序:先
cfsetispeed/ospeed,再 tcsetattr。
VMIN/VTIME` 交互複雜:VMIN=0, VTIME>0` 為帶超時非阻塞讀;VMIN>0, VTIME=0` 為阻塞等滿。
- 權限問題:需加入
dialout 組或 chmod 666 /dev/ttyUSB0。
- 殘留數據:配置前後
tcflush(fd, TCIOFLUSH),防舊數據干擾。
六、 總結
struct termios 是串口編程的「基因密碼」。熟記四大標志組核心宏、掌握 tcgetattr/setattr/cfmakeraw 配置流程、理解 VMIN/VTIME 讀取語義、養成「讀取-修改-寫回」習慣,即可從容應對各類串口通信場景。