Linux中斷編程
中斷:是指CPU在運行過程中,出現了某種異常事件,需要CPU先暫停當前工作,轉而去處理新產生的異常事件,處理完后再返回暫停的事件繼續往下執行。就例如我們正在使用手機進行微信視頻聊天,這時突然有人打電話過來,這時手機的處理方式是手機來來電鈴聲響起,通知用戶電話來了。
中斷,就是來處理未來時間內可能會發生的事件, 中斷事件也稱為異常事件。有了中斷處理,則可大大提高CPU處理效率。
在單片機中,我們也常用中斷方式來處理一些緊急事件,幫我們實現快速響應一些實時性的事件。因此我們在編寫中斷服務函數時都是代碼盡可能簡潔、一定不能處理死循環、若需要處理的事情比較多則應在中斷中設定標志位,然后將邏輯代碼放到主函數中去實現。
在Linux內核中,我們一般會將中斷分為頂半部分和底半部分。頂半部分主要是處理耗時短的代碼(像單片機中設置標志位),啟動底半部分代碼;底半部分主要是處理耗時比較長的代碼,完成中斷響應后的事件處理。
1. Linux下外部中斷
??要使用外部中斷,則需要完成中斷三要素的配置:中斷號(irq)、中斷服務函數、中斷觸發方式(電平觸發、邊沿觸發)。
1.1 相關接口函數
- 獲取中斷號gpio_to_irq
??在Linux內核中提供了方便函數獲取引腳中斷號
int gpio_to_irq(unsigned gpio)
函數功能: 獲取中斷號
返回值: 成功返回對應GPIO的中斷號irq
- 注冊中斷request_irq
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
函數功能: 注冊中斷
形 參: irq --中斷號,gpio_to_irq函數返回值。
?? ?handler --中斷服務函數。
?? ??服務函數原型:typedef irqreturn_t (*irq_handler_t)(int, void *);
?? ?flags --中斷觸發方式。
?? ??#define IRQF_TRIGGER_RISING 0x00000001 //上升沿
?? ??#define IRQF_TRIGGER_FALLING 0x00000002 //下升沿
???? #define IRQF_TRIGGER_HIGH 0x00000004//高電平
????#define IRQF_TRIGGER_LOW 0x00000008//低電平
????#define IRQF_SHARED 0x00000080 //共享中斷
???name --中斷注冊標志。
???dev --傳給中斷服務函數的參數。
返回值: 成功返回0,失敗返回其它值。
- 中斷服務函數
typedef irqreturn_t (*irq_handler_t)(int, void *);
函數功能: 中斷服務函數
形 參: 第一個參數為中斷號;第二個參數為注冊函數傳入的參數dev
返回值:
?? enum irqreturn {
????IRQ_NONE = (0 << 0), //如果不是本中斷的則返回這個值,只在共享中斷中使用
????IRQ_HANDLED = (1 << 0), //正確執行中斷程序返回這個值,常用
????IRQ_WAKE_THREAD = (1 << 1), //表示去喚醒中斷處理者的線程
??};
??注意: irqreturn_t (*irq_handler_t)(int, void *);函數中不能出現帶休眠的函數,如msleep函數;該函數必須要返回值。
- 注銷free_irq
free_irq(unsigned int irq, void *dev_id)
函數功能: 注銷中斷
形 參: irq --中斷號,gpio_to_irq函數返回值。
???dev --傳給中斷服務函數的參數。需和注冊時保持一致
2. 工作隊列
??中斷處理函數分為中斷頂半部分和中斷底半部分。頂半部分代碼實現即為中斷服務函數,而底半部分代碼則可由工作隊列完成。
2.1 工作隊列簡介
在操作系統中,如果我們需要進行一項工作處理,往往需要創建一個任務來加入內核的調度隊列。一個任務對應一個處理函數,如果要進行不同的事務處理,則需要創建多個不同的任務。任務作為CPU調度的基本單元,任務數量越大,則調度成本越高。工作隊列(workqueue)機制簡化了基礎的任務創建和處理機制,一個workqueue對應一個實體task任務處理,工作隊列中可以掛載多個工作實體,每一個工作都能對應不同的工作處理函數。即用戶只需要創建一個workqueue,則可以完成多個掛接不同處理函數的工作隊列。
工作隊列還具有將工作推后執行機制,工作隊列可以把工作推后,交由一個內核線程去執行,也就是說,這個下半部分可以在進程上下文中執行。最重要的就是工作隊列允許被重新調度甚至是睡眠。
workqueue的處理依賴于task任務。一個workqueue隊列會創建關聯其對應的task任務,一個workqueue會掛載多個工作進行處理,每個工作都有工作處理函數。當workqueue得到調度,即其關聯的task得到運行,在每次task的調度期間,都會從工作隊列中按照先后順序取出一個work來進行處理。workqueue模塊在初始化時,會創建一個系統默認的工作隊列,用戶可根據需要將work添加到該隊列中去執行。
2.2 工作相關函數接口
- 工作結構體struct work_struct
#include
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func; /*工作處理函數*/
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
??在工作結構體體中,我們需要關心的成員是工作處理函數:work_func_t func,簡單來說即一個工作會對應有一個處理函數。工作處理函數原型如下:
#include
typedef void (*work_func_t)(struct work_struct *work);
- 初始化工作INIT_WORK
#define INIT_WORK(_work, _func)
函數功能: 初始化工作,以宏的方式實現
形 參: _work --工作結構體體指針
???_func --工作處理函數
- 工作調度schedule_work
int schedule_work(struct work_struct *work)
2.3工作隊列使用步驟
- 定義工作結構體struct work_struct,初始化工作INIT_WORK;
- 編寫工作處理函數void (*work_func_t)(struct work_struct *work);
- 在合適的地方調調度工作(一般在中斷頂半部分);
2.4工作隊列使用示例
??下面以按鍵為例,實現中斷方式按鍵檢測,通過工作隊列處理底半部分代碼,雜項設備框架實現設備注冊。
- K1 – GPX3_2
- K2 --GPX3_3
- K3 --GPX3_4
- K4 --GPX3_5
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct work_struct key_work;/*工作結構體*/
struct _KEY
{
unsigned int gpio;/*按鍵引腳*/
char name[20];/*按鍵名*/
int irq;/*中斷號*/
int key_num;/*按鍵編號*/
};
static struct _KEY KEY_GPIO_PIN[]=
{
{EXYNOS4_GPX3(2),"key1",0,1},
{EXYNOS4_GPX3(3),"key2",0,2},
{EXYNOS4_GPX3(4),"key3",0,3},
{EXYNOS4_GPX3(5),"key4",0,4},
};
static struct _KEY *key_p;
static unsigned int key_val;
/*工作服務函數*/
void key_work_func(struct work_struct *work)
{
msleep(30);/*按鍵消抖*/
if(gpio_get_value(key_p->gpio)==0)
{
printk(" key%d 按下n",key_p->key_num);
}
else
{
printk(" key%d 松開n",key_p->key_num);
}
key_val=key_p->key_num;
}
/*中斷服務函數*/
static irqreturn_t key_irq_handler(int irq, void *dev)
{
key_p=(struct _KEY *)dev;
schedule_work(&key_work);/*調度工作*/
return IRQ_HANDLED;/*中斷正常處理*/
}
static int key_open(struct inode *inode, struct file *file)
{
printk("設備打開成功n");
return 0;
}
static ssize_t key_read(struct file *file, char __user *buf, size_t cnt, loff_t *seek)
{
int res=copy_to_user(buf,&key_val, 4);
key_val=0;
return 4-res;
}
static int key_release(struct inode *inode, struct file *file)
{
printk("設備關閉成功n");
return 0;
}
/*文件操作集合*/
static struct file_operations key_fops=
{
.owner= THIS_MODULE, /*當前模塊文件操作集合所有者*/
.open=key_open,/*open函數接口*/
.read=key_read,/*read函數接口*/
.release=key_release,/*close函數接口*/
};
/*
字符設備注冊:主設備+次設備號
主設備 --用來區分類(雜項設備、輸入設備)
次設備號 --對應哪個設備
雜項設備的主設備號固定為:10
*/
static struct miscdevice key_miscdev = {
.minor = MISC_DYNAMIC_MINOR,/*次設備號255由內核分配*/
.name = "tiny4412_key",/*設備節點名字,會在/dev下生成*/
.fops = &key_fops,/**/
};
static int __init tiny4412_key_module_init(void)
{
int i=0;
int res;
printk("hello,驅動注冊成功n");
/*初始化工作*/
INIT_WORK(&key_work,key_work_func);
/*注冊中斷*/
for(i=0;i(key_gpio_pin)>
審核編輯:湯梓紅
-
Linux
+關注
關注
87文章
11293瀏覽量
209340 -
編程
+關注
關注
88文章
3614瀏覽量
93686 -
中斷
+關注
關注
5文章
898瀏覽量
41471
發布評論請先 登錄
相關推薦
評論