UART VTIME AND VMIN

众所周知,在Linux系统下所有设备都是以文件的形式存在,串口也一样。

通常I/O操作都是有阻塞与非阻塞的两种方式。


其中"超时"这个概念其实是阻塞中的一种处理手段,本质还是属于阻塞的I/O模式.

在Linux中串口的IO操作 本文将它分为三种状态:

阻塞状态

超时状态

非阻塞状态


这三种状态的转换组合有这么几种:

阻塞 --> 超时

阻塞 --> 非阻塞

超时 --> 阻塞

超时 --> 非阻塞

非阻塞 --> 阻塞


我们一个一个来分析

首先在一个串口的描述符打开的时候指定它的模式是阻塞还是阻塞

?
1
2
3
fd = open( "/dev/tttyS0" ,O_RDWR | O_NOCTTY); //以阻塞模式打开串口
fd = open( "/dev/tttyS0" ,O_RDWR | O_NOCTTY | O_NDELAY); //以非阻塞模式打开串口
//O_NDELAY 等价于 O_NOBLOCK


当一个串口是阻塞状态的时候便可以设置它为超时状态。

利用 struct termios 的 cc_t c_cc[NCCS] 成员

    c_cc[VTIME非规范模式读取时的超时时间(单位:百毫秒)

    c_cc[VMIN]  非规范模式读取时的最小字符数

如需需要设置超时则c_cc[VMIN必须等于0

这代表能够读取的最小字符是0个,即使用read读取数据超时read返回0


有一个需要注意的地方!

当c_cc[VTIME] 设置为 0 且 c_cc[VMIN] == 0 的时候,代表超时0秒(姑且这么叫吧!)

这个时候使用read读取数据会立即返回(有读到数据时返回字节数,没有数据和一般超时一样返回0)

但是!

虽然这时候在现象上看起来和非阻塞模式一样(read都不会阻塞)但返回值不同


非阻塞模式: read没有读到数据立即返回-1

超时0秒时:  read没有读到数据立即返回 0  (设置了超时的阻塞模式)

?
1
2
3
4
5
6
7
8
9
10
11
ret = read(fd,recvbuf,BUF_SIZE);
if (ret == -1) //非阻塞模式时"无数据返回"
{
     //do something
}
 
ret = read(fd,recvbuf,BUF_SIZE);
if (ret == 0) //阻塞模式设置超时0秒时"超时返回"
{
     //do something
}

补充:在非阻塞模式下修改c_cc[VMIN] 和 c_cc[VTIME] 的情况

若在非阻塞模式下修改

c_cc[VMIN]为0并且c_cc[VTIME]也为0时read无数据会返回 0 (现象同"超时0秒"一样)

这时倘若将c_cc[VMIN]或者c_cc[VTIME]中任意一个项修改成>0,那么read就返回-1了。 


虽然表现形式一样,但在编程时必须要了解自己使用的是哪一种模式和串口当前的状态才能更好的分析和处理问题。

这里说一下我曾经遇到过的一个问题:

我在打开串口时使用阻塞模式打开,但是没有设置c_cc[VMIN]的值,而它初始化后就是0,所以发现串口没有被阻塞,其实原因就是串口模式还是阻塞模式没错,但是它是超时0秒的状态,所以在没有数据到达时read也返回了。 


关于阻塞模式下c_cc[VMIN] 和 c_cc[VTIME]的取值与现象,以下简称为VMIN和VTIME

这两个值有这些组合

VTIME VMIN 说明
0 0 "超时0秒"
0 >0 一直阻塞到接收到VMIN个数据时read返回
>0 0 普通超时
>0 >0

当接收到第一个字节时开始计算超时。

如果超时时间未到但数据已经达到VMIN个read立即返回。

如果超时时间到了就返回当前读到的个数。



阻塞状态和非阻塞状态的切换

非阻塞状态时使用

?
1
  fcntl(fd,F_SETFL,0);

即可转换成阻塞状态,同样可以设置超时

当非阻塞状态已经设置了超时时,在转换成阻塞状态后超时也随同生效

  

阻塞状态时使用

?
1
2
fcntl(fd,F_SETFL,FNDELAY);
//FNDELAY等价于FNONBLOCK

即可转换成非阻塞状态,超时失效


这里提一下 fcntl.h中几个宏的定义

?
1
2
3
4
5
6
7
8
9
/* Define some more compatibility macros to be backward compatible with
    BSD systems which did not managed to hide these kernel macros.  */
#ifdef  __USE_BSD
# define FAPPEND  O_APPEND
# define FFSYNC       O_FSYNC
# define FASYNC       O_ASYNC
# define FNONBLOCK    O_NONBLOCK
# define FNDELAY  O_NDELAY
#endif /* Use BSD.  */

现在一目了然了吧?打开串口时非阻塞模式的O_NDELAY或O_NONBLOCK选项

fcntl设置非阻塞模式的第3个参数FNDELAY或者FNONBLOCK 其实都是O_NONBLOCK主要就是为了兼容



2015-01-21补充:

如果在非阻塞模式下调用read时没有马上读到数据会立即返回-1,错误提示是(Resource temporarily unavailable)

而且会造成后面读到的数据可能是前一次要读的数据,导致每一次都读了前一次的数据。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值