TTY 2

6.4.4  使用tcgetattr函数与tcsetattr函数控制终端

为了便于通过程序来获得和修改终端参数,Linux还提供了tcgetattr函数和tcsetattr函数。tcgetattr用于获取终端的相关参数,而tcsetattr函数用于设置终端参数。这两个函数的具体信息如表6.2所示。

表6.2   tcgetattr函数和tcsetattr函数

头文件

<termios.h>

<unistd.h>

函数形式

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所示。

表6.5   c_oflag参数

   

   

CBAUD

波特率( 4+1 位)(非 POSIX

CBAUDEX

附加波特率( 1 位)(非 POSIX

CSIZE

字符长度,取值范围为 CS5 CS6 CS7 CS8

CSTOPB

设置两个停止位

CREAD

使用接收器

PARENB

使用奇偶校验

PARODD

对输入使用奇偶校验,对输出使用偶校验

HUPCL

关闭设备时挂起

CLOCAL

忽略调制解调器线路状态

CRTSCTS

使用 RTS/CTS 流控制

c_lflag:本地模式标志,控制终端编辑功能,具体参数如表6.6所示。

表6.6   c_lflag参数

   

   

ISIG

当输入 INTR QUIT SUSP DSUSP 时,产生相应的信号

ICANON

使用标准输入模式

XCASE

ICANON XCASE 同时设置的情况下,终端只使用大写。如果只设置了 XCASE ,则输入字符将被转换为小写字符,除非字符使用了转义字符(非 POSIX ,且 Linux 不支持该参数)

ECHO

显示输入字符

ECHOE

如果 ICANON 同时设置, ERASE 将删除输入的字符, WERASE 将删除输入的单词

ECHOK

如果 ICANON 同时设置, KILL 将删除当前行

ECHONL

如果 ICANON 同时设置,即使 ECHO 没有设置依然显示换行符

ECHOPRT

如果 ECHO ICANON 同时设置,将删除打印出的字符(非 POSIX

TOSTOP

向后台输出发送 SIGTTOU 信号

c_cc[NCCS]:控制字符,用于保存终端驱动程序中的特殊字符,如输入结束符等。c_cc中定义了如表6.7所示的控制字符。

表6.7   c_cc支持的控制字符

   

   

VINTR

Interrupt 字符

VEOL

附加的 End-of-file 字符

VQUIT

Quit 字符

VTIME

非规范模式读取时的超时时间

VERASE

Erase 字符

VSTOP

Stop 字符

VKILL

Kill 字符

VSTART

Start 字符

VEOF

End-of-file 字符

VSUSP

Suspend 字符

VMIN

非规范模式读取时的最小字符数

 

 

tcsetattr函数用于设置终端的相关参数。参数fd为打开的终端文件描述符,参数optional_actions用于控制修改起作用的时间,而结构体termios_p中保存了要修改的参数。
optional_actions可以取如下的值。
 
TCSANOW:不等数据传输完毕就立即改变属性。
TCSADRAIN:等待所有数据传输结束才改变属性。
TCSAFLUSH:清空输入输出缓冲区才改变属性。

错误信息:
EBADF:非法的文件描述符。
EINTR:tcsetattr函数调用被信号中断。
EINVAL:参数optional_actions使用了非法值,或参数termios中使用了非法值。
ENCTTY:非终端的文件描述符。

实例演练:
程序p6.2.c通过修改终端控制字符,将终端输入结束符由“Ctrl+D”,修改成了“Ctrl+G”。首先,程序调用 tcgetattr函数获得标准输入的termios信息,将termios结构体中的c_cc[VEOF]控制字符的修改成0x07(即 Ctrl+G);然后,使用tcsetattr函数将修改后的termios参数设置到终端中。具体代码如下所示:

    

//p6.2.c 修改终端控制字符示例
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>

int main(void){
//term用于存储获得的终端参数信息
struct termios term;
int err;

//获得标准输入的终端参数,将获得的信息保存在term变量中
if(tcgetattr(STDIN_FILENO,&term)==-1){
perror("Cannot get standard input description");
return 1;
}

//修改获得的终端信息的结束控制字符
term.c_cc[VEOF]=(cc_t)0x07;

//使用tcsetattr函数将修改后的终端参数设置到标准输入中
//err用于保存函数调用后的结果
err=tcsetattr(STDIN_FILENO,TCSAFLUSH,&term);

//如果err为-1或是出现EINTR错误(函数执行被信号中断),
//给出相关出错信息
if(err==-1 && err==EINTR){
perror("Failed to change EOF character");
return 1;
}

return 0;
}

使用gcc编译p6.2.c程序,得到名为p6.2的可执行程序。在执行p6.2程序前,按“Ctrl+D”可以使终端结束。执行p6.2程序后,按“Ctrl+D”失去了作用,而输入“Ctrl+G”实现了原来“Ctrl+D”的功能。

 

 

 

 

 

 

串口配置时一些重要且应注意的事项:

       除了一般的波特率、数据位、校验位、停止位、超时配置外,还有一些配置也是很重要的,否则有可能出错丢码或者不正常的现象,在此做一份记录;

本人测试过的正常代码实例:

// 打开串口:

static INT32 Open_Dev(char *dev)

{

       INT32 fd;

       if((fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY )) == -1)

       {

              //perror(">can not open the dev!");

              return -1;

       }

       return fd;

}

 

// 关闭串口:

static void Close_Dev(INT32 fd)

{

       if(fd > 0)

       {

              close(fd);

       }

}

 

// 设置波特率:

static INT32 Set_Baudrate(INT32 fd,UINT32 baudrate)

{

       UINT32 speed_arr[] = {B57600,B38400,B19200,B9600,B4800,B2400,B1200,B600,B300,};    //define in termios.h

       UINT32 baud_arr[] = {57600,38400,19200,9600,4800,2400,1200,600,300,};

 

       UINT32 i;

       UINT32 status;

       struct termios opt;

 

       tcgetattr(fd,&opt);              //get attr

       for(i=0; i<sizeof(baud_arr)/sizeof(UINT32); i++)

       {

              if(baudrate == baud_arr[i])

              {

                     tcflush(fd,TCIOFLUSH);                           //flush

                     cfsetispeed(&opt,speed_arr[i]);            //set  in baudrate

                     cfsetospeed(&opt,speed_arr[i]);           //set out baudrate

                     status = tcsetattr(fd,TCSANOW,&opt);

                     if(status != 0)

                     {

                            //perror(">Set_Baudrate: tcseattr failed!/n");

                            return RET_ERR;

                     }

                     tcflush(fd,TCIOFLUSH);

                     return RET_OK;

              }

       }

 

       return RET_ERR;

      

}

 

// 设置数据位、校验位、停止位和其它重要配置:

static INT32 Set_Parity(INT32 fd,UINT32 databits,UINT32 stopbits,char parity)

{

       struct termios opt;

       if(tcgetattr(fd,&opt) != 0)

       {

              //perror(">Set_Parity: tcgetattr failed!/n");

              return (RET_ERR);

       }

 

       //data bits

       opt.c_cflag &= ~CSIZE;

       switch (databits)

       {

              case 5:

                 opt.c_cflag |= CS5;

                 break;

              case 6:

                 opt.c_cflag |= CS6;

                 break;

              case 7:

                 opt.c_cflag |= CS7;

                 break;

            case 8:

                 opt.c_cflag |= CS8;

                 break;

            default:

                 //perror(">Unsupported databits/n");

                 return (RET_ERR);

                 break;

       }

      

       //parity

       switch (parity)

       {

           case 'n':

           case 'N':

                 opt.c_cflag &= ~PARENB;          //Clear parity enable

                 opt.c_iflag &= ~INPCK;               // Disnable parity checking

                 break;

           case 'o':

           case 'O':

                 opt.c_cflag |= (PARODD | PARENB);

                 opt.c_iflag |= INPCK;             // Enable parity checking

                 break;

            case 'e':

           case 'E':

                 opt.c_cflag |= PARENB;        // Enable parity

                 opt.c_cflag &= ~PARODD;               

                 opt.c_iflag |= INPCK;           // Enable parity checking

                 break;

           case 'S':

           case 's':                                            //as no parity

                 opt.c_cflag &= ~PARENB;

                 opt.c_cflag &= ~CSTOPB;

                 break;

           default:

                 //perror(">Unsupported parity/n");

                 return (RET_ERR);

                 break;

       }

      

       // stop bits 

       switch (stopbits)

       {

           case 1:

                 opt.c_cflag &= ~CSTOPB;

                 break;

           case 2:

                 opt.c_cflag |= CSTOPB;

                 break;

           default:

                 //perror(">Unsupported stopbits/n");

                 return (RET_ERR);

                 break;

       }

      

      

       tcflush(fd,TCIFLUSH);     // Update the options and do it NOW

       //time out(here no use  because "open(o_NDELAY)")

       opt.c_cc[VTIME] = 150;         // 15 seconds     (1/10sec)

       opt.c_cc[VMIN] = 0;

 

  /*----------------------  重要 ----------------------*/  

       // 保证本程序不会成为端口的所有者,从而妨碍控制工作和挂起信号 .

        opt.c_cflag     |= (CLOCAL | CREAD);

 

// 选择行方式输入 : 行式输入是不经处理的 .

opt.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG); 

      

// 选择行式输出

opt.c_oflag  &= ~OPOST;  

                                  

       // 取消软件流控制(不设置可能存在丢码)

       opt.c_iflag &= ~(IXON | IXOFF | IXANY);                  

   /*----------------------------------------------------*/

 

       if (tcsetattr(fd,TCSANOW,&opt) != 0)

       {

              //perror(">Set_Parity: tcsetattr failed!/n");

              return (RET_ERR);

       }

      

       return (RET_OK);

}

 

    注意上面实例是中的红色字样(重要的设置);

    设置数据位、校验位、停止位和其它重要配置中的红字部分:因为有些版本的 linux 可能存已默认配置,所以不用配置也可能,但如果默认配置不是这样而又没有进行配置则就可能会出现丢码或者收发码不正常的情况 (本人曾遇到过)!!!

你可能感兴趣的:(c,linux,struct,input,character,终端)