上一篇文章中,我们详细分析了VTIM和VMIN的功能,

《嵌入式Linux 串口编程系列2--termios的VMIN和VTIME深入理解》

也明白了这两个参数设计的初衷和使用方法,接下来我们 就详细的说明一下,具体编程中,我们要将VMIN 设置的足够大,将VTIME设置的尽量小,同时在应用接收线程中,配合select机制。我们来分析下为什么要这么设计:

VMIN设置的足够大,这样在串口在有数据接收时,不会因为接收了 几个或者几十个字符就 改变了read的阻塞状态,至于说要设定多大,这个范围一般是不超过255的,因为VMIN是一个 char类型,数值上不能超过255,超过255了,会被认为是0.

VTIME设置的尽量小:如果VTIME设置成0,那么串口只有接收VMIN个字节才会改变read阻塞 状态,而上面说到VMIN又比较大,这显然是不行的,而如果VTIME设置的比较大,比如说200,换算成时间就是20秒,也就是说,串口在接收完数据后,需要等待20秒才会 改变read阻塞状态,这显然也是不行的。那么可能就会有人说,0不行,200不行,那就设定为1呗,这个想法可以有,不过不是万能的,要根据实际情况来定,比如通信波特率如果设置的过小,那么也就意味着两个字符之间的间隔时间就会很长,所以VTIME设置的太小,可能会提前改变read阻塞状态,不过一般常用的波特率9600,115200,就不存在这个问题,设置成1即可。

配合select使用:这里我开始有点迷惑,既然有了VTIME,而且把VTIME设置的足够小了,为什么还要select呢,因为select也要设置超时改变read阻塞的,答案是这里的select是为了 处理 串口无数据接收的,因为我们使用的过程中,串口的发送我们是可以控制的,但是串口的数据接收时随机的,可能会不停的有数据进来,也可能偶尔有数据进来,也可能一直没有数据进来,那么对于没有数据进来的这段时间,我们的应用程序里,必然是有read函数的,这个时候read会一直阻塞(抛开一些非阻塞设置),这肯定不是我们想要的,我们可能 会说,可以用一个线程啊,阻塞就阻塞呗,这当然是一个方案,不过我们还是倾向于不要让任何一个进程一直阻塞,这一点可以参考之前的文章《 嵌入式Linux编程之select使用总结》。 当然为了程序的实时性,select的超时时间也不要设置的过长。

还有一点需要注意:我们在read 以后,尽可能的清理掉串口缓存,这样防止下次有新数据来了后,数据混淆的bug。

程序框架如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <limits.h>
#include <asm/ioctls.h>
#include <time.h>
#include <pthread.h>
#include <string.h>typedef int u16;
typedef char u8;#define DATA_LEN    200u16 openSerial(u16 iPort)
{u16 iFd;struct termios opt;u8 cSerialName[15];if(iPort >= 10){printf("no this serial:ttySP%d\n", iPort);exit(1);   //err return}sprintf(cSerialName, "/dev/ttySP%d", iPort - 1);printf("open serial name:%s\n", cSerialName);iFd = open(cSerialName, O_RDWR | O_NOCTTY);if( iFd < 0){perror(cSerialName);return -1;}//串口输入输出模式 各种参数配置tcgetattr(iFd, &opt);cfsetispeed(&opt, B115200);cfsetospeed(&opt, B115200);//raw modeopt.c_lflag   &=   ~(ECHO   |   ICANON   |   IEXTEN   |   ISIG);opt.c_iflag   &=   ~(BRKINT   |   ICRNL   |   INPCK   |   ISTRIP   |   IXON);opt.c_oflag   &=   ~(OPOST);opt.c_cflag   &=   ~(CSIZE   |   PARENB);opt.c_cflag   |=   CS8;//VMIN和VTIME配置opt.c_cc[VMIN] = DATA_LEN;opt.c_cc[VTIME] = 1;if( tcsetattr(iFd, TCSANOW, &opt) < 0){return -1;}return iFd;
}static struct timeval tv_timeout;
static fd_set fs_read;
static u16 usartFd;int
main( void )
{u16 cnt = 0;u16 iRet2,rx_len;u8 rx_buf[256];usartFd = openSerial( 1 );while( 1 ){    FD_ZERO( &fs_read );FD_SET(usartFd, &fs_read);tv_timeout.tv_sec = 0;tv_timeout.tv_usec = 10000;   //10000usiRet2 = select(usartFd + 1, &fs_read, NULL, NULL, &tv_timeout);if( iRet2 ){rx_len = read(usartFd, rx_buf, DATA_LEN);tcflush(usartFd, TCIFLUSH);         //清除串口缓存if( rx_len ){write(usartFd, rx_buf, rx_len);   //将接收的数据 原样打印}}}exit( 0 );
}

上面的程序框架,测试可以实现串口的不定长数据接收。不过这个VTIME和VMIN的 设计也有一个小问题,那就是VTIME的单位是100ms,这就意味着,最短时间也是100ms,所以如果 想要进一步提高不定长数据接收的实时性,就不能 采用这种方式, 而是要将VTIME设置为0,VMIN则也是尽可能小,或者直接设置为0,不过这就要求在应用程序中做相应的 接收处理,可能会稍微麻烦。不过这个100ms对于串口通信来讲,还是可以接受的,大多数的串口通信,比如说Modbus通信,通信间隔一般都会超过100ms,毕竟太快的通信频率本身就不适合串口。

嵌入式Linux 串口编程系列3——通过VTIM、VMIN、select实现串口不定长数据接收功能相关推荐

  1. 基于HAL库STM32串口驱动不定长数据接收

    STM32串口驱动不定长数据接收带环形缓冲区 最新框架代码 使用方法 源码 串口接口文件 环形缓冲区接口文件 移植图示 使用涉及4个文件, UART_Port.c UART_Port.h Circul ...

  2. 嵌入式Linux 串口编程系列2--termios的VMIN和VTIME深入理解

    在上一篇文章中,我们介绍了串口的一些基本知识.串口配置接口 termios结构体的概念,串口的配置参数有n多个,这里面不用都背下来,什么时候使用,翻看手册即可,但是有两个 参数是一定要理解的,就是VM ...

  3. 嵌入式Linux 串口编程系列4——EasyARM287开发板通过freemodbus实现Modbus通信

    前面的文章分析了串口的一些基本知识,在工业应用中,串口通信比较常用的协议就是Modbus RTU,freemodbus是一款微型modbus协议栈,之前对各种单片机.小型处理器支持的比较好,从V1.6 ...

  4. STM32CubeMX系列教程8:配置工程模板(串口+不定长数据收发+DMA+IDLE中断+软中断)

    文章目录 摘要 生成工程 配置外设 1.配置时钟与Debug 2.配置串口与DMA 3.配置定时器与中断 配置时钟树 配置工程设置 点击`GENERATE CODE`生成工程 修改源码 配置软中断 配 ...

  5. STM32从零到一,从标准库移植到HAL库,UART串口1以DMA模式收发不定长数据代码详解+常见问题 一文解析

    前言 本文的参考资料 感谢提供标准库版本的CSDN同学:这两篇文章至少是我看过的最详细的标准库配置DMA版本.而且代码实测稳定能用. STM32 | DMA配置和使用如此简单(超详细)_...| .. ...

  6. ZYNQ进阶之路14--PS端uart串口接收不定长数据

    ZYNQ进阶之路14--PS端uart串口接收不定长数据 导语 ZYNQ串口简介 实现步骤 导语 繁忙的博主又来了,本节我们实现一个比较简单的东西:串口.之前的章节中我们也有使用PS端的串口进行收发数 ...

  7. I.MX6U嵌入式Linux应用编程学习

    I.MX6U嵌入式Linux应用编程学习 目录 I.MX6U嵌入式Linux应用编程学习 〇.备忘 0.0 本文大写字母缩写说明 0.1 C编译 0.2 NFS与文件夹挂载 一.应用编程概念 1.1 ...

  8. C语言嵌入式Linux高级编程

    C语言本质上是编程语言的"通用语言",在今天仍具有极大的影响力.那么,C语言到底学到什么程度,才能够进行嵌入式内核.驱动的开发? 本课程为系列课程中的一个小节,入门介绍篇,介绍嵌入 ...

  9. 外网访问arm嵌入式linux_嵌入式Linux系统编程——文件读写访问、属性、描述符、API

    Linux 的文件模型是从 Unix 的继承而来,所以 Linux 继承了 UNIX 本身的大部分特性,然后加以扩展,本章从 UNIX 系统接口来描述 Linux 系统结构的特性. 操作系统是通过一系 ...

最新文章

  1. Hyper-v 3.0虚拟化平台群集共享磁盘无法failover的故障
  2. 主流Kubernetes发行版梳理,看完就会选了
  3. 【Bootstrap+JSP+Mysql学习笔记(二)】开发环境配置(二)
  4. linux 内存管理 page fault带来的性能问题
  5. HP服务器350g5怎么安装系统,HP ML350 G5服务器安装SCO 5.0.7流程
  6. 手把手教你从零构建属于自己的小linux
  7. java中Date,SimpleDateFormat
  8. ROS获取KinectV2相机的彩色图和深度图并制作bundlefusion需要的数据集
  9. 《过早退出是一切失败的根源》读后感
  10. 微软将人工智能嵌入Windows 10更新
  11. mysql中下杠怎么打_怎么打字母下方的短横杠?,下横杠怎么打
  12. BDD怎样帮助你解决沟通问题并增进协作
  13. vim编码设置问题(转)
  14. docker container
  15. NYOJ 305 表达式求值 (字符串处理)
  16. 类加载的过程(加载、验证、准备、解析、初始化)
  17. linux新手记录;可执行文件直接运行
  18. Nepxion Discovery(1) 全链路蓝绿发布
  19. 学点字符串匹配——zbox
  20. CVE(2017-15715、2021-41773、2021-40438)漏洞复现

热门文章

  1. 路由器全千兆什么意思?什么是全千兆路由器?
  2. linux(centos7)部署kubernetes(k8s 1.16.2)集群环境及测试
  3. 三维SLAM算法LeGO-LOAM源码阅读(三)
  4. BES2300YP蓝牙耳机出现关机失败不断重启问题的解决方法
  5. java 代码打开jar文件_Java基础之用记事本编辑java代码运行,并且打成jar包后运行...
  6. java用中点画圆法_Bresenham画圆算法 与中点画圆法
  7. yql failed_YQL的测试帖子
  8. css代码价格,CSS三种价格表样式-CSS应用实例
  9. 使用iPhone和iPad的10个小窍门
  10. 1507四舍五入c语言,JavaScript中用于四舍五入的Math.round()方法讲解