串口是計算機上的串行通信的物理接口。計算機歷史上,串口之前被普遍用於鏈接計算機和終端設備和各類外部設備。儘管以太網接口和USB接口也是以一個串行流進行數據傳送的。但是串口鏈接一般特指那些與RS-232標準兼容的硬件或者調制解調器的接口。儘管現在在很是多我的計算機上。原來用以鏈接外部設備的串口已經普遍的被USB和Firewire替代;而原來用以鏈接網絡的串口則被以太網替代。還實用以鏈接終端的串口設備則已經被MDA或者VGA取而代之。php
但是。一方面因爲串口自己造價廉價技術成熟,還有一方面因爲串口的控制檯功能RS-232標準高度標準化並且很是普及,因此直到現在它仍然被普遍應用到各類設備上。ios
某些計算機使用一個叫作UART的集成電路來做爲串口設備。這個集成電路可以進行字符和異步串行通信序列之間的轉換,並且可以本身主動地處理數據的時序。而某些低端設備則會讓CPU直接經過輸出針來傳送數據。這樣的技術叫作bit-banging。編程
因爲「串口」。RS-232和UARTs基本上老是在同一個語境中出現,因此這些名詞通常會被搞混。如下逐一解釋如下一些重要的名詞和術語。數組
計算機可以每次傳送一個或者多個位(bit)的數據。「串行」指的式每次僅僅傳輸一位(1bit)數據。網絡
當需要經過串行通信傳輸一個字(word)的數據時,僅僅能以每次一位的方式接收或者發送。每個位多是on(1)或者off(0)。異步
很是多技術術語中經常用mark表示on,而space表示off。async
串行數據的速度通常用每秒傳輸的字節數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)。它們之間的差異很easy。每個信號對。比方傳送和接收,它們倆正好是相反的。假設需要將兩個DTE或者DCE設備鏈接起來的話,需要適配器或者交叉線纜將信號對交換。性能
RS-232是EIA(Electronic Industries Association)定義的串行通訊的電器接口。RS-232其實有三種(A,B和C),它們分別採用不一樣的電壓來表示on和off。ui
最被普遍使用的是RS-232C,它將mark(on)比特的電壓定義爲-3V到-12V之間。而將space(off)的電壓定義到+3V到+12V之間。儘管RS-232C標準說信號最遠被傳輸8m,但其實你可以使用它傳輸更長的距離。直到信號波特率已經小到不行了爲止。 RS-232的連結線中除去用來傳入傳出數據的電線,另外一些用來提供時序,狀態和握手的電線:
RS-232 針腳定義
DB-25
針腳 | 描寫敘述 | 針腳 | 描寫敘述 | 針腳 | 描寫敘述 | 針腳 | 描寫敘述 | 針腳 | 描寫敘述 |
1 | Earth Ground | 6 | DSR - Data Set Ready | 11 | Unassigned | 16 | Secondary RXD | 21 | Signal Quality Detect |
2 | TXD - Transmitted Data | 7 | GND - Logic Ground | 12 | Secondary DCD | 17 | Receiver Clock | 22 | Ring Detect |
3 | RXD - Received Data | 8 | DCD - Data Carrier Detecter | 13 | Secondary CTS | 18 | Unassigned | 23 | Data Rate Select |
4 | RTS - Request To Send | 9 | Reserved | 14 | Secondary TXD | 19 | Secondary RTS | 24 | Transmit Clock |
5 | CTS - Clear To Send | 10 | Reserved | 15 | Transmit Clock | 20 | DTR - Data Terminal Ready | 25 | Unassigned |
DB-9
針腳 | 名稱 | 全名 | 方向(主機 外設) |
3 | TD | Transmit Data | -> |
2 | RD | Receive Data | <- |
7 | RTS | Request To Send | -> |
8 | CTS | Clear To Send | <- |
6 | DSR | Data Set Ready | <- |
4 | DTR | Data Terminal Ready | -> |
1 | CD | Data Carrier Detect | <- |
9 | RI | Ring Indicator | <- |
5 | - | Signal Ground |
另外兩個比較常見的串行接口的標準式RS-422和RS-574。RS-422使用更低的電壓和差分信號,這樣可以將傳輸距離擴張到300m。而RS-574定義了一般可以見到的用在電腦上的9針鏈接器和電壓。
RS-232標準定義了18個不一樣的串行通訊的信號。而這些之中,惟獨例如如下6個可以在UNIX環境中使用。
從技術角度講,GND不能算是信號。但是沒有它其它信號都不能用了。基本上,logic ground有點像一個參考電壓,經過它來推斷哪一個電壓表示正哪一個電壓表示負。
TXD信號負載着從你的電腦或者設備到還有一端(比方調制解調器)的數據。Mark範圍的電壓被解析成1,而space範圍電壓被解析成0。
RXD於TXD正好相反。它負載着從還有一端的電腦或者設備上傳到你的工做站的數據。Mark和space的解析方法於TXD一致。
DCD信號一般來自串口連結線的還有一端。這條信號線上的space電壓表示還有一端的電腦或者設備現在已經鏈接。但是,DCD信號線卻不是總可以獲得的,有些設備上有這條信號線,而有的則沒有。
DTR信號是你的工做站產生的,用以告訴還有一端的電腦或者設備你已是否已經準備好了。
Space電壓表示準備好了。而mark電壓表示沒有準備好。
當你在工做站上打開串行接口時,DTR一般本身主動被設置位有效。
CTS則一般來自連結線的還有一端。
Space電壓表示你可以從工做站送出不少其它的數據。CTS通常用來協調你的工做站和還有一端之間的串行數據流。
假設RTS信號被設置成space電壓。這表示你準備好了一些數據需要傳送。
和CTS同樣,RTS也被用來協調工做站和還有一端的電腦或者設備之間的數據流。有些工做站上會一直將這個信號設置位space。
計算機爲了弄懂傳給它的串行數據。它需要肯定每個字符開始和結束的位置。
這通常是用異步串行數據來完畢的。
在異步模式中,除非有字符被傳輸,不然串行數據線老是處於mark(1)狀態。有一個start位會被增長傳輸字符的各個位以前,在字符自己的位以後會有一個可選的parity位和一個或者多個stop位。Start位老是space(0)並且它會告訴計算機新的串行數據過來了。數據可以隨時被送出或者接收。這就是所謂的異步。
#ref(): File not found: "async.gif" at page "Linux串口編程具體解釋"
那個可選的parity位不過所有傳輸位的和,這個和用以表示傳輸字符中有奇數個1仍是偶數個1。
在偶數parity中,假設有傳輸字符中有偶數個1,那麼parity位被設置成0。而傳輸字符中有奇數個1,那麼parity位被設置成1。在奇數parity中,位設置與此相反。另外一些術語。比方space parity, mark parity和no parity。Space parity是指parity位會一直被設置位0。而mark parity正好與此相反,parity會一直是1。No parity的意思就是根本不會傳輸parity位。 剩餘的位叫作stop位。傳輸字符之間可以有1個,1.5個或者2個stop位。而且,它們的值老是1。傳統上,Stop位式用給計算機一些時間處理前面的字符的,但是它僅僅是被用來同步接收數據的計算機和接受的字符。
異步數據一般被表示成"8N1","7E1",或者與此相似的形式。這表示「8數據位,no parity和1個stop bit」,還有對應得。「7數據位。even parity和1個stop bit」。
全雙工(Full duplex)是說計算機可以同一時候接受和發送數據——也就是它有兩個分開的傳輸數據通道(一個傳入。一個傳出)。
半雙工(Half duplex)表示計算機不能同一時候接受和發送數據,而在某一時刻它僅僅能單一的傳送或者接收。這一般意味着,它僅僅有一個數據通道。半雙工並不是說RS-232的某些信號不能使用。而是。它通常是使用了有別於RS-232的其它不支持全雙工的標準。
兩個串行接口之間的數據傳輸流一般需要協調一致才行。這多是由於用以通訊的某個串行接口或者某些存儲介質的中間串行通訊鏈路的限制形成的。對於異步數據這裏有兩個方法作到這一點。
第一種方法一般被叫作「軟件」流控制。這樣的方法採用特殊字符來開始(XON,DC1,八進制數021)或者結束(XOFF,DC3或者八進制數023)數據流。而這些字符都在ASCII中定義好了。
儘管這些編碼對於傳輸文本信息很實用,但是它們卻不能被用於在特殊程序中的其它類型的信息。
另一種方法叫作「硬件」流控制。
這樣的方法使用RS-232標準的CTS和RTS信號來代替以前提到的特殊字符。
當準備就緒時,接受一方會將CTS信號設置成爲space電壓,而還沒有準備就緒時它會被設置成爲mark電壓。對應得,發送方會在準備就緒的狀況下將RTS設置成space電壓。正因爲硬件流控制使用了於數據分隔的信號,因此與需要傳輸特殊字符的軟件流控制相比它的速度很是快。但是,並不是所有的硬件和操做系統都支持CTS/RTS流控制。
一般,直到有傳輸數據時,接收和傳輸信號會保持在mark電壓。
假設一個信號掉到space電壓並且持續了很是長時間。通常來講是1/4到1/2秒,那麼就說有一個break條件存在了。
BREAK經常被用來重置一條數據線或者用來改變像調制解調器這種設備的通信模式。
與異步數據不一樣,同步數據是一個穩定的字節流。爲了能夠在線路上讀取到數據,計算機必須提供或者接受一個時鐘,這樣才幹保證發送端和接收端同步。
雖然已經有同步時鐘。計算機仍是必須以某種方式標誌數據流的開端。作這件事情最多見的辦法就是使用像Serial Data Link Control("SDLC")或者High-Speed Data Link Control("HDLC")這種數據包通信協議。
這些協議每個都定義了一個肯定的比特序列來表示數據包的開始和結束。
固然,它們也定義了一個用來表示沒有傳輸數據的比特序列。這些比特序列可以幫助計算機識別數據包的開端。
因爲同步協議可以不使用每個字符的同步比特位,因此一般它們的性能比異步通信快最少25%,而且通常比較適用於遠距離的網絡連接或者有兩個串口接口的配置的狀況。雖然同步通信的速度有優點,大部分RS-232硬件卻不支持它,因爲同步通信需要其它的硬件和軟件。
和其它設備同樣,Linux也是經過設備文件來提供訪問串口的功能。
當需要訪問串口的時候,你僅僅需要open對應的文件。
Linux系統上通常有一個或者多個串口,而這些串口設備文件名稱字比較奇怪,如比如下這樣
串口設備文件名稱
操做系統 | 串口1 | 串口2 | USB/RS-232轉換器 |
Windows | COM1 | COM2 | - |
Linux | /dev/ttyS0 | /dev/ttyS1 | /dev/ttyUSB0 |
因爲串口和其它設備同樣,在類Unix系統中都是以設備文件的形式存在的。因此,理所固然得你可以使用open(2)系統調用/函數來訪問它。
但Linux系統中卻有一個略微不方便的地方,那就是普通用戶通常不能直接訪問設備文件。
你可以選擇下面方式作一些調整,以便你編寫的程序可以訪問串口。
OK.假如你已經準備好了讓串口設備文件可以被所有用戶訪問,你可以在Linux系統中實驗一下如下這個程序,它可以打開計算機的串口1。
#include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> /* File control definitions */ #include <errno.h> #include <termios.h> /* POSIX terminal control definitions */ /* * 'open_port()' - Open serial port 1 * Returns the file descriptor on success or -1 on error. */ int open_port(void) { int fd; /* File descriptor for the port */ fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { /* * Could not open the port. */ perror("open_port: Unable to open /dev/ttyS0 -"); } else { fcntl(fd, F_SETFL, 0); return (fd); } }
打開串口鏈接的時候,程序在open函數中除了Read+Write模式之外還指定了兩個選項;
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
標誌O_NOCTTY可以告訴UNIX這個程序不會成爲這個port上的「控制終端」。假設不這樣作的話。所有的輸入,比方鍵盤上過來的Ctrl+C停止信號等等。會影響到你的進程。而有些程序比方getty(1M/8)則會在打開登陸進程的時候使用這個特性,但是一般狀況下,用戶程序不會使用這個行爲。
O_NDELAY標誌則是告訴UNIX,這個程序並不關心DCD信號線的狀態——也就是不關心port還有一端是否已經鏈接。
假設不指定這個標誌的話,除非DCD信號線上有space電壓不然這個程序會一直睡眠。
給port上寫入數據也很是easy。使用write(2)系統調用就可以發送數據了:
n = write(fd, "ATZ\r", 4); if (n < 0) fputs("write() of 4 bytes failed!\n", stderr);
和寫入其它設備文件的方式一樣,write函數也會返回發送數據的字節數或者在錯誤發生的時候返回-1。
一般,發送數據最多見的錯誤就是EIO。當調制解調器或者數據鏈路將Data Carrier Detect(DCD)信號線弄掉了,就會發生這個錯誤。
而且。直相當閉port這個狀況會一直持續。
從串口上讀取數據的時候就得耍花招了。
因爲,假設你在原數據模式(raw data mode)操做port的話。每個read(2)系統調用都會返回從串口輸入緩衝區中實際獲得的字符的個數。
在不能獲得數據的狀況下,read(2)系統調用就會一直等着,僅僅到有port上新的字符可以讀取或者發生超時或者錯誤的狀況發生(這裏跟打開方式有關。通常的打開方式應該是默認非堵塞的,這裏有疑問)。假設需要read(2)函數迅速返回的話,你可以使用如下這個方式:
fcntl(fd, F_SETFL, FNDELAY);非堵塞可以使用while循環重複讀取,配合超時設置
標誌FNDELAY可以保證read(2)函數在port上讀不到字符的時候返回0。需要回到正常(堵塞)模式的時候,需要再次在不帶FNDELAY標誌的狀況下調用fcntl(2)函數:
fcntl(fd, F_SETFL, 0);堵塞模式可以跟時間參數配合設置超時
固然,假設你最初就是以O_NDELAY標誌打開串口的,你也可在以後使用這種方法改變讀取的行爲方式。
可以使用close(2)系統調用關閉串口:
close(fd);
關閉串口會將DTR信號線設置成low。這會致使很是多調制解調器掛起。
很是多系統都支持POSIX終端(串口)接口。程序可以利用這個接口來改變終端的參數,比方。波特率,字符大小等等。要使用這個port的話。你必須將<termios.h>頭文件包括到你的程序中。這個頭文件裏定義了終端控制結構體和POSIX控制函數。
與串口操做相關的最重要的兩個POSIX函數可能就是tcgetattr(3)和tcsetattr(3)。顧名思義,這兩個函數分別用來取得設設置終端的屬性。調用這兩個函數的時候,你需要提供一個包括着所有串口選項的termios結構體:
termios結構體成員
成員 | 描寫敘述 |
c_cflag | 控制選項 |
c_lflag | 行選項 |
c_iflag | 輸入選項 |
c_oflag | 輸出選項 |
c_cc | 控制字符 |
c_ispeed | 輸入波特率(NEW) |
c_ospeed | 輸出波特率(NEW) |
經過termios結構體的c_cflag成員可以控制波特率,數據的比特數,parity,中止位和硬件流控制。如下這張表列出了所有可以使用的常數。
c_cflag常數
常量 | 描寫敘述 |
CBAUD | Bit mask for baud rate |
B0 | 0 baud (drop DTR) |
B50 | 50 baud |
B75 | 75 baud |
B110 | 110 baud |
B134 | 134.5 baud |
B150 | 150 baud |
B200 | 200 baud |
B300 | 300 baud |
B600 | 600 baud |
B1200 | 1200 baud |
B1800 | 1800 baud |
B2400 | 2400 baud |
B4800 | 4800 baud |
B9600 | 9600 baud |
B19200 | 19200 baud |
B38400 | 38400 baud |
B57600 | 57,600 baud |
B76800 | 76,800 baud |
B115200 | 115,200 baud |
EXTA | External rate clock |
EXTB | External rate clock |
CSIZE | Bit mask for data bits |
CS5 | 5 data bits |
CS6 | 6 data bits |
CS7 | 7 data bits |
CS8 | 8 data bits |
CSTOPB | 2 stop bits (1 otherwise) |
CREAD | Enable receiver |
PARENB | Enable parity bit |
PARODD | Use odd parity instead of even |
HUPCL | Hangup (drop DTR) on last close |
CLOCAL | Local line - do not change "owner" of port |
LOBLK | Block job control output |
CNEW_RTSCTS/CRTSCTS | Enable hardware flow control (not supported on all platforms) |
在傳統的POSIX編程中,當鏈接一個本地的(不經過調制解調器)或者遠程的終端(經過調制解調器)時,這裏有兩個選項應當一直打開,一個是CLOCAL。還有一個是CREAD。
這兩個選項可以保證你的程序不會變成port的所有者。而port所有者必須去處理髮散性做業控制和掛斷信號,同一時候還保證了串行接口驅動會讀取過來的數據字節。
波特率常數(CBAUD,B9600等等)一般指用到那些不支持c_ispeed和c_ospeed成員的舊的接口上。後面文章將會提到怎樣使用其它POSIX函數來設置波特率。
千萬不要直接用使用數字來初始化c_cflag(固然還有其它標誌)。最好的方法是使用位運算的與或非組合來設置或者清除這個標誌。不一樣的操做系統版本號會使用不一樣的位模式,使用常數定義和位運算組合來避免反覆工做從而提升程序的可移植性。
不一樣的操做系統會將波特率存儲在不一樣的位置。
舊的編程接口將波特率存儲在上表所看到的的c_cflag成員中,而新的接口實裝則提供了c_ispeed和c_ospeed成員來保存實際波特率的值。
程序中可是使用cfsetospeed(3)和cfsetispeed(3)函數在termios結構體中設置波特率而不用去管底層操做系統接口。
如下的代碼是個很典型的設置波特率的樣例。
struct termios options; /* * Get the current options for the port... */ tcgetattr(fd, &options);
/* * Set the baud rates to 19200... */ cfsetispeed(&options, B19200); cfsetospeed(&options, B19200); /* * Enable the receiver and set local mode... */ options.c_cflag |= (CLOCAL | CREAD); /* * Set the new options for the port... */ tcsetattr(fd, TCSANOW, &options);
函數tcgetattr(3)會將當前串口配置回填到termio結構體option中。而後,程序設置了輸入輸出的波特率並且將本地模式(CLOCAL)和串行數據接收(CREAD)設置爲有效,接着將新的配置做爲參數傳遞給函數tcsetattr(3)。常量TCSANOW標誌所有改變必須立馬生效而不用等到傳輸數據結束。
其它還有一些常數可以保證等待數據結束或者刷新輸入輸出以後再生效。
tcsetattr常量
常量 | 描寫敘述 |
TCSANOW | Make changes now without waiting for data to complete |
TCSADRAIN | Wait until everything has been transmitted |
TCSAFLUSH | Flush input and output buffers and make the change |
不一樣的系統上可能支持不一樣的輸入輸出速度。因此。經過串口鏈接兩臺機器或者設備的時候。應該將波特率設置成二者中較小的那個,即MIN(speed1, speed2)。
設置字符大小的時候。這裏卻沒有像設置波特率那麼方便的函數。
因此,程序中需要一些位掩碼運算來把事情搞定。
字符大小以比特爲單位指定:
options.c_flag &= ~CSIZE; /* Mask the character size bits */ options.c_flag |= CS8; /* Select 8 data bits */
與設置字符大小的方式差點兒相同。這裏仍然需要組合一些位掩碼來將奇偶校驗設爲有效和奇偶校驗的類型。
UNIX串口驅動可以生成even,odd和no parity位碼。設置space奇偶校驗需要耍點小手段。
options.c_cflag &= ~PARENB options.c_cflag &= ~CSTOPB options.c_cflag &= ~CSIZE; options.c_cflag |= CS8;
options.c_cflag |= PARENB options.c_cflag &= ~PARODD options.c_cflag &= ~CSTOPB options.c_cflag &= ~CSIZE; options.c_cflag |= CS7;
options.c_cflag |= PARENB options.c_cflag |= PARODD options.c_cflag &= ~CSTOPB options.c_cflag &= ~CSIZE; options.c_cflag |= CS7;
options.c_cflag &= ~PARENB options.c_cflag &= ~CSTOPB options.c_cflag &= ~CSIZE; options.c_cflag |= CS8;
某些版本號的UNIX系統支持經過CTS(Clear To Send)和RTS(Request To Send)信號線來設置硬件流控制。假設系統上定義了CNEW_RTSCTS和CRTSCTS常量,那麼很是可能它會支持硬件流控制。
使用如下的方法將硬件流控制設置成有效:
options.c_cflag |= CNEW_RTSCTS; /* Also called CRTSCTS
將它設置成爲無效的方法與此相似:
options.c_cflag &= ~CNEW_RTSCTS;
本地模式成員變量c_lflag可以控制串口驅動如何控制輸入字符。一般。你可能需要經過c_lflag成員來設置經典輸入和原始輸入模式。
成員變量c_lflag可以使用的常量
ISIG | Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals |
ICANON | Enable canonical input (else raw) |
XCASE | Map uppercase \lowercase (obsolete) |
ECHO | Enable echoing of input characters |
ECHOE | Echo erase character as BS-SP-BS |
ECHOK | Echo NL after kill character |
ECHONL | Echo NL |
NOFLSH | Disable flushing of input buffers after interrupt or quit characters |
IEXTEN | Enable extended functions |
ECHOCTL | Echo control characters as ^char and delete as ~? |
ECHOPRT | Echo erased character as character erased |
ECHOKE | BS-SP-BS entire line on line kill |
FLUSHO | Output being flushed |
PENDIN | Retype pending input at next read or input char |
TOSTOP | Send SIGTTOU for background output |
經典輸入是以面向行設計的。在經典輸入模式中輸入字符會被放入一個緩衝之中。這樣可以以與用戶交互的方式編輯緩衝的內容。直到收到CR(carriage return)或者LF(line feed)字符。
選擇使用經典輸入模式的時候,你一般需要選擇ICANON。ECHO和ECHOE選項:
options.c_lflag |= (ICANON | ECHO | ECHOE);
原始輸入根本不會被處理。輸入字符僅僅是被原封不動的接收。
普通狀況中。假設要使用原始輸入模式。程序中需要去掉ICANON,ECHO。ECHOE和ISIG選項:
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
可以經過輸入模式成員c_iflag來控制從port上收到的字符的輸入過程。與c_cflag同樣,c_iflag的終於值是想要使用的所有狀態的位運算OR的組合。
c_iflag成員可以使用的常量
常量 | 描寫敘述 |
INPCK | Enable parity check |
IGNPAR | Ignore parity errors |
PARMRK | Mark parity errors |
ISTRIP | Strip parity bits |
IXON | Enable software flow control (outgoing) |
IXOFF | Enable software flow control (incoming) |
IXANY | Allow any character to start flow again |
IGNBRK | Ignore break condition |
BRKINT | Send a SIGINT when a break condition is detected |
INLCR | Map NL to CR |
IGNCR | Ignore CR |
ICRNL | Map CR to NL |
IUCLC | Map uppercase to lowercase |
IMAXBEL | Echo BEL on input line too long |
當程序在c_cflag中設置了奇偶校驗成員(PARENB)的時候。程序就需要將輸入奇偶校驗設置成爲有效。與奇偶校驗相關的常量有INPCK,IGNPAR,PARMRK和ISTRIP。普通狀況下,你可能需要選擇INPCK和ISTRIP將奇偶校驗設置爲有效同一時候從接收字串中脫去奇偶校驗位:
options.c_iflag |= (INPCK | ISTRIP);
IGNPAR是一個比較危急選項。即使有發生錯誤時。它也會告訴串口驅動直接忽略奇偶校驗錯誤給數據放行。
這個選項在測試連接的通信質量時比較實用而一般不會被用在實際程序中。
PARMRK會致使奇偶校驗錯誤被標誌成特殊字符增長到輸入流之中。
假設IGNPAR選項也是有效的,那麼一個NUL(八進制000)字符會被增長到發生奇偶校驗錯誤的字符前面。不然,DEL(八進制177)和NUL字符會和出錯的字符一塊兒送出。
軟件流控制可以經過IXON,IXOFF和IXANY常量設置成有效:
options.c_iflag |= (IXON | IXOFF | IXANY);
將其設置爲無效的時候,很是easy,僅僅需要對這些位取反:
options.c_iflag &= ~(IXON | IXOFF | IXANY);
XON(start data)和XOFF(stop data)字符倒是在c_cc數組中定義的,如下會具體描寫敘述這個數組。
成員變量c_oflag之中包含了輸出過濾選項。
和輸入模式類似,程序可以選擇使用通過加工的或者原始的數據輸出。
c_oflag成員的常量
常量 | 描寫敘述 |
OPOST | Postprocess output (not set = raw output) |
OLCUC | Map lowercase to uppercase |
ONLCR | Map NL to CR-NL |
OCRNL | Map CR to NL |
NOCR | No CR output at column 0 |
ONLRET | NL performs CR function |
OFILL | Use fill characters for delay |
OFDEL | Fill character is DEL |
NLDLY | Mask for delay time needed between lines |
NL0 | No delay for NLs |
NL1 | Delay further output after newline for 100 milliseconds |
CRDLY | Mask for delay time needed to return carriage to left column |
CR0 | No delay for CRs |
CR1 | Delay after CRs depending on current column position |
CR2 | Delay 100 milliseconds after sending CRs |
CR3 | Delay 150 milliseconds after sending CRs |
TABDLY | Mask for delay time needed after TABs |
TAB0 | No delay for TABs |
TAB1 | Delay after TABs depending on current column position |
TAB2 | Delay 100 milliseconds after sending TABs |
TAB3 | Expand TAB characters to spaces |
BSDLY | Mask for delay time needed after BSs |
BS0 | No delay for BSs |
BS1 | Delay 50 milliseconds after sending BSs |
VTDLY | Mask for delay time needed after VTs |
VT0 | No delay for VTs |
VT1 | Delay 2 seconds after sending VTs |
FFDLY | Mask for delay time needed after FFs |
FF0 | No delay for FFs |
FF1 | Delay 2 seconds after sending FFs |
經過在c_oflag成員變量中設置OPOST選項的方法程序可以選擇加工過的輸入。
options.c_oflag |= OPOST;
在所有選項其中,你可能只需要使用ONLCR選項來將行分隔符映射到CR-LF組合對上。其它選項主要是歷史遺留,只與行打印機和終端跟不上串行數據的年代有關。
原始輸出方式可以經過在c_oflag中重置OPOST選項來選擇:
options.c_oflag &= ~OPOST;
假設OPOST選項被設置成無效的話,其它c_oflag中的選項都會失效。
字符數組c_cc裏面包含了控制字符的定義和超時參數。這個數組的每個元素都是以常量定義的。
成員變量c_cc中的控制字符
常量 | 描寫敘述 | 鍵 |
VINTR | Interrupt | CTRL-C |
VQUIT | Quit | CTRL-Z |
VERASE | Erase | Backspace (BS) |
VKILL | Kill-line | CTRL-U |
VEOF | End-of-file | CTRL-D |
VEOL | End-of-line | Carriage return (CR) |
VEOL2 | Second end-of-line | Line feed (LF) |
VMIN | Minimum number of characters to read | - |
VSTART | Start flow | CTRL-Q (XON) |
VSTOP | Stop flow | CTRL-S (XOFF) |
VTIME | Time to wait for data (tenths of seconds) | - |
用來作軟件流控制的字符包括在數組c_cc的VSTART和VSTOP元素裏面。一般狀況下,它們應該被設置成DC1(八進制021)和DC3(八進制023)。它們在ASCII標準中表明着XON和XOFF字符。
UNIX串口驅動提供了設置字符和包超時的能力。
數組c_cc中有兩個元素可以用來設置超時:VMIN和VTIME。在經典輸入模式或者經過open(2)和fcntl(2)函數傳遞NDELAY選項時,超時設置會被忽略。
VMIN可以指定讀取的最小字符數。假設它被設置爲0,那麼VTIME值則會指定每個字符讀取的等待時間。
假設VMIN不爲零,VTIME會指定等待第一個字符讀取操做的時間。假設在這個指定時間中可以開始讀取某個字符,直到VMIN個數的全部字符全部被讀取,其它讀取操做將會被堵塞(等待)。也就是說,一旦讀取第一個字符,串口驅動的預期就是接收到整個字符包(一共VMIN字節)。假設在贊成的時間內沒有字符被讀取。那麼read(2)調用就會返回0。經過這種方法可以確切得告訴串口驅動程序需要讀取N個字節,而且read(2)調用僅僅會返回N或者0。然而,超時設置僅僅對第一個字符的讀取操做有效。因此,假設因爲某些緣由驅動程序在N字節的包中丟失某個字符的話,read(2)調用將會一直等下去。
VTIME可以以十分之中的一個秒爲單位指定等待字符輸入的時間。假設VTIME設置爲0(默認狀況)。除非open(2)或者fcntl(2)函數設置了NDELAY選項,不然read(2)將會永久得堵塞(等待)。
說到串口通信就不得不提一下經過調劑解調器通信的方式。這裏給出的程序樣例都適用於支持「其實的」標準AT命令集的調制解調器。
調制解調器是一種可以將數字信號的串行數據轉化爲模擬信號頻率的設備。經過這樣的轉換,信息就可以經過像電話線或者有線電視線纜那樣的模擬數據鏈路來傳輸了。
口語中,經常將調制解調器稱做「貓」。標準的電話調制解調器可以將串行數據轉化爲可以經過電話線傳輸的音頻;因爲這樣的轉化很之快又很複雜,因此假設你去聽一下的話。這些音頻很像是大聲尖叫時發出來的聲音。
今天可以見到的調制解調器可以經過電話線每秒傳輸53000比特——5.3Kbps——的數據。還有就是,大多數調制解調器都使用數據壓縮技術,這樣就可以將某些類型數據的傳輸比特率提升到100kbps。
於調制解調器通信的第一步就是要以原始輸入模式打開和配置串口。
int fd; struct termios options;
/* open the port */ fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY); fcntl(fd, F_SETFL, 0); /* get the current options */ tcgetattr(fd, &options); /* set raw input, 1 second timeout */ options.c_cflag |= (CLOCAL | CREAD); options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); options.c_oflag &= ~OPOST; options.c_cc[VMIN] = 0; options.c_cc[VTIME] = 10; /* set the options */ tcsetattr(fd, TCSANOW, &options);
接下來就需要和調制解調器創建通信鏈接。
最好的辦法就是給調制解調器發送「AT」命令。這也會讓比較僅僅能的調制解調器探測到你正在使用的波特率。
假設正確地鏈接到調制解調器上,並且調制解調器開啓電源。它會返回一個迴應信號「OK」。
int /* O - 0 = MODEM ok, -1 = MODEM bad */ init_modem(int fd) /* I - Serial port file */ { char buffer[255]; /* Input buffer */ char *bufptr; /* Current char in buffer */ int nbytes; /* Number of bytes read */ int tries; /* Number of tries so far */ for (tries = 0; tries < 3; tries ++) { /* send an AT command followed by a CR */ if (write(fd, "AT\r", 3) < 3) continue; /* read characters into our string buffer until we get a CR or NL */ bufptr = buffer; while ((nbytes = read(fd, bufptr, buffer + sizeof(buffer) - bufptr - 1)) > 0) { bufptr += nbytes; if (bufptr[-1] == '\n' | bufptr[-1] == '\r') break; } /* nul terminate the string and see if we got an OK response */ *bufptr = '\0'; if (strncmp(buffer, "OK", 2) == 0) return (0); } return (-1); }
大多數調制解調器都支持「AT」命令集。之因此這樣叫是因爲這個命令集中的每個命令都是以「AT」字符開頭。每個命令都是以第一列的AT開頭字符後面跟上特殊命令參數和一個回車符CR(八進制015)。調制解調器處理完這條命令以後會依據命令回覆一些文本消息。
經過ATD命令可以撥打一個指定號碼。除過號碼和分隔符(-)之外,你還可以指定以音頻("T")或者脈衝("P")方式撥號,暫停一秒(",")和等待撥號音("W"):
ATDT 555-1212 ATDT 18008008008W1234,1,1234 ATD T555-1212WP1234
調制解調器可能回覆如下列出的某個消息:
NO DIALTONE BUSY NO CARRIER CONNECT CONNECT baud
經過ATH命令可以讓調制解調器掛斷。
因爲,調制解調器假設在「命令」模式的話,你可能就不能打普通電話了。
假設DTR信號線掉了的話,大部分調制解調器也會掛斷。
你可以將波特率設置成0並且持續至少1秒來作到這一點。再次讓DTR掉落相同也可以把調制解調器又一次拉回命令模式。
調制解調器成功掛斷之後。它會回覆一個"NO CARRIER"回來。假設調制解調器仍然保持鏈接。它則會發送"CONNECT"或者"CONNECT baud"這種消息。
經過ATZ命令可以重置調制解調器。
重置以後它會回覆字符串"OK"。
首先,也是最重要的一點。千萬不要使用回聲輸入(input echoing)。回聲輸入會致使調制解調器和計算機之間產生反饋循環。
其次,當發送調制解調器命令時,命令必須以回車(CR)而不是換行(NL)結束。C語言中回車的字符常量是"\r"。
最後,處理調制解調器通信的時候,要必定保證你使用了調制解調器支持的波特率。儘管大多數調制解調器都支持本身主動探測波特率,但你也會注意到某些(通常是19.2kbps或者比較老的調制解調器)有侷限性。
所謂高級串口編程事實上說的就是使用更直接的底層的ioctl(2)和select(2)系統調用來操做串口。
前文中之前提到使用tcgetattr和tcsetattr函數來配置串口。UNIX環境下,這些函數都是使用ioctl(2)系統調用來實現的。
系統調用ioctl可以帶三個參數:
int ioctl(int fd, int request, ...);
顯然,fd參數對於串口編程來講就是串口設備文件的文件描寫敘述符咯。而request參數是在<termios.h>頭文件裏定義的常量,而且通常不會超出下表所列的範圍。
串口的IOCTL請求
REQUEST | 描寫敘述 | POSIX函數 |
TCGETS | Gets the current serial port settings. | tcgetattr |
TCSETS | Sets the serial port settings immediately. | tcsetattr(fd, TCSANOW, &options) |
TCSETSF | Sets the serial port settings after flushing the input and output buffers. | tcsetattr(fd, TCSAFLUSH, &options) |
TCSETSW | Sets the serial port settings after allowing the input and output buffers to drain/empty. | tcsetattr(fd, TCSADRAIN, &options) |
TCSBRK | Sends a break for the given time. | tcsendbreak, tcdrain |
TCXONC | Controls software flow control. | tcflow |
TCFLSH | Flushes the input and/or output queue. | tcflush |
TIOCMGET | Returns the state of the "MODEM" bits. | None |
TIOCMSET | Sets the state of the "MODEM" bits. | None |
FIONREAD | Returns the number of bytes in the input buffer. | None |
TIOCMGET ioctl可以取得當前調制解調器的狀態位。這個狀態位囊括了除去RXD和TXD信號線的所有RS-232信號。這些都在下表中列出。
控制信號常量
常量 | 描寫敘述 |
TIOCM_LE | DSR (data set ready/line enable) |
TIOCM_DTR | DTR (data terminal ready) |
TIOCM_RTS | RTS (request to send) |
TIOCM_ST | Secondary TXD (transmit) |
TIOCM_SR | Secondary RXD (receive) |
TIOCM_CTS | CTS (clear to send) |
TIOCM_CAR | DCD (data carrier detect) |
TIOCM_CD | Synonym for TIOCM_CAR |
TIOCM_RNG | RNG (ring) |
TIOCM_RI | Synonym for TIOCM_RNG |
TIOCM_DSR | DSR (data set ready) |
好比如下這個程序片斷,你可以經過給ioctl帶一個用來保存狀態位的整形變量的指針來取得狀態位。
#include <unistd.h> #include <termios.h> int fd; int status; ioctl(fd, TIOCMGET, &status);
TIOCMSET ioctl可以設置上面定義的調制解調器狀態位。
如下的樣例展現怎樣使用它來將DTR信號線設成掉線狀態。
#include <unistd.h> #include <termios.h> int fd; int status; ioctl(fd, TIOCMGET, &status); status &= ~TIOCM_DTR; ioctl(fd, TIOCMSET, &status);
可能被設置的狀態位取決於操做系統,驅動和正在使用的模式。關於更具體的信息應該去看下面你所使用的操做系統的文檔。