Linux串口poll实现稳定读写 (linux串口poll读写)
在嵌入式开发中,串口是一种常用的通信方式,可以实现设备之间的数据传输。在Linux系统中,串口通信可以通过串口设备文件来实现。但是,在高速数据传输或大量数据读写的情况下,直接使用串口设备文件会出现延迟或数据丢失等问题。为了解决这些问题,可以使用串口poll技术来实现串口的稳定读写。
一、Linux串口通信
Linux系统将串口设备当作字符设备来进行管理,串口设备文件一般位于/dev/ttyS*,其中“*”代表串口号。例如,之一个串口设备文件为/dev/ttyS0,第二个串口设备文件为/dev/ttyS1。在使用串口之前,需要先打开串口设备文件以获取文件描述符,然后就可以进行串口读写操作。
串口读写操作一般使用系统调用 read() 和 write() 来实现。read() 函数用于从串口中读取数据,函数原型为:
ssize_t read(int fd, void *buf, size_t count);
其中,fd为打开的串口设备的文件描述符,buf为存储读取数据的缓冲区,count为希望读取的字节数。函数返回值为实际读取到的字节数。
write() 函数用于向串口中写入数据,函数原型为:
ssize_t write(int fd, const void *buf, size_t count);
其中,fd为打开的串口设备的文件描述符,buf为待写入数据的缓冲区,count为待写入数据的字节数。函数返回值为实际成功写入的字节数。
二、poll函数介绍
poll() 函数是Linux系统中用于多路复用I/O的函数之一,可以在多个文件描述符上进行监视,当其中的任意一个文件描述符就绪时,函数就可以返回。poll() 函数的功能类似于 select() 函数,但是 select() 函数有一些限制,例如文件描述符的大小限制、效率较低等问题。因此,在Linux系统中,poll() 函数被广泛应用。
poll() 函数的原型如下:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
其中,fds是指向 struct pollfd 结构体数组的指针,nfds为数组中结构体的数量,timeout为等待时间。poll() 函数的返回值为就绪文件描述符的数量。
struct pollfd 结构体定义如下:
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 监视事件 */
short revents; /* 实际发生的事件 */
};
其中,fd为文件描述符,events为监视事件,可以是以下事件的任意组合:
·POLLIN:有数据可读
·POLLOUT:写数据不会阻塞
·POLLPRI:有紧急数据可读
·POLLRDNORM:有数据可读
·POLLRDBAND:有优先数据可读
·POLLWRNORM:写数据不会阻塞
·POLLWRBAND:写优先数据可读
revents为实际发生的事件,与 events 中的事件相对应。
三、
在Linux系统中,使用 poll() 函数可以实现串口的稳定读写。具体实现方法如下:
1. 打开串口设备文件并设置相关参数,例如波特率、数据位、停止位等。
2. 创建 struct pollfd 结构体数组,并将串口设备文件的文件描述符和监视事件加入数组中。
3. 调用 poll() 函数进行监视,当串口设备有数据可读时,poll() 函数将返回1,此时可以使用 read() 函数从串口中读取数据。
4. 当需要向串口中写入数据时,可以使用 write() 函数进行写入,但需要先判断串口是否可写(即 POLLOUT 事件是否已经就绪),如果没有就绪则需要等待。
具体代码实现如下:
// 打开串口设备文件
int fd = open(“/dev/ttyS0”, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd
perror(“Open ttyS0 error.”);
return -1;
}
// 设置串口参数
struct termios opt;
tcgetattr(fd, &opt);
cfsetispeed(&opt, B9600);
cfsetospeed(&opt, B9600);
opt.c_cflag &= ~PARENB; // 无奇偶校验
opt.c_cflag &= ~CSTOPB; // 1位停止位
opt.c_cflag &= ~CSIZE; // 8位数据位
opt.c_cflag |= CS8; // 8位数据位
opt.c_cc[VTIME] = 0; // 读取超时时间0s
opt.c_cc[VMIN] = 1; // 最少读取1个字符
tcsetattr(fd, TCSANOW, &opt);
// 创建 struct pollfd 结构体
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN;
// 监视串口设备
while (1) {
int ret = poll(fds, 1, 1000); // 等待1000ms
if (ret
perror(“Poll error.”);
break;
} else if (ret == 0) {
printf(“Poll timeout.\n”);
} else {
if (fds[0].revents & POLLIN) { // 串口可读
char data[1024] = {0};
int len = read(fd, data, 1024);
if (len > 0) {
printf(“Read data: %s\n”, data);
}
}
if (fds[0].revents & POLLOUT) { // 串口可写
char data[1024] = “Hello World!”;
int len = strlen(data);
int n = write(fd, data, len);
if (n != len) {
printf(“Write error.\n”);
}
}
}
}
在上述代码中,打开串口设备文件后,使用 tcgetattr() 和 tcsetattr() 函数设置串口参数,然后创建 struct pollfd 结构体并将监视事件添加到数组中。在 while 循环中,使用 poll() 函数等待串口设备就绪,当有数据可读时使用 read() 函数读取数据,当需要写入数据时则使用 write() 函数,但必须先判断 POLLOUT 事件是否已经就绪。
四、
通过使用 Linux 中的 poll() 函数,可以实现串口的稳定读写。poll() 函数可以同时监视多个文件描述符的就绪状态,并且可以指定等待时间。使用 poll() 函数实现串口读写时,需要先设置串口参数,然后创建 struct pollfd 结构体,并将监视事件加入数组中。在进行串口读写时,必须先判断相应的事件是否已经就绪,以避免出现延迟或数据丢失等问题。