嵌入式Linux 串口编程系列3——通过VTIM、VMIN、select实现串口不定长数据接收功能
上一篇文章中,我们详细分析了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实现串口不定长数据接收功能相关推荐
- 基于HAL库STM32串口驱动不定长数据接收
STM32串口驱动不定长数据接收带环形缓冲区 最新框架代码 使用方法 源码 串口接口文件 环形缓冲区接口文件 移植图示 使用涉及4个文件, UART_Port.c UART_Port.h Circul ...
- 嵌入式Linux 串口编程系列2--termios的VMIN和VTIME深入理解
在上一篇文章中,我们介绍了串口的一些基本知识.串口配置接口 termios结构体的概念,串口的配置参数有n多个,这里面不用都背下来,什么时候使用,翻看手册即可,但是有两个 参数是一定要理解的,就是VM ...
- 嵌入式Linux 串口编程系列4——EasyARM287开发板通过freemodbus实现Modbus通信
前面的文章分析了串口的一些基本知识,在工业应用中,串口通信比较常用的协议就是Modbus RTU,freemodbus是一款微型modbus协议栈,之前对各种单片机.小型处理器支持的比较好,从V1.6 ...
- STM32CubeMX系列教程8:配置工程模板(串口+不定长数据收发+DMA+IDLE中断+软中断)
文章目录 摘要 生成工程 配置外设 1.配置时钟与Debug 2.配置串口与DMA 3.配置定时器与中断 配置时钟树 配置工程设置 点击`GENERATE CODE`生成工程 修改源码 配置软中断 配 ...
- STM32从零到一,从标准库移植到HAL库,UART串口1以DMA模式收发不定长数据代码详解+常见问题 一文解析
前言 本文的参考资料 感谢提供标准库版本的CSDN同学:这两篇文章至少是我看过的最详细的标准库配置DMA版本.而且代码实测稳定能用. STM32 | DMA配置和使用如此简单(超详细)_...| .. ...
- ZYNQ进阶之路14--PS端uart串口接收不定长数据
ZYNQ进阶之路14--PS端uart串口接收不定长数据 导语 ZYNQ串口简介 实现步骤 导语 繁忙的博主又来了,本节我们实现一个比较简单的东西:串口.之前的章节中我们也有使用PS端的串口进行收发数 ...
- I.MX6U嵌入式Linux应用编程学习
I.MX6U嵌入式Linux应用编程学习 目录 I.MX6U嵌入式Linux应用编程学习 〇.备忘 0.0 本文大写字母缩写说明 0.1 C编译 0.2 NFS与文件夹挂载 一.应用编程概念 1.1 ...
- C语言嵌入式Linux高级编程
C语言本质上是编程语言的"通用语言",在今天仍具有极大的影响力.那么,C语言到底学到什么程度,才能够进行嵌入式内核.驱动的开发? 本课程为系列课程中的一个小节,入门介绍篇,介绍嵌入 ...
- 外网访问arm嵌入式linux_嵌入式Linux系统编程——文件读写访问、属性、描述符、API
Linux 的文件模型是从 Unix 的继承而来,所以 Linux 继承了 UNIX 本身的大部分特性,然后加以扩展,本章从 UNIX 系统接口来描述 Linux 系统结构的特性. 操作系统是通过一系 ...
最新文章
- Hyper-v 3.0虚拟化平台群集共享磁盘无法failover的故障
- 主流Kubernetes发行版梳理,看完就会选了
- 【Bootstrap+JSP+Mysql学习笔记(二)】开发环境配置(二)
- linux 内存管理 page fault带来的性能问题
- HP服务器350g5怎么安装系统,HP ML350 G5服务器安装SCO 5.0.7流程
- 手把手教你从零构建属于自己的小linux
- java中Date,SimpleDateFormat
- ROS获取KinectV2相机的彩色图和深度图并制作bundlefusion需要的数据集
- 《过早退出是一切失败的根源》读后感
- 微软将人工智能嵌入Windows 10更新
- mysql中下杠怎么打_怎么打字母下方的短横杠?,下横杠怎么打
- BDD怎样帮助你解决沟通问题并增进协作
- vim编码设置问题(转)
- docker container
- NYOJ 305 表达式求值 (字符串处理)
- 类加载的过程(加载、验证、准备、解析、初始化)
- linux新手记录;可执行文件直接运行
- Nepxion Discovery(1) 全链路蓝绿发布
- 学点字符串匹配——zbox
- CVE(2017-15715、2021-41773、2021-40438)漏洞复现
热门文章
- 路由器全千兆什么意思?什么是全千兆路由器?
- linux(centos7)部署kubernetes(k8s 1.16.2)集群环境及测试
- 三维SLAM算法LeGO-LOAM源码阅读(三)
- BES2300YP蓝牙耳机出现关机失败不断重启问题的解决方法
- java 代码打开jar文件_Java基础之用记事本编辑java代码运行,并且打成jar包后运行...
- java用中点画圆法_Bresenham画圆算法 与中点画圆法
- yql failed_YQL的测试帖子
- css代码价格,CSS三种价格表样式-CSS应用实例
- 使用iPhone和iPad的10个小窍门
- 1507四舍五入c语言,JavaScript中用于四舍五入的Math.round()方法讲解