在 linux 系统里,我们可以借助 select
、poll
或者 epoll
这些 i/o 多路复用机制达成串口数据读取的触发方式。
这些机制能够让程序在特定文件描述符(像串口设备文件描述符)有数据可读时得到通知,进而进行数据读取操作,而不是像轮询方式那样持续调用 read
函数。
示例代码(使用select实现)
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <termios.h> #include <unistd.h> #include <sys/select.h> // 设置串口波特率 void setspeed(int fd, int speed) { struct termios options; tcgetattr(fd, &options); switch (speed) { case 115200: cfsetispeed(&options, b115200); cfsetospeed(&options, b115200); break; // 可以添加更多波特率设置 default: cfsetispeed(&options, b115200); cfsetospeed(&options, b115200); break; } tcsetattr(fd, tcsanow, &options); } // 设置串口参数 void setparity(int fd, int databits, int stopbits, char parity) { struct termios options; tcgetattr(fd, &options); // 设置数据位 options.c_cflag &= ~csize; switch (databits) { case 7: options.c_cflag |= cs7; break; case 8: options.c_cflag |= cs8; break; default: options.c_cflag |= cs8; break; } // 设置奇偶校验位 switch (parity) { case 'n': case 'n': options.c_cflag &= ~parenb; options.c_cflag &= ~parodd; break; case 'o': case 'o': options.c_cflag |= (parodd | parenb); break; case 'e': case 'e': options.c_cflag |= parenb; options.c_cflag &= ~parodd; break; default: options.c_cflag &= ~parenb; options.c_cflag &= ~parodd; break; } // 设置停止位 switch (stopbits) { case 1: options.c_cflag &= ~cstopb; break; case 2: options.c_cflag |= cstopb; break; default: options.c_cflag &= ~cstopb; break; } tcsetattr(fd, tcsanow, &options); } int main(void) { int fd; unsigned char buf[300]; unsigned short len; fd = open("/dev/ttys1", o_rdwr); if (fd == -1) { perror("can't open serial\n"); return -1; } setparity(fd, 8, 1, 'n'); setspeed(fd, 115200); fd_set readfds; struct timeval timeout; while (1) { // 清空文件描述符集 fd_zero(&readfds); // 将串口文件描述符加入读文件描述符集 fd_set(fd, &readfds); // 设置超时时间 timeout.tv_sec = 0; timeout.tv_usec = 20000; // 调用 select 函数等待事件发生 int activity = select(fd + 1, &readfds, null, null, &timeout); if (activity < 0) { perror("select error"); break; } else if (activity > 0) { // 检查是否是串口文件描述符有数据可读 if (fd_isset(fd, &readfds)) { len = read(fd, buf, 100); if (len > 0) { write(fd, buf, len); } } } } close(fd); return 0; }
代码解释
setspeed
函数:
- 此函数用于设置串口的波特率,
- 它借助
tcsetattr
函数来配置串口的输入和输出波特率
setparity
函数:
- 该函数用于设置串口的数据位、停止位和奇偶校验位
- 同样是通过
tcsetattr
函数来完成串口参数的配置
main
函数:
- 打开串口设备文件
/dev/ttys1
,并且设置串口参数。 - 构建一个
fd_set
类型的文件描述符集readfds
,把串口文件描述符fd
添加到该集合中。 - 设定一个超时时间
timeout
,防止select
函数一直阻塞。 - 调用
select
函数等待事件发生,若有数据可读,select
函数会返回大于 0 的值。 - 利用
fd_isset
宏检查是否是串口文件描述符有数据可读,若有则调用read
函数读取数据,然后将数据回写到串口。
通过这种方式,程序就无需持续轮询 read
函数,而是在有数据到达时才进行读取操作。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论