詳解linux下的串列埠通訊開發

helloxchen發表於2010-11-08
詳解linux下的串列埠通訊開發

序列口是計算機一種常用的介面,具有連線線少,通訊簡單,得到廣泛的使用。常用的串列埠是RS-232-C介面(又稱EIA RS-232-C)它是在1970年由美國電子工業協會(EIA)聯合貝爾系統、調變解調器廠家及計算機終端生產廠家共同制定的用於序列通訊的標準。串列埠通訊指的是計算機依次以位(bit)為單位來傳送資料,序列通訊使用的範圍很廣,在嵌入式系統開發過程中串列埠通訊也經常用到通訊方式之一。

Linux對所有裝置的訪問是透過裝置檔案來進行的,串列埠也是這樣,為了訪問串列埠,只需開啟其裝置檔案即可操作串列埠裝置。在linux系統下面,每一個串列埠裝置都有裝置檔案與其關聯,裝置檔案位於系統的/dev目錄下面。如linux下的/ttyS0/ttyS1分別表示的是串列埠1和串列埠2。下面來詳細介紹linux下是如何使用串列埠的:

1. 串列埠操作需要用到的標頭檔案

#include /*標準輸入輸出定義*/

#include /*標準函式庫定義*/

#include /*Unix 標準函式定義*/

#include

#include

#include /*檔案控制定義*/

#include /*POSIX 終端控制定義*/

#include /*錯誤號定義*/

#include /*字串功能函式*/

2. 串列埠通訊波特率設定

波特率的設定定義在,其包含在標頭檔案裡。

常用的波特率常數如下:

B0-------à0 B1800-------à1800

B50-----à50 B2400------à2400

B75-----à75 B4800------à4800

B110----à110 B9600------à9600

B134----à134.5 B19200-----à19200

B200----à200 B38400------à38400

B300----à300 B57600------à57600

B600----à600 B76800------à76800

B1200---à1200 B115200-----à115200

假定程式中想要設定通訊的波特率,使用cfsetispeed( )cfsetospeed( )函式來操作,獲取波特率資訊是透過cfgetispeed()和cfgetospeed()函式來完成的。比如可以這樣來指定串列埠通訊的波特率:

#include //標頭檔案定義

........

........

.......

struct termios opt /*定義指向termios 結構型別的指標opt*/

/***************以下設定通訊波特率****************/

cfsetispeed(&optB9600 ) /*指定輸入波特率,9600bps*/

cfsetospeed(&optB9600)/*指定輸出波特率,9600bps*/

/************************************************/

.........

..........

一般來說,輸入、輸出的波特率應該是一致的。

3. 串列埠屬性配置

在程式中,很容易配置串列埠的屬性,這些屬性定義在結構體struct termios中。為在程式中使用該結構體,需要包含檔案,該標頭檔案定義了結構體struct termios。該結構體定義如下:

#define NCCS 19

struct termios {

tcflag_t c_iflag; /* 輸入引數 */

tcflag_t c_oflag; /* 輸出引數 */

tcflag_t c_cflag; /* 控制引數*/

tcflag_t c_ispeed; /* 輸入波特率 */

tcflag_t c_ospeed; /* 輸出波特率 */

cc_t c_line; /* 線控制 */

cc_t c_cc[NCCS]; /* 控制字元*/

};

其中成員c_linePOSIX(Portable Operating System Interface for UNIX)系統中不使用。對於支援POSIX終端介面的系統中,對於埠屬性的設定和獲取要用到兩個重要的函式是:

1.int tcsetattrint fdint opt_DE*ptr

該函式用來設定終端控制屬性,其引數說明如下:

l fd:待操作的檔案描述符

l opt_DE:選項值,有三個選項以供選擇:

TCSANOW 不等資料傳輸完畢就立即改變屬性

TCSADRAIN:等待所有資料傳輸結束才改變屬性

TCSAFLUSH:清空輸入輸出緩衝區才改變屬性

l *ptr指向termios結構的指標

函式返回值:成功返回0,失敗返回-1

2.int tcgetattrint fd*ptr

該函式用來獲取終端控制屬性,它把串列埠的預設設定賦給了termios資料資料結構,其引數說明如下:

l fd:待操作的檔案描述符

l *ptr指向termios結構的指標

函式返回值:成功返回0,失敗返回-1

4. 開啟串列埠

在前面已經提到linux下的串列埠訪問是以裝置檔案形式進行的,所以開啟串列埠也即是開啟檔案的操作。函式原型可以如下所示:

int open(“DE_name”,int open_Status

引數說明:

1.DE_name:要開啟的裝置檔名

比如要開啟串列埠1,即為/dev/ttyS0

2.open_Status:檔案開啟方式,可採用下面的檔案開啟模式:

l O_RDONLY:以只讀方式開啟檔案

l O_WRONLY:以只寫方式開啟檔案

l O_RDWR:以讀寫方式開啟檔案

l O_APPEND:寫入資料時新增到檔案末尾

l O_CREATE:如果檔案不存在則產生該檔案,使用該標誌需要設定訪問許可權位mode_t

l O_EXCL:指定該標誌,並且指定了O_CREATE標誌,如果開啟的檔案存在則會產生一個錯誤

l O_TRUNC:如果檔案存在並且成功以寫或者只寫方式開啟,則清除檔案所有內容,使得檔案長度變為0

l O_NOCTTY:如果開啟的是一個終端裝置,這個程式不會成為對應這個埠的控制終端,如果沒有該標誌,任何一個輸入,例如鍵盤中止訊號等,都將影響程式。

l O_NONBLOCK:該標誌與早期使用的O_NDELAY標誌作用差不多。程式不關心DCD訊號線的狀態,如果指定該標誌,程式將一直在休眠狀態,直到DCD訊號線為0

函式返回值:

成功返回檔案描述符,如果失敗返回-1

例如假定以可讀寫方式開啟/dev/ttyS0裝置,就可以這樣操作:

#include //標頭檔案包含

......

......

int fd; /* 檔案描述符 */

fd = open("/dev/ttyS0", O_RDWR | 0_NOCTTY) /*以讀寫方式開啟裝置*/

if(fd == -1)

perror("Can not open Serial_Port 1n")/*開啟失敗時的錯誤提示*/

........

........

5. 串列埠讀操作(接收端)

open函式開啟裝置檔案,函式返回一個檔案描述符(file descriptors,fd),透過檔案描述符來訪問檔案。讀串列埠操作是透過read函式來完成的。函式原型如下:

int read(int fd, *buffer,length)

引數說明:

1.int fd:檔案描述符

2.*buffer:資料緩衝區

3.length:要讀取的位元組數

函式返回值:

讀操作成功讀取返回讀取的位元組數,失敗則返回-1

6. 串列埠寫操作(傳送端)

寫串列埠操作是透過write函式來完成的。函式原型如下:

write(int fd, *buffer,length);

引數說明:

1.fd:檔案描述符

2.*buffer:儲存寫入資料的資料緩衝區

3.length:寫入緩衝去的資料位元組數

函式返回值:

成功返回寫入資料的位元組數,該值通常等於length,如果寫入失敗返回-1

例如:向終端裝置傳送初始化命令

#include //標頭檔案包含

......

......

int n

sbuf[]={Hellothis is a Serial_Port testn }//待傳送資料

int len_send="sizeof"sbuf);//傳送緩衝區位元組數定義

n = write(fd,sbuf,len_send); //寫緩衝區

if(n == -1)

{

printf("Wirte sbuf error.n");

}

......

......

7. 關閉串列埠

對裝置檔案的操作與對普通檔案的操作一樣,開啟操作之後還需要關閉,關閉串列埠用函式close( )來操作,函式原型為:

int close(int fd);

引數說明:

fd:檔案描述符

函式返回值:

成功返回0,失敗返回-1

NAME

termios, tcgetattr, tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow, cfmakeraw, cfgetospeed, cfgetispeed, cfsetispeed, cfsetospeed - 獲取和設定終端屬性,行控制,獲取和設定波特率

SYNOPSIS 總覽

#include <>
#include <>

int tcgetattr(int fd, struct termios *termios_p);

int tcsetattr(int fd, int optional_actions, struct termios *termios_p);

int tcsendbreak(int fd, int duration);

int tcdrain(int fd);

int tcflush(int fd, int queue_selector);

int tcflow(int fd, int action);

int cfmakeraw(struct termios *termios_p);

speed_t cfgetispeed(struct termios *termios_p);

speed_t cfgetospeed(struct termios *termios_p);

int cfsetispeed(struct termios *termios_p, speed_t speed);

int cfsetospeed(struct termios *termios_p, speed_t speed);

DESCRIPTION 描述

termios 函式族提供了一個常規的終端介面,用於控制非同步通訊埠。

這裡描述的大部分屬性有一個 termios_p 型別的引數,它是指向一個 termios 結構的指標。這個結構包含了至少下列成員:

tcflag_t c_iflag;      /* 輸入模式 */
tcflag_t c_oflag; /* 輸出模式 */
tcflag_t c_cflag; /* 控制模式 */
tcflag_t c_lflag; /* 本地模式 */
cc_t c_cc[NCCS]; /* 控制字元 */

c_iflag 標誌常量:

IGNBRK
忽略輸入中的 BREAK 狀態。
BRKINT
如果設定了 IGNBRK,將忽略 BREAK。如果沒有設定,但是設定了 BRKINT,那麼 BREAK 將使得輸入和輸出佇列被重新整理,如果終端是一個前臺程式組的控制終端,這個程式組中所有程式將收到 SIGINT 訊號。如果既未設定 IGNBRK 也未設定 BRKINT,BREAK 將視為與 NUL 字元同義,除非設定了 PARMRK,這種情況下它被視為序列 377 。
IGNPAR
忽略楨錯誤和奇偶校驗錯。
PARMRK
如果沒有設定 IGNPAR,在有奇偶校驗錯或楨錯誤的字元前插入 377 。如果既沒有設定 IGNPAR 也沒有設定 PARMRK,將有奇偶校驗錯或楨錯誤的字元視為 。
INPCK
啟用輸入奇偶檢測。
ISTRIP
去掉第八位。
INLCR
將輸入中的 NL 翻譯為 CR。
IGNCR
忽略輸入中的回車。
ICRNL
將輸入中的回車翻譯為新行 (除非設定了 IGNCR)。
IUCLC
(不屬於 POSIX) 將輸入中的大寫字母對映為小寫字母。
IXON
啟用輸出的 XON/XOFF 流控制。
IXANY
(不屬於 POSIX.1;XSI) 允許任何字元來重新開始輸出。(?)
IXOFF
啟用輸入的 XON/XOFF 流控制。
IMAXBEL
(不屬於 POSIX) 當輸入佇列滿時響零。Linux 沒有實現這一位,總是將它視為已設定。

POSIX.1 中定義的 c_oflag 標誌常量:

OPOST
啟用具體實現自行定義的輸出處理。

其餘 c_oflag 標誌常量定義在 POSIX 1003.1-2001 中,除非另外說明。

OLCUC
(不屬於 POSIX) 將輸出中的小寫字母對映為大寫字母。
ONLCR
(XSI) 將輸出中的新行符對映為回車-換行。
OCRNL
將輸出中的回車對映為新行符
ONOCR
不在第 0 列輸出回車。
ONLRET
不輸出回車。
OFILL
傳送填充字元作為延時,而不是使用定時來延時。
OFDEL
(不屬於 POSIX) 填充字元是 ASCII DEL (0177)。如果不設定,填充字元則是 ASCII NUL。
NLDLY
新行延時掩碼。取值為 NL0NL1
CRDLY
回車延時掩碼。取值為 CR0, CR1, CR2, 或 CR3
TABDLY
水平跳格延時掩碼。取值為 TAB0, TAB1, TAB2, TAB3 (或 XTABS)。取值為 TAB3,即 XTABS,將擴充套件跳格為空格 (每個跳格符填充 8 個空格)。(?)
BSDLY
回退延時掩碼。取值為 BS0BS1。(從來沒有被實現過)
VTDLY
豎直跳格延時掩碼。取值為 VT0VT1
FFDLY
進表延時掩碼。取值為 FF0FF1

c_cflag 標誌常量:

CBAUD
(不屬於 POSIX) 波特率掩碼 (4+1 位)。
CBAUDEX
(不屬於 POSIX) 擴充套件的波特率掩碼 (1 位),包含在 CBAUD 中。

(POSIX 規定波特率儲存在 termios 結構中,並未精確指定它的位置,而是提供了函式 cfgetispeed()cfsetispeed() 來存取它。一些系統使用 c_cflag 中 CBAUD 選擇的位,其他系統使用單獨的變數,例如 sg_ispeedsg_ospeed 。)

CSIZE
字元長度掩碼。取值為 CS5, CS6, CS7, 或 CS8
CSTOPB
設定兩個停止位,而不是一個。
CREAD
開啟接受者。
PARENB
允許輸出產生奇偶資訊以及輸入的奇偶校驗。
PARODD
輸入和輸出是奇校驗。
HUPCL
在最後一個程式關閉裝置後,降低 modem 控制線 (結束通話)。(?)
CLOCAL
忽略 modem 控制線。
LOBLK
(不屬於 POSIX) 從非當前 shell 層阻塞輸出(用於 shl )。(?)
CIBAUD
(不屬於 POSIX) 輸入速度的掩碼。CIBAUD 各位的值與 CBAUD 各位相同,左移了 IBSHIFT 位。
CRTSCTS
(不屬於 POSIX) 啟用 RTS/CTS (硬體) 流控制。

c_lflag 標誌常量:

ISIG
當接受到字元 INTR, QUIT, SUSP, 或 DSUSP 時,產生相應的訊號。
ICANON
啟用標準模式 (canonical mode)。允許使用特殊字元 EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, 和 WERASE,以及按行的緩衝。
XCASE
(不屬於 POSIX; Linux 下不被支援) 如果同時設定了 ICANON,終端只有大寫。輸入被轉換為小寫,除了以 字首的字元。輸出時,大寫字元被字首 ,小寫字元被轉換成大寫。
ECHO
回顯輸入字元。
ECHOE
如果同時設定了 ICANON,字元 ERASE 擦除前一個輸入字元,WERASE 擦除前一個詞。
ECHOK
如果同時設定了 ICANON,字元 KILL 刪除當前行。
ECHONL
如果同時設定了 ICANON,回顯字元 NL,即使沒有設定 ECHO。
ECHOCTL
(不屬於 POSIX) 如果同時設定了 ECHO,除了 TAB, NL, START, 和 STOP 之外的 ASCII 控制訊號被回顯為 ^X, 這裡 X 是比控制訊號大 0x40 的 ASCII 碼。例如,字元 0x08 (BS) 被回顯為 ^H。
ECHOPRT
(不屬於 POSIX) 如果同時設定了 ICANONIECHO,字元在刪除的同時被列印。
ECHOKE
(不屬於 POSIX) 如果同時設定了 ICANON,回顯 KILL 時將刪除一行中的每個字元,如同指定了 ECHOEECHOPRT 一樣。
DEFECHO
(不屬於 POSIX) 只在一個程式讀的時候回顯。
FLUSHO
(不屬於 POSIX; Linux 下不被支援) 輸出被重新整理。這個標誌可以透過鍵入字元 DISCARD 來開關。
NOFLSH
禁止在產生 SIGINT, SIGQUIT 和 SIGSUSP 訊號時重新整理輸入和輸出佇列。
TOSTOP
向試圖寫控制終端的後臺程式組傳送 SIGTTOU 訊號。
PENDIN
(不屬於 POSIX; Linux 下不被支援) 在讀入下一個字元時,輸入佇列中所有字元被重新輸出。(bash 用它來處理 typeahead)
IEXTEN
啟用實現自定義的輸入處理。這個標誌必須與 ICANON 同時使用,才能解釋特殊字元 EOL2,LNEXT,REPRINT 和 WERASE,IUCLC 標誌才有效。

c_cc 陣列定義了特殊的控制字元。符號下標 (初始值) 和意義為:

VINTR
(003, ETX, Ctrl-C, or also 0177, DEL, rubout) 中斷字元。發出 SIGINT 訊號。當設定 ISIG 時可被識別,不再作為輸入傳遞。
VQUIT
(034, FS, Ctrl-) 退出字元。發出 SIGQUIT 訊號。當設定 ISIG 時可被識別,不再作為輸入傳遞。
VERASE
(0177, DEL, rubout, or 010, BS, Ctrl-H, or also #) 刪除字元。刪除上一個還沒有刪掉的字元,但不刪除上一個 EOF 或行首。當設定 ICANON 時可被識別,不再作為輸入傳遞。
VKILL
(025, NAK, Ctrl-U, or Ctrl-X, or also @) 終止字元。刪除自上一個 EOF 或行首以來的輸入。當設定 ICANON 時可被識別,不再作為輸入傳遞。
VEOF
(004, EOT, Ctrl-D) 檔案尾字元。更精確地說,這個字元使得 tty 緩衝中的內容被送到等待輸入的使用者程式中,而不必等到 EOL。如果它是一行的第一個字元,那麼使用者程式的 read() 將返回 0,指示讀到了 EOF。當設定 ICANON 時可被識別,不再作為輸入傳遞。
VMIN
非 canonical 模式讀的最小字元數。
VEOL
(0, NUL) 附加的行尾字元。當設定 ICANON 時可被識別。
VTIME
非 canonical 模式讀時的延時,以十分之一秒為單位。
VEOL2
(not in POSIX; 0, NUL) 另一個行尾字元。當設定 ICANON 時可被識別。
VSWTCH
(not in POSIX; not supported under Linux; 0, NUL) 開關字元。(只為 shl 所用。)
VSTART
(021, DC1, Ctrl-Q) 開始字元。重新開始被 Stop 字元中止的輸出。當設定 IXON 時可被識別,不再作為輸入傳遞。
VSTOP
(023, DC3, Ctrl-S) 停止字元。停止輸出,直到鍵入 Start 字元。當設定 IXON 時可被識別,不再作為輸入傳遞。
VSUSP
(032, SUB, Ctrl-Z) 掛起字元。傳送 SIGTSTP 訊號。當設定 ISIG 時可被識別,不再作為輸入傳遞。
VDSUSP
(not in POSIX; not supported under Linux; 031, EM, Ctrl-Y) 延時掛起訊號。當使用者程式讀到這個字元時,傳送 SIGTSTP 訊號。當設定 IEXTEN 和 ISIG,並且系統支援作業管理時可被識別,不再作為輸入傳遞。
VLNEXT
(not in POSIX; 026, SYN, Ctrl-V) 字面上的下一個。引用下一個輸入字元,取消它的任何特殊含義。當設定 IEXTEN 時可被識別,不再作為輸入傳遞。
VWERASE
(not in POSIX; 027, ETB, Ctrl-W) 刪除詞。當設定 ICANON 和 IEXTEN 時可被識別,不再作為輸入傳遞。
VREPRINT
(not in POSIX; 022, DC2, Ctrl-R) 重新輸出未讀的字元。當設定 ICANON 和 IEXTEN 時可被識別,不再作為輸入傳遞。
VDISCARD
(not in POSIX; not supported under Linux; 017, SI, Ctrl-O) 開關:開始/結束丟棄未完成的輸出。當設定 IEXTEN 時可被識別,不再作為輸入傳遞。
VSTATUS
(not in POSIX; not supported under Linux; status request: 024, DC4, Ctrl-T).

這些符號下標值是互不相同的,除了 VTIME,VMIN 的值可能分別與 VEOL,VEOF 相同。 (在 non-canonical 模式下,特殊字元的含義更改為延時含義。MIN 表示應當被讀入的最小字元數。TIME 是以十分之一秒為單位的計時器。如果同時設定了它們,read 將等待直到至少讀入一個字元,一旦讀入 MIN 個字元或者從上次讀入字元開始經過了 TIME 時間就立即返回。如果只設定了 MIN,read 在讀入 MIN 個字元之前不會返回。如果只設定了 TIME,read 將在至少讀入一個字元,或者計時器超時的時候立即返回。如果都沒有設定,read 將立即返回,只給出當前準備好的字元。) (?)

tcgetattr() 得到與 fd 指向的物件相關的引數,將它們儲存於 termios_p 引用的 termios 結構中。函式可以從後臺程式中呼叫;但是,終端屬性可能被後來的前臺程式所改變。

tcsetattr() 設定與終端相關的引數 (除非需要底層支援卻無法滿足),使用 termios_p 引用的 termios 結構。optional_actions 指定了什麼時候改變會起作用:

TCSANOW
改變立即發生
TCSADRAIN
改變在所有寫入 fd 的輸出都被傳輸後生效。這個函式應當用於修改影響輸出的引數時使用。
TCSAFLUSH
改變在所有寫入 fd 引用的物件的輸出都被傳輸後生效,所有已接受但未讀入的輸入都在改變發生前丟棄。

tcsendbreak() 傳送連續的 0 值位元流,持續一段時間,如果終端使用非同步序列資料傳輸的話。如果 duration 是 0,它至少傳輸 0.25 秒,不會超過 0.5 秒。如果 duration 非零,它傳送的時間長度由實現定義。

如果終端並非使用非同步序列資料傳輸,tcsendbreak() 什麼都不做。

tcdrain() 等待直到所有寫入 fd 引用的物件的輸出都被傳輸。

tcflush() 丟棄要寫入 引用的物件,但是尚未傳輸的資料,或者收到但是尚未讀取的資料,取決於 queue_selector 的值:

TCIFLUSH
重新整理收到的資料但是不讀
TCOFLUSH
重新整理寫入的資料但是不傳送
TCIOFLUSH
同時重新整理收到的資料但是不讀,並且重新整理寫入的資料但是不傳送

tcflow() 掛起 fd 引用的物件上的資料傳輸或接收,取決於 action 的值:

TCOOFF
掛起輸出
TCOON
重新開始被掛起的輸出
TCIOFF
傳送一個 STOP 字元,停止終端裝置向系統傳送資料
TCION
傳送一個 START 字元,使終端裝置向系統傳輸資料

開啟一個終端裝置時的預設設定是輸入和輸出都沒有掛起。

波特率函式被用來獲取和設定 termios 結構中,輸入和輸出波特率的值。新值不會馬上生效,直到成功呼叫了 tcsetattr() 函式。

設定速度為 B0 使得 modem "掛機"。與 B38400 相應的實際位元率可以用 (8) 調整。

輸入和輸出波特率被儲存於 termios 結構中。

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;

cfgetospeed() 返回 termios_p 指向的 termios 結構中儲存的輸出波特率

cfsetospeed() 設定 termios_p 指向的 termios 結構中儲存的輸出波特率為 speed。取值必須是以下常量之一:

        B0
B50
B75
B110
B134
B150
B200
B300
B600
B1200
B1800
B2400
B4800
B9600
B19200
B38400
B57600
B115200
B230400
零值 B0 用來中斷連線。如果指定了 B0,不應當再假定存在連線。通常,這樣將斷開連線。CBAUDEX 是一個掩碼,指示高於 POSIX.1 定義的速度的那一些 (57600 及以上)。因此,B57600 & CBAUDEX 為非零。

cfgetispeed() 返回 termios 結構中儲存的輸入波特率。

cfsetispeed() 設定 termios 結構中儲存的輸入波特率為 speed。如果輸入波特率被設為0,實際輸入波特率將等於輸出波特率。

RETURN VALUE 返回值

cfgetispeed() 返回 termios 結構中儲存的輸入波特率。

cfgetospeed() 返回 termios 結構中儲存的輸出波特率。

其他函式返回:

0
成功
-1
失敗,並且為 errno 置值來指示錯誤。

注意 tcsetattr() 返回成功,如果任何所要求的修改可以實現的話。因此,當進行多重修改時,應當在這個函式之後再次呼叫 tcgetattr() 來檢測是否所有修改都成功實現。

NOTES 注意

Unix V7 以及很多後來的系統有一個波特率的列表,在十四個值 B0, ..., B9600 之後可以看到兩個常數 EXTA, EXTB ("External A" and "External B")。很多系統將這個列表擴充套件為更高的波特率。

tcsendbreak 中非零的 duration 有不同的效果。SunOS 指定中斷 duration*N 秒,其中 N 至少為 0.25,不高於 0.5 。Linux, AIX, DU, Tru64 傳送 duration 微秒的 break 。FreeBSD, NetBSD, HP-UX 以及 MacOS 忽略 duration 的值。在 Solaris 和 Unixware 中, tcsendbreak 搭配非零的 duration 效果類似於 tcdrain

所有的範例來源自 miniterm.c. The type ahead 暫存器被限制在 255 個字元, 就跟標準輸入程式的最大字串長度相同 ().

參考程式碼中的註解它會解釋不同輸入模式的使用. 我希望這些程式碼都能被瞭解. 標準輸入程式的程式範例的註解寫得最好, 其它的範例都只在不同於其它範例的地方做註解.

敘述不是很完整, 但可以激勵你對這範例做實驗, 以延生出合於你所需應用程式的最佳解.

別忘記要把序列埠的許可權設定正確 (也就是: chmod a+rw /dev/ttyS1)!

#include 
#include
#include
#include
#include

/* 鮑率設定被定義在 , 這在 被引入 */
#define BAUDRATE B38400
/* 定義正確的序列埠 */
#define MODEMDEVICE "/dev/ttyS1"
#define _POSIX_SOURCE 1 /* POSIX 系統相容 */

#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE;

main()
{
int fd,c, res;
struct termios oldtio,newtio;
char buf[255];
/*
開啟資料機裝置以讀取並寫入而不以控制 tty 的模式
因為我們不想程式在送出 CTRL-C 後就被殺掉.
*/
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fd <0) {perror(MODEMDEVICE); exit(-1); }

tcgetattr(fd,&oldtio); /* 儲存目前的序列埠設定 */
bzero(&newtio, sizeof(newtio)); /* 清除結構體以放入新的序列埠設定值 */

/*
BAUDRATE: 設定 bps 的速度. 你也可以用 cfsetispeed 及 cfsetospeed 來設定.
CRTSCTS : 輸出資料的硬體流量控制 (只能在具完整線路的纜線下工作
參考 Serial-HOWTO 第七節)
CS8 : 8n1 (8 位元, 不做同位元檢查,1 個終止位元)
CLOCAL : 本地連線, 不具資料機控制功能
CREAD : 致能接收字元
*/
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

/*
IGNPAR : 忽略經同位元檢查後, 錯誤的位元組
ICRNL : 比 CR 對應成 NL (否則當輸入訊號有 CR 時不會終止輸入)
在不然把裝置設定成 raw 模式(沒有其它的輸入處理)
*/
newtio.c_iflag = IGNPAR | ICRNL;

/*
Raw 模式輸出.
*/
newtio.c_oflag = 0;

/*
ICANON : 致能標準輸入, 使所有回應機能停用, 並不送出訊號以叫用程式
*/
newtio.c_lflag = ICANON;

/*
初始化所有的控制特性
預設值可以在 /usr/include/termios.h 找到, 在註解中也有,
但我們在這不需要看它們
*/
newtio.c_cc[VINTR] = 0; /* Ctrl-c */
newtio.c_cc[VQUIT] = 0; /* Ctrl- */
newtio.c_cc[VERASE] = 0; /* del */
newtio.c_cc[VKILL] = 0; /* @ */
newtio.c_cc[VEOF] = 4; /* Ctrl-d */
newtio.c_cc[VTIME] = 0; /* 不使用分割字元組的計時器 */
newtio.c_cc[VMIN] = 1; /* 在讀取到 1 個字元前先停止 */
newtio.c_cc[VSWTC] = 0; /* '' */
newtio.c_cc[VSTART] = 0; /* Ctrl-q */
newtio.c_cc[VSTOP] = 0; /* Ctrl-s */
newtio.c_cc[VSUSP] = 0; /* Ctrl-z */
newtio.c_cc[VEOL] = 0; /* '' */
newtio.c_cc[VREPRINT] = 0; /* Ctrl-r */
newtio.c_cc[VDISCARD] = 0; /* Ctrl-u */
newtio.c_cc[VWERASE] = 0; /* Ctrl-w */
newtio.c_cc[VLNEXT] = 0; /* Ctrl-v */
newtio.c_cc[VEOL2] = 0; /* '' */

/*
現在清除資料機線並啟動序列埠的設定
*/
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);

/*
終端機設定完成, 現在處理輸入訊號
在這個範例, 在一行的開始處輸入 'z' 會退出此程式.
*/
while (STOP==FALSE) { /* 迴圈會在我們發出終止的訊號後跳出 */
/* 即使輸入超過 255 個字元, 讀取的程式段還是會一直等到行終結符出現才停止.
如果讀到的字元組低於正確存在的字元組, 則所剩的字元會在下一次讀取時取得.
res 用來存放真正讀到的字元組個數 */
res = read(fd,buf,255);
buf[res]=0; /* 設定字串終止字元, 所以我們能用 printf */
printf(":%s:%dn", buf, res);
if (buf[0]=='z') STOP=TRUE;
}
/* 回存舊的序列埠設定值 */
tcsetattr(fd,TCSANOW,&oldtio);
}

3.2 非標準輸入程式

在非標準的輸入程式模式下, 輸入的資料不會被組合成一行而輸入後的處理功能 (清除, 殺掉, 刪除, 等等.) 都不能使用. 這個模式有兩個功能控制引數: c_cc[VTIME] 設定字元輸入時間計時器, 及 c_cc[VMIN] 設定滿足讀取功能的最低字元接收個數.

如果 MIN > 0 且 TIME = 0, MIN 設定為滿足讀取功能的最低字元接收個數. 由於 TIME 是 零, 所以計時器將不被使用.

如果 MIN = 0 且 TIME > 0, TIME 將被當做逾時設定值. 滿足讀取功能的情況為讀取到單一字元, 或者超過 TIME 所定義的時間 (t = TIME *0.1 s).

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/24790158/viewspace-1041147/,如需轉載,請註明出處,否則將追究法律責任。

上一篇: using select on a fifo
詳解linux下的串列埠通訊開發
請登入後發表評論 登入
全部評論

相關文章