嵌入式學(xué)習(xí)(六)——嵌入式Linux設(shè)備驅(qū)動開發(fā)(三)

姓名:李萌怡? 學(xué)號:19020100103? 學(xué)院:電子工程學(xué)院

轉(zhuǎn)自:https://blog.csdn.net/light_in_dark/article/details/73372520

【嵌牛導(dǎo)讀】:學(xué)習(xí)了嵌入式linux設(shè)備系統(tǒng)開發(fā)的內(nèi)核部分中關(guān)于時(shí)間和定時(shí)器的設(shè)置后,需要了解中斷源和中斷號的定義宅楞,并學(xué)習(xí)對此進(jìn)行設(shè)置的方法歼狼。

【嵌牛鼻子】:中斷源? 中斷號

【嵌牛提問】:中斷源和中斷號是什么蚪缀?應(yīng)該怎樣處理敛滋?

【嵌牛正文】

一、中斷源和中斷號

中斷是硬件通過硬件電路產(chǎn)生的年扩,因此慌申,如果一個(gè)外設(shè)沒有獨(dú)立的中斷線,也就不能產(chǎn)生中斷睡汹。

中斷控制器的工作是收集硬件產(chǎn)生的中斷肴甸,然后根據(jù)預(yù)先設(shè)定好的優(yōu)先級依次提交給cpu。

對arm處理器來說囚巴,中斷控制器的發(fā)展經(jīng)歷了3個(gè)階段:

PIC (Programmable Interrupt Controller)

VIC (Vector Interrupt Controller)

GIC (General Interrupt Controller)

中斷源: 是硬件概念原在;中斷號: 是軟件概念,從0開始依次向后排彤叉。在linux內(nèi)核中中斷號和中斷源有一定的對應(yīng)關(guān)系庶柿。每種不同處理器的中斷源都不一樣,從中斷源到中斷號的轉(zhuǎn)換方式也不一樣秽浇。中斷號都定義在“mach/irqs.h”中浮庐,可以用兩種不同的方法來查找中斷號:

(1)芯片內(nèi)部的外設(shè)

首先明確設(shè)備的名字,然后利用名字匹配柬焕,自行在irqs.h中找到對應(yīng)的中斷號审残;

比如看門狗設(shè)備對應(yīng)的中斷號為IRQ_WDT, rtc硬件對應(yīng)的為IRQ_RTC_ALARAM/IRQ_RTC_TIC。

(2)芯片外部連接的設(shè)備

由于設(shè)備的中斷引腳通常都連接到GPIO斑举,因此可以利用GPIO號來找到中斷號:中斷號 = gpio_to_irq(GPIO號)

在注冊中斷處理函數(shù)時(shí)搅轿,可以用如下方式包含該頭文件,并引用其中的宏:

#include <mach/irqs.h>

二懂昂、Linux的中斷處理

(1)核心結(jié)構(gòu)體

A介时、irq_desc

定義在“l(fā)inux/irqdesc.h”

對應(yīng)一個(gè)中斷號。linux內(nèi)核在啟動時(shí)分配了一個(gè)irq_desc的數(shù)組凌彬,數(shù)組中共有NR_IRQS個(gè)成員沸柔。每個(gè)irq_desc中記錄對應(yīng)中斷的各類信息,比如中斷的處理函數(shù)铲敛,中斷的發(fā)生次數(shù)等褐澎。

irq_desc由內(nèi)核負(fù)責(zé)準(zhǔn)備。

B伐蒋、irqaction

定義在“l(fā)inux/interrupt.h”

每個(gè)irqaction用于封裝一個(gè)中斷處理函數(shù)工三。結(jié)構(gòu)體由驅(qū)動人員負(fù)責(zé)分配。

irqaction中包含中斷號先鱼;中斷處理函數(shù)指針俭正;中斷的執(zhí)行標(biāo)志;中斷名等

C焙畔、irq_handler_t

定義在“l(fā)inux/interrupt.h”掸读,如下:

typedef irqreturn_t (*irq_handler_t)(int, void *);

中斷處理函數(shù)類型,中斷處理函數(shù)由驅(qū)動負(fù)責(zé)實(shí)現(xiàn)儿惫,記錄在irqaction中澡罚。

irqreturn_t只有兩個(gè)值,IRQ_NONE/IRQ_HANDLED肾请。如果中斷不是由本設(shè)備引起的留搔,則返回IRQ_NONE,否則返回IRQ_HANDLED铛铁。

函數(shù)參數(shù)irq為中斷號隔显,void *為傳遞給中斷處理函數(shù)的參數(shù),對應(yīng)irqaction->dev_id避归。

(2)中斷處理函數(shù)

驅(qū)動人員在設(shè)計(jì)中斷處理函數(shù)時(shí)荣月,要遵循的要求是:

A、可嵌套不可重入

B梳毙、不能睡眠

C哺窄、如果硬件有中斷的狀態(tài)寄存器,軟件要負(fù)責(zé)清除中斷的標(biāo)志位账锹。一般來說萌业,如果不清除標(biāo)志位,設(shè)備無法再次產(chǎn)生中斷奸柬。

kzalloc(size, GFP_KERNEL); //可能睡眠

kzalloc(size, GFP_ATOMIC); //不會睡眠

D生年、中斷處理函數(shù)的注冊和注銷

#include <linux/interrupt.h>

#include <mach/irqs.h> //片內(nèi)外設(shè)

#include <linux/gpio.h> //片外外設(shè)

#include <mach/gpio.h>

//確定中斷號

#define KEY_IRQ? ? gpio_to_irq(gpio號);

//中斷處理函數(shù)

static irqreturn_t key_service(int irq, void *dev_id)

{

? ? //根據(jù)硬件及軟件的相關(guān)要求完成工作

? ? ...

? ? return IRQ_HANDLED 或 IRQ_NONE;

}

//注冊中斷處理函數(shù),必須檢查返回值

//request_irq內(nèi)部會分配并初始化irqaction

//如果是內(nèi)部設(shè)備產(chǎn)生的中斷廓奕,一般不需要共享抱婉,也不用配置IO,則flags可以為0

u32 flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;

int ret;

ret = request_irq(KEY_IRQ, /* 中斷號 */

? ? ? ? ? ? ? ? ? key_service, /* 中斷處理函數(shù) */

? ? ? ? ? ? ? ? ? flags, /* 中斷的標(biāo)志 */

? ? ? ? ? ? ? ? ? "xxx", /* 中斷處理函數(shù)的名字 */

? ? ? ? ? ? ? ? ? dev_id);

//最后的參數(shù)dev_id為傳給中斷處理函數(shù)的參數(shù)桌粉,一般會設(shè)置為私有結(jié)構(gòu)體的指針蒸绩,不能為NULL。實(shí)際上铃肯,如果是非共享的中斷患亿,dev_id可以為NULL。

if (ret) {

? ? printk("Cannot register interrupt handler\n");

? ? return -1;

}

//注銷中斷處理函數(shù)

free_irq(irq, dev_id);

//參數(shù)為中斷號和dev_id押逼。dev_id一定要和request_irq中的最后一個(gè)參數(shù)一致步藕。

//可以人為關(guān)閉(mask)/打開某個(gè)中斷:

disable_irq(int irq);

enable_irq(int irq);

//上面的兩個(gè)函數(shù)支持嵌套,也就是說挑格,如果調(diào)用了3次disable_irq咙冗,需要enable_irq3次,才能真正使能中斷漂彤,要確保先調(diào)用disable_irq雾消,再調(diào)用enable_irq;

//如果要屏蔽整個(gè)cpu的中斷瞬逊,可以用:

local_irq_disable();

local_irq_enable();

(3)中斷下半部

A、下半部的含義

在進(jìn)入中斷處理函數(shù)前仪或,會默認(rèn)關(guān)閉本中斷。對于某些要求迅速響應(yīng)或數(shù)據(jù)吞吐量很大的中斷士骤,要考慮將中斷處理函數(shù)的工作分為兩個(gè)部分范删,分別稱為中斷的上半部和下半部。

下半部的實(shí)現(xiàn)有多種方法拷肌,包括softirq到旦,tasklet和工作隊(duì)列(work queue)。

對于驅(qū)動來說巨缘,只會使用tasklet和工作隊(duì)列(work queue)添忘。

可以打開或關(guān)閉本cpu的下半部:

local_bh_enable();

local_bh_disable();

B、tasklet

tasklet特點(diǎn)

a若锁、在上半部執(zhí)行完后馬上執(zhí)行搁骑,但此時(shí)中斷是全部打開的;

b、執(zhí)行tasklet時(shí)內(nèi)核仍處于中斷上下文又固,因此不能睡眠;

c仲器、tasklet的執(zhí)行函數(shù)不會重入;

d、如果在tasklet的執(zhí)行期間再次發(fā)生調(diào)度仰冠,第二次調(diào)度無效;

#include <linux/interrupt.h>

//聲明tasklet結(jié)構(gòu)體

struct tasklet_struct mytask;

//tasklet的執(zhí)行函數(shù)

void bo_service(unsigned long data)

{

? ? ...

}?

//上半部的執(zhí)行函數(shù)

irqreturn_t up_service(int irq, void *dev_id)

{

? ? //首先完成和硬件交互之類的重要工作

? ? //觸發(fā)tasklet下半部

? ? tasklet_schedule(&mytask);

? ? 或tasklet_hi_schedule(&mytask);

? ? ...

? ? return IRQ_HANDLED 或 IRQ_NONE;

}

//初始化tasklet

tasklet_init(&mytask, bo_service, (unsigned long)dev);

C乏冀、工作隊(duì)列(work queue)

工作隊(duì)列特點(diǎn)

a、推后到進(jìn)程上下文執(zhí)行洋只,此時(shí)中斷是全部打開的;

b辆沦、執(zhí)行work時(shí)處于進(jìn)程上下文,因此可以睡眠;

c识虚、work的執(zhí)行函數(shù)不會重入;

d肢扯、如果在work的執(zhí)行期間再次發(fā)生調(diào)度,第二次調(diào)度無效;

如果是redhat舷礼,則內(nèi)核創(chuàng)建一組內(nèi)核線程鹃彻,名字為events/x,由該內(nèi)核線程負(fù)責(zé)執(zhí)行下半部函數(shù)妻献;

x為cpu的編號蛛株,可以為0,1等育拨;

如果是Android谨履,則內(nèi)核創(chuàng)建的內(nèi)核線程名字為kworker/x:y

x是cpu編號,y應(yīng)該是該cpu上的第幾個(gè)內(nèi)核線程熬丧;

#include <linux/interrupt.h>

#include <linux/workqueue.h>

//聲明tasklet結(jié)構(gòu)體

struct work_struct mywork;

//工作隊(duì)列的執(zhí)行函數(shù)

void bo_service(struct work_struct *data)

{

? ? ...

}?

//上半部的執(zhí)行函數(shù)

irqreturn_t up_service(int irq, void *dev_id)

{

? ? //首先完成和硬件交互之類的重要工作

? ? //默認(rèn)情況下笋粟,上下半部在同一cpu上執(zhí)行

? ? //可以喚醒給定cpu上的線程執(zhí)行下半部

? ? //1代表cpu1,0代表cpu0

? ? schedule_work(&mywork);

? ? 或schedule_work_on(1, &mywork);

? ? ...

? ? return IRQ_HANDLED 或 IRQ_NONE;

}

//初始化工作隊(duì)列

INIT_WORK(&mywork, bo_service);

//注冊上半部

ret = request_irq(KEY_IRQ, up_service, flags, "wq_test", &mywork);

if (ret) {

? ? printk("cannot request irq %d\n", KEY_IRQ);

? ? return ret;

}

工作隊(duì)列的實(shí)現(xiàn)在“l(fā)inux/workqueue.h”。

————————————————

版權(quán)聲明:本文為CSDN博主「light_in_dark」的原創(chuàng)文章害捕,遵循CC 4.0 BY-SA版權(quán)協(xié)議绿淋,轉(zhuǎn)載請附上原文出處鏈接及本聲明。

原文鏈接:https://blog.csdn.net/light_in_dark/article/details/73372520

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末尝盼,一起剝皮案震驚了整個(gè)濱河市吞滞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盾沫,老刑警劉巖裁赠,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赴精,居然都是意外死亡融蹂,警方通過查閱死者的電腦和手機(jī)硝清,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進(jìn)店門疾掰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來共缕,“玉大人,你說我怎么就攤上這事谭确∠瞥保” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵琼富,是天一觀的道長仪吧。 經(jīng)常有香客問我,道長鞠眉,這世上最難降的妖魔是什么薯鼠? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮械蹋,結(jié)果婚禮上出皇,老公的妹妹穿的比我還像新娘。我一直安慰自己哗戈,他們只是感情好郊艘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唯咬,像睡著了一般纱注。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胆胰,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天狞贱,我揣著相機(jī)與錄音,去河邊找鬼蜀涨。 笑死瞎嬉,一個(gè)胖子當(dāng)著我的面吹牛蝎毡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播氧枣,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼沐兵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了便监?” 一聲冷哼從身側(cè)響起痒筒,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茬贵,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體移袍,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡解藻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了葡盗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片螟左。...
    茶點(diǎn)故事閱讀 39,769評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖觅够,靈堂內(nèi)的尸體忽然破棺而出胶背,到底是詐尸還是另有隱情,我是刑警寧澤喘先,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布钳吟,位于F島的核電站,受9級特大地震影響窘拯,放射性物質(zhì)發(fā)生泄漏红且。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一涤姊、第九天 我趴在偏房一處隱蔽的房頂上張望暇番。 院中可真熱鬧,春花似錦思喊、人聲如沸壁酬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舆乔。三九已至,卻和暖如春剂公,著一層夾襖步出監(jiān)牢的瞬間蜕煌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工诬留, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斜纪,地道東北人贫母。 一個(gè)月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像盒刚,于是被迫代替她去往敵國和親腺劣。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容