|  | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 嵌入式Linux串口應用編程之串口配置 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 串口的設(she)置(zhi)主(zhu)要是設(she)置(zhi)struct termios結(jie)構體(ti)的各(ge)成員值(zhi),如下所(suo)示:     #include<termios.h> termios是在Posix規范中(zhong)定(ding)義的(de)標準(zhun)接(jie)口,表示(shi)終(zhong)(zhong)端(duan)設(she)(she)備(bei)(包括虛擬終(zhong)(zhong)端(duan)、串口等)。因為串口是一(yi)種終(zhong)(zhong)端(duan)設(she)(she)備(bei),所(suo)以(yi)通過終(zhong)(zhong)端(duan)編程(cheng)接(jie)口對其進行配(pei)置和控制(zhi)。因此在具體討論串口相(xiang)關編程(cheng)之前(qian),需要先了解一(yi)下終(zhong)(zhong)端(duan)的(de)相(xiang)關知識。 終端(duan)是指用戶與(yu)計算機進行(xing)對話的接(jie)口,如(ru)鍵(jian)盤、顯(xian)示器(qi)和串(chuan)口設備等物(wu)理設備,X Window上的虛(xu)擬終端(duan)。類(lei)UNIX操作系(xi)統都有文(wen)本(ben)式虛(xu)擬終端(duan),使用【Ctrl+Alt】+F1~F6鍵(jian)可以(yi)進入文(wen)本(ben)式虛(xu)擬終端(duan),在X Window上可以(yi)打開幾十個以(yi)上的圖(tu)形式虛(xu)擬終端(duan)。類(lei)UNIX操作系(xi)統的虛(xu)擬終端(duan)有xterm、rxvt、zterm、eterm等,而(er)Windows上有crt、putty等虛(xu)擬終端(duan)。   終端有三種工(gong)作模式(shi),分別為規范模式(shi)(canonical mode)、非規范模式(shi)(non-canonical mode)和原(yuan)始模式(shi)(raw mode)。 通過在termios結(jie)構(gou)的(de)c_lflag中設置ICANNON標志來定(ding)義(yi)終端是以規范模(mo)式(shi)(shi)(設置ICANNON標志)還(huan)是以非規范模(mo)式(shi)(shi)(清除ICANNON標志)工作,默認(ren)情(qing)況為規范模(mo)式(shi)(shi)。 在規范(fan)模式下(xia),所(suo)有的(de)(de)(de)輸(shu)入是(shi)基于(yu)行(xing)(xing)(xing)進行(xing)(xing)(xing)處(chu)理的(de)(de)(de)。在用(yong)(yong)戶(hu)輸(shu)入一(yi)(yi)個行(xing)(xing)(xing)結(jie)束符(fu)(回(hui)車(che)(che)符(fu)、EOF等)之前,系統(tong)調用(yong)(yong)read()函(han)數(shu)(shu)是(shi)讀(du)(du)不到用(yong)(yong)戶(hu)輸(shu)入的(de)(de)(de)任何字(zi)(zi)符(fu)的(de)(de)(de)。除了(le)EOF之外的(de)(de)(de)行(xing)(xing)(xing)結(jie)束符(fu)(回(hui)車(che)(che)符(fu)等)與普(pu)通字(zi)(zi)符(fu)一(yi)(yi)樣會被(bei)read()函(han)數(shu)(shu)讀(du)(du)取(qu)到緩沖區中。在規范(fan)模式中,行(xing)(xing)(xing)編輯是(shi)可行(xing)(xing)(xing)的(de)(de)(de),而且一(yi)(yi)次(ci)調用(yong)(yong)read()函(han)數(shu)(shu)多只(zhi)能讀(du)(du)取(qu)一(yi)(yi)行(xing)(xing)(xing)數(shu)(shu)據(ju)。如果在read()函(han)數(shu)(shu)中被(bei)請(qing)求讀(du)(du)取(qu)的(de)(de)(de)數(shu)(shu)據(ju)字(zi)(zi)節數(shu)(shu)小(xiao)于(yu)當(dang)前行(xing)(xing)(xing)可讀(du)(du)取(qu)的(de)(de)(de)字(zi)(zi)節數(shu)(shu),則read()函(han)數(shu)(shu)只(zhi)會讀(du)(du)取(qu)被(bei)請(qing)求的(de)(de)(de)字(zi)(zi)節數(shu)(shu),剩下(xia)的(de)(de)(de)字(zi)(zi)節下(xia)次(ci)再被(bei)讀(du)(du)取(qu)。 在非(fei)規范模式(shi)下,所有(you)(you)的(de)輸入是即時有(you)(you)效的(de),不(bu)需(xu)要用戶另外輸入行(xing)結束符,而且(qie)不(bu)可進行(xing)行(xing)編輯(ji)。在非(fei)規范模式(shi)下,對參數MIN(c_cc[VMIN])和(he)TIME(c_cc[VTIME])的(de)設置(zhi)(zhi)決定(ding)read()函數的(de)調(diao)用方(fang)式(shi)。設置(zhi)(zhi)可以有(you)(you)4種不(bu)同的(de)情況。     ● MIN = 0和TIME = 0:read()函數立即返回。若有可讀數據,則讀取數據并返回被讀取的字節數,否則讀取失敗并返回0。 按照嚴(yan)格意義(yi)來講,原(yuan)始(shi)(shi)模式(shi)(shi)是一種特(te)殊的非規范模式(shi)(shi)。在(zai)原(yuan)始(shi)(shi)模式(shi)(shi)下,所有的輸入數(shu)據以(yi)字節(jie)為單位被處(chu)理。在(zai)這個模式(shi)(shi)下,終端(duan)是不(bu)可(ke)(ke)回(hui)顯的,而且所有特(te)定的終端(duan)輸入/輸出控(kong)制(zhi)處(chu)理不(bu)可(ke)(ke)用(yong)(yong)。通過(guo)調用(yong)(yong)cfmakeraw()函數(shu)可(ke)(ke)以(yi)將終端(duan)設置為原(yuan)始(shi)(shi)模式(shi)(shi),而且該函數(shu)通過(guo)以(yi)下代碼可(ke)(ke)以(yi)得到實(shi)現:     termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP 現在(zai)講解設置串口(kou)的基本(ben)方法。如(ru)上所述,串口(kou)設置基本(ben)的操(cao)作包括波特(te)率設置,校(xiao)驗位(wei)和(he)(he)停止(zhi)位(wei)設置。在(zai)這(zhe)個結構中(zhong)為重要的是(shi)c_cflag,通過對它的賦值(zhi),用戶(hu)可以設置波特(te)率、字符大小、數據(ju)位(wei)、停止(zhi)位(wei)、奇偶校(xiao)驗位(wei)和(he)(he)硬(ying)軟流(liu)控等。另外,c_iflag和(he)(he)c_cc也是(shi)比較常(chang)用的標志(zhi)。在(zai)此主(zhu)要對這(zhe)3個成員(yuan)進行詳細說明。c_cflag支持的常(chang)量名(ming)稱如(ru)表(biao)(biao)2.11所示(shi)。其(qi)中(zhong)設置波特(te)率宏名(ming)為相應的波特(te)率數值(zhi)前加上B,由于數值(zhi)較多,本(ben)表(biao)(biao)沒有(you)全部列出。 表2.11 c_cflag支持的常量名稱 
 續表 
 在這(zhe)里,對于c_cflag成員不能(neng)直接對其(qi)初始化,而(er)要將(jiang)其(qi)通過(guo)“與(yu)”、“或”操作使用其(qi)中的某些選項。 輸入模(mo)式(shi)標志c_iflag用(yong)于控制端口接(jie)收端的字符輸入處理(li)。c_iflag支持(chi)的常量名稱,如表2.12所示。 表2.12 c_iflag支(zhi)持的常量(liang)名稱 
 續表 
 c_oflag用于控制(zhi)終端(duan)端(duan)口發(fa)送(song)出去的(de)字符處理(li),c_oflag支持的(de)常(chang)量(liang)名稱(cheng)如表2.13所示。因為現在(zai)終端(duan)的(de)速度比以前(qian)快得多,所以大部分延時掩碼幾乎(hu)沒什(shen)么(me)用途。 表(biao)2.13 c_oflag支持的常量名稱 
 c_lflag用于控(kong)制(zhi)終(zhong)端的(de)本地數據處(chu)理和(he)工作模式(shi),c_lflag所(suo)支持的(de)常量名稱如表(biao)2.14所(suo)示。 表(biao)2.14 c_lflag支(zhi)持的常量名稱 
 續表 
 c_cc定義特殊控制特性,c_cc所支持(chi)的常量(liang)名稱如表2.15所示。 表2.15 c_cc支(zhi)持的常(chang)量名稱(cheng) 
 下面就詳細講解設置串口屬(shu)性的基本流(liu)程。 1.保存原先串口配置(zhi) 首先,為(wei)了安全起見和(he)以(yi)后(hou)調試程序方便,可(ke)以(yi)先保(bao)存(cun)原先串口的配置(zhi),在這里(li)可(ke)以(yi)使用函數(shu)(shu)tcgetattr(fd, &old_cfg)。該函數(shu)(shu)得到由fd指向的終(zhong)端的配置(zhi)參數(shu)(shu),并(bing)將它們保(bao)存(cun)于termios結構變量(liang)old_cfg中。該函數(shu)(shu)還(huan)可(ke)以(yi)測(ce)試配置(zhi)是否正(zheng)確、該串口是否可(ke)用等。若調用成功,函數(shu)(shu)返回(hui)值為(wei)0,若調用失敗,函數(shu)(shu)返回(hui)值為(wei)-1,其使用如下所示:     if  (tcgetattr(fd, &old_cfg)  !=  0)  2.激活選項(xiang) CLOCAL和CREAD分別(bie)用于本地連接和接收使(shi)能,因(yin)此,首先(xian)要通過位掩碼(ma)的方式激(ji)活這(zhe)兩個選項(xiang)。 newtio.c_cflag |= CLOCAL | CREAD; 調(diao)用cfmakeraw()函數可以將終端設(she)置為原始模式(shi),在(zai)后面(mian)的實例中,采用原始模式(shi)進行串口(kou)數據(ju)通(tong)信。 cfmakeraw(&new_cfg); 3.設置波特率 設置波特率有(you)專(zhuan)門的函數,用戶(hu)不(bu)能(neng)直(zhi)接通過位掩(yan)碼來操作。設置波特率的主要函數有(you)cfsetispeed()和(he)cfsetospeed()。這兩個函數的使用很(hen)簡(jian)單,如下所示:     cfsetispeed(&\&new_cfg, B115200); cfsetispeed()函數(shu)在termios結構中設置數(shu)據(ju)輸入波特率,而cfsetospeed()函數(shu)在termios結構中設置數(shu)據(ju)輸入波特率。一(yi)般來說(shuo),用戶需將終端的輸入和輸出波特率設置成一(yi)樣的。這(zhe)幾個函數(shu)在成功時返(fan)回0,失敗時返(fan)回-1。 4.設置字(zi)符(fu)大小 與設置波特率不同,設置字(zi)符大小并沒(mei)有現成可用(yong)的(de)函數(shu),需要(yao)用(yong)位(wei)掩碼。一般首先去(qu)除數(shu)據位(wei)中的(de)位(wei)掩碼,再重新按要(yao)求(qiu)設置,如下(xia)所示(shi):     new_cfg.c_cflag &= ~CSIZE; /* 用數據位掩碼清空數據位設置 */ 5.設(she)置奇偶校驗位 設置(zhi)奇偶(ou)校(xiao)驗(yan)(yan)(yan)位需要用(yong)到termios中(zhong)的兩個成員:c_cflag和c_iflag。首先(xian)要激(ji)活(huo)c_cflag中(zhong)的校(xiao)驗(yan)(yan)(yan)位使能標志PARENB和確認是否要進行校(xiao)驗(yan)(yan)(yan),這樣會對(dui)輸出數(shu)據(ju)產(chan)生校(xiao)驗(yan)(yan)(yan)位,而對(dui)輸入(ru)數(shu)據(ju)進行校(xiao)驗(yan)(yan)(yan)檢(jian)查(cha)。同時還要激(ji)活(huo)c_iflag中(zhong)的對(dui)于輸入(ru)數(shu)據(ju)的奇偶(ou)校(xiao)驗(yan)(yan)(yan)使能(INPCK)。如使能奇校(xiao)驗(yan)(yan)(yan)時,代(dai)碼如下(xia)所示:     new_cfg.c_cflag |= (PARODD | PARENB); 而使(shi)能偶(ou)校驗時,代碼如下所示:     new_cfg.c_cflag |= PARENB; 6.設置停止(zhi)位   設置停(ting)(ting)止(zhi)位(wei)是通過激(ji)活(huo)c_cflag中的CSTOPB而實(shi)現的。若(ruo)停(ting)(ting)止(zhi)位(wei)為(wei)一個(ge)比特,則清除CSTOPB;若(ruo)停(ting)(ting)止(zhi)位(wei)為(wei)兩(liang)個(ge),則激(ji)活(huo)CSTOPB。以下分別(bie)是停(ting)(ting)止(zhi)位(wei)為(wei)一個(ge)和兩(liang)個(ge)比特時的代碼:     new_cfg.c_cflag &=  ~CSTOPB;		/* 將停止位設置為一個比特 */ 7.設置少字符和等待時間 在(zai)對接收字符和等待時(shi)間沒有特別要求的情(qing)況(kuang)下,可以將其(qi)設(she)置(zhi)為0,則在(zai)任何情(qing)況(kuang)下read()函數立即返回(hui),此時(shi)串口操作會設(she)置(zhi)為非阻塞方式,如(ru)下所示(shi):     new_cfg.c_cc[VTIME]  = 0;   8.清(qing)除(chu)串(chuan)口(kou)緩沖 由于串(chuan)口在重新設置(zhi)后,需要對(dui)當(dang)前(qian)的串(chuan)口設備進(jin)行(xing)適當(dang)的處(chu)理,這時就可(ke)調用在<termios.h>中(zhong)聲明的tcdrain()、tcflow()、tcflush()等函數來處(chu)理目前(qian)串(chuan)口緩沖中(zhong)的數據,它們的格式(shi)如下(xia)所示(shi):     int tcdrain(int fd); /* 使程序阻塞,直到輸出緩沖區的數據全部發送完畢 */ 在(zai)本實例中(zhong)使用(yong)tcflush()函數,對于在(zai)緩沖區中(zhong)尚(shang)未(wei)傳輸的(de)數據(ju),或者(zhe)收到的(de)但是尚(shang)未(wei)讀取的(de)數據(ju),其處理方法(fa)取決(jue)于queue_selector的(de)值(zhi),它可能的(de)取值(zhi)有以下幾種。     ● TCIFLUSH:對接收到而未被讀取的數據進行清空處理。   如在本例中所采用的是(shi)第一種方法,當然可以使用TCIOFLUSH參數(shu): tcflush(fd, TCIFLUSH); 9.激活配置(zhi) 在(zai)完成全部(bu)串口配置后,要激活剛(gang)才的(de)配置并使配置生效。這里(li)用(yong)到(dao)的(de)函(han)數是tcsetattr(),它的(de)函(han)數原型是: tcsetattr(int fd, int optional_actions, const struct termios *termios_p);   其(qi)中,參數termios_p是termios類型的新配置變量。   參數optional_actions可能的取值有以下3種。     ● TCSANOW:配置的修改立即生效。   該(gai)函數若調用成功則(ze)返(fan)回0,若失敗則(ze)返(fan)回-1,代碼如下所示:     if ((tcsetattr(fd, TCSANOW, &new_cfg)) != 0) 下面(mian)給(gei)出了(le)串口配(pei)置(zhi)(zhi)的(de)完整函(han)(han)數(shu)。為了(le)函(han)(han)數(shu)的(de)通用(yong)(yong)性(xing),通常將常用(yong)(yong)的(de)選(xuan)項都在函(han)(han)數(shu)中列出,這樣可以(yi)(yi)大(da)(da)大(da)(da)方(fang)便(bian)以(yi)(yi)后用(yong)(yong)戶(hu)的(de)調試使用(yong)(yong)。該設置(zhi)(zhi)函(han)(han)數(shu)如下所示(shi):     int set_com_config(int fd,int baud_rate,  本文選自華清遠見嵌入式培訓教材《從實踐中學嵌入式Linux應用程序開發》 熱點鏈接(jie):   
         1、嵌入式Linux串口應用編程基礎知識 |