LINUX 使用tcgetattr與tcsetattr函式控制終端
為了便於通過程式來獲得和修改終端引數,Linux還提供了tcgetattr函式和tcsetattr函式。tcgetattr用於獲取終端的相關引數,而tcsetattr函式用於設定終端引數。這兩個函式的具體資訊如表6.2所示。
表6.2 tcgetattr函式和tcsetattr函式
標頭檔案 |
|||
函式形式 |
int tcgetattr(int fd, struct termios *termios_p); int tcsetattr(int fd, int optional_actions, const struct termios *termios_p); |
||
返回值 |
成功 |
失敗 |
是否設定errno |
0 |
−1 |
是 |
說明:tcgetattr函式用於獲取與終端相關的引數。引數fd為終端的檔案描述符,返回的結果儲存在termios結構體中,該結構體一般包括如下的成員:
-
tcflag_t c_iflag;
-
tcflag_t c_oflag;
-
tcflag_t c_cflag;
-
tcflag_t c_lflag;
-
cc_t c_cc[NCCS];
其具體意義如下。
c_iflag:輸入模式標誌,控制終端輸入方式,具體引數如表6.3所示。
表6.3 c_iflag引數表
鍵 值 |
說 明 |
IGNBRK |
忽略BREAK鍵輸入 |
BRKINT |
如果設定了IGNBRK,BREAK鍵的輸入將被忽略,如果設定了BRKINT ,將產生SIGINT中斷 |
IGNPAR |
忽略奇偶校驗錯誤 |
PARMRK |
標識奇偶校驗錯誤 |
INPCK |
允許輸入奇偶校驗 |
ISTRIP |
去除字元的第8個位元 |
INLCR |
將輸入的NL(換行)轉換成CR(回車) |
IGNCR |
忽略輸入的回車 |
ICRNL |
將輸入的回車轉化成換行(如果IGNCR未設定的情況下) |
IUCLC |
將輸入的大寫字元轉換成小寫字元(非POSIX) |
IXON |
允許輸入時對XON/XOFF流進行控制 |
IXANY |
輸入任何字元將重啟停止的輸出 |
IXOFF |
允許輸入時對XON/XOFF流進行控制 |
IMAXBEL |
當輸入佇列滿的時候開始響鈴,Linux在使用該引數而是認為該引數總是已經設定 |
c_oflag:輸出模式標誌,控制終端輸出方式,具體引數如表6.4所示。
表6.4 c_oflag引數
鍵 值 |
說 明 |
OPOST |
處理後輸出 |
OLCUC |
將輸入的小寫字元轉換成大寫字元(非POSIX) |
ONLCR |
將輸入的NL(換行)轉換成CR(回車)及NL(換行) |
OCRNL |
將輸入的CR(回車)轉換成NL(換行) |
ONOCR |
第一行不輸出回車符 |
ONLRET |
不輸出回車符 |
OFILL |
傳送填充字元以延遲終端輸出 |
OFDEL |
以ASCII碼的DEL作為填充字元,如果未設定該引數,填充字元將是NUL(‘\0’)(非POSIX) |
NLDLY |
換行輸出延時,可以取NL0(不延遲)或NL1(延遲0.1s) |
CRDLY |
回車延遲,取值範圍為:CR0、CR1、CR2和 CR3 |
TABDLY |
水平製表符輸出延遲,取值範圍為:TAB0、TAB1、TAB2和TAB3 |
BSDLY |
空格輸出延遲,可以取BS0或BS1 |
VTDLY |
垂直製表符輸出延遲,可以取VT0或VT1 |
FFDLY |
換頁延遲,可以取FF0或FF1 |
c_cflag:控制模式標誌,指定終端硬體控制資訊,具體引數如表6.5所示。
LINUX 使用tcgetattr函式與tcsetattr函式控制終端二
2009-11-24 15:30
表6.5 c_oflag引數
c_lflag:本地模式標誌,控制終端編輯功能,具體引數如表6.6所示。 表6.6 c_lflag引數
c_cc[NCCS]:控制字元,用於儲存終端驅動程式中的特殊字元,如輸入結束符等。c_cc中定義瞭如表6.7所示的控制字元。 表6.7 c_cc支援的控制字元
tcsetattr函式用於設定終端的相關引數。引數fd為開啟的終端檔案描述符,引數optional_actions用於控制修改起作用的時間,而結構體termios_p中儲存了要修改的引數。 錯誤資訊: 例項演練:
使用gcc編譯p6.2.c程式,得到名為p6.2的可執行程式。在執行p6.2程式前,按“Ctrl+D”可以使終端結束。執行p6.2程式後,按“Ctrl+D”失去了作用,而輸入“Ctrl+G”實現了原來“Ctrl+D”的功能 |
轉自:http://blog.chinaunix.net/uid-10747583-id-97303.html
======================================================================================================================
串列埠操作需要的標頭檔案
-
#include /*標準輸入輸出定義*/
-
#include /*標準函式庫定義*/
-
#include /*Unix 標準函式定義*/
-
#include
-
#include
-
#include /*檔案控制定義*/
-
#include /*PPSIX 終端控制定義*/
-
#include /*錯誤號定義*/
1.開啟串列埠
在前面已經提到linux下的串列埠訪問是以裝置檔案形式進行的,所以開啟串列埠也即是開啟檔案的操作。函式原型可以如下所示:
int open(“DE_name”,int open_Status)
引數說明:
(1)DE_name:要開啟的裝置檔名
比如要開啟串列埠1,即為/dev/ttyS0。
(2)open_Status:檔案開啟方式,可採用下面的檔案開啟模式:
O_RDONLY:以只讀方式開啟檔案
O_WRONLY:以只寫方式開啟檔案
O_RDWR:以讀寫方式開啟檔案
O_APPEND:寫入資料時新增到檔案末尾
O_CREATE:如果檔案不存在則產生該檔案,使用該標誌需要設定訪問許可權位mode_t
O_EXCL:指定該標誌,並且指定了O_CREATE標誌,如果開啟的檔案存在則會產生一個錯誤
O_TRUNC:如果檔案存在並且成功以寫或者只寫方式開啟,則清除檔案所有內容,使得檔案長度變為0
O_NOCTTY:如果開啟的是一個終端裝置,這個程式不會成為對應這個埠的控制終端,如果沒有該標誌,任何一個輸入,例如鍵盤中止訊號等,都將影響程序。
O_NONBLOCK:該標誌與早期使用的O_NDELAY標誌作用差不多。程式不關心DCD訊號線的狀態,如果指定該標誌,程序將一直在休眠狀態,直到DCD訊號線為0。
函式返回值:
成功返回檔案描述符,如果失敗返回-1
例如:
在 Linux 下串列埠檔案是位於 /dev 下的。串列埠一 為 /dev/ttyS0,串列埠二 為 /dev/ttyS1。開啟串列埠是通過使用標準的檔案開啟函式操作:
-
int fd;
-
/*以讀寫方式開啟串列埠*/
-
fd = open( "/dev/ttyS0", O_RDWR);
-
if (fd==-1)
-
{
-
/* 不能開啟串列埠一*/
-
perror(" 提示錯誤!");
-
}
2.設定串列埠
最基本的設定串列埠包括波特率設定,效驗位和停止位設定。串列埠的設定主要是設定
struct termios 結構體的各成員值。
-
struct termio
-
{ unsigned short c_iflag; /* 輸入模式標誌 */
-
unsigned short c_oflag; /* 輸出模式標誌 */
-
unsigned short c_cflag; /* 控制模式標誌*/
-
unsigned short c_lflag; /* local mode flags */
-
unsigned char c_line; /* line discipline */
-
unsigned char c_cc[NCC]; /* control characters */
-
};
設定這個結構體很複雜,我這裡就只說說常見的一些設定:
2.1 波特率設定
波特率的設定定義在,其包含在標頭檔案裡。
常用的波特率常數如下:
B0-------à0 B1800-------à1800
B50-----à50 B2400------à2400
B75-----à75 B4800------à4800
B110----à110 B9600------à9600
B134----à134.5 B19200-----à19200
B200----à200 B38400------à38400
B300----à300 B57600------à57600
B600----à600 B76800------à76800
B1200---à1200 B115200-----à115200
假定程式中想要設定通訊的波特率,使用cfsetispeed( )和cfsetospeed( )函式來操作,獲取波特率資訊是通過cfgetispeed()和cfgetospeed()函式來完成的。
比如可以這樣來指定串列埠通訊的波特率:
-
#include //標頭檔案定義
-
........
-
.......
-
struct termios opt; /*定義指向termios 結構型別的指標opt*/
-
/***************以下設定通訊波特率****************/
-
cfsetispeed(&opt,B9600 ); /*指定輸入波特率,9600bps*/
-
cfsetospeed(&opt,B9600);/*指定輸出波特率,9600bps*/
-
/************************************************/
-
.........
-
..........
一般來說,輸入、輸出的波特率應該是一致的。
下面是另一個修改波特率的程式碼:
-
struct termios Opt;
-
tcgetattr(fd, &Opt);
-
cfsetispeed(&Opt,B19200); /*設定為19200Bps*/
-
cfsetospeed(&Opt,B19200);
-
tcsetattr(fd,TCANOW,&Opt);
設定波特率的例子函式:
-
/**
-
*@brief 設定串列埠通訊速率
-
*@param fd 型別 int 開啟串列埠的檔案控制代碼
-
*@param speed 型別 int 串列埠速度
-
*@return void
-
*/
-
int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
-
B38400, B19200, B9600, B4800, B2400, B1200, B300, };
-
int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400,
-
19200, 9600, 4800, 2400, 1200, 300, };
-
void set_speed(int fd, int speed){
-
int i;
-
int status;
-
struct termios Opt;
-
tcgetattr(fd, &Opt);
-
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++) {
-
if (speed == name_arr[i]) {
-
tcflush(fd, TCIOFLUSH);
-
cfsetispeed(&Opt, speed_arr[i]);
-
cfsetospeed(&Opt, speed_arr[i]);
-
status = tcsetattr(fd1, TCSANOW, &Opt);
-
if (status != 0) {
-
perror("tcsetattr fd1");
-
return;
-
}
-
tcflush(fd,TCIOFLUSH);
-
}
-
}
-
}
2.2 設定效驗的函式:
-
/**
-
*@brief 設定串列埠資料位,停止位和效驗位
-
*@param fd 型別 int 開啟的串列埠檔案控制代碼
-
*@param databits 型別 int 資料位 取值 為 7 或者8
-
*@param stopbits 型別 int 停止位 取值為 1 或者2
-
*@param parity 型別 int 效驗型別 取值為N,E,O,,S
-
*/
-
int set_Parity(int fd,int databits,int stopbits,int parity)
-
{
-
struct termios options;
-
if ( tcgetattr( fd,&options) != 0) {
-
perror("SetupSerial 1");
-
return(FALSE);
-
}
-
options.c_cflag &= ~CSIZE;
-
switch (databits) /*設定資料位數*/
-
{
-
case 7:
-
options.c_cflag |= CS7;
-
break;
-
case 8:
-
options.c_cflag |= CS8;
-
break;
-
default:
-
fprintf(stderr,"Unsupported data sizen"); return (FALSE);
-
}
-
switch (parity)
-
{
-
case 'n':
-
case 'N':
-
options.c_cflag &= ~PARENB; /* Clear parity enable */
-
options.c_iflag &= ~INPCK; /* Enable parity checking */
-
break;
-
case 'o':
-
case 'O':
-
options.c_cflag |= (PARODD | PARENB); /* 設定為奇效驗*/
-
options.c_iflag |= INPCK; /* Disnable parity checking */
-
break;
-
case 'e':
-
case 'E':
-
options.c_cflag |= PARENB; /* Enable parity */
-
options.c_cflag &= ~PARODD; /* 轉換為偶效驗*/
-
options.c_iflag |= INPCK; /* Disnable parity checking */
-
break;
-
case 'S':
-
case 's': /*as no parity*/
-
options.c_cflag &= ~PARENB;
-
options.c_cflag &= ~CSTOPB;break;
-
default:
-
fprintf(stderr,"Unsupported parityn");
-
return (FALSE);
-
}
2.3 設定停止位
-
switch (stopbits)
-
{
-
case 1:
-
options.c_cflag &= ~CSTOPB;
-
break;
-
case 2:
-
options.c_cflag |= CSTOPB;
-
break;
-
default:
-
fprintf(stderr,"Unsupported stop bitsn");
-
return (FALSE);
-
}
-
/* Set input parity option */
-
if (parity != 'n')
-
options.c_iflag |= INPCK;
-
tcflush(fd,TCIFLUSH);
-
options.c_cc[VTIME] = 150; /* 設定超時15 seconds*/
-
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
-
if (tcsetattr(fd,TCSANOW,&options) != 0)
-
{
-
perror("SetupSerial 3");
-
return (FALSE);
-
}
-
return (TRUE);
-
}
在上述程式碼中,有兩句話特別重要:
options.c_cc[VTIME] = 0; /* 設定超時0 seconds*/
options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/
這兩句話決定了對串列埠讀取的函式read()的一些功能。我將著重介紹一下他們對read()函式的影響。
對串列埠操作的結構體是
-
Struct{
-
tcflag_t c_iflag; /*輸入模式標記*/
-
tcflag_t c_oflag; /*輸出模式標記*/
-
tcflag_t c_cflag; /*控制模式標記*/
-
tcflag_t c_lflag; /*本地模式標記*/
-
cc_t c_line; /*線路規程*/
-
cc_t c_cc[NCCS]; /*控制符號*/
-
};
其中cc_t, c_line只有在一些特殊的系統程式(比如,設定通過tty裝置來通訊的網路協議)中才會用。在陣列c_cc中有兩個下標(VTIME和VMIN)對應的元素不是控制符,並且只是在原始模式下有效。只有在原始模式下,他們決定了read()函式在什麼時候返回。在標準模式下,除非設定了O_NONBLOCK選項,否則只有當遇到檔案結束符或各行的字元都已經編輯完畢後才返回。
控制符VTIME和VMIN之間有著複雜的關係。VTIME定義要求等待的零到幾百毫秒的時間量(通常是一個8位的unsigned char變數,取值不能大於cc_t)。 VMIN定義了要求等待的最小位元組數(不是要求讀的位元組數——read()的第三個引數才是指定要求讀的最大位元組數),這個位元組數可能是0。
l) 如果VTIME取0,VMIN定義了要求等待讀取的最小位元組數。函式read()只有在讀取了VMIN個位元組的資料或者收到一個訊號的時候才返回。
2) 如果VMIN取0,VTIME定義了即使沒有資料可以讀取,read()函式返回前也要等待幾百毫秒的時間量。這時,read()函式不需要像其通常情況那樣要遇到一個檔案結束標誌才返回0。
3) 如果VTIME和VMIN都不取0,VTIME定義的是當接收到第一個位元組的資料後開始計算等待的時間量。如果當呼叫read函式時可以得到資料,計時器馬上開始計時。如果當呼叫read函式時還沒有任何資料可讀,則等接收到第一個位元組的資料後,計時器開始計時。函式read可能會在讀取到VMIN個位元組的資料後返回,也可能在計時完畢後返回,這主要取決於哪個條件首先實現。不過函式至少會讀取到一個位元組的資料,因為計時器是在讀取到第一個資料時開始計時的。
4) 如果VTIME和VMIN都取0,即使讀取不到任何資料,函式read也會立即返回。同時,返回值0表示read函式不需要等待檔案結束標誌就返回了。
這就是這兩個變數對read函式的影響。
2.4 串列埠屬性配置
在程式中,很容易配置串列埠的屬性,這些屬性定義在結構體struct termios中。為在程式中使用該結構體,需要包含檔案,該標頭檔案定義了結構體struct termios。該結構體定義如下:
#define NCCS 19
struct termios {
tcflag_t c_iflag; /* 輸入引數 */
tcflag_t c_oflag; /* 輸出引數 */
tcflag_t c_cflag; /* 控制引數*/
tcflag_t c_ispeed; /* 輸入波特率 */
tcflag_t c_ospeed; /* 輸出波特率 */
cc_t c_line; /* 線控制 */
cc_t c_cc[NCCS]; /* 控制字元*/
};
其中成員c_line在POSIX(Portable Operating System Interface for UNIX)系統中不使用。對於支援POSIX終端介面的系統中,對於埠屬性的設定和獲取要用到兩個重要的函式是:
(1).int tcsetattr(int fd,int opt_DE,*ptr)
該函式用來設定終端控制屬性,其引數說明如下:
fd:待操作的檔案描述符
opt_DE:選項值,有三個選項以供選擇:
TCSANOW: 不等資料傳輸完畢就立即改變屬性
TCSADRAIN:等待所有資料傳輸結束才改變屬性
TCSAFLUSH:清空輸入輸出緩衝區才改變屬性
*ptr:指向termios結構的指標
函式返回值:成功返回0,失敗返回-1。
(2).int tcgetattr(int fd,*ptr)
該函式用來獲取終端控制屬性,它把串列埠的預設設定賦給了termios資料資料結構,其引數說明如下:
fd:待操作的檔案描述符
*ptr:指向termios結構的指標
函式返回值:成功返回0,失敗返回-1。
2.5 注意的問題:
如果不是開發終端之類的,只是串列埠傳輸資料,而不需要串列埠來處理,那麼使用原始模式(Raw Mode)方式來通訊,設定方式如下:
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
options.c_oflag &= ~OPOST; /*Output*/
3.讀寫串列埠
3.1 串列埠讀操作(接收端)
用open函式開啟裝置檔案,函式返回一個檔案描述符(file descriptors,fd),通過檔案描述符來訪問檔案。讀串列埠操作是通過read函式來完成的。函式原型如下:
int read(int fd, *buffer,length);
引數說明:
(1).int fd:檔案描述符
(2).*buffer:資料緩衝區
(3).length:要讀取的位元組數
函式返回值:
讀操作成功讀取返回讀取的位元組數,失敗則返回-1。
3.2 串列埠寫操作(傳送端)
寫串列埠操作是通過write函式來完成的。函式原型如下:
write(int fd, *buffer,length);
引數說明:
(1).fd:檔案描述符
(2).*buffer:儲存寫入資料的資料緩衝區
(3).length:寫入緩衝去的資料位元組數
函式返回值:
成功返回寫入資料的位元組數,該值通常等於length,如果寫入失敗返回-1。
例如:向終端裝置傳送初始化命令
設定好串列埠之後,讀寫串列埠就很容易了,把串列埠當作檔案讀寫就是。
·傳送資料
char buffer[1024];
int Length;int nByte;
nByte = write(fd, buffer ,Length)
4.關閉串列埠
關閉串列埠就是關閉檔案。
close(fd);
5.例子
下面是一個簡單的讀取串列埠資料的例子,使用了上面定義的一些函式和標頭檔案
-
/**********************************************************************
-
程式碼說明:使用串列埠二測試的,傳送的資料是字元,
-
但是沒有傳送字串結束符號,所以接收到後,後面加上了結束符號。
-
我測試使用的是單片機發送資料到第二個串列埠,測試通過。
-
**********************************************************************/
-
#define FALSE -1
-
#define TRUE 0
-
/*********************************************************************/
-
int OpenDev(char *Dev)
-
{
-
int fd = open( Dev, O_RDWR );
-
//| O_NOCTTY | O_NDELAY
-
if (-1 == fd)
-
{
-
perror("Can't Open Serial Port");
-
return -1;
-
}
-
else
-
return fd;
-
}
-
int main(int argc, char **argv){
-
int fd;
-
int nread;
-
char buff[512];
-
char *dev = "/dev/ttyS1"; //串列埠二
-
fd = OpenDev(dev);
-
set_speed(fd,19200);
-
if (set_Parity(fd,8,1,'N') == FALSE) {
-
printf("Set Parity Errorn");
-
exit (0);
-
}
-
while (1) //迴圈讀取資料
-
{
-
while((nread = read(fd, buff, 512))>0)
-
{
-
printf("nLen %dn",nread);
-
buff[nread+1] = '';
-
printf( "n%s", buff);
-
}
-
}
-
//close(fd);
-
// exit (0);
-
}