1. 屬性描述
串口屬於終端設備,其接口屬性用termios結構描述,如程序清單13.9所示。
程序清單13.9 termios結構
struct termios {
tcflag_t c_cflag/* 控制標誌*/
tcflag_t c_iflag;/* 輸入標誌*/
tcflag_t c_oflag;/* 輸出標誌*/
tcflag_t c_lflag;/* 本地標誌*/
tcflag_t c_cc[NCCS];/* 控制字符*/
};
粗略而言,
控制標誌影響到RS-232串行線(如:忽略調制解調器的狀態線、每個字符需要一個或兩個停止位等),
輸入標誌由終端設備驅動程序用來控制字符的輸入(如:剝除輸入字節的第8位,允許輸入奇偶校驗等),
輸出控制則控制驅動程序輸出(如:執行輸出處理、將換行符映射爲CR/LF等),
本地標誌影響驅動程序和用戶之間的接口(如:本地回顯的開和關等),
c_cc數組則包含了所有可以更改的特殊字符。
(1)控制標誌
c_cflag成員控制着波特率、數據位、奇偶校驗、停止位以及流控制,表13.4列出了c_cflag可用的部分選項。
c_cflag成員的CREAD和CLOCAL選項通常是要啓用的,這兩個選項使驅動程序啓動接收字符裝置,同時忽略串口信號線的狀態。
標誌 |
說明 |
標誌 |
說明 |
CBAUD |
波特率位屏蔽 |
CSIZE |
數據位屏蔽 |
B0 |
0位/秒(掛起) |
CS5 |
5位數據位 |
B110 |
100位/秒 |
CS6 |
6位數據位 |
B134 |
134位/秒 |
CS7 |
7位數據位 |
B1200 |
1200位/秒 |
CS8 |
8位數據位 |
B2400 |
2400位/秒 |
CSTOPB |
2位停止位,否則爲1位 |
B4800 |
4800位/秒 |
CREAD |
啓動接收 |
B9600 |
9600位/秒 |
PARENB |
進行奇偶校驗 |
B19200 |
19200位/秒 |
PARODD |
奇校驗,否則爲偶校驗 |
B57600 |
57600位/秒 |
HUPCL |
最後關閉時斷開 |
B115200 |
115200位/秒 |
CLOCAL |
忽略調製調解器狀態行 |
B460800 |
460800位/秒 |
— |
— |
(2)輸入標誌
c_iflag成員負責控制串口輸入數據的處理,表13.5所示是c_iflag的部分可用標誌。
標誌 |
說明 |
INPCK |
打開輸入奇偶校驗 |
IGNPAR |
忽略奇偶錯字符 |
PARMRK |
標記奇偶錯 |
ISTRIP |
剝除字符第8位 |
IXON |
啓用/停止輸出控制流起作用 |
IXOFF |
啓用/停止輸入控制流起作用 |
IGNBRK |
忽略BREAK條件 |
INLCR |
將輸入的NL轉換爲CR |
IGNCR |
忽略CR |
ICRNL |
將輸入的CR轉換爲NL |
設置輸入校驗
當c_cflag成員的PARENB(奇偶校驗)選項啓用時,c_iflag的也應啓用奇偶校驗選項。
操作方法是啓用INPCK和ISTRIP選項:
options.c_iflag |= (INPCK | ISTRIP);
注意:IGNPAR選項在一些場合的應用帶有一定的危險性,它指示串口驅動程序忽略奇偶校驗錯誤,也就是說,IGNPAR使奇偶校驗出錯的字符也通過輸入。這在測試通信鏈路的質量時也許有用,但在通常的數據通信應用中不應使用。
設置軟件流控制
使用軟件流控制是啓用IXON、IXOFF和IXANY選項:
options.c_iflag |= (IXON | IXOFF | IXANY);
相反,要禁用軟件流控制是禁止上面的選項:
options.c_iflag &= ~(IXON | IXOFF | IXANY);
(3)輸出標誌
c_oflag成員管理輸出過濾,如表13.6所示是c_oflag成員的部分選項標誌。
標誌 |
說明 |
BSDLY |
退格延遲屏蔽 |
CMSPAR |
標誌或空奇偶性 |
CRDLY |
CR延遲屏蔽 |
FFDLY |
換頁延遲屏蔽 |
OCRNL |
將輸出的CR轉換爲NL |
OFDEL |
填充符爲DEL,否則爲NULL |
OFILL |
對於延遲使用填充符 |
OLCUC |
將輸出的小寫字符轉換爲大寫字符 |
ONLCR |
將NL轉換爲CR-NL |
ONLRET |
NL執行CR功能 |
ONOCR |
在0列不輸出CR |
OPOST |
執行輸出處理 |
OXTABS |
將製表符擴充爲空格 |
啓用輸出處理
啓用輸出處理需要在c_oflag成員中啓用OPOST選項,其操作方法如下:
options.c_oflag |= OPOST;
使用原始輸出
使用原始輸出,就是禁用輸出處理,使數據能不經過處理、過濾地完整地輸出到串口接口。當OPOST被禁止,c_oflag其它選項也被忽略,其操作方法如下:
options.c_oflag &= ~OPOST;
(4)本地標誌
本地標誌c_lflag控制着串口驅動程序如何管理輸入的字符,如表13.7所示是c_lflag的部分可用標誌。
標誌 |
說明 |
ISIG |
啓用終端產生的信號 |
ICANON |
啓用規範輸入 |
XCASE |
規範大/小寫表示 |
ECHO |
進行回送 |
ECHOE |
可見擦除字符 |
ECHOK |
回送kill符 |
ECHONL |
回送NL |
NOFLSH |
在中斷或退出鍵後禁用刷清 |
IEXTEN |
啓用擴充的輸入字符處理 |
ECHOCTL |
回送控制字符爲^(char) |
ECHOPRT |
硬拷貝的可見擦除方式 |
ECHOKE |
Kill的可見擦除 |
PENDIN |
重新打印未決輸入 |
TOSTOP |
對於後臺輸出發送SIGTTOU |
選擇規範模式
規範模式是行處理的。調用read讀取串口數據時,每次返回一行數據。當選擇規範模式時,需要啓用ICANON、ECHO和ECHOE選項:
options.c_lflag |= (ICANON | ECHO | ECHOE);
當串口設備作爲用戶終端時,通常要把串口設備配置成規範模式。
選擇原始模式
在原始模式下,串口輸入數據是不經過處理的,在串口接口接收的數據被完整保留。要使串口設備工作在原始模式,需要關閉ICANON、ECHO、ECHOE和ISIG選項,其操作方法如下:
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
(5)控制字符組
c_cc數組的長度是NCCS,一般介於15-20之間。c_cc數組的每個成員的下標都用一個宏表示,表13.8列出了c_cc的部分下標標誌名及其對應說明。
標誌 |
說明 |
VINTR |
中斷 |
VQUIT |
退出 |
VERASE |
擦除 |
VEOF |
行結束 |
VEOL |
行結束 |
VMIN |
需讀取的最小字節數 |
VTIME |
與“VMIN”配合使用,是指限定的傳輸或等待的最長時間 |
在規範模式下,調用read讀取串口數據時,通常是返回一行數據。而在原始模式下,串口輸入數據是不分行的。在原始模式下,返回讀取數據的數量需要考慮兩個變量:MIN和TIME。MIN和TIME在c_cc數組中的下標名爲VMIN和VTIME。
MIN是指一次read調用期望返回的最小字節數。TIME與MIN組合使用,其具體含義分以下四種情形:
1)當MIN > 0,TIME > 0時
TIME爲接收到第一個字節後允許的數據傳輸或等待的最長分秒數(1分秒= 0.1秒)。定時器在收到第一個字節後啓動,在計時器超時之前,若已收到MIN個字節,則read返回MIN個字節,否則,在計時器超時後返回實際接收到的字節。
注意:因爲只有在接收到第一個字節時才啓動,所以至少可以返回1個字節。這種情形中,在接到第一個字節之前,調用者阻塞。如果在調用read時數據已經可用,則如同在read後數據立即被接到一樣。
2)當MIN > 0,TIME = 0時
MIN個字節完整接收後,read才返回,這可能會造成read無限期地阻塞。
3)當MIN = 0, TIME > 0時
TIME爲允許等待的最大時間,計時器在調用read時立即啓動,在串口接到1字節數據或者計時器超時後即返回,如果是計時器超時,則返回0。
4)當MIN = 0,TIME = 0時
如果有數據可用,則read最多返回所要求的字節數,如果無數據可用,則read立即返回0。
2. 屬性設置
使用函數tcgetattr和tcsetattr可以獲取和設置串口termios結構屬性,如程序清單13.10所示。
程序清單13.10設置和獲取termios結構屬性
#include <termios.h>/* 使用終端接口函數需要使用此頭文件*/
int tcgetattr(int fd, struct termios *termptr);
int tcsetattr(int fd, int opt, const struct termios *termptr);
其中:fd爲串口設備文件描述符,termptr參數在tcgetattr函數中是用於存放串口設置的termios結構體,opt是整形變量,使用方法如下:
1)TCSANOW:更改立即發生;
2)TCSADRAIN:發送了所有輸出後更改才發生,若更改輸出參數則應用此選項;
3)TCSAFLUSH:發送了所有輸出後更改才發生,更進一步,在更改發生時未讀的所有輸入數據被刪除(Flush)。
在串口驅動程序裏,有輸入緩衝區和輸出緩衝區。在改變串口屬性時,緩衝區中的數據可能還存在,這時需要考慮到更改後的屬性什麼時候起作用。tcsetattr的參數opt可以指定在什麼時候新的串口屬性才起作用。
上述兩函數執行時,若成功則返回0,若出錯則返回-1。
掌握瞭如何獲取和設置串口的屬性結構後,下面將介紹串口主要屬性的修改,即修改termios結構體的成員。
termios結構體的各個成員的各個選項中除需要用屏蔽標誌的選項外(如波特率選項、數據位選項等),都是按位表示的,對這些選項的設置或清除可以直接用“^”或“&”邏輯運算來完成。
需要用屏蔽標誌的選項的話則需要先用“&”運算清除原設置,再用“^”運算設置新選項。例如,爲了設置字符長度,需先用字符長度屏蔽標誌CSIZE將表示字符長度的位清0,然後再將對應位設置爲CS5、CS6、CS7或CS8。
(1)設置波特率
串口的輸入和輸出波特率可分別用cfsetispeed()和cfsetospeed()函數來設置,如程序清單13.11所示。
程序清單13.11設置串口輸入/輸出波特率函數
#include <termios.h>
int cfsetispeed(struct termios *termptr, speed_t speed);
int cfsetospeed(struct termios *termptr, speed_t speed);
這兩個函數若執行成功返回0,若出錯則返回-1。
使用這兩個函數時,應當理解輸入、輸出波特率是存在串口設備termios結構中的。在調用任一cfset函數之前,先要用tcgetattr獲得設備的termios結構。與此類似,在調用任一cfset函數後,波特率都被設置到termios結構中。爲使這種更改影響到設備,應當調用tcsetattr函數。操作方法如程序清單13.12所示。
程序清單13.12設置波特率示例
if (tcgetattr(fd, &opt)< 0) {
return ERROR;
}
cfsetispeed(&opt, B9600);
cfsetospeed(&opt, B9600);
if (tcsetattr(fd, TCSANOW, &opt)<0) {
return ERROR;
}
(2)設置數據位
設置數據位不需要專用的函數,只需要在設置數據位之前用數據位屏蔽標誌(CSIZE)把對應數據位清零,然後再設置新的數據位即可,如下所示:
options.c_cflag &= ~CSIZE;/* 先把數據位清零*/
options.c_cflag |= CS8;/* 把數據位設置爲8位*/
(3)設置奇偶校驗
正如設置數據位一樣,設置奇偶校驗是在直接在cflag成員上設置。下面是各種類型的校驗設置方法。
1)無奇偶校驗(8N1):
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
2)7位數據位奇偶校驗(7E1):
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
3)奇校驗(7O1):
options.c_cflag|= PARENB;
options.c_cflag |= PARODD;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
串口設置示例:
static int UART2_Init(void)
{
struct termios opt; //屬性描述
fdUart2 = open(DEV_UART2, O_RDWR | O_NOCTTY);
if(fdUart2 < 0)
{
perror(DEV_UART2);
return -1;
}
tcgetattr(fdUart2, &opt); //獲取串口屬性結構體對象
cfsetispeed(&opt, B38400); //設置輸入波特率
cfsetospeed(&opt, B38400); //設置輸出波特率
/* raw mode */ //偶校驗
opt.c_lflag &= ~(ECHO | ICANON | IEXTEN |ISIG); //設置本地標誌:不進行回送,關閉規範輸入,關閉擴充輸入字符處理,關閉終端產生的信號
opt.c_iflag &= ~(IXON | ISTRIP); // 關閉輸出流控制, 不剝除第8位
opt.c_iflag |= (ICRNL | BRKINT | INPCK);// 將輸入的CR轉換爲NL,使得輸入和輸出隊列被刷新 ,打開奇偶校驗
opt.c_oflag &= ~(OPOST); // 設置輸出標誌:不執行輸出處理
opt.c_cflag &= ~(PARODD | CSIZE); // 關閉輸入輸出是奇校驗 ,使用屏蔽位
opt.c_cflag |= (CS8 | CLOCAL | CREAD | PARENB);//8位數據位,保證程序不會佔用串口,能夠從串口讀取輸入數據,允許輸出產生奇偶信息以及輸入的奇偶校驗
/*'DATA_LEN' bytes can be read by serial*/
opt.c_cc[VMIN] = DATA_LEN; //讀取字符的最少個
opt.c_cc[VTIME] = 1; //讀取一個字符等待1*(1/10)s
tcflush(fdUart2,TCIOFLUSH); //清空所有正在發生的IO數據
if (tcsetattr(fdUart2, TCSANOW, &opt) < 0) //激活配置(將修改後的termios數據設置到串口)
{
return -1;
}
return fdUart2;
}