0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux中斷編程

嵌入式技術(shù) ? 來源:嵌入式技術(shù) ? 作者:嵌入式技術(shù) ? 2022-09-23 09:01 ? 次閱讀

Linux中斷編程

中斷:是指CPU在運行過程中,出現(xiàn)了某種異常事件,需要CPU先暫停當(dāng)前工作,轉(zhuǎn)而去處理新產(chǎn)生的異常事件,處理完后再返回暫停的事件繼續(xù)往下執(zhí)行。就例如我們正在使用手機進行微信視頻聊天,這時突然有人打電話過來,這時手機的處理方式是手機來來電鈴聲響起,通知用戶電話來了。

中斷,就是來處理未來時間內(nèi)可能會發(fā)生的事件, 中斷事件也稱為異常事件。有了中斷處理,則可大大提高CPU處理效率。

單片機中,我們也常用中斷方式來處理一些緊急事件,幫我們實現(xiàn)快速響應(yīng)一些實時性的事件。因此我們在編寫中斷服務(wù)函數(shù)時都是代碼盡可能簡潔、一定不能處理死循環(huán)、若需要處理的事情比較多則應(yīng)在中斷中設(shè)定標(biāo)志位,然后將邏輯代碼放到主函數(shù)中去實現(xiàn)。

在Linux內(nèi)核中,我們一般會將中斷分為頂半部分和底半部分。頂半部分主要是處理耗時短的代碼(像單片機中設(shè)置標(biāo)志位),啟動底半部分代碼;底半部分主要是處理耗時比較長的代碼,完成中斷響應(yīng)后的事件處理。

1. Linux下外部中斷

??要使用外部中斷,則需要完成中斷三要素的配置:中斷號(irq)、中斷服務(wù)函數(shù)、中斷觸發(fā)方式(電平觸發(fā)、邊沿觸發(fā))。

1.1 相關(guān)接口函數(shù)

  • 獲取中斷號gpio_to_irq

??在Linux內(nèi)核中提供了方便函數(shù)獲取引腳中斷號

int gpio_to_irq(unsigned gpio)
函數(shù)功能: 獲取中斷號
返回值: 成功返回對應(yīng)GPIO的中斷號irq
  • 注冊中斷request_irq
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
函數(shù)功能: 注冊中斷
形 參: irq --中斷號,gpio_to_irq函數(shù)返回值。
?? ?handler --中斷服務(wù)函數(shù)。
?? ??服務(wù)函數(shù)原型:typedef irqreturn_t (*irq_handler_t)(int, void *);
?? ?flags --中斷觸發(fā)方式。
?? ??#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 --中斷注冊標(biāo)志。
???dev --傳給中斷服務(wù)函數(shù)的參數(shù)。
返回值: 成功返回0,失敗返回其它值。
  • 中斷服務(wù)函數(shù)
typedef irqreturn_t (*irq_handler_t)(int, void *);
函數(shù)功能: 中斷服務(wù)函數(shù)
形 參: 第一個參數(shù)為中斷號;第二個參數(shù)為注冊函數(shù)傳入的參數(shù)dev
返回值:
?? enum irqreturn {
????IRQ_NONE = (0 << 0), //如果不是本中斷的則返回這個值,只在共享中斷中使用
????IRQ_HANDLED = (1 << 0), //正確執(zhí)行中斷程序返回這個值,常用
????IRQ_WAKE_THREAD = (1 << 1), //表示去喚醒中斷處理者的線程
??};

??注意: irqreturn_t (*irq_handler_t)(int, void *);函數(shù)中不能出現(xiàn)帶休眠的函數(shù),如msleep函數(shù);該函數(shù)必須要返回值。

  • 注銷free_irq
free_irq(unsigned int irq, void *dev_id)
函數(shù)功能: 注銷中斷
形 參: irq --中斷號,gpio_to_irq函數(shù)返回值。
???dev --傳給中斷服務(wù)函數(shù)的參數(shù)。需和注冊時保持一致

2. 工作隊列

??中斷處理函數(shù)分為中斷頂半部分和中斷底半部分。頂半部分代碼實現(xiàn)即為中斷服務(wù)函數(shù),而底半部分代碼則可由工作隊列完成。

2.1 工作隊列簡介

操作系統(tǒng)中,如果我們需要進行一項工作處理,往往需要創(chuàng)建一個任務(wù)來加入內(nèi)核的調(diào)度隊列。一個任務(wù)對應(yīng)一個處理函數(shù),如果要進行不同的事務(wù)處理,則需要創(chuàng)建多個不同的任務(wù)。任務(wù)作為CPU調(diào)度的基本單元,任務(wù)數(shù)量越大,則調(diào)度成本越高。工作隊列(workqueue)機制簡化了基礎(chǔ)的任務(wù)創(chuàng)建和處理機制,一個workqueue對應(yīng)一個實體task任務(wù)處理,工作隊列中可以掛載多個工作實體,每一個工作都能對應(yīng)不同的工作處理函數(shù)。即用戶只需要創(chuàng)建一個workqueue,則可以完成多個掛接不同處理函數(shù)的工作隊列。

工作隊列還具有將工作推后執(zhí)行機制,工作隊列可以把工作推后,交由一個內(nèi)核線程去執(zhí)行,也就是說,這個下半部分可以在進程上下文中執(zhí)行。最重要的就是工作隊列允許被重新調(diào)度甚至是睡眠。

workqueue的處理依賴于task任務(wù)。一個workqueue隊列會創(chuàng)建關(guān)聯(lián)其對應(yīng)的task任務(wù),一個workqueue會掛載多個工作進行處理,每個工作都有工作處理函數(shù)。當(dāng)workqueue得到調(diào)度,即其關(guān)聯(lián)的task得到運行,在每次task的調(diào)度期間,都會從工作隊列中按照先后順序取出一個work來進行處理。workqueue模塊在初始化時,會創(chuàng)建一個系統(tǒng)默認(rèn)的工作隊列,用戶可根據(jù)需要將work添加到該隊列中去執(zhí)行。

2.2 工作相關(guān)函數(shù)接口

  • 工作結(jié)構(gòu)體struct work_struct
#include 
struct work_struct {
	atomic_long_t data;
	struct list_head entry;
	work_func_t func;  /*工作處理函數(shù)*/
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};

??在工作結(jié)構(gòu)體體中,我們需要關(guān)心的成員是工作處理函數(shù):work_func_t func,簡單來說即一個工作會對應(yīng)有一個處理函數(shù)。工作處理函數(shù)原型如下:

#include 
typedef void (*work_func_t)(struct work_struct *work);
  • 初始化工作INIT_WORK
#define INIT_WORK(_work, _func)
函數(shù)功能: 初始化工作,以宏的方式實現(xiàn)
形 參: _work --工作結(jié)構(gòu)體體指針
???_func --工作處理函數(shù)
  • 工作調(diào)度schedule_work
int schedule_work(struct work_struct *work)

2.3工作隊列使用步驟

  1. 定義工作結(jié)構(gòu)體struct work_struct,初始化工作INIT_WORK;
  2. 編寫工作處理函數(shù)void (*work_func_t)(struct work_struct *work);
  3. 在合適的地方調(diào)調(diào)度工作(一般在中斷頂半部分);

2.4工作隊列使用示例

??下面以按鍵為例,實現(xiàn)中斷方式按鍵檢測,通過工作隊列處理底半部分代碼,雜項設(shè)備框架實現(xiàn)設(shè)備注冊。

poYBAGMsVOqAM5YjAAC9181SmfI038.png#pic_centerpYYBAGMsVOuAZpP4AAGtQ8N96qc548.png#pic_center
  • 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;/*工作結(jié)構(gòu)體*/
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;
/*工作服務(wù)函數(shù)*/
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;
}
/*中斷服務(wù)函數(shù)*/
static irqreturn_t key_irq_handler(int irq, void *dev)
{
	key_p=(struct _KEY *)dev;
	schedule_work(&key_work);/*調(diào)度工作*/
	return IRQ_HANDLED;/*中斷正常處理*/
}
static int key_open(struct inode *inode, struct file *file)
{
	printk("設(shè)備打開成功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("設(shè)備關(guān)閉成功n");
	return 0;
}
/*文件操作集合*/
static struct file_operations key_fops=
{
	.owner= THIS_MODULE, /*當(dāng)前模塊文件操作集合所有者*/
	.open=key_open,/*open函數(shù)接口*/
	.read=key_read,/*read函數(shù)接口*/
	.release=key_release,/*close函數(shù)接口*/
};
/*
字符設(shè)備注冊:主設(shè)備+次設(shè)備號
主設(shè)備  --用來區(qū)分類(雜項設(shè)備、輸入設(shè)備)
次設(shè)備號  --對應(yīng)哪個設(shè)備
雜項設(shè)備的主設(shè)備號固定為:10
*/
static struct miscdevice key_miscdev = {
	.minor	= MISC_DYNAMIC_MINOR,/*次設(shè)備號255由內(nèi)核分配*/
	.name	= "tiny4412_key",/*設(shè)備節(jié)點名字,會在/dev下生成*/
	.fops	= &key_fops,/**/
};

static int __init tiny4412_key_module_init(void)
{
	int i=0;
	int res;
    printk("hello,驅(qū)動注冊成功n");
	/*初始化工作*/
	INIT_WORK(&key_work,key_work_func);
	/*注冊中斷*/
	for(i=0;i(key_gpio_pin)>
poYBAGMsVOuAfzGRAAEtRbFUGc8924.png#pic_center

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11180

    瀏覽量

    208503
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3544

    瀏覽量

    93491
  • 中斷
    +關(guān)注

    關(guān)注

    5

    文章

    894

    瀏覽量

    41326
收藏 人收藏

    評論

    相關(guān)推薦

    Linux內(nèi)核中斷設(shè)計與實現(xiàn)

    裸機編程中使用中斷比較麻煩,需要配置寄存器、使能IRQ等等。而在Linux驅(qū)動編程中,內(nèi)核提供了完善的終端框架,只需要申請中斷,然后注冊
    發(fā)表于 07-29 08:57 ?754次閱讀

    芯靈思Sinlinx A33開發(fā)板 Linux中斷編程原理說明

    。step2 內(nèi)核關(guān)于 CPU 的中斷linux 中斷注冊函數(shù)中的 irq 中斷號并不是芯片物理上的編號,而是由芯片商在移植 Linux
    發(fā)表于 01-31 17:24

    芯靈思Sinlinx A33開發(fā)板 Linux中斷編程 2:程序框架

    根據(jù)上一個帖子的分析,想要實現(xiàn)按鍵中斷,首先得知道引腳對應(yīng)的中斷號,LRADC0對應(yīng)的中斷編號#define SUNXI_IRQ_LRADC (SUNXI_GIC_START + 30)/* 62
    發(fā)表于 02-01 16:28

    芯靈思Sinlinx A33開發(fā)板Linux中斷編程 3-應(yīng)用程序

    應(yīng)用程序代碼參考#include #include #include #include #include #include #include #include #include #include#defineDEV_NAME "/dev/mybtn"int main(int argc, char *args[]){ int fd = 0; int ret = 0; unsigned char recv_buf[1] = {"0"}; fd = open(DEV_NAME, O_RDONLY); //fd = open(DEV_NAME, O_RDONLY|O_NONBLOCK); if(fd < 0) {perror("open"); } while(1) {strcpy(recv_buf, "0000");//讀取按鍵數(shù)據(jù)ret = read(fd, recv_buf, 1);if((ret < 0) && (errno != EAGAIN)) {perror("read");exit(-1);}//輸出按鍵狀態(tài)printf("%s\r\n", recv_buf); } return 0;}
    發(fā)表于 02-11 16:45

    芯靈思SinlinxA33開發(fā)板Linux中斷編程4-最終代碼(1)

    = IRQ_TYPE_EDGE_BOTH; irq = gpio_to_irq( GPIOL(14) );//發(fā)生中斷號為irq的中斷會執(zhí)行key_isr函數(shù)。注冊成功會在/proc/irq號/KEY文件夾出現(xiàn)或 cat
    發(fā)表于 02-13 16:09

    Linux 2.6 中斷處理原理簡介

    Linux 2.6 中斷處理原理簡介 中斷描述符表(Interrupt Descriptor Table,IDT)是一個系統(tǒng)表,它與每一個中斷或異常向量相聯(lián)系,每一個向量在表中存放的
    發(fā)表于 02-05 10:52 ?789次閱讀

    LINUX網(wǎng)絡(luò)編程

    linux開發(fā)編程教程資料——LINUX網(wǎng)絡(luò)編程,感興趣的小伙伴們可以看一看。
    發(fā)表于 08-23 16:23 ?0次下載

    Linux網(wǎng)絡(luò)編程

    linux開發(fā)編程教程資料——Linux網(wǎng)絡(luò)編程,感興趣的小伙伴們可以看一看。
    發(fā)表于 08-23 16:23 ?0次下載

    嵌入式Linux設(shè)備驅(qū)動開發(fā)之中斷編程詳解

    11.5 中斷編程 前面所講述的驅(qū)動程序中都沒有涉及中斷處理,而實際上,有很多Linux的驅(qū)動都是通過中斷的方式來進行內(nèi)核和硬件的交互。
    發(fā)表于 10-18 17:33 ?0次下載

    你了解linux中斷機制?

    中斷是指在CPU正常運行期間,由于內(nèi)外部事件或由程序預(yù)先安排的事件引起的CPU暫時停止正在運行的程序,轉(zhuǎn)而為該內(nèi)部或外部事件或預(yù)先安排的事件服務(wù)的程序中去,服務(wù)完畢后再返回去繼續(xù)運行被暫時中斷的程序。Linux中通常分為外部
    發(fā)表于 05-05 15:30 ?685次閱讀

    Linux驅(qū)動技術(shù)之一內(nèi)核中斷

    在硬件上,中斷源可以通過中斷控制器向CPU提交中斷,進而引發(fā)中斷處理程序的執(zhí)行,不過這種硬件中斷體系每一種CPU都不一樣,而
    發(fā)表于 05-08 13:49 ?623次閱讀

    嵌入式Linux中斷驅(qū)動

    用過STM32的大概都知道,基本每個GPIO管腳都支持中斷模式,這樣在檢測外部插入一個硬件設(shè)備時,通過GPIO管腳電平中斷就非常方便。那么AM3354的片子是否支持GPIO管腳電平中斷呢?答案是肯定
    發(fā)表于 11-01 16:57 ?4次下載
    嵌入式<b class='flag-5'>Linux</b><b class='flag-5'>中斷</b>驅(qū)動

    Linux中斷情景分析

    在一個系統(tǒng)中,中斷時常發(fā)生,而且線程調(diào)度也是由一個硬件定時器時時刻刻發(fā)出中斷來支撐的??梢哉f中斷就是linux系統(tǒng)的靈魂。
    發(fā)表于 06-23 14:22 ?524次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>中斷</b>情景分析

    什么是LInux 操作系統(tǒng)中斷

    LInux 操作系統(tǒng)中斷 什么是系統(tǒng)中斷 這個沒啥可說的,大家都知道; CPU 在執(zhí)行任務(wù)途中接收到中斷請求,需要保存現(xiàn)場后去處理中斷請求!
    的頭像 發(fā)表于 11-10 11:29 ?539次閱讀
    什么是<b class='flag-5'>LInux</b> 操作系統(tǒng)<b class='flag-5'>中斷</b>

    Linux應(yīng)用編程的基本概念

    Linux應(yīng)用編程涉及到在Linux環(huán)境下開發(fā)和運行應(yīng)用程序的一系列概念。以下是一些涵蓋Linux應(yīng)用編程的基本概念。
    的頭像 發(fā)表于 10-24 17:19 ?106次閱讀