当前位置: 代码网 > 服务器>服务器>Linux > Linux下利用select实现串口数据读取过程

Linux下利用select实现串口数据读取过程

2025年09月29日 Linux 我要评论
在 linux 系统里,我们可以借助select、poll或者epoll这些 i/o 多路复用机制达成串口数据读取的触发方式。这些机制能够让程序在特定文件描述符(像串口设备文件描述符)有数据可读时得到

在 linux 系统里,我们可以借助 selectpoll 或者 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 函数,而是在有数据到达时才进行读取操作。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com