1. 程式人生 > >Linux串列埠程式設計詳解 linux串列埠相關設定函式

Linux串列埠程式設計詳解 linux串列埠相關設定函式

tcgetattr   

函式用於獲取與終端相關的引數。引數fd為終端的檔案描述符,返回的結果儲存在termios 結構體

http://baike.baidu.com/view/5644808.htm?fr=aladdin

tcsetattr  函式用於設定終端的相關引數 

tcflush 

Unix終端I/O函式。作用:清空終端未完成的輸入/輸出請求及資料

對於波特率的設定通常使用cfsetospeed和cfsetispeed函式來完成。獲取波特率資訊是通過cfgetispeed和cfgetospeed函式來完成的。

常用函式

使用open()函式開啟串列埠,open()函式有兩個引數,第一個是要開啟的裝置名(如:/dev/ttyS0)。第二個是開啟的方式。開啟方式有以下三種:

  • O_RDWR,表示以讀寫方式開啟串列埠。

  • O_NOCTTY,表示不成為埠的控制終端,如果沒有這個選項,則任何輸入(鍵盤按鍵)都會中斷程式的執行。

  • O_NDELAY,表示程式不會關注DCD訊號線所處的狀態,即不管對端裝置是執行或掛起。如果沒有該選項,則程式會被設定成睡眠狀態,直到DCD訊號為低為止。

成功開啟串列埠則會返回檔案描述符,開啟失敗則返回-1。下面是一個開啟串列埠的示例:

fd = open("/dev/ttyS0",O_RDWR|O_NDELAY|O_NDELAY);

使用close()關閉開啟的串列埠,唯一的引數是開啟串列埠的檔案描述符。下面是一個關閉串列埠的示例:

close(fd);        //fd是開啟串列埠返回的檔案描述符

用write()函式向串列埠寫資料。下面是一個向串列埠寫資料的示例:

n = write(fd,buff,len);    
/* n表示成功寫到串列埠的位元組數,如果寫入失敗則返回-1
   fd是開啟串列埠返回的檔案描述符
   buff表示寫入的內容
   len表示寫入資訊的長度。
*/

用read()函式從串列埠讀取資料。下面是一個從串列埠讀資料的示例:

n = read(fd,buff,len);
/* n表示從串列埠讀到位元組數
   fd是檔案描述符
   buff是讀入位元組存放的緩衝區
   len表示讀入的位元組數
*/

通過fcntl()函式可以操作檔案描述符,用以控制讀取資料的狀態。fcntl(fd,F_SETFL,0)表示沒有資料則阻塞,處於等待狀態,直到有資料到來;fcntl(fd,F_SETFL,FNDELAY)表示當埠沒有資料時馬上返回0。

設定串列埠屬性

所有的串列埠屬性都在一個名為termios的結構體中,要使用該結構體要包含termios.h標頭檔案。在該標頭檔案中還定義兩個重要的函式tcgetattr()和tcsetattr(),分別用以獲取和設定串列埠的屬性。如:tcgetattr(fd,&old_termios),tcsetattr(fd,TCSANOW,&new_termios)。old_termios是舊的串列埠屬性,new_termios是重新設定的新串列埠屬性。tcsetattr()函式中常量的意義是:

  • TCSANOW表示新設定的串列埠屬性馬上生效。

  • TCSADRAIN表示等所有資料傳送完成後才生效。

  • TCSAFLUSH表示馬上清空輸入和輸出快取,然後應用新的串列埠設定。

termios結構體內容:

成員            描述
-------------------------------------------
c_cflag         控制模式標誌
c_lflag         本地模式標誌
c_iflag         輸入模式標誌
c_oflag         輸出模式標誌
c_line          line discipline
c_cc[NCCS]      控制字元
c_ispeed        輸入波特率
c_ospeed        輸出波特率

在termios結構中的四個標誌控制了輸入輸出的四個不同部份。輸入模式標誌c_iflag決定如何解釋和處理接收的字元。輸出模式標誌c_oflag決定如何解釋和處理髮送到tty裝置的字元。控制模式標誌決定裝置的一系列協議特徵,這一標誌只對物理裝置有效。本地模式標誌c_lflag決定字元在輸出前如何收集和處理。

在串列埠傳輸中,用波特率來表示傳輸的速度,1波特表示在1秒鐘內可以傳輸1個碼元。波特率設定可以使用cfsetispeed(&new_termios,B19200)和cfsetospeed(&new_termios,B19200)這兩個函式來完成,預設的波特率為9600baud。cfsetispeed()函式用來設定輸入的波特率,cfsetospeed()函式用來設定輸出的波特率。B19200是termios.h標頭檔案裡定義的一個巨集,表示19200的波特率。

CLOCAL和CREAD是c_cflag成員中與速率相關的標誌,在串列埠程式設計中,這兩個標誌一定要有效,以確保程式在突發的作業控制或掛起時,不會成為埠的佔有都,同時串列埠的接收驅動會自動讀入資料。設定方法如下:

termios_new.c_cflag |= CLOCAL;                 //保證程式不會成為端的佔有者
termios_new.c_cflag |= CREAD;                  //使埠能讀取輸入的資料

設定串列埠屬性不能直接賦值,要通過對termios不同成員進行"與"和"或"操作來實現。在termios.h檔案,定義了各種常量,如上面介紹的CLOCAL,CREAD。這些常量的值是掩碼,通過把這些常量與termios結構成員進行邏輯操作就可實現串列埠屬性的設定。在程式設計時用"|="來啟用屬性,用"&=~"來取消屬性。


c_iflag輸入標誌說明

  • BRKINT和IGNBRK

    如果設定了IGNBRK,中斷條件被忽略。如果沒有設定IGNBRK而設定了BRKINT,中斷條件清空輸入輸出佇列中所有的資料並且向tty的前臺程序組中所有程序傳送一個SIGINT訊號。如果這兩個都沒有設定,中斷條件會被看作一個0字元。這時,如果設定了PARMRK,當檢測到一個幀誤差時將會嚮應用程式傳送三個位元組'\377''\0''\0',而不是隻傳送一個'\0'。

  • PARMRK和IGNPAR

    如果設定了IGNPAR,則忽略接收到的資料的奇偶檢驗錯誤或幀錯誤(除了前面提到的中斷條件)。如果沒有設定IGNPAR而設定了PARMRK,當接收到的位元組存在奇偶檢驗錯誤或幀錯誤的時候。將嚮應用程式傳送一個三位元組的'\377''\0''\n'錯誤報告。其中n表示所接收到的位元組。如果兩者都沒有設定,除了接收到的位元組存在奇偶檢驗錯誤或幀誤差之外的中止條件都會嚮應用程式傳送一個單位元組('\0')的報告。

  • INPCK

    如果設定,則進行奇偶校驗。如果不進行奇偶檢驗,PARMRK和IGNPAR將對存在的奇偶校驗錯誤不產生任何的影響。

  • ISTRIP

    如果設定,所接收到的所有位元組的高位將會被去除,保證它們是一個7位的字元。

  • INLCR

    如果設定,所接收到的換行字元('\n')將會被轉換成回車符('\r')。

  • IGNCR

    如果設定,則會忽略所有接收的回車符('\r')。

  • ICRNL

    如果設定,但IGNCR沒有設定,接收到的回車符嚮應用程式傳送時會變換成換行符。

  • IUCLC

    如果IUCLC和IEXTEN都設定,接收到的所有大寫字母傳送給應程式時都被轉換成小寫字母。POSIX中沒有定義該標記。

  • IXOFF

    如果設定,為避免tty裝置的輸入緩衝區溢位,tty裝置可以向終端傳送停止符^S和開始符^Q,要求終端停止或重新開始向計算機發送資料。通過停止符和開始符來控制資料流的方式叫軟體流控制,軟體流控制方式較少用,我們主要還是用硬體流控制方式。硬體流控制在c_cflag標誌中設定。

  • IXON

    如果設定,接收到^S後會停止向這個tty裝置輸出,接收到^Q後會恢復輸出。

  • IXANY

    如果設定,則接到任何字元都會重新開始輸出,而不僅僅是^Q字元。

  • IMAXBEL

    如果設定,當輸入緩衝區空間滿時,再接收到的任何字元就會發出警報符'\a'。POSIX中沒有定義該標記。


c_oflag輸出標誌說明

OPOST是POSIX定義的唯一一個標誌,只有設定了該標誌後,其它非POSIX的輸出標記才會生效。

  • OPOST

    開啟該標記,後面的輸出標記才會生效。否則,不會對輸出資料進行處理。

  • OLCUC

    如果設定,大寫字母被轉換成小寫字母輸出。

  • ONLCR

    如果設定,在傳送換行符('\n')前先發送回車符('\r')。

  • ONOCR

    如果設定,當current column為0時,回車符不會被髮送也不會被處理。

  • OCRNL

    如果設定,回車符會被轉換成換行符。另外,如果設定了ONLRET,則current column會被設為0.

  • ONLRET

    如果設定,當一個換行符或回車符被髮送的時候,current column會被設定為0。

  • OXTABS

    如果設定,製表符會被轉換成空格符。

c_cflag控制模式標誌說明

  • CLOCAL

    如果設定,modem的控制線將會被忽略。如果沒有設定,則open()函式會阻塞直到載波檢測線宣告modem處於摘機狀態為止。

  • CREAD

    只有設定了才能接收字元,該標記是一定要設定的。

  • CSIZE

    設定傳輸字元的位數。CS5表示每個字元5位,CS6表示每個字元6位,CS7表示每個字元7位,CS8表示每個字元8位。

  • CSTOPB

    設定停止位的位數,如果設定,則會在每幀後產生兩個停止位,如果沒有設定,則產生一個停止位。一般都是使用一位停止位。需要兩位停止位的裝置已過時了。

  • HUPCL

    如果設定,當裝置最後開啟的檔案描述符關閉時,串列埠上的DTR和RTS線會減弱訊號,通知Modem結束通話。也就是說,當一個使用者通過Modem拔號登入系統,然後登出,這時Modem會自動結束通話。

  • PARENB和PARODD

    如果設定PARENB,會產生一個奇偶檢驗位。如果沒有設定PARODD,則產生偶校驗位,如果設定了PARODD,則產生奇校驗位。如果沒有設定PARENB,則PARODD的設定會被忽略。

  • CRTSCTS

    使用硬體流控制。在高速(19200bps或更高)傳輸時,使用軟體流控制會使效率降低,這個時候必須使用硬體流控制。


c_cc[]控制字元說明

只有在本地模式標誌c_lflag中設定了IEXITEN時,POSIX沒有定義的控制字元才能在Linux中使用。每個控制字元都對應一個按鍵組合(^C、^H等),但VMIN和VTIME這兩個控制字元除外,它們不對應控制符。這兩個控制字元只在原始模式下才有效。

  • c_cc[VINTR]

    預設對應的控制符是^C,作用是清空輸入和輸出佇列的資料並且向tty裝置的前臺程序組中的每一個程式傳送一個SIGINT訊號,對SIGINT訊號沒有定義處理程式的程序會馬上退出。

  • c_cc[VQUIT]

    預設對應的控制符是^\,作用是清空輸入和輸出佇列的資料並向tty裝置的前臺程序組中的每一個程式傳送一個SIGQUIT訊號,對SIGQUIT訊號沒有定義處理程式的程序會馬上退出。

  • c_cc[verase]

    預設對應的控制符是^H或^?,作用是在標準模式下,刪除本行前一個字元,該字元在原始模式下沒有作用。

  • c_cc[VKILL]

    預設對應的控制符是^U,在標準模式下,刪除整行字元,該字元在原始模式下沒有作用。

  • c_cc[VEOF]

    預設對應的控制符是^D,在標準模式下,使用read()返回0,標誌一個檔案結束。

  • c_cc[VSTOP]

    預設對應的控制字元是^S,作用是使用tty裝置暫停輸出直到接收到VSTART控制字元。或者,如果裝置了IXANY,則等收到任何字元就開始輸出。

  • c_cc[VSTART]

    預設對應的控制字元是^Q,作用是重新開始被暫停的tty裝置的輸出。

  • c_cc[VSUSP]

    預設對應的控制字元是^Z,使當前的前臺程序接收到一個SIGTSTP訊號。

  • c_cc[VEOL]和c_cc[VEOL2]

    在標準模式下,這兩個下標在行的末尾加上一個換行符('\n'),標誌一個行的結束,從而使用緩衝區中的資料被髮送,並開始新的一行。POSIX中沒有定義VEOL2。

  • c_cc[VREPRINT]

    預設對應的控制符是^R,在標準模式下,如果設定了本地模式標誌ECHO,使用VERPRINT對應的控制符和換行符在本地顯示,並且重新列印當前緩衝區中的字元。POSIX中沒有定義VERPRINT。

  • c_cc[VWERASE]

    預設對應的控制字元是^W,在標準模式下,刪除緩衝區末端的所有空格符,然後刪除與之相鄰的非空格符,從而起到在一行中刪除前一個單詞的效果。POSIX中沒有定義VWERASE。

  • c_cc[VLNEXT]

    預設對應的控制符是^V,作用是讓下一個字元原封不動地進入緩衝區。如果要讓^V字元進入緩衝區,需要按兩下^V。POSIX中沒有定義VLNEXT。

要禁用某個控制字元,只需把它設定為_POSIX_VDISABLE即可。但該常量只在Linux中有效,所以如果程式要考慮移植性的問題,請不要使用該常量。

c_lflag本地模式標誌說明

  • ICANON

    如果設定,則啟動標準模式,如果沒有設定,則啟動原始模式。

  • ECHO

    如果設定,則啟動本地回顯。如果沒有設定,則除了ECHONL之外,其他以ECHO開頭的標記都會失效。

  • ECHOCTL

    如果設定,則以^C的形式列印控制字元,如:按Ctrl+C顯示^C,按Ctrl+?顯示^?。

  • ECHOE

    如果在標準模式下設定了ECHOE標誌,則當收到一個ERASE控制符時將刪除前一個顯示字元。

  • ECHOK和ECHOKE

    在標準模式下,當接收到一個KILL控制符,則在緩衝區中刪除當前行。如果ECHOK、ECHOKE和ECHOE都沒有設定,則用ECHOCTL表示的KILL字元(^U)將會在輸出終端上顯示,表示當前行已經被刪除。

    如果已經設定了ECHOE和ECHOK,但沒有設定ECHOKE,將會在輸出終端顯示ECHOCTL表示的KILL字元,緊接著是換行,如果設定了OPOST,將會通過OPOST處理程式進行適當的處理。

    如果ECHOK、ECHOKE和ECHOE都有設定,則會刪除當前行。

    在POSIX中沒有定義ECHOKE標記,在沒有定義ECHOKE標記的系統中,設定ECHOK則表示同時設定了ECHOKE標誌。

  • ECHONL

    如果在標準模式下設定了該標誌,即使沒有設定ECHO標誌,換行符還是會被顯示出來。

  • ECHOPRT

    如果設定,則字元會被簡單地打印出來,包括各種控制字元。在POSIX中沒有定義該標誌。

  • ISIG

    如果設定,與INTR、QUIT和SUSP相對應的訊號SIGINT、SIGQUIT和SIGTSTP會發送到tty裝置的前臺程序組中的所有程序。

  • NOFLSH

    一般情況下,當接收到INTR或QUIT控制符的時候會清空輸入輸出佇列,當接收到SUSP控制符時會清空輸入佇列。但是如果設定了NOFLUSH標誌,則所有佇列都不會被清空。

  • TOSTOP

    如果設定,則當一個非前臺程序組的程序試圖向它的控制終端寫入資料時,訊號SIGTTOU會被被髮送到這個程序所在的程序組。預設情況下,這個訊號會使程序停止,就像收到SUSP控制符一樣。

  • IEXIEN

    預設已設定,我們不應修改它。在Linux中IUCLC和幾個與刪除字元相關的標記都要求在設定了IEXIEN才能正常工作。


下面介紹一些常用串列埠屬性的設定方法。

  • 設定流控制

    termios_new.c_cflag &= ~CRTSCTS;            //不使用流控制
    termios_new.c_cflag |= CRTSCTS;                 //使用硬體流控制
    termios_new.c_iflag |= IXON|IXOFF|IXANY;        //使用軟體流控制
    
  • 遮蔽字元大小位

    termios_new.c_cflag &= ~CSIZE; 
    
  • 設定資料位大小

    termios_new.c_cflag |= CS8;         //使用8位資料位
    termios_new.c_cflag |= CS7;         //使用7位資料位
    termios_new.c_cflag |= CS6;         //使用6位資料位
    termios_new.c_cflag |= CS5;         //使用5位資料位
    
  • 設定奇偶校驗方式

    termios_new.c_cflag &= ~PARENB;       //無奇偶校驗
    
    termios_new.c_cflag |= PARENB;            //奇校驗
    termios_new.c_cflag = ~PARODD;       
    
    termios_new.c_cflag |= PARENB;            //偶校驗
    termios_new.c_cflag &= ~PARODD;       
    
  • 停止位

    termios_new.c_cflag |= CSTOPB;            //2位停止位
    termios_new.c_cflag &= ~CSTOPB;       //1位停止位  
    
  • 輸出模式

    termios_new.c_cflag &= ~OPOST;        //原始資料(RAW)輸出
    
  • 控制字元

    termios_new.c_cc[VMIN] = 1;               //讀取字元的最小數量
    termios_new.c_cc[VTIME] = 1;              //讀取第一個字元的等待時間
    
  • 關閉終端回顯,鍵盤輸入的字元不會在終端視窗顯示。

    #include <stdio.h>
    #include <stdlib.h>
    #include <termios.h>
    #include <unistd.h>
    
    int main(void)
    {
            struct termios ts,ots;
            char passbuf[1024];
    
            tcgetattr(STDIN_FILENO,&ts);  /* STDIN_FILENO的值是1,表示標準輸入的檔案描述符 */
            ots = ts;
    
            ts.c_lflag &= ~ECHO;         /* 關閉回終端回顯功能*/
            ts.c_lflag |= ECHONL;
            tcsetattr(STDIN_FILENO,TCSAFLUSH,&ts);  /* 應用新終端設定 */
    
            fgets(passbuf,1024,stdin);     /* 輸入字元不會在終端顯示 */
            printf("you input character = %s\n",passbuf);
    
            tcsetattr(STDIN_FILENO,TCSANOW,&ots);  /* 恢復舊的終端裝置 */
    }


串列埠本身,標準和硬體 

串列埠是計算機上的序列通訊的物理介面。計算機歷史上,串列埠曾經被廣泛用於連線計算機和終端裝置和各種外部裝置。雖然乙太網介面和USB介面也是以一個序列流進行資料傳送的,但是串列埠連線通常特指那些與RS-232標準相容的硬體或者調變解調器的介面。雖然現在在很多個人計算機上,原來用以連線外部裝置的串列埠已經廣泛的被USB和Firewire替代;而原來用以連線網路的串列埠則被乙太網替代,還有用以連線終端的串列埠裝置則已經被MDA或者VGA取而代之。但是,一方面因為串列埠本身造價便宜技術成熟,另一方面因為串列埠的控制檯功能RS-232標準高度標準化並且非常普及,所以直到現在它仍然被廣泛應用到各種裝置上。 某些計算機使用一個叫做UART的積體電路來作為串列埠裝置。這個積體電路可以進行字元和非同步序列通訊序列之間的轉換,並且可以自動地處理資料的時序。而某些低端裝置則會讓CPU直接通過輸出針來傳送資料,這種技術叫做bit-banging。 因為“串列埠”,RS-232和UARTs基本上總是在同一個語境中出現,所以這些名詞通常會被搞混。下面逐一解釋以下一些重要的名詞和術語。

什麼是序列通訊 

計算機可以每次傳送一個或者多個位(bit)的資料。“序列”指的式每次只傳輸一位(1bit)資料。 當需要通過序列通訊傳輸一個字(word)的資料時,只能以每次一位的方式接收或者傳送。每個位可能是on(1)或者off(0)。很多技術術語中經常用mark表示on,而space表示off。

序列資料的速度通常用每秒傳輸的位元組數bits-per-second(bps)或者波特率(baud)表示。這個值表示的是每秒鐘被送出的0和1的個數。很久很久以前,300bps就是很快的速度了,而現在的電腦可以處理高達430,800的RS-232速率。表示波特率的單位還有kpbs和Mbps,1kps=1000bps而1Mbps=1000kbps。 一般有人提到序列裝置的時候,它通常說可能是某種資料通訊裝置-DCE(Data Communications Equipment)或者資料終端裝置-DTE(Data Terminal Equipment)。它們之間的區別非常簡單,每個訊號對,比如傳送和接收,它們倆正好是相反的。如果需要將兩個DTE或者DCE裝置連線起來的話,需要介面卡或者交叉線纜將訊號對交換。

什麼是RS-232 

RS-232是EIA(Electronic Industries Association)定義的序列通訊的電器介面。RS-232事實上有三種(A,B和C),它們分別採用不同的電壓來表示on和off。最被廣泛使用的是RS-232C,它將mark(on)位元的電壓定義為-3V到-12V之間,而將space(off)的電壓定義到+3V到+12V之間。雖然RS-232C標準說訊號最遠被傳輸8m,但事實上你可以使用它傳輸更長的距離,直到訊號波特率已經小到不行了為止。 RS-232的連結線中除去用來傳入傳出資料的電線,還有一些用來提供時序,狀態和握手的電線:

RS-232 針腳定義

DB-25

針腳 描述 針腳 描述 針腳 描述 針腳 描述