https://blog.csdn.net/williamwang2013/article/details/8560552

linux使用terminfo数据库来描述终端能力以及调用对应功能的方法
POSIX定义了完成终端I/O的标准方法:TERMIOS函数族

#include <termios.h>
#include <unistd.h>
struct termios{tcflag_t c_iflag;      /* input modes */tcflag_t c_oflag;      /* output modes */tcflag_t c_cflag;      /* control modes */tcflag_t c_lflag;      /* local modes */cc_t c_cc [NCCS];      /* control chars */
}

其具体意义如下。

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 非规范模式读取时的最小字符数

1.模式

Cbreak模式

除了"Del"和"Ctrl"键外,接受其他所有字符输入, raw()和cbreak()两个函数都可以禁止行缓冲(line buffering)。区别是:在raw()函数模式下,处理挂起(CTRLZ)、 中断或退出(CTRLC) 等控制字符时,将直接传送给程序去处理而不产生终端信号;而在 cbreak()模式下,控制字符将被终端驱动程序解释成其它字符。

Raw模式

可以禁止行缓冲(line buffering),处理挂起(CTRLZ)、中断或退出(CTRLC)等控制字符时,将直接传送给程序去处理而不产生终端信号
详情查看curses库中的cbreak()和 raw()函数。
另外可以用命令直接操作
stty -a 这个命令用来查看当前终端的设置情况
stty sane 如果不小心设错了终端模式,可用这个命令恢复,另一种恢复办法是在设置之前保存当前stty设置,在需要时再读出
stty -g > save_stty 将当前设置保存到文件save_atty中
stty $(cat save_stty) 读出save_atty文件,恢复原终端设置

2.获取和设置终端属性

int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions, struct termios *termios_p);
其中optional_actions决定什么时候起作用,可取如下值
TCSANOW:不等数据传输完毕就立即改变属性。
TCSADRAIN:等待所有数据传输结束才改变属性。
TCSAFLUSH:清空输入输出缓冲区才改变属性。

注意:当进行多重修改时,应当在这个函数之后再次调用 tcgetattr() 来检测是否所有修改都成功实现。

3.波特率函数

获取波特率

speed_t cfgetospeed(struct termios *termios_p);
speed_t cfgetispeed(struct termios *termios_p);

设置波特率

int cfsetospeed(struct termios *termios_p, speed_t speed);
int cfsetispeed(struct termios *termios_p, speed_t speed);

speed取值必须是以下常量之一:
B0 B50 B75 B110 B134 B150 B200 B300 B600 B1200 B1800 B2400 B4800 B9600 B19200 B38400 B57600 B115200 B230400
其中:零值 B0 用来中断连接。如果指定了 B0,不应当再假定存在连接。通常,这样将断开连接。CBAUDEX 是一个掩码,指示高于 POSIX.1 定义的速度的那一些 (57600 及以上)。因此,B57600 & CBAUDEX 为非零。

4.线路控制函数

int tcdrain (int fd); //等待所有写入fd中的数据输出

int tcflush (int fd, int queue_selector); //丢弃要写入fd,但尚未传输的数据,或者收到但是尚未读取的数据。
取决于queue_selector 的值:
TCIFLUSH: 刷新收到的数据但是不读
TCOFLUSH: 刷新写入的数据但是不传送
TCIOFLUSH: 同时刷新收到的数据但是不读,并且刷新写入的数据但是不传送

int tcflow (int fd, int action); //挂起 fd 上的数据传输或接收。
取决于 action 的值:
TCOOFF :挂起输出
TCOON :重新开始被挂起的输出
TCIOFF :发送一个 STOP 字符,停止终端设备向系统传送数据
TCION :发送一个 START 字符,使终端设备向系统传输数据
打开一个终端设备时的默认设置是输入和输出都没有挂起。

int tcsendbreak (int fd, int duration); //传送连续的 0 值比特流,持续一段时间,如果终端使用异步串行数据传输的话。
如果 duration 是 0,它至少传输 0.25 秒,不会超过 0.5 秒。如果 duration 非零,它发送的时间长度由实现定义。
如果终端并非使用异步串行数据传输,tcsendbreak() 什么都不做。

5.进程组控制函数

pid_t tcgetpgrp(int fd); //获取前台进程组的进程组ID

int tcsetpgrp(int fd, pid_t pgrpid); //设置前台进程组的进程组ID

pid_t tcgetsid(int fd); //获取会话首进程的进程组ID

6.cfmakeraw

设置终端的 Raw 模式 ,设置终端属性:

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;

7.其他

#include <stdio.h>
char *ctermid(char *s); //决定控制终端名称

#include <unistd.h>
int isatty(int desc); //判断描述符是否为终端

char *ttyname(int desc); //返回终端名称

int getopt(int argc,char * const argv[ ],const char * optstring); //分析命令行参数
其中argc和argv是由main()传递的参数个数和内容,optstring 则代表欲处理的选项字符串。

修改终端控制字符示例

#include
#include
#include
#include
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;
}

O_NOCTTY:如果打开的是一个终端设备,这个程序不会成为对应这个端口的控制终端,如果没有该标志,任何一个输入,例如键盘中止信号等,都将影响进程。

设置波特率的例子函数

/**
*@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);}}
}

设置效验的函数

/**
*@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);
}

设置停止位

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()的一些功能。

对串口操作的结构体是

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个字节的数据或者收到一个信号的时候才返回。

  1. 如果VMIN取0,VTIME定义了即使没有数据可以读取,read()函数返回前也要等待几百毫秒的时间量。这时,read()函数不需要像其通常情况那样要遇到一个文件结束标志才返回0。

  2. 如果VTIME和VMIN都不取0,VTIME定义的是当接收到第一个字节的数据后开始计算等待的时间量。如果当调用read函数时可以得到数据,计时器马上开始计时。如果当调用read函数时还没有任何数据可读,则等接收到第一个字节的数据后,计时器开始计时。函数read可能会在读取到VMIN个字节的数据后返回,也可能在计时完毕后返回,这主要取决于哪个条件首先实现。不过函数至少会读取到一个字节的数据,因为计时器是在读取到第一个数据时开始计时的。

  3. **如果VTIME和VMIN都取0,即使读取不到任何数据,函数read也会立即返回。**同时,返回值0表示read函数不需要等待文件结束标志就返回了。

这就是这两个变量对read函数的影响。

注意的问题:
如果不是开发终端之类的,只是串口传输数据,而不需要串口来处理,那么使用原始模式(Raw Mode)方式来通讯,设置方式如下:

options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
options.c_oflag &= ~OPOST; /*Output*/

串口termios函数相关推荐

  1. 串口termios函数【转】

    (转自:https://blog.csdn.net/williamwang2013/article/details/8560552) linux使用terminfo数据库来描述终端能力以及调用对应功能 ...

  2. Termios 函数说明

    Termios 函数说明: Linux中的控制台界面,要大量用到Termios库的内容.但是总的来看,这些东西已经过时了,应该用更简单的方案来代替它了,尽管它是POSIX标准的一部分. 然而现在,时代 ...

  3. linux串口termios

    linux串口termios NAME termios, tcgetattr, tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow, cfmakeraw, ...

  4. STM8学习笔记---串口printf函数的实现

    在使用单片机的时候,串口是要经常使用的功能,特别是在调试代码的过程中,经常需要使用串口打印出某些变量值,来判断程序执行流程是否正常.但是单片机默认情况下没有printf函数,如果需要使用printf函 ...

  5. (实测可用)STM32CubeMX教程-STM32L431RCT6开发板研究串口通信(串口发送函数)

    一.开发板平台简介: 1.开发板资源简介 (1)开发板主芯片型号:STM32L431RCT6 (2)开发板主芯片封装:LQFP-64_10x10x05P (3)开发板主芯片内核:ARM® Cortex ...

  6. DIY单片机串口打印函数print

    原始的单片机串口只能发送单字节数据,再加个封装也就能发送个字符串,但是无法发送数字变量,要发送数字变量那基本要引入C语言的库函数printf,但是这个pintf函数 好用确实是好用但是有个很大的缺点相 ...

  7. 学习笔记——stm32串口中断函数的逻辑理解

    这片文章主要是讲解原子给的代码里面的串口中断的中断函数,前面是我个人的学习路径. 关于stm32串口的学习,主要分为以下几个点 1.USART的功能和内部结构. 功能包括同步.异步模式.双工通信.半工 ...

  8. 51单片机串口打印函数

    51单片机串口通信调试printf函数重定向输出打印 1.首先需要实现串口的发送和接收. 2.输出printf需要哪些步骤: 1)需要引入头文件 <stdio.h> 2)添加如下代码: c ...

  9. STM32多串口printf函数

    1.配置usart2的串口配置,gpio,rcc enable,跟上面类似 2.勾选usb micro lib,跟上面类似 3.添加头文件#include <stdarg.h>,编写USA ...

  10. windows串口通信函数API

    windows串口通讯主要函数 先列个目录表 1.CreateFile - 打开串口: 2.SetupComm-初始化一个指定的通信设备的通信参数 3.ReadFile - 读数据: 4.WriteF ...

最新文章

  1. Fleury算法找欧拉环游
  2. 朝鲜时蔬(分数据点写算法+毒瘤数学)
  3. 让source insight更好的支持中文
  4. Linux系统编程:使用mutex互斥锁和条件变量实现多个生成者和消费者模型
  5. 【mac apache】了解自带的apache
  6. js中数组的操作方法
  7. 阅读《软件工程—理论方法与实践》第四章心得体会
  8. 一行代码进行闰年的判断
  9. SignalTap II里面Power-Up Trigger的使用
  10. python编辑器使用技巧大全_markdown编辑器常用功能汇总
  11. 提供 web前端、H5、html页面 技术服务
  12. word中如何去掉页眉横线?
  13. 输入球心三维坐标及球半径求算球体的球面坐标数据(C/C++)
  14. 计算机中rom的意思是什么,ROM 是什么意思
  15. html 页眉选项卡,连续两个奇数页页眉 再选择插入菜单——分页,执行2次
  16. rails 中的pluck 方法
  17. 我的人生哲学(三十六岁版)
  18. 1688按图搜索商品(拍立淘)获取数据的教程
  19. centos7 安装docker-ce社区版 ,最新版本docker,docker阿里云加速
  20. android 各国时区

热门文章

  1. python常用颜色表示_OpenCV+Python常用颜色空间
  2. 模拟人生java电脑版_模拟人生免费版电脑版
  3. 【黑马程序员济南】我的“黑马”经历
  4. 【超详细】私有仓库Gitlab的安装与使用详细教程
  5. android 广播监听截屏,Android 应用监听截屏操作
  6. 永磁同步电机矢量控制学习--MTPA控制策略
  7. tensorflow中常用的激活函数
  8. 给js对象定义属性的方法
  9. Nginx入门5:搭建静态资源服务器;(入门级演示,没多少内容;)
  10. 微信小程序布局及嵌套地图