🎥 視頻教程請關注 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 和日志追蹤觸發行為 |
? 今日作業建議
- 在你的開發板上找一個 GPIO 按鍵,編寫設備樹 + 驅動注冊中斷。
- 使用 printk 或 dev_info() 輸出中斷觸發信息,驗證中斷觸發次數。
- 嘗試通過
workqueue
延遲執行中斷下半部邏輯,如點亮 LED。
🎥 視頻教程請關注 B 站:“嵌入式 Jerry”