1实验目的
通过编写多路复用式串口读写,进一步理解多路复用函数的用法,同时更加熟练地掌握LINUX设备文件的读写方法。
2、实验内容
本实验中,实现两台机器(宿主机和目标板)之间的串口通信,而且每台机器均可以发送数据和接收数据。除了串口的设备名称不同,两台机器上的程序基本相同。
首先,程序打开串口设备文件并进行相关配置,调用select()函数或poll()函数,使它等待从标准输入(终端)文件中的输入数据及串口设备的输入数据。
1、如果有标准输入文件上的数据,则写入到串口,使对方读取。
2、如果有串口设备上的输入数据,则将数据写入到普通文件中。
select函数
LINUX系统提供select函数来实现多路复用输入/输出模型,原型:
int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout);
参数maxfd是需要监视的最大的文件描述符值+1;rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合。struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。
- 1.timeout=NULL(阻塞:select将一直被阻塞,直到某个文件描述符上发生了事件)
2.timeout所指向的结构设为非零时间(等待固定时间:如果在指定的时间段里有事件发生或者时间耗尽,函数均返回)
3.timeout所指向的结构,时间设为0(非阻塞:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生)
。
返回值:返回对应位仍然为1的fd的总数。注意啦:只有那些可读,可写以及有异常条件待处理的fd位仍然为1。否则为0哦。举个例子,比如recv(), 在没有数据到来调用它的时候,你的线程将被阻塞,如果数据一直不来,你的线程就要阻塞很久.这样显然不好。所以采用select来查看套节字是否可读(也就是是否有数据读了) 。
对于fd_set类型通过下面四个宏来操作:
FD_ZERO(fd_set *fdset) 将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
FD_SET(fd_set *fdset) 用于在文件描述符集合中增加一个新的文件描述符。
FD_CLR(fd_set *fdset) 用于在文件描述符集合中删除一个文件描述符。
FD_ISSET(int fd,fd_set *fdset) 用于测试指定的文件描述符是否在该集合中。
UNIX系统通常会在头文件<sys/select.h>中定义常量FD_SETSIZE,它是数据类型fd_set的描述字数量,其值通常是1024,这样就能表示<1024的fd。
使用select函数的过程一般是:
先调用宏FD_ZERO将指定的fd_set清零,然后调用宏FD_SET将需要测试的fd加入fd_set,接着调用函数select测试fd_set中的所有fd,最后用宏FD_ISSET检查某个fd在函数select调用后,相应位是否仍然为1。
ps:其实就是 select函数将没有事件发生的fd_set类型变量位清0(tmp_inset).
实现代码如下:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
const char *file_name ="./file_name.dat";
int set_opt(int,int,int,char,int);
//"/dev/ttySAC3"是con2,靠近耳机接口的串口 讯为开发板
void main()
{
int fd,nByte;
char *uart3 = "/dev/ttySAC3";
char *uart_out = "please input\r\n";
int fds[2],recv_fd,max_fd;
char buff[500];
struct timeval tv;
unsigned loop =1;
int res,real_read,i;
fd_set inset,tmp_inset;
if((recv_fd=open(file_name,O_CREAT|O_WRONLY,0644))<0){
printf("open");
return 1;
}
fds[0]=STDIN_FILENO;
if((fd = open(uart3, O_RDWR|O_NOCTTY))<0) //open uart
printf("open %s is failed",uart3);
else{
fds[1]=fd;
set_opt(fd, 115200, 8, 'N', 1); //configuration
write(fd,uart_out, strlen(uart_out));
FD_ZERO(&inset);
FD_SET(fds[0],&inset);
FD_SET(fds[1],&inset);
max_fd=(fds[0]>fds[1])?fds[0]:fds[1];
tv.tv_sec=30;
tv.tv_usec=0;
printf("input some words (enter 'quit' to exit: \n");
while(loop &&(FD_ISSET(fds[0],&inset)||FD_ISSET(fds[1],&inset)))
{
tmp_inset=inset;
res= select (max_fd+1,&tmp_inset,NULL,NULL,&tv);//!!!
switch(res)
{
case -1:
{
printf("select");
loop=0;
}break;
case 0:{
printf("select time out");
loop=0;
}break;
default:{
for(i=0;i<2;i++){
if(FD_ISSET(fds[i],&tmp_inset)){//测试指定的文件描述符是否在该集合中。 是否有可读文件
memset(buff,0,500);
real_read=read(fds[i],buff,500);
if((real_read<0)&&(errno!=EAGAIN)){ //出错
loop=0;
}else if(!real_read) //出错
{
close(fds[i]);
FD_CLR(fds[i],&inset);
}
else
{
buff[real_read]='\0';
if(i==0)
{
write(fds[1],buff,strlen(buff));
printf("Input some words ,enter 'quit'to exit \r\n");
}else if(i==1)
{
write(recv_fd,buff,real_read);
}
if(strncmp(buff,"quit",4)==0){
loop=0;
}
}
}
}
}break;
}
}
}
close(recv_fd);
return 0;
}
int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
struct termios newtio,oldtio;
if ( tcgetattr( fd,&oldtio) != 0) {
perror("SetupSerial 1");
return -1;
}
bzero( &newtio, sizeof( newtio ) );
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
switch( nBits )
{
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
}
switch( nEvent )
{
case 'O':
newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
break;
case 'E':
newtio.c_iflag |= (INPCK | ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
break;
case 'N':
newtio.c_cflag &= ~PARENB;
break;
}
switch( nSpeed )
{
case 2400:
cfsetispeed(&newtio, B2400);
cfsetospeed(&newtio, B2400);
break;
case 4800:
cfsetispeed(&newtio, B4800);
cfsetospeed(&newtio, B4800);
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
break;
case 460800:
cfsetispeed(&newtio, B460800);
cfsetospeed(&newtio, B460800);
break;
default:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
}
if( nStop == 1 )
newtio.c_cflag &= ~CSTOPB;
else if ( nStop == 2 )
newtio.c_cflag |= CSTOPB;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH);
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
perror("com set error");
return -1;
}
// printf("set done!\n\r");
return 0;
}
运行结果如下:
目标板输入数据并显示提示语句::
串口助手接收显示:
串口助手输入数据,目标板接收到后保存到 xxx.dat 文档:
超过30S退出:
3、将select()改成poll()
(2p50)
poll 与 select 很类似,都是对描述符进行遍历,查看是否有描述符就绪。如果有就返回就绪文件描述符的个数将。poll 函数如下:
#include <poll.h>
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout)
第一个参数指向结构数组第一个元素的指针,每个数组都是一个 pollfd 结构,用于指定测试某个给定描述符 fd 的条件。
第二个参数为需要监听的文件个数,即第一个参数的元素数目
第三个参数: 单位ms ,为0是无限延时。
struct pollfd
{
int fd; //需要监听的文件描述符
short events;//关心 fd 上发生的事件
short revents;//fd 实际上上发生的事件
}
要测试的条件由 events 成员指定,函数在相应的 revents 成员中返回该描述符的状态(每个描述符都有两个变量,一个为调用值,另一个为返回结果,从而避免值-结果参数)这两个成员中的每一个都由指定某个特定条件的一位或多位组合而成。下标列出指定 events 标志以及测试 revents 标志的一些常值。
ps:只要理解 结构体里的备注就行了,poll机制比select效率更高,也更加通俗易懂。
代码如下:
根据自己情况修改设备节点噢!!! poll我直接在ubuntu上玩的。
void main()
{
struct pollfd fds[2];
int fd,nByte;
char *uart3 = "/dev/ttyUSB0";
char *uart_out = "please input\r\n";
int recv_fd;
char buff[500];
struct timeval tv;
unsigned loop =1;
int res,real_read,i;
if((recv_fd=open(file_name,O_CREAT|O_WRONLY,0644))<0){
printf("open");
return 1;
}
fds[0].fd=STDIN_FILENO;
if((fd = open(uart3, O_RDWR|O_NOCTTY))<0) //open uart
printf("open %s is failed",uart3);
else{
fds[1].fd=fd;
set_opt(fd, 115200, 8, 'N', 1); //configuration
write(fd,uart_out, strlen(uart_out));
for(i=0;i<2;i++)
fds[i].events=POLLIN;
printf("input some words (enter 'quit' to exit: \n");
while(loop &&(fds[0].events||fds[1].events))
{
if(poll(fds,2,0)<0)
{
printf("Poll error or time out \n");
return 1;
}
for(i=0;i<2;i++){
if(fds[i].revents){
memset(buff,0,500);
real_read=read(fds[i].fd,buff,500);
if((real_read<0)&&(errno!=EAGAIN)){
loop=0;
}else if(!real_read)
{
close(fds[i].fd);
fds[i].events=0;
}
else
{
buff[real_read]='\0';
if(i==0)
{
write(fds[1].fd,buff,strlen(buff));
printf("Input some words ,enter 'quit'to exit \r\n");
}else if(i==1)
{
write(recv_fd,buff,real_read);
}
if(strncmp(buff,"quit",4)==0){
loop=0;
}
}
}
}
}
}
close(recv_fd);
return 0;
}
本文借鉴华清远见嵌入式培训教材《从实践中学嵌入式Linux应用程序开发》
ps:好记性不如烂笔头~
来源:https://blog.csdn.net/weixin_39763896/article/details/100130532