linux串口中断怎么配置?linux串口中断处理流程详解

在 Linux 系统中,串口(UART/Serial Port)通信通常可以通过轮询(Polling)中断(Interrupt)方式实现,中断方式效率更高,因为它允许 CPU 在等待数据时执行其他任务,直到硬件触发中断才处理数据。

以下是关于 Linux 串口中断的详细说明,包括:

【北京迅为】嵌入式学习之Linux驱动(第十七期_串口_全新升级)_基于RK3568开发板
加载中
【北京迅为】嵌入式学习之Linux驱动(第十七期_串口_全新升级)_基于RK3568开发板
  1. 用户空间如何通过中断方式读写串口
  2. 内核空间如何实现串口中断驱动
  3. 常见问题与调试技巧

用户空间:使用中断方式读写串口

在用户空间,Linux 提供了两种主要机制来实现“非阻塞”或“事件驱动”的串口通信:

使用 select() / poll() / epoll()

这是最常用、最推荐的方式,虽然它不是真正的“硬件中断”,但它通过内核的事件通知机制模拟了中断行为,避免忙等待。

示例代码(使用 select):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/select.h>
#include <string.h>
int open_serial_port(const char device) {
    int fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd < 0) {
        perror("open");
        return -1;
    }
    return fd;
}
int main() {
    int fd = open_serial_port("/dev/ttyS0");
    if (fd < 0) return 1;
    // 配置串口(略,实际需设置波特率、数据位等)
    struct termios tty;
    tcgetattr(fd, &tty);
    cfsetispeed(&tty, B9600);
    cfsetospeed(&tty, B9600);
    tty.c_cflag &= ~PARENB;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;
    tty.c_cflag &= ~CRTSCTS;
    tty.c_cflag |= CREAD | CLOCAL;
    tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    tty.c_iflag &= ~(IXO

linux串口中断怎么配置?linux串口中断处理流程详解

N | IXOFF | IXANY); tty.c_iflag &= ~(INLCR | ICRNL | IGNCR); tty.c_oflag &= ~OPOST; tty.c_cc[VMIN] = 1; tty.c_cc[VTIME] = 0; tcsetattr(fd, TCSANOW, &tty); fd_set readfds; struct timeval timeout; while (1) { FD_ZERO(&readfds); FD_SET(fd, &readfds); timeout.tv_sec = 1; timeout.tv_usec = 0; int ret = select(fd + 1, &readfds, NULL, NULL, &timeout); if (ret < 0) { perror("select"); break; } else if (ret == 0) { printf("Timeoutn"); continue; } if (FD_ISSET(fd, &readfds)) { char buf[256]; ssize_t n = read(fd, buf, sizeof(buf)); if (n > 0) { printf("Received %d bytes: ", n); fwrite(buf, 1, n, stdout); printf("n"); } } } close(fd); return 0; }

✅ 优点:简单、可移植、无需修改内核。
❌ 缺点:不是真正的硬件中断,仍由内核调度。


使用 SIGIO 信号(真实中断通知)

Linux 支持通过 F_SETSIGFASYNC 实现基于信号的中断通知。

示例代码(使用 SIGIO):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <signal.h>
#include <string.h>
volatile sig_atomic_t got_data = 0;
void sigio_handler(int sig) {
    got_data = 1;
}
int main() {
    int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd < 0) {
        perror("open");
        return 1;
    }
    // 配置串口(同上,略)
    // 设置进程组 ID 以便接收 SIGIO
    fcntl(fd, F_SETOWN, getpid());
    // 启用异步 I/O 和信号通知
    int flags = f

linux串口中断怎么配置?linux串口中断处理流程详解

cntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | FASYNC); // 设置信号类型为 SIGIO fcntl(fd, F_SETSIG, SIGIO); struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = sigio_handler; sigaction(SIGIO, &sa, NULL); char buf[256]; while (1) { if (got_data) { got_data = 0; ssize_t n = read(fd, buf, sizeof(buf)); if (n > 0) { printf("Received: "); fwrite(buf, 1, n, stdout); printf("n"); } } usleep(10000); // 避免忙等待 } close(fd); return 0; }

⚠️ 注意:SIGIO 需要内核支持,并且某些嵌入式平台可能未启用。


内核空间:实现串口中断驱动

如果你需要编写 Linux 内核模块来处理串口中断,你需要实现一个字符设备驱动,并注册中断服务程序(ISR)。

基本步骤:

  1. 分配并初始化 uart_port 结构。
  2. 注册中断处理函数。
  3. 实现 uart_ops 中的 .tx_empty.set_termios 等回调。
  4. 在中断服务程序中读取 FIFO 数据,并通过 tty_flip_buffer_push() 提交给 TTY 层。

示例:简化的中断处理函数

static irqreturn_t my_uart_isr(int irq, void dev_id) {
    struct my_uart_port port = dev_id;
    unsigned int status;
    // 读取状态寄存器
    status = readl(port->membase + UART_REG_LSR);
    if (status & UART_LSR_DR) {
        // 数据就绪,从数据寄存器读取
        unsigned char data = readb(port->membase + UART_REG_DATA);
        // 将数据放入 TTY 翻转缓冲区
        tty_insert_flip_string(&port->tty->port, &data, 1);
        tty_flip_buffer_push(&port->tty->port);
    }
    if (status & UART_LSR_THRE) {
        //

linux串口中断怎么配置?linux串口中断处理流程详解

发送寄存器为空,可以继续发送 if (!uart_circ_empty(&port->tty->port.xmit)) { // 发送下一个字节 unsigned char ch = port->tty->port.xmit[port->tty->port.xmit_head]; writeb(ch, port->membase + UART_REG_DATA); port->tty->port.xmit_head = (port->tty->port.xmit_head + 1) & (UART_XMIT_SIZE - 1); } } return IRQ_HANDLED; }

注册中断:

int ret = request_irq(irq_number, my_uart_isr, IRQF_SHARED, "my_uart", port);
if (ret) {
    dev_err(&pdev->dev, "Failed to request IRQ %dn", irq_number);
    return ret;
}

常见问题与调试

问题 可能原因 解决方案
收不到中断 中断未使能、引脚复用错误 检查设备树(DTS)中 UART 引脚配置
数据错乱 波特率不匹配、FIFO 未清空 检查波特率设置,清除 FIFO
中断频繁触发 噪声干扰、未屏蔽错误中断 检查 LSR 寄存器,屏蔽错误中断
用户空间收不到数据 TTY 层未正确推送 检查 tty_flip_buffer_push() 是否调用

  • 用户空间:推荐使用 select()/poll() 实现高效非阻塞 I/O,或使用 SIGIO 实现信号驱动。
  • 内核空间:需实现完整的 UART 驱动框架,注册中断服务程序,并通过 TTY 层提交数据。
  • 调试工具sttyminicomcat /proc/interruptsdmesg 等。

如需进一步深入某一部分(如设备树配置、DMA 支持、低功耗模式等),欢迎继续提问!

首发原创文章,作者:世雄 - 原生数据库架构专家,如若转载,请注明出处:https://idctop.com/article/487158.html

(0)
excel分数函数怎么用?excel求平均分公式
上一篇 2026年7月12日 13:54
linux系统如何查看硬盘?linux检测硬盘命令
下一篇 2026年7月12日 13:56

相关推荐

  • linux setup怎么安装?linux系统常用命令大全

    Linux Setup安装的核心在于明确发行版需求、准备引导介质并执行标准化分区与网络配置,通常通过U盘启动进入Live环境后运行图形化安装向导或命令行脚本即可完成,整个过程耗时约15至30分钟,很多初学者在面对Linux Setup安装时,往往被复杂的终端命令吓退,或者在分区环节反复纠结,现代Linux发行版……

    2026年7月8日
    17600
  • linux mysql source怎么用?linux mysql source命令详解

    在Linux环境下使用source命令导入MySQL数据时,核心在于确保文件路径绝对正确、字符集一致以及数据库连接权限无误,这是解决导入失败最高效的路径,很多运维人员或开发者在迁移数据时,习惯性地使用mysql -u root -p database < file.sql这种重定向方式,但在处理大型脚本或……

    2026年7月4日
    12100
  • linux python如何发布?python发布到linux的详细教程

    在Linux环境下发布Python项目,核心在于构建独立的虚拟环境、正确配置依赖包以及使用Docker或systemd进行进程管理,这能彻底解决环境冲突并实现自动化部署,很多开发者在本地调试完美无缺的代码,一旦上传到服务器就报错连连,这种“在我机器上是好的”困境,根源往往不在于代码逻辑,而在于Linux系统环境……

    2026年7月6日
    10400
  • Linux帐号怎么管理?linux系统用户权限设置教程

    Linux 帐号管理的核心在于最小权限原则与自动化审计,通过合理的用户组策略、SSH 密钥认证及定期日志审查,可构建高安全性的系统环境,在服务器运维的日常场景中,账号不仅仅是登录系统的凭证,更是安全防线的第一道关卡,很多初学者习惯使用 root 账号直接操作,这无异于将金库钥匙挂在门口,业内专家指出,绝大多数安……

    2026年7月8日
    11100
  • linux xz命令怎么用?linux xz压缩解压命令详解

    xz命令是Linux系统中基于LZMA算法的高效压缩工具,其核心优势在于极高的压缩比,特别适合对存储成本敏感且对CPU资源有一定余量的服务器环境,但需注意其解压速度相对较慢且不支持增量备份,在Linux生态中,压缩工具的选择往往是一场关于“空间”与“时间”的博弈,gzip和bzip2虽然普及,但在处理大型日志文……

    2026年7月12日
    6100
  • linux ls color怎么设置?linux命令颜色配置方法

    Linux ls 命令默认不显示颜色,需通过配置 alias 或修改 .bashrc 文件启用,并配合 dircolors 工具自定义不同文件类型的显示色彩,以实现高效的终端视觉管理,在 Linux 终端中,黑白分明的文件列表往往让系统管理员感到枯燥且容易出错,启用颜色高亮不仅能提升视觉体验,更能通过色彩快速区……

    2026年7月6日
    9400
  • linux编程api怎么用?linux常用api接口有哪些

    Linux编程API的核心在于通过系统调用与标准库交互,掌握POSIX标准接口是构建高性能、跨平台应用的基础,建议优先使用glibc而非直接调用底层syscall以保证可移植性,在Linux生态中,编程不仅仅是写代码,更是与操作系统内核进行一场精密的对话,许多初学者容易陷入“能跑就行”的误区,却忽视了API调用……

    2026年7月6日
    17700
  • Arm Linux怎么配置WiFi?Linux系统WiFi驱动安装教程

    在ARM Linux设备上配置Wi-Fi,核心在于正确加载驱动并配置NetworkManager或wpa_supplicant,通常通过ip link查看设备状态,使用nmtui或编辑/etc/wpa_supplicant/wpa_supplicant.conf文件即可完成连接,ARM架构因其低功耗和高集成度特……

    2026年7月8日
    2000
  • Linux shell并发执行如何优化?shell并发脚本编写技巧

    Linux Shell并发处理的核心在于利用后台进程(Background Jobs)、管道(Pipes)以及GNU Parallel等工具,将串行任务转化为并行执行,从而显著提升I/O密集型或CPU密集型任务的运行效率,在服务器运维和数据处理场景中,串行执行脚本往往意味着时间的无谓浪费,想象一下,如果你需要处……

    2026年7月6日
    7700
  • linux ls d命令怎么用?linux ls d参数详解

    在 Linux 中,ls -d 是一个非常有用的组合,它的主要作用是只列出目录本身,而不列出目录中的内容,基本用法ls -d <目录名>为什么需要 -d?默认情况下,ls 命令会列出指定目录下的文件和子目录,如果你只想查看目录本身的属性(如权限、所有者、大小等),而不想看到里面的内容,就需要使用……

    2026年7月10日
    9300

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注