linux串口数据读取方法

  |

Linux下的串口操作说简单也简单,说复杂也复杂。简单在于其读写调用与文件操作一致,使用方便。但是参数设置相对复杂,而且在一些操作上需要注意技巧。

在串口数据读取方法上,有一点很值得注意,就是操作超时退出处理。如果没有这方面的处理,只用使用read()函数阻塞读取,那么一旦串口buffer中没有数据可读,将会导致进程卡死,造成通信中断。

超时保护措施有几种:

  1. 采用中断方式读取。
  2. 启动一个定时器手动进行计时退出保护。
  3. 使用select查询。

前两种方式使用的话当然没有问题,但是用法比较复杂,如果不是有很特殊的要求,不建议使用,方法3完全可以满足一般使用,而且代码简洁,独立性很强。

select()函数的原型是:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

其作用是监视文件描述符(句柄)的状态,如果该描述符状态发生预期的变化,则返回关联值。语言表达的可能比较复杂,从函数的几个参数逐步来看吧。

int nfds,文件描述符+1 ,存在多个文件描述符时,取最大的加1。

fd_set *readfds,存放读状态的数据体指针,如果设置了该值,则表示文件描述符出现可读状态时返回。

fd_set *writefds,与上面类似,不同的是存在可写状态时返回。

fd_set *exceptfds,这里是存在异常状态时返回,一般没有使用。

struct timeval *timeout,超时参数结构体,原型为:

struct timeval { long tv_sec; /* 秒*/ long tv_usec; /* 微秒*/ };

FD_ZERO()进行清零,FD_SET()绑定描述符。

示例:

/*—————————–Start——————————*/
int fd; //文件描述符
int rtv; //存放返回值
fdset fs_read; //读状态数据集(这个名字不好取,只可意会吧)
struct timeval timeout;//超时设置参数结构体
FD_ZERO(&fs_read); //数据集清零
FD_SET(fd, &fs_read);//绑定描述符与数据集
rtv = select(fd + 1, &fs_read, NULL, NULL, &timeout);//查询改描述符关联设备是否存在数据可读
if(rtv == -1)//select函数调用失败
{
perror(“select() Error!”);
}
else if(rtv) //存在可读数据,准备读取
{
read(……..);
//GO ON….
}
/*—————————–End——————————*/

这里需要注意:

  • 每次调用select()函数前,必须使用FD_ZERO()进行清零,否则select()在一次超时后将会根据之前的数据,直接超时返回。
  • 串口通信时,如果出现校验失败,必须先将串口buffer中剩余的数据读出清除,然后重新开始下一次的握手。否则,由于buffer中一直有残留数据,select()可正常返回,导致数据定长读取时无法通过校验,进入恶性死循环。