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