驱动开发硬核特训 · Day 17:深入掌握中断机制与驱动开发中的应用实战

🎥 视频教程请关注 B 站:“嵌入式 Jerry”


一、前言

在嵌入式驱动开发中,“中断”几乎无处不在。无论是 GPIO 按键、串口通信、网络设备,还是 SoC 上的各种控制器,中断都扮演着核心触发机制的角色。对中断机制掌握程度的高低,直接影响到你驱动开发水平的深度。

本篇将从以下结构展开:

  • 中断的基本概念与分类
  • Linux 内核中的中断框架
  • request_irq() 的实战用法与原理解析
  • 中断处理函数与上下半部机制
  • 中断在 DTS 中的描述与匹配方式
  • 实战:为一个 GPIO 按键注册中断
  • 调试技巧与常见误区

二、中断基础概念

2.1 什么是中断?

中断(Interrupt)是指处理器在运行过程中,外部设备或内部条件发生特定事件时,打断当前正在执行的任务,转而执行一个中断服务程序(ISR)的机制。

2.2 中断的分类

类型描述
外部中断外设发出的信号,如 GPIO、UART、定时器等
内部中断处理器内部事件触发,如异常、软中断等
边沿触发在电平变化的瞬间触发(如上升沿、下降沿)
电平触发在保持某一电平时持续触发(如高电平、低电平)

三、Linux 内核中的中断机制

Linux 内核为中断提供了统一的框架,通过 IRQ(Interrupt Request)编号管理设备之间的中断。
在这里插入图片描述

3.1 中断号(IRQ number)

每个硬件中断都被映射为一个唯一的 IRQ 号,Linux 内核使用这个编号来注册/处理中断。

可通过如下命令查看系统中断分配:

cat /proc/interrupts

3.2 request_irq() 的框架

int request_irq(unsigned int irq,
                irq_handler_t handler,
                unsigned long flags,
                const char *name,
                void *dev);
参数含义
irq要申请的中断号
handler中断服务函数
flags触发方式,如 IRQF_TRIGGER_FALLING 等
name中断名称,用于 /proc/interrupts 识别
dev通常为 pdev 或私有结构体指针,用于共享中断区分

四、中断处理函数与上下半部机制

中断处理时间越短越好,因此 Linux 提供 中断上下半部机制

部分描述
上半部(top half)中断刚发生时的 ISR(中断服务函数),需快速返回
下半部(bottom half)通过 tasklet、workqueue 等机制延迟处理

示例:使用 workqueue 实现下半部

static void my_work_handler(struct work_struct *work)
{
    pr_info("Bottom half executed.\n");
}

static DECLARE_WORK(my_work, my_work_handler);

static irqreturn_t my_irq_handler(int irq, void *dev)
{
    schedule_work(&my_work);
    return IRQ_HANDLED;
}

五、设备树中断描述与解析

5.1 DTS 中的中断定义

button@0 {
    compatible = "gpio-keys";
    gpios = <&gpio3 19 GPIO_ACTIVE_LOW>;
    linux,code = <KEY_ENTER>;
    label = "Enter Button";
    interrupt-parent = <&gpio3>;
    interrupts = <19 IRQ_TYPE_EDGE_FALLING>;
};

5.2 驱动中解析中断号

irq = of_irq_get(np, 0);
ret = request_irq(irq, handler, IRQF_TRIGGER_FALLING, "button", dev);

六、实战讲解:为 GPIO 按键注册中断

以下是一个完整的按键中断驱动代码片段:

static irqreturn_t button_isr(int irq, void *dev_id)
{
    pr_info("Button interrupt triggered!\n");
    return IRQ_HANDLED;
}

static int btn_probe(struct platform_device *pdev)
{
    int irq;
    struct device_node *np = pdev->dev.of_node;

    irq = of_irq_get(np, 0);
    if (irq < 0)
        return irq;

    return devm_request_irq(&pdev->dev, irq, button_isr,
                            IRQF_TRIGGER_FALLING, "button", NULL);
}

驱动匹配表

static const struct of_device_id btn_of_match[] = {
    { .compatible = "myvendor,button" },
    { }
};
MODULE_DEVICE_TABLE(of, btn_of_match);

七、调试技巧与排错建议

7.1 无法响应中断?

  • 检查 of_irq_get() 返回值
  • 确保设备树中 interrupt-parent 正确
  • 查看内核是否启用了相应的 GPIO 中断支持
  • 使用 cat /proc/interrupts 观察中断是否触发计数增加

7.2 中断多次触发?

  • 检查 IRQF_TRIGGER_* 设置是否与实际硬件匹配
  • 排查上拉/下拉电阻或信号抖动问题

八、问答环节总结

Q1:一个驱动中能注册多个中断吗?

可以。使用 request_irq() 多次注册不同 irq。

Q2:可以使用中断做耗时操作吗?

不建议。应放入下半部如 workqueue 中。

Q3:中断共享怎么办?

设置 IRQF_SHARED,并正确传入 dev_id 区分中断源。

Q4:中断服务函数可以被抢占吗?

在默认情况下,中断服务函数运行在中断上下文,不能被抢占,但可以被更高优先级中断打断。


九、总结提炼

关键点说明
中断是驱动中的基础机制几乎所有外设都依赖中断响应事件
request_irq() 是核心接口注册中断的入口函数
使用设备树描述中断资源通过 interrupts + interrupt-parent 精确匹配
上下半部机制非常重要把耗时操作放到 workqueue/tasklet
实战中注意调试利用 /proc/interrupts 和日志追踪触发行为

✅ 今日作业建议

  1. 在你的开发板上找一个 GPIO 按键,编写设备树 + 驱动注册中断。
  2. 使用 printk 或 dev_info() 输出中断触发信息,验证中断触发次数。
  3. 尝试通过 workqueue 延迟执行中断下半部逻辑,如点亮 LED。

🎥 视频教程请关注 B 站:“嵌入式 Jerry”


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值