国产成人精品三级麻豆,色综合天天综合高清网,亚洲精品夜夜夜,国产成人综合在线女婷五月99播放,色婷婷色综合激情国产日韩


嵌入式Linux串口應(yīng)用編程之串口配置

分享到:
           

    串口的設(shè)置主要是設(shè)置struct termios結(jié)構(gòu)體的各成員值,如下所示:

    #include<termios.h>
    struct termios
    {
        unsigned short c_iflag; /* 輸入模式標(biāo)志 */
        unsigned short c_oflag; /* 輸出模式標(biāo)志 */
        unsigned short c_cflag; /* 控制模式標(biāo)志 */
        unsigned short c_lflag; /* 本地模式標(biāo)志 */
        unsigned char c_line; /* 線路規(guī)程 */
        unsigned char c_cc[NCC]; /* 控制特性 */
        speed_t c_ispeed; /* 輸入速度 */
        speed_t c_ospeed; /* 輸出速度 */
    };

    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。
    ● MIN > 0和TIME = 0:read()函數(shù)會(huì)被阻塞,直到MIN個(gè)字節(jié)數(shù)據(jù)可被讀取。
    ● MIN = 0和TIME > 0:只要有數(shù)據(jù)可讀或者經(jīng)過(guò)TIME個(gè)十分之一秒的時(shí)間,read()函數(shù)則立即返回,返回值為被讀取的字節(jié)數(shù)。如果超時(shí)并且未讀到數(shù)據(jù),則read()函數(shù)返回0。
    ● MIN > 0和TIME > 0:當(dāng)有MIN個(gè)字節(jié)可讀或者兩個(gè)輸入字符之間的時(shí)間間隔超過(guò)TIME個(gè)十分之一秒時(shí),read()函數(shù)才返回。因?yàn)樵谳斎氲谝粋(gè)字符后系統(tǒng)才會(huì)啟動(dòng)定時(shí)器,所以,在這種情況下,read()函數(shù)至少讀取一個(gè)字節(jié)后才返回。

    按照嚴(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
    | INLCR | IGNCR | ICRNL | IXON);
    termios_p->c_oflag &= ~OPOST;
    termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    termios_p->c_cflag &= ~(CSIZE | PARENB);
    termios_p->c_cflag |= CS8;

    現(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支持的常量名稱

CBAUD 波特率的位掩碼
B0 0波特率(放棄DTR)

續(xù)表

CBAUD 波特率的位掩碼
B1800 1800波特率
B2400 2400波特率
B4800 4800波特率
B9600 9600波特率
B19200 19200波特率
B38400 38400波特率
B57600 57600波特率
B115200 115200波特率
EXTA 外部時(shí)鐘率
EXTB 外部時(shí)鐘率
CSIZE 數(shù)據(jù)位的位掩碼
CS5 5個(gè)數(shù)據(jù)位
CS6 6個(gè)數(shù)據(jù)位
CS7 7個(gè)數(shù)據(jù)位
CS8 8個(gè)數(shù)據(jù)位
CSTOPB 2個(gè)停止位(不設(shè)則是1個(gè)停止位)
CREAD 接收使能
PARENB
PARODD
校驗(yàn)位使能
使用奇校驗(yàn)而不使用偶校驗(yàn)
HUPCL 后關(guān)閉時(shí)掛線(放棄DTR)
CLOCAL 本地連接(不改變端口所有者)
CRTSCTS 硬件流控

    在這里,對(duì)于c_cflag成員不能直接對(duì)其初始化,而要將其通過(guò)“與”、“或”操作使用其中的某些選項(xiàng)。

    輸入模式標(biāo)志c_iflag用于控制端口接收端的字符輸入處理。c_iflag支持的常量名稱,如表2.12所示。

表2.12 c_iflag支持的常量名稱

INPCK 奇偶校驗(yàn)使能
IGNPAR 忽略奇偶校驗(yàn)錯(cuò)誤
PARMRK 奇偶校驗(yàn)錯(cuò)誤掩碼
ISTRIP 裁減掉第8位比特
IXON 啟動(dòng)輸出軟件流控
IXOFF 啟動(dòng)輸入軟件流控

續(xù)表

INPCK 奇偶校驗(yàn)使能
IXANY 允許輸入任意字符可以重新啟動(dòng)輸出(默認(rèn)為輸入起始字符才重啟輸出)
IGNBRK 忽略輸入終止條件
BRKINT 當(dāng)檢測(cè)到輸入終止條件時(shí)發(fā)送SIGINT信號(hào)
INLCR 將接收到的NL(換行符)轉(zhuǎn)換為CR(回車(chē)符)
IGNCR 忽略接收到的CR(回車(chē)符)
ICRNL 將接收到的CR(回車(chē)符)轉(zhuǎn)換為NL(換行符)
IUCLC 將接收到的大寫(xiě)字符映射為小寫(xiě)字符
IMAXBEL 當(dāng)輸入隊(duì)列滿時(shí)響鈴

    c_oflag用于控制終端端口發(fā)送出去的字符處理,c_oflag支持的常量名稱如表2.13所示。因?yàn)楝F(xiàn)在終端的速度比以前快得多,所以大部分延時(shí)掩碼幾乎沒(méi)什么用途。

表2.13 c_oflag支持的常量名稱

OPOST 啟用輸出處理功能,如果不設(shè)置該標(biāo)志則其他標(biāo)志都被忽略
OLCUC 將輸出中的大寫(xiě)字符轉(zhuǎn)換成小寫(xiě)字符
ONLCR 將輸出中的換行符('\n')轉(zhuǎn)換成回車(chē)符('\r')
ONOCR 如果當(dāng)前列號(hào)為0,則不輸出回車(chē)符
OCRNL 將輸出中的回車(chē)符('\r')轉(zhuǎn)換成換行符('\n')
ONLRET 不輸出回車(chē)符
OFILL 發(fā)送填充字符以提供延時(shí)
OFDEL 如果設(shè)置該標(biāo)志,則表示填充字符為DEL字符,否則為NUL字符
NLDLY 換行符延時(shí)掩碼
CRDLY 回車(chē)符延時(shí)掩碼
TABDLY 制表符延時(shí)掩碼
BSDLY 水平退格符延時(shí)掩碼
VTDLY 垂直退格符延時(shí)掩碼
FFLDY 換頁(yè)符延時(shí)掩碼

    c_lflag用于控制終端的本地?cái)?shù)據(jù)處理和工作模式,c_lflag所支持的常量名稱如表2.14所示。

表2.14 c_lflag支持的常量名稱

ISIG 若收到信號(hào)字符(INTR、QUIT等),則會(huì)產(chǎn)生相應(yīng)的信號(hào)
ICANON 啟用規(guī)范模式
ECHO 啟用本地回顯功能
ECHOE 若設(shè)置ICANON,則允許退格操作

續(xù)表

ECHOK 若設(shè)置ICANON,則KILL字符會(huì)刪除當(dāng)前行
ECHONL 若設(shè)置ICANON,則允許回顯換行符
ECHOCTL 若設(shè)置ECHO,則控制字符(制表符、換行符等)會(huì)顯示成“^X”,其中X的ASCII碼等于給相應(yīng)控制字符的ASCII碼加上0x40。例如,退格字符(0x08)會(huì)顯示為“^H”('H'的ASCII碼為0x48)
ECHOPRT 若設(shè)置ICANON和IECHO,則刪除字符(退格符等)和被刪除的字符都會(huì)被顯示
ECHOKE 若設(shè)置ICANON,則允許回顯在ECHOE和ECHOPRT中設(shè)定的KILL字符
NOFLSH 在通常情況下,當(dāng)接收到INTR、QUIT和SUSP控制字符時(shí),會(huì)清空輸入和輸出隊(duì)列。如果設(shè)置該標(biāo)志,則所有的隊(duì)列不會(huì)被清空
TOSTOP 若一個(gè)后臺(tái)進(jìn)程試圖向它的控制終端進(jìn)行寫(xiě)操作,則系統(tǒng)向該后臺(tái)進(jìn)程的進(jìn)程組發(fā)送SIGTTOU信號(hào)。該信號(hào)通常終止進(jìn)程的執(zhí)行
IEXTEN 啟用輸入處理功能

    c_cc定義特殊控制特性,c_cc所支持的常量名稱如表2.15所示。

表2.15 c_cc支持的常量名稱

VINTR 中斷控制字符,對(duì)應(yīng)鍵為Ctrl+C
VQUIT 退出操作符,對(duì)應(yīng)鍵為Ctrl+Z
VERASE 刪除操作符,對(duì)應(yīng)鍵為Backspace(BS)
VKILL 刪除行符,對(duì)應(yīng)鍵為Ctrl+U
VEOF 文件結(jié)尾符,對(duì)應(yīng)鍵為Ctrl+D
VEOL 附加行結(jié)尾符,對(duì)應(yīng)鍵為Carriage return(CR)
VEOL2 第二行結(jié)尾符,對(duì)應(yīng)鍵為L(zhǎng)ine feed(LF)
VMIN 指定少讀取的字符數(shù)
VTIME 指定讀取的每個(gè)字符之間的超時(shí)時(shí)間

    下面就詳細(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)
    {
        perror("tcgetattr");
        return -1;
    }

    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);
    cfsetospeed(&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è)置 */
    new_cfg.c_cflag |= CS8;

    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);
    new_cfg.c_iflag |= INPCK;

    而使能偶校驗(yàn)時(shí),代碼如下所示:

    new_cfg.c_cflag |= PARENB;
    new_cfg.c_cflag &= ~PARODD; /* 清除偶奇校驗(yàn)標(biāo)志,則配置為偶校驗(yàn) */
    new_cfg.c_iflag |= INPCK;

    6.設(shè)置停止位

    設(shè)置停止位是通過(guò)激活c_cflag中的CSTOPB而實(shí)現(xiàn)的。若停止位為一個(gè)比特,則清除CSTOPB;若停止位為兩個(gè),則激活CSTOPB。以下分別是停止位為一個(gè)和兩個(gè)比特時(shí)的代碼:

    new_cfg.c_cflag &= ~CSTOPB; /* 將停止位設(shè)置為一個(gè)比特 */
    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;
    new_cfg.c_cc[VMIN] = 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ā)送完畢 */
    int tcflow(int fd, int action); /* 用于暫;蛑匦麻_(kāi)始輸出 */
    int tcflush(int fd, int queue_selector); /* 用于清空輸入/輸出緩沖區(qū) */

    在本實(shí)例中使用tcflush()函數(shù),對(duì)于在緩沖區(qū)中尚未傳輸?shù)臄?shù)據(jù),或者收到的但是尚未讀取的數(shù)據(jù),其處理方法取決于queue_selector的值,它可能的取值有以下幾種。

    ● TCIFLUSH:對(duì)接收到而未被讀取的數(shù)據(jù)進(jìn)行清空處理。
    ● TCOFLUSH:對(duì)尚未傳送成功的輸出數(shù)據(jù)進(jìn)行清空處理。
    ● TCIOFLUSH:包括前兩種功能,即對(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:配置的修改立即生效。
    ● TCSADRAIN:配置的修改在所有寫(xiě)入fd的輸出都傳輸完畢之后生效。
    ● TCSAFLUSH:所有已接收但未讀入的輸入都將在修改生效之前被丟棄。

    該函數(shù)若調(diào)用成功則返回0,若失敗則返回-1,代碼如下所示:

    if ((tcsetattr(fd, TCSANOW, &new_cfg)) != 0)
    {
        perror("tcsetattr");
        return -1;
    }

    下面給出了串口配置的完整函數(shù)。為了函數(shù)的通用性,通常將常用的選項(xiàng)都在函數(shù)中列出,這樣可以大大方便以后用戶的調(diào)試使用。該設(shè)置函數(shù)如下所示:

    int set_com_config(int fd,int baud_rate,
    int data_bits, char parity, int stop_bits)
    {
        struct termios new_cfg,old_cfg;
        int speed;

        /* 保存并測(cè)試現(xiàn)有串口參數(shù)設(shè)置,在這里如果串口號(hào)等出錯(cuò),會(huì)有相關(guān)的出錯(cuò)信息 */
        if (tcgetattr(fd, &old_cfg) != 0)
        {
            perror("tcgetattr");
            return -1;
        }
        new_cfg = old_cfg;
        cfmakeraw(&new_cfg); /* 配置為原始模式 */
        new_cfg.c_cflag &= ~CSIZE;
        /* 設(shè)置波特率 */
        switch (baud_rate)
        {
            case 2400:
            {
                speed = B2400;
            }
            break;
            case 4800:
            {
                speed = B4800;
            }
            break;
            case 9600:
            {
                speed = B9600;
            }
            break;
            case 19200:
            {
                speed = B19200;
            }
            break;
            case 38400:
            {
                speed = B38400;
            }
            break;

            default:
            case 115200:
            {
                speed = B115200;
            }
            break;
        }
        cfsetispeed(&new_cfg, speed);
        cfsetospeed(&new_cfg, speed);

        switch (data_bits) /* 設(shè)置數(shù)據(jù)位 */
        {
            case 7:
            {
                new_cfg.c_cflag |= CS7;
            }
            break;

            default:
            case 8:
            {
                new_cfg.c_cflag |= CS8
;             }
            break;
        }

        switch (parity) /* 設(shè)置奇偶校驗(yàn)位 */
        {
            default:
            case 'n':
            case 'N':
            {
                new_cfg.c_cflag &= ~PARENB;
                new_cfg.c_iflag &= ~INPCK;
            }
            break;

            case 'o':
            case 'O':
            {
                new_cfg.c_cflag |= (PARODD | PARENB);
                new_cfg.c_iflag |= INPCK;
            }
            break;

            case 'e':
            case 'E':
            {
                new_cfg.c_cflag |= PARENB;
                new_cfg.c_cflag &= ~PARODD;
                new_cfg.c_iflag |= INPCK;
            }
            break;

            case 's': /* as no parity */
            case 'S':
            {
                new_cfg.c_cflag &= ~PARENB;
                new_cfg.c_cflag &= ~CSTOPB;
            }
            break;
        }

        switch (stop_bits) /* 設(shè)置停止位 */
        {
            default:
            case 1:
            {
                new_cfg.c_cflag &= ~CSTOPB;
            }
            break;

            case 2:
            {
                new_cfg.c_cflag |= CSTOPB;
            }
        }

        /* 設(shè)置等待時(shí)間和小接收字符 */
        new_cfg.c_cc[VTIME] = 0;
        new_cfg.c_cc[VMIN] = 1;
        tcflush(fd, TCIFLUSH); /* 處理未接收字符 */
        if ((tcsetattr(fd, TCSANOW, &new_cfg)) != 0) /* 激活新配置 */
        {
            perror("tcsetattr");
            return -1;
        }
        return 0;
    }

    本文選自華清遠(yuǎn)見(jiàn)嵌入式培訓(xùn)教材《從實(shí)踐中學(xué)嵌入式Linux應(yīng)用程序開(kāi)發(fā)》

   熱點(diǎn)鏈接:

   1、嵌入式Linux串口應(yīng)用編程基礎(chǔ)知識(shí)
   2、Linux下多路復(fù)用I/O接口
   3、linux 文件鎖的實(shí)現(xiàn)及其應(yīng)用
   4、底層文件I/O操作的系統(tǒng)調(diào)用
   5、Linux中的文件及文件描述符

更多新聞>>