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

 更新时间:2025年09月29日 11:31:02   作者:luoqice  
文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设置串口参数及select检测数据流程

在 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 函数,而是在有数据到达时才进行读取操作。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • linux ssh 别名登录小技巧

    linux ssh 别名登录小技巧

    为了方便登录服务器,我们一般使用putty、SecureCRT等等工具。在终端如何通过服务器别名来进行登录呢
    2015-07-07
  • shell脚本批量执行ping和telnet测试网络方式

    shell脚本批量执行ping和telnet测试网络方式

    文章介绍了如何通过创建shell脚本来简化本地网络测试任务,包括ping和telnet测试,文章详细描述了脚本的创建、编辑、赋予执行权限以及执行的步骤,并提供了具体的脚本示例
    2024-12-12
  • 浅谈Linux系统中的异常堆栈跟踪的简单实现

    浅谈Linux系统中的异常堆栈跟踪的简单实现

    下面小编就为大家带来一篇浅谈Linux系统中的异常堆栈跟踪的简单实现。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • linux手动、自动更改网卡MAC地址的方法

    linux手动、自动更改网卡MAC地址的方法

    这篇文章主要给大家介绍了关于linux如何手动、自动更改网卡MAC地址的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用linux具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-06-06
  • Linux中RPM文件操作的常用命令总结

    Linux中RPM文件操作的常用命令总结

    这篇文章主要给大家介绍了关于Linux中RPM文件操作的常用命令,文中通过示例介绍的很详细,对大家的理解和学习很有帮助,有需要的朋友们可以参考借鉴,下面来一起学习学习吧。
    2016-11-11
  • 用DNSPod和Squid打造自己的CDN (五) 安装Squid的前期准备

    用DNSPod和Squid打造自己的CDN (五) 安装Squid的前期准备

    从本章开始,大家将会学到如何在Linux下面安装、编译程序,还会学到程序编译的优化方法,最后会通过源代码编译的方式把Squid安装上
    2013-04-04
  • 使用反向ssh从外网访问内网主机的方法详解

    使用反向ssh从外网访问内网主机的方法详解

    这篇文章主要给大家介绍了使用反向ssh从外网访问内网主机的方法,文中介绍的非常详细,对大家具有一定的参考价值,需要的朋友们下来要起看看吧。
    2017-04-04
  • Linux/Unix系统中进程与文件的关系解读

    Linux/Unix系统中进程与文件的关系解读

    Linux/Unix系统将进程抽象为文件,通过/proc伪文件系统动态展示运行时信息,实现对进程的灵活管理,文章解析了进程文件与普通文件的异同,介绍了文件描述符监控、OOM调整等实际应用,并探讨了该设计理念的哲学意义与技术局限
    2025-08-08
  • Linux中screen命令及使用方法

    Linux中screen命令及使用方法

    Screen是一款由GNU计划开发的用于命令行终端切换的自由软件。这篇文章主要介绍了Linux中的screen命令及使用方法,需要的朋友可以参考下
    2020-02-02
  • CentOS下.htaccess不起作用的解决方法

    CentOS下.htaccess不起作用的解决方法

    PHP 5.2的问题解决后,现在就是安装WordPress了。装好了没有任何问题,接下来继续配置WordPress,开启静态URL链接。然后手动编辑了.htaccess文件,将WordPress生成的相关代码拷贝进去了。
    2011-04-04

最新评论