Linux 串列埠程式設計 深入瞭解 termios

SoldierJazz2019發表於2017-05-18

前言

這一系列串列埠程式設計重點在應用層程式設計,但是在講解原理與相關概念時需要對驅動框架有個基礎的認識。如果只是淺嘗輒止,以後在遇到串列埠驅動與應用層程式除錯難免遇到瓶頸。關於 tty驅動架構參見我的其他部落格:Linux tty驅動程式架構。有時瞭解下底層以及實現方式對於應用層程式設計的幫助也是大有裨益的。之前部落格講了終端,tty,控制檯等概念以及區別,因此在串列埠程式設計相關章節中提及串列埠,有時也會用終端,tty等來替代,注意它們實際上所指是相同的。

在 Linux 中可以通過一組函式呼叫(通用終端介面,簡稱GTI)來控制終端,這組函式呼叫與用於讀寫資料的函式是分離的,這就使得讀寫資料的介面非常簡潔,同時又允許可以對終端或串列埠的行為進行更精細地控制。但由於需要支援大量不同型別的硬體,GTI 中實現的 IO 介面卻不簡潔。

詳解 termios

termios 是在 POSIX 規範中定義的標準介面,它類似與 System V 中的 termio 介面。通過設定 termios 型別的資料結構中的值和使用一小組函式呼叫,就可以對終端介面進行控制。termios 的結構體定義以及相關函式呼叫參見 termios.h 標頭檔案。termios 結構的定義如下:


如定義所示,影響終端的引數按照不同模式分成如下幾類:

  • 輸入模式
  • 輸出模式
  • 控制模式
  • 本地模式
  • 線路規程
  • 特殊控制字元
  • 輸入速率
  • 輸出速率

輸入模式

輸入模式控制輸入資料(終端驅動程式從序列口或鍵盤接收到的字元)在被傳遞給程式之前的處理方式。通過設定 termios 結構中 c_iflag 成員的標誌對它們進行控制。所有的標誌都被定義為巨集,這也是所有終端模式都採用的方法。可用於 c_iflag 成員的巨集如下所示:

  • BRKINT:當在輸入行中檢測到一個終止狀態(連線丟失)時,產生一箇中斷。
  • IGNBRK:忽略輸入行中的終止狀態。
  • ICRNL:將接收到的回車符轉換為新行符。
  • IGNCR:忽略接收到的回車符。
  • INLCR:將接收到的新行符轉換為回車符。
  • IGNPAR:忽略奇偶校驗錯誤的字元。
  • INPCK:對接收到的字元執行奇偶校驗。
  • PARMRK:對奇偶校驗錯誤做出標記。
  • ISTRIP:將所有接收到的字元裁剪為 7 位元位。
  • IXOFF:對輸入啟動軟體流控。
  • IXON:對輸出啟動軟體流控。
如果 BRKINT 和 IGNBRK 標誌都未被設定,則輸入行中的終止狀態就被讀取為 NULL 字元。

輸出模式

輸出模式控制輸出字元的處理方式,即由程式傳送出去的字元在傳遞到序列口或螢幕之前是如何處理的。可用於 c_oflag 成員的巨集如下所示:

  • OPOST:開啟輸出處理功能。
  • ONLCR:將輸出中的換行符轉換為回車/換行符。
  • OCRNL:將輸出中的回車符轉換為新行符。
  • ONOCR:在第0列不輸出回車符。
  • ONLRET:不輸出回車符。
  • OFILL:傳送填充字元以提供延時。
  • OFDEL:用DEL而不是NULL字元作為填充字元。
  • NLDLY:新行符延時選擇。
  • CRDLY:回車符延時選擇。
  • TABDLY:製表符延時選擇。
  • BSDLY:退格符延時選擇。
  • VTDLY:垂直製表符延時選擇。
  • FFDLY:換頁符延時選擇。
如果沒有設定 OPOST,其他標誌都被忽略,輸出模式使用頻率較小。

控制模式


控制模式控制終端的硬體特性。通過設定 termios 結構中 c_cflag 標誌對控制模式進行配置。可用於 c_cflag 成員巨集如下所示:
  • CLOCAL:忽略所有調變解調器的狀態行。
  • CREAD:啟動字元接收器。
  • CS5:發收採用5位資料位。
  • CS6:發收採用6位資料位。
  • CS7:發收採用7位資料位。
  • CS8:發收採用8位資料位。
  • CSTOPB:字元采用兩位停止位。
  • HUPCL:關閉時結束通話調變解調器。
  • PARENB:使能奇偶校驗。
  • PARODD:使用奇校驗。
若設定了 HUPCL,當終端驅動程式檢測到與終端對應的最後一個檔案描述符被關閉時,它將通過設定調變解調器控制線來結束通話線路。控制模式主要用於序列線連線的物理模型中,是在串列埠程式設計中十分重要的標誌。

本地模式


本地模式控制終端的各種特性。通過設定 termios 結構中 c_lflag 標誌對本地模式進行配置。可用於 c_lflag 成員巨集如下所示:
  • ECHO:啟用輸入字元的本地回顯功能。
  • ECHOE:接收到 ERASE 時執行退格、空格、退格的動作組合。
  • ECHOK:接收到 KILL 字元時執行行刪除操作。
  • ECHONL:回顯新行符。
  • ICANON:啟用標準輸入處理。
  • IEXTEN:啟用基於特定實現的函式。
  • ISIG:啟用新號。
  • NOFLSH:禁止清空佇列。
  • TOSTOP:在試圖進行寫操作之前給後臺程式傳送一個訊號。
這裡最重要的標誌是 ECHO 和 ICANON。如果設定了 ICANON 標誌,就啟用標準輸入行處理模式,否則,就啟動非標準模式。

特殊控制字元

特殊控制字元是一些字元組合,如 Ctrl+C,當使用者鍵入這樣的組合鍵,終端會採取特殊處理方式。termios 中 c_cc 陣列將各種特殊字元對映到對應的支援函式。每個字元位置(陣列下標)由對應的巨集定義的。根據終端是否被設定為標準模式(即上節提到的 ICANON 標誌),陣列使用也分為標準與非標準兩種情形。

標準模式可以使用的陣列下標:

  • VEOF:EOF 字元。
  • VEOL:EOL 字元。
  • VERASE:ERASE 字元。
  • VINTR:INTR 字元。
  • VKILL:KILL 字元。
  • VQUIT:QUIT 字元。
  • VSUSP:SUSP 字元。
  • VSTART:START 字元。
  • VSTOP:STOP 字元。
非標準模式可以使用的陣列下標:
  • VINTR:INTR 字元。
  • VMIN:MIN 值。
  • VQUIT:QUIT 字元。
  • VSUSP:SUSP 字元。
  • VTIME:TIME 值。
  • VSTART:START 字元。
  • VSTOP:STOP 字元。
字元的詳細解釋如下標所示:

字元 說明
INTR 該字元使終端驅動程式向與終端相連的程式傳送 SIGINT 訊號
QUIT 該字元使終端驅動程式向與終端相連的程式傳送 SIGQUIT 訊號
ERASE 該字元使終端驅動程式刪除輸入行中的最後一個字元
KILL 該字元使終端驅動程式刪除整個輸入行
EOF 該字元使終端驅動程式將輸入行中的全部字元傳遞給正在讀取輸入的應用程式。若輸入行為空,read為0
EOL 作用類似於行結束符,效果和常用的新行符相同
SUSP 該字元使終端驅動程式向與終端相連的程式傳送SIGSUSP訊號,用於掛起當前應用程式
STOP 字元作用“截流”,即阻止向終端的進一步輸出。用於支援 XON/XOFF 流控,通常被設定為 ASCII 的XOFF
START 重新啟動被 STOP 暫停的輸出,通過被設定為 ASCII 的 XON 字元。

VTIME 和 VMIN

TIME值和MIN值只能用於非標準模式,關於二者的使用詳見部落格:Linux termios 串列埠程式設計之 VTIME與VMIN

SHELL下使用 stty 訪問終端模式

在 shell 下可以使用 stty 可以訪問終端 termios。如:

#列印串列埠裝置 ttyUSB0 設定情況。
root@ubuntu:/# stty -F /dev/ttyUSB0 -a    
#設定 ttyUSB0 為 115200 波特率,8位資料位。
root@ubuntu:/# stty -F /dev/ttyUSB0 ispeed 115200 ospeed 115200 cs8   

在設定成功之後就可以通過 cat、echo 等 shell 命令對裝置進行讀寫了。

本篇重點在於介紹 termios 概念以及詳細模式標誌位等,大部分是不需要記憶的,使用頻率也很低。

關於 Linux 串列埠程式設計的其他文章,可以移步至以下連結:

  1. 《Linux 串列埠程式設計<一> 一些背景》
  2. 《Linux 串列埠程式設計<二> 深入瞭解 termios》
  3. 《Linux 串列埠程式設計<三> 使用termios與API 進行串列埠程式開發》
  4. 《Linux 串列埠程式設計<四> 串列埠裝置程式開發》

如果有其他想法或者疑問可以給我郵件或者評論~:-D



相關文章