1. 串口簡介
串行口是計算機一種常用的接口,具有連接線少,通訊簡單,得到廣泛的使用.常用的串口是 RS-232-C 接口(又稱 EIA RS-232-C)它是在 1970 年由美國電子工業協會(EIA)聯合貝爾系統、 調制解調器廠家及計算機終端生產廠家共同制定的用於串行通訊的標准.它的全名是"數據終端設備(DTE)和數據通訊設備(DCE)之間串行二進制數據交換接口技術標准"該標准規定采用一個 25 個腳的 DB25 連接器,對連接器的每個引腳的信號內容加以規定,還對各種信號的電平加以規定.傳輸距離在碼元畸變小於 4% 的情況下,傳輸電纜長度應為 50 英尺.
Linux 操作系統從一開始就對串行口提供了很好的支持,本文就 Linux 下的串行口通訊編程進行簡單的介紹,如果要非常深入了解,建議看看本文所參考的 《Serial Programming Guide for POSIX Operating Systems》
計算機串口的引腳說明
| 序號 | 信號名稱 | 符號 | 流向 | 功能 |
| 2 | 發送數據 | TXD | DTE→DCE | DTE發送串行數據 |
| 3 | 接收數據 | RXD | DTE←DCE | DTE 接收串行數據 |
| 4 | 請求發送 | RTS | DTE→DCE | DTE 請求 DCE 將線路切換到發送方式 |
| 5 | 允許發送 | CTS | DTE←DCE | DCE 告訴 DTE 線路已接通可以發送數據 |
| 6 | 數據設備准備好 | DSR | DTE←DCE | DCE 准備好 |
| 7 | 信號地 | 信號公共地 | ||
| 8 | 載波檢測 | DCD | DTE←DCE | 表示 DCE 接收到遠程載波 |
| 20 | 數據終端准備好 | DTR | DTE→DCE | DTE 准備好 |
| 22 | 振鈴指示 | RI | DTE←DCE | 表示 DCE 與線路接通,出現振鈴 |
2. 串口操作
串口操作需要的頭文件
1 #include <stdio.h> /*標准輸入輸出定義*/ 2 #include <stdlib.h> /*標准函數庫定義*/ 3 #include <unistd.h> /*Unix 標准函數定義*/ 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <fcntl.h> /*文件控制定義*/ 7 #include <termios.h> /*PPSIX 終端控制定義*/ 8 #include <errno.h> /*錯誤號定義*/
3. 打開串口
Linux系統上一般有一個或者多個串口,而這些串口設備文件名字比較奇怪,如比下面這樣
| 操作系統 | 串口1 | 串口2 | USB/RS-232轉換器 |
| Windows | COM1 | COM2 | - |
| Linux | /dev/ttyS0 | /dev/ttyS1 | /dev/ttyUSB0 |
因為串口和其他設備一樣,在類Unix系統中都是以設備文件的形式存在的,所以,理所當然得你可以使用open(2)系統調用/函數來訪問它.但Linux系統中卻有一個稍微不方便的地方,那就是普通用戶一般不能直接訪問設備文件.你可以選擇以下方式做一些調整,以便你編寫的程序可以訪問串口.
- 改變設備文件的訪問權限設置 [#cd9bd1e0]
- 以root超級用戶的身份運行程序 [#kdd0e577]
- 將你的程序編寫位setuid程序,以串口設備所有者的身份運行程序 [#s7b703ff]
OK.假如你已經准備好了讓串口設備文件可以被所有用戶訪問,你可以在Linux系統中實驗一下下面這個程序,它可以打開計算機的串口1.
1 int fd; 2 /*以讀寫方式打開串口*/ 3 fd = open( "/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
4 if (-1 == fd){
5 /* 不能打開串口一*/
6 perror(" 提示錯誤!");
7 }
打開串口連接的時候,程序在open函數中除了Read+Write模式以外還指定了兩個選項;
標志O_NOCTTY可以告訴UNIX這個程序不會成為這個端口上的“控制終端”.如果不這樣做的話,所有的輸入,比如鍵盤上過來的Ctrl+C中止信號等等,會影響到你的進程.而有些程序比如getty(1M/8)則會在打開登錄進程的時候使用這個特性,但是通常情況下,用戶程序不會使用這個行為.
O_NDELAY標志則是告訴UNIX,這個程序並不關心DCD信號線的狀態——也就是不關心端口另一端是否已經連接.如果不指定這個標志的話,除非DCD信號線上有space電壓否則這個程序會一直睡眠.
4. 設置串口
最基本的設置串口包括波特率設置,效驗位和停止位設置.
很多系統都支持POSIX終端(串口)接口.程序可以利用這個接口來改變終端的參數,比如,波特率,字符大小等等.要使用這個端口的話,你必須將<termios.h>頭文件包含到你的程序中.這個頭文件中定義了終端控制結構體和POSIX控制函數.
與串口操作相關的最重要的兩個POSIX函數可能就是tcgetattr(3)和tcsetattr(3).顧名思義,這兩個函數分別用來取得設設置終端的屬性.調用這兩個函數的時候,你需要提供一個包含着所有串口選項的termios結構體,串口的設置主要是設置struct termios結構體的各成員值.
| 成員 | 描述 |
| c_cflag | 控制選項 |
| c_lflag | 行選項 |
| c_iflag | 輸入選項 |
| c_oflag | 輸出選項 |
| c_cc | 控制字符 |
| c_ispeed | 輸入波特率(NEW) |
| c_ospeed | 輸出波特率(NEW) |
通過termio結構體的c_cflag成員可以控制波特率,數據的比特數,parity,停止位和硬件流控制,下面這張表列出了所有可以使用的常數
| 常量 | 描述 |
| 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.這兩個選項可以保證你的程序不會變成端口的所有者,而端口所有者必須去處理發散性作業控制和掛斷信號,同時還保證了串行接口驅動會讀取過來的數據字節.
波特率常數(CBAUD,B9600等等)通常指用到那些不支持c_ispeed和c_ospeed成員的舊的接口上.后面文章將會提到如何使用其他POSIX函數來設置波特率.
千萬不要直接用使用數字來初始化c_cflag(當然還有其他標志),最好的方法是使用位運算的與或非組合來設置或者清除這個標志.不同的操作系統版本會使用不同的位模式,使用常數定義和位運算組合來避免重復工作從而提高程序的可移植性.
波特率設置
不同的操作系統會將波特率存儲在不同的位置.舊的編程接口將波特率存儲在上表所示的c_cflag成員中,而新的接口實裝則提供了c_ispeed和c_ospeed成員來保存實際波特率的值.
程序中可是使用cfsetospeed(3)和cfsetispeed(3)函數在termios結構體中設置波特率而不用去管底層操作系統接口.下面的代碼是個非常典型的設置波特率的例子.
1 struct termios options; 2 3 /* 4 * Get the current options for the port... 5 */ 6 tcgetattr(fd, &options); 7 /* 8 * Set the baud rates to 19200... 9 */ 10 cfsetispeed(&options, B19200); 11 cfsetospeed(&options, B19200); 12 13 /* 14 * Enable the receiver and set local mode... 15 */ 16 options.c_cflag |= (CLOCAL | CREAD); 17 18 /* 19 * Set the new options for the port... 20 */ 21 tcsetattr(fd, TCSANOW, &options);
函數tcgetattr(3)會將當前串口配置回填到termio結構體option中.然后,程序設置了輸入輸出的波特率並且將本地模式(CLOCAL)和串行數據接收(CREAD)設置為有效,接着將新的配置作為參數傳遞給函數tcsetattr(3).常量TCSANOW標志所有改變必須立刻生效而不用等到數據傳輸結束.其他另一些常數可以保證等待數據結束或者刷新輸入輸出之后再生效.
| 常量 | 描述 |
| 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).
設置字符大小
設置字符大小的時候,這里卻沒有像設置波特率那么方便的函數.所以,程序中需要一些位掩碼運算來把事情搞定.字符大小以比特為單位指定:
1 options.c_flag &= ~CSIZE; /* Mask the character size bits */ 2 options.c_flag |= CS8; /* Select 8 data bits */
設置奇偶校驗和停止位
與設置字符大小的方式差不多,這里仍然需要組合一些位掩碼來將奇偶校驗設為有效和奇偶校驗的類型.UNIX串口驅動可以生成even,odd和no parity位碼.設置space奇偶校驗需要耍點小手段.
- No parity (8N1)
1 options.c_cflag &= ~PARENB 2 options.c_cflag &= ~CSTOPB 3 options.c_cflag &= ~CSIZE; 4 options.c_cflag |= CS8;
- Even parity (7E1)
1 options.c_cflag |= PARENB 2 options.c_cflag &= ~PARODD 3 options.c_cflag &= ~CSTOPB 4 options.c_cflag &= ~CSIZE; 5 options.c_cflag |= CS7;
- Odd parity (7O1)
1 options.c_cflag |= PARENB 2 options.c_cflag |= PARODD 3 options.c_cflag &= ~CSTOPB 4 options.c_cflag &= ~CSIZE; 5 options.c_cflag |= CS7;
- Space parity is setup the same as no parity (7S1)
1 options.c_cflag &= ~PARENB 2 options.c_cflag &= ~CSTOPB 3 options.c_cflag &= ~CSIZE; 4 options.c_cflag |= CS8;
1 /** 2 *@brief 設置串口數據位,停止位和效驗位 3 *@param fd 類型 int 打開的串口文件句柄 4 *@param databits 類型 int 數據位 取值 為 7 或者8 5 *@param stopbits 類型 int 停止位 取值為 1 或者2 6 *@param parity 類型 int 效驗類型 取值為N,E,O,,S 7 */ 8 int set_Parity(int fd,int databits,int stopbits,int parity) 9 { 10 struct termios options; 11 if ( tcgetattr( fd,&options) != 0) { 12 perror("SetupSerial 1"); 13 return(FALSE); 14 } 15 options.c_cflag &= ~CSIZE; 16 switch (databits) /*設置數據位數*/ 17 { 18 case 7: 19 options.c_cflag |= CS7; 20 break; 21 case 8: 22 options.c_cflag |= CS8; 23 break; 24 default: 25 fprintf(stderr,"Unsupported data size\n"); return (FALSE); 26 } 27 switch (parity) 28 { 29 case 'n': 30 case 'N': 31 options.c_cflag &= ~PARENB; /* Clear parity enable */ 32 options.c_iflag &= ~INPCK; /* Enable parity checking */ 33 break; 34 case 'o': 35 case 'O': 36 options.c_cflag |= (PARODD | PARENB); /* 設置為奇效驗*/ 37 options.c_iflag |= INPCK; /* Disnable parity checking */ 38 break; 39 case 'e': 40 case 'E': 41 options.c_cflag |= PARENB; /* Enable parity */ 42 options.c_cflag &= ~PARODD; /* 轉換為偶效驗*/ 43 options.c_iflag |= INPCK; /* Disnable parity checking */ 44 break; 45 case 'S': 46 case 's': /*as no parity*/ 47 options.c_cflag &= ~PARENB; 48 options.c_cflag &= ~CSTOPB;break; 49 default: 50 fprintf(stderr,"Unsupported parity\n"); 51 return (FALSE); 52 } 53 /* 設置停止位*/ 54 switch (stopbits) 55 { 56 case 1: 57 options.c_cflag &= ~CSTOPB; 58 break; 59 case 2: 60 options.c_cflag |= CSTOPB; 61 break; 62 default: 63 fprintf(stderr,"Unsupported stop bits\n"); 64 return (FALSE); 65 } 66 /* Set input parity option */ 67 if (parity != 'n') 68 options.c_iflag |= INPCK; 69 tcflush(fd,TCIFLUSH); 70 options.c_cc[VTIME] = 150; /* 設置超時15 seconds*/ 71 options.c_cc[VMIN] = 0; /* Update the options and do it NOW */ 72 if (tcsetattr(fd,TCSANOW,&options) != 0) 73 { 74 perror("SetupSerial 3"); 75 return (FALSE); 76 } 77 return (TRUE); 78 }
需要注意的是:
如果不是開發終端之類的,只是串口傳輸數據,而不需要串口來處理,那么使用原始模式(Raw Mode)方式來通訊,設置方式如下:
1 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/ 2 options.c_oflag &= ~OPOST; /*Output*/
設置硬件流控制
某些版本的UNIX系統支持通過CTS(Clear To Send)和RTS(Request To Send)信號線來設置硬件流控制.如果系統上定義了CNEW_RTSCTS和CRTSCTS常量,那么很可能它會支持硬件流控制.使用下面的方法將硬件流控制設置成有效:
1 options.c_cflag |= CNEW_RTSCTS; /* Also called CRTSCTS
將它設置成為無效的方法與此類似:
1 options.c_cflag &= ~CNEW_RTSCTS;
本地設置
本地模式成員變量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 |
成員變量c_lflag可以使用的常量
選擇經典輸入
經典輸入是以面向行設計的.在經典輸入模式中輸入字符會被放入一個緩沖之中,這樣可以以與用戶交互的方式編輯緩沖的內容,直到收到CR(carriage return)或者LF(line feed)字符.
選擇使用經典輸入模式的時候,你通常需要選擇ICANON,ECHO和ECHOE選項:
1 options.c_lflag |= (ICANON | ECHO | ECHOE);
選擇原始輸入
原始輸入根本不會被處理.輸入字符只是被原封不動的接收.一般情況中,如果要使用原始輸入模式,程序中需要去掉ICANON,ECHO,ECHOE和ISIG選項:
1 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
輸入選項
可以通過輸入模式成員c_iflag來控制從端口上收到的字符的輸入過程.與c_cflag一樣,c_iflag的最終值是想要使用的所有狀態的位運算OR的組合.
| 常量 | 描述 |
| 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_iflag成員可以使用的常量
設置輸入奇偶校驗選項
當程序在c_cflag中設置了奇偶校驗成員(PARENB)的時候,程序就需要將輸入奇偶校驗設置成為有效.與奇偶校驗相關的常量有INPCK,IGNPAR,PARMRK和ISTRIP.一般情況下,你可能需要選擇INPCK和ISTRIP將奇偶校驗設置為有效同時從接收字串中脫去奇偶校驗位:
1 options.c_iflag |= (INPCK | ISTRIP);
IGNPAR是一個比較危險選項,即便有錯誤發生時,它也會告訴串口驅動直接忽略奇偶校驗錯誤給數據放行.這個選項在測試鏈接的通訊質量時比較有用而通常不會被用在實際程序中.
PARMRK會導致奇偶校驗錯誤被標志成特殊字符加入到輸入流之中.如果IGNPAR選項也是有效的,那么一個NUL(八進制000)字符會被加入到發生奇偶校驗錯誤的字符前面.否則,DEL(八進制177)和NUL字符會和出錯的字符一起送出.
設置軟件流控制
軟件流控制可以通過IXON,IXOFF和IXANY常量設置成有效:
1 options.c_iflag |= (IXON | IXOFF | IXANY);
將其設置為無效的時候,很簡單,只需要對這些位取反:
1 options.c_iflag &= ~(IXON | IXOFF | IXANY);
XON(start data)和XOFF(stop data)字符卻是在c_cc數組中定義的,下面會詳細描述這個數組.
輸出選項
成員變量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成員的常量
選擇加工過的輸出
通過在c_oflag成員變量中設置OPOST選項的方法程序可以選擇加工過的輸入.
1 options.c_oflag |= OPOST;
在所有選項當中,你可能只需要使用ONLCR選項來將行分隔符映射到CR-LF組合對上.其他選項主要是歷史遺留,僅僅與行打印機和終端跟不上串行數據的年代有關.
選擇原始輸出
原始輸出方式可以通過在c_oflag中重置OPOST選項來選擇:
1 options.c_oflag &= ~OPOST;
如果OPOST選項被設置成無效的話,其他c_oflag中的選項都會失效.
控制字符
字符數組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中的控制字符
設置軟件流控制字符
用來做軟件流控制的字符包含在數組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)將會永久得阻塞(等待).
5. 讀寫串口
設置好串口之后,讀寫串口就很容易了,把串口當作文件讀寫就是.
- 發送數據
1 char buffer[1024];int Length;int nByte;nByte = write(fd, buffer ,Length)
和寫入其他設備文件的方式相同,write函數也會返回發送數據的字節數或者在發生錯誤的時候返回-1.通常,發送數據最常見的錯誤就是EIO,當調制解調器或者數據鏈路將Data Carrier Detect(DCD)信號線弄掉了,就會發生這個錯誤.而且,直至關閉端口這個情況會一直持續.
- 讀取串口數據
使用文件操作read函數讀取,如果設置為原始數據模式(Raw Date Mode)傳輸數據,那么read函數返回的字符數是實際串口收到的字符數,也就是返回從串口輸入緩沖區中實際得到的字符的個數.在不能得到數據的情況下,read(2)系統調用就會一直等着,只到有端口上新的字符可以讀取或者發生超時或者錯誤的情況發生.
1 char buff[1024];int Len;int readByte = read(fd,buff,Len);
如果需要read(2)函數迅速返回的話,可以使用操作文件的函數來實現異步讀取,如fcntl,或者select等來操作:
1 fcntl(fd, F_SETFL, FNDELAY);
標志FNDELAY可以保證read(2)函數在端口上讀不到字符的時候返回0.需要回到正常(阻塞)模式的時候,需要再次在不帶FNDELAY標志的情況下調用fcntl(2)函數:
1 fcntl(fd, F_SETFL, 0);
當然,如果你最初就是以O_NDELAY標志打開串口的,你也可在之后使用這個方法改變讀取的行為方式.
6. 關閉串口
關閉串口就是關閉文件.
1 close(fd);
關閉串口會將DTR信號線設置成low,這會導致很多調制解調器掛起.
7. 例子
下面是一個簡單的讀取串口數據的例子,使用了上面定義的一些函數和頭文件.
1 /********************************************************************** 2 代碼說明:使用串口二測試的,發送的數據是字符, 3 但是沒有發送字符串結束符號,所以接收到后,后面加上了結束符號。 4 我測試使用的是單片機發送數據到第二個串口,測試通過。 5 **********************************************************************/ 6 #define FALSE -1 7 #define TRUE 0 8 /*********************************************************************/ 9 int OpenDev(char *Dev) 10 { 11 int fd = open( Dev, O_RDWR ); //| O_NOCTTY | O_NDELAY 12 if (-1 == fd) 13 { 14 perror("Can't Open Serial Port"); 15 return -1; 16 } 17 else 18 return fd; 19 } 20 int main(int argc, char **argv){ 21 int fd; 22 int nread; 23 char buff[512]; 24 char *dev = "/dev/ttyS1"; //串口二 25 fd = OpenDev(dev); 26 set_speed(fd,19200); 27 if (set_Parity(fd,8,1,'N') == FALSE) { 28 printf("Set Parity Error\n"); 29 exit (0); 30 } 31 while (1) //循環讀取數據 32 { 33 while((nread = read(fd, buff, 512))>0) 34 { 35 printf("\nLen %d\n",nread); 36 buff[nread+1] = '\0'; 37 printf( "\n%s", buff); 38 } 39 } 40 //close(fd); 41 // exit (0); 42 }
