![]() |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
嵌入式Linux串口應(yīng)用編程之串口配置 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
串口的設(shè)置主要是設(shè)置struct termios結(jié)構(gòu)體的各成員值,如下所示: #include<termios.h> termios是在Posix規(guī)范中定義的標(biāo)準(zhǔn)接口,表示終端設(shè)備(包括虛擬終端、串口等)。因為串口是一種終端設(shè)備,所以通過終端編程接口對其進行配置和控制。因此在具體討論串口相關(guān)編程之前,需要先了解一下終端的相關(guān)知識。 終端是指用戶與計算機進行對話的接口,如鍵盤、顯示器和串口設(shè)備等物理設(shè)備,X Window上的虛擬終端。類UNIX操作系統(tǒng)都有文本式虛擬終端,使用【Ctrl+Alt】+F1~F6鍵可以進入文本式虛擬終端,在X Window上可以打開幾十個以上的圖形式虛擬終端。類UNIX操作系統(tǒng)的虛擬終端有xterm、rxvt、zterm、eterm等,而Windows上有crt、putty等虛擬終端。 終端有三種工作模式,分別為規(guī)范模式(canonical mode)、非規(guī)范模式(non-canonical mode)和原始模式(raw mode)。 通過在termios結(jié)構(gòu)的c_lflag中設(shè)置ICANNON標(biāo)志來定義終端是以規(guī)范模式(設(shè)置ICANNON標(biāo)志)還是以非規(guī)范模式(清除ICANNON標(biāo)志)工作,默認(rèn)情況為規(guī)范模式。 在規(guī)范模式下,所有的輸入是基于行進行處理的。在用戶輸入一個行結(jié)束符(回車符、EOF等)之前,系統(tǒng)調(diào)用read()函數(shù)是讀不到用戶輸入的任何字符的。除了EOF之外的行結(jié)束符(回車符等)與普通字符一樣會被read()函數(shù)讀取到緩沖區(qū)中。在規(guī)范模式中,行編輯是可行的,而且一次調(diào)用read()函數(shù)多只能讀取一行數(shù)據(jù)。如果在read()函數(shù)中被請求讀取的數(shù)據(jù)字節(jié)數(shù)小于當(dāng)前行可讀取的字節(jié)數(shù),則read()函數(shù)只會讀取被請求的字節(jié)數(shù),剩下的字節(jié)下次再被讀取。 在非規(guī)范模式下,所有的輸入是即時有效的,不需要用戶另外輸入行結(jié)束符,而且不可進行行編輯。在非規(guī)范模式下,對參數(shù)MIN(c_cc[VMIN])和TIME(c_cc[VTIME])的設(shè)置決定read()函數(shù)的調(diào)用方式。設(shè)置可以有4種不同的情況。 ● MIN = 0和TIME = 0:read()函數(shù)立即返回。若有可讀數(shù)據(jù),則讀取數(shù)據(jù)并返回被讀取的字節(jié)數(shù),否則讀取失敗并返回0。 按照嚴(yán)格意義來講,原始模式是一種特殊的非規(guī)范模式。在原始模式下,所有的輸入數(shù)據(jù)以字節(jié)為單位被處理。在這個模式下,終端是不可回顯的,而且所有特定的終端輸入/輸出控制處理不可用。通過調(diào)用cfmakeraw()函數(shù)可以將終端設(shè)置為原始模式,而且該函數(shù)通過以下代碼可以得到實現(xiàn): termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP 現(xiàn)在講解設(shè)置串口的基本方法。如上所述,串口設(shè)置基本的操作包括波特率設(shè)置,校驗位和停止位設(shè)置。在這個結(jié)構(gòu)中為重要的是c_cflag,通過對它的賦值,用戶可以設(shè)置波特率、字符大小、數(shù)據(jù)位、停止位、奇偶校驗位和硬軟流控等。另外,c_iflag和c_cc也是比較常用的標(biāo)志。在此主要對這3個成員進行詳細(xì)說明。c_cflag支持的常量名稱如表2.11所示。其中設(shè)置波特率宏名為相應(yīng)的波特率數(shù)值前加上B,由于數(shù)值較多,本表沒有全部列出。 表2.11 c_cflag支持的常量名稱
續(xù)表
在這里,對于c_cflag成員不能直接對其初始化,而要將其通過“與”、“或”操作使用其中的某些選項。 輸入模式標(biāo)志c_iflag用于控制端口接收端的字符輸入處理。c_iflag支持的常量名稱,如表2.12所示。 表2.12 c_iflag支持的常量名稱
續(xù)表
c_oflag用于控制終端端口發(fā)送出去的字符處理,c_oflag支持的常量名稱如表2.13所示。因為現(xiàn)在終端的速度比以前快得多,所以大部分延時掩碼幾乎沒什么用途。 表2.13 c_oflag支持的常量名稱
c_lflag用于控制終端的本地數(shù)據(jù)處理和工作模式,c_lflag所支持的常量名稱如表2.14所示。 表2.14 c_lflag支持的常量名稱
續(xù)表
c_cc定義特殊控制特性,c_cc所支持的常量名稱如表2.15所示。 表2.15 c_cc支持的常量名稱
下面就詳細(xì)講解設(shè)置串口屬性的基本流程。 1.保存原先串口配置 首先,為了安全起見和以后調(diào)試程序方便,可以先保存原先串口的配置,在這里可以使用函數(shù)tcgetattr(fd, &old_cfg)。該函數(shù)得到由fd指向的終端的配置參數(shù),并將它們保存于termios結(jié)構(gòu)變量old_cfg中。該函數(shù)還可以測試配置是否正確、該串口是否可用等。若調(diào)用成功,函數(shù)返回值為0,若調(diào)用失敗,函數(shù)返回值為-1,其使用如下所示: if (tcgetattr(fd, &old_cfg) != 0) 2.激活選項 CLOCAL和CREAD分別用于本地連接和接收使能,因此,首先要通過位掩碼的方式激活這兩個選項。 newtio.c_cflag |= CLOCAL | CREAD; 調(diào)用cfmakeraw()函數(shù)可以將終端設(shè)置為原始模式,在后面的實例中,采用原始模式進行串口數(shù)據(jù)通信。 cfmakeraw(&new_cfg); 3.設(shè)置波特率 設(shè)置波特率有專門的函數(shù),用戶不能直接通過位掩碼來操作。設(shè)置波特率的主要函數(shù)有cfsetispeed()和cfsetospeed()。這兩個函數(shù)的使用很簡單,如下所示: cfsetispeed(&\&new_cfg, B115200); cfsetispeed()函數(shù)在termios結(jié)構(gòu)中設(shè)置數(shù)據(jù)輸入波特率,而cfsetospeed()函數(shù)在termios結(jié)構(gòu)中設(shè)置數(shù)據(jù)輸入波特率。一般來說,用戶需將終端的輸入和輸出波特率設(shè)置成一樣的。這幾個函數(shù)在成功時返回0,失敗時返回-1。 4.設(shè)置字符大小 與設(shè)置波特率不同,設(shè)置字符大小并沒有現(xiàn)成可用的函數(shù),需要用位掩碼。一般首先去除數(shù)據(jù)位中的位掩碼,再重新按要求設(shè)置,如下所示: new_cfg.c_cflag &= ~CSIZE; /* 用數(shù)據(jù)位掩碼清空數(shù)據(jù)位設(shè)置 */ 5.設(shè)置奇偶校驗位 設(shè)置奇偶校驗位需要用到termios中的兩個成員:c_cflag和c_iflag。首先要激活c_cflag中的校驗位使能標(biāo)志PARENB和確認(rèn)是否要進行校驗,這樣會對輸出數(shù)據(jù)產(chǎn)生校驗位,而對輸入數(shù)據(jù)進行校驗檢查。同時還要激活c_iflag中的對于輸入數(shù)據(jù)的奇偶校驗使能(INPCK)。如使能奇校驗時,代碼如下所示: new_cfg.c_cflag |= (PARODD | PARENB); 而使能偶校驗時,代碼如下所示: new_cfg.c_cflag |= PARENB; 6.設(shè)置停止位 設(shè)置停止位是通過激活c_cflag中的CSTOPB而實現(xiàn)的。若停止位為一個比特,則清除CSTOPB;若停止位為兩個,則激活CSTOPB。以下分別是停止位為一個和兩個比特時的代碼: new_cfg.c_cflag &= ~CSTOPB; /* 將停止位設(shè)置為一個比特 */ 7.設(shè)置少字符和等待時間 在對接收字符和等待時間沒有特別要求的情況下,可以將其設(shè)置為0,則在任何情況下read()函數(shù)立即返回,此時串口操作會設(shè)置為非阻塞方式,如下所示: new_cfg.c_cc[VTIME] = 0; 8.清除串口緩沖 由于串口在重新設(shè)置后,需要對當(dāng)前的串口設(shè)備進行適當(dāng)?shù)奶幚恚@時就可調(diào)用在<termios.h>中聲明的tcdrain()、tcflow()、tcflush()等函數(shù)來處理目前串口緩沖中的數(shù)據(jù),它們的格式如下所示: int tcdrain(int fd); /* 使程序阻塞,直到輸出緩沖區(qū)的數(shù)據(jù)全部發(fā)送完畢 */ 在本實例中使用tcflush()函數(shù),對于在緩沖區(qū)中尚未傳輸?shù)臄?shù)據(jù),或者收到的但是尚未讀取的數(shù)據(jù),其處理方法取決于queue_selector的值,它可能的取值有以下幾種。 ● TCIFLUSH:對接收到而未被讀取的數(shù)據(jù)進行清空處理。 如在本例中所采用的是第一種方法,當(dāng)然可以使用TCIOFLUSH參數(shù): tcflush(fd, TCIFLUSH); 9.激活配置 在完成全部串口配置后,要激活剛才的配置并使配置生效。這里用到的函數(shù)是tcsetattr(),它的函數(shù)原型是: tcsetattr(int fd, int optional_actions, const struct termios *termios_p); 其中,參數(shù)termios_p是termios類型的新配置變量。 參數(shù)optional_actions可能的取值有以下3種。 ● TCSANOW:配置的修改立即生效。 該函數(shù)若調(diào)用成功則返回0,若失敗則返回-1,代碼如下所示: if ((tcsetattr(fd, TCSANOW, &new_cfg)) != 0) 下面給出了串口配置的完整函數(shù)。為了函數(shù)的通用性,通常將常用的選項都在函數(shù)中列出,這樣可以大大方便以后用戶的調(diào)試使用。該設(shè)置函數(shù)如下所示: int set_com_config(int fd,int baud_rate, 本文選自華清遠(yuǎn)見嵌入式培訓(xùn)教材《從實踐中學(xué)嵌入式Linux應(yīng)用程序開發(fā)》 熱點鏈接:
1、嵌入式Linux串口應(yīng)用編程基礎(chǔ)知識 |