1. 程式人生 > >Note06_Key按鍵驅動_中斷方式

Note06_Key按鍵驅動_中斷方式

 

 

  • KEY 硬體原理圖

如下圖所示,按鍵共有4個,K1——K4,對應的中斷號為 XEINT26——29(並非與硬體實際的中斷號對應),其對應的GPIO 引腳為GPX3_2——5。

 

  • KEY 相關暫存器

1)GPX3CON:GPIO PIN 模式設定暫存器

GPX3_2,對應的中斷模式需設定為0xF,GPX3_2——5 ==》EXT_INT43[5——2]

......

2)EXT_INT43_CON
中斷觸發方式設定暫存器

EXT_INT43[5——2],中斷觸發方式,設定為0x4,上下邊沿都觸發;

......

......

從如下圖,可知EXT_INT43,對應的中斷ID 為64.

 

  • 中斷簡介

1)中斷源分類1:

中斷包含內部中斷和外部中斷:內部中斷:如若中斷由控制器或者SOC 上的完整的裝置產生的;比如 觸控式螢幕、串列埠等中斷方式;

外部中斷:即只連線到GPIO 引腳上,中斷訊號通過SOC引腳從SOC外部得到的中斷,沒有其他外部的裝置類控制其中斷的發生,只能通過設定SFR 來配置中斷模式、觸發方式等;4412外部中斷共計32個:

外部中斷

中斷ID

中斷號

中斷 0—15

48——63

EINT[0:15]

中斷16—31

64

EINT[16:31]

2)中斷源分類2:

共享外部中斷:公共的外接中斷,中斷源ID號: SPI[127:0]

私有中斷:私有外設中斷 中斷源ID號: PPI[15:0]

軟中斷:由軟體設定的中斷源 中斷源ID號: SGI[15:0]

3)中斷處理流程:

首先儲存斷點 ==》查詢中斷入口函式 ==》中斷處理 ==》返回斷點;

  1. 中斷源觸發中斷;
  2. 首先確認GIC是否接收到中斷;然後確認是否將中斷轉給CPU;
  3. 首先確認CPU是否接收到中斷;然後確認接受的中斷號的許可權及優先順序;

3)exynos4412中斷簡介:

中斷控制器 gic 作用是進行中斷時間的接收和轉發;其資源共160箇中斷源(軟中斷16個、私有16個、外部128個),而CPU只識別中斷號,所以中斷處理的第一步就是獲取硬體觸發的中斷源,並轉化為CPU可識別的中斷號,該過程由gic完成。

4)中斷體系結構

略;待補充後補全;

【遺留】exynos 4412晶片如何將中斷和中斷號對應

  • 中斷資源註冊函式

1)編寫驅動中斷模組是,利用核心的如下函式介面註冊中斷:

int request_irq(

    unsigned int irq,     //中斷號

    irq_handler_t handler,  //中斷處理函式

    unsigned long flags,     //中斷註冊的觸發方式

    const char *name,    //中斷的名字

    void *dev        //給中斷處理函式傳入的引數

);

2)中斷引數解釋:

irq: //中斷號

1> 對於內部中斷號

         arch/arm/mach-exynos/include/mach/irqs.h

    2> 對於外部中斷對應的中斷號的獲取

        a. IRQ_EINT(eint);  

        b. 在知道gpio編號的情況下,可以通過gpio的編號,得到對應註冊的中斷號

           gpio_to_irq(gpionum); //通過gpio編號返回中斷號

handler: //中斷處理函式

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

中斷處理函式的形參:(發生中斷的中斷號, 註冊中斷時為其傳入的引數)

中斷處理函式返回值:

    IRQ_NONE   //中斷處理失敗

    IRQ_HANDLED //中斷成功處理

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: //中斷處理函式傳入的引數

中斷髮生後,由核心呼叫處理函式時傳給處理函式的第二實參

  • 驅動程式程式碼

1)按鍵中斷初始化:

 5 // KEY 中斷初始化: 中斷號、按鍵名、按鍵計數

  6 struct millkey{

  7     int irqnum;

  8     char *name;

  9     int keycnt;

 10 }keys[] = {

 11     { IRQ_EINT(26), "KEY1", 0 },

 12     { IRQ_EINT(27), "KEY2", 0 },

 13     { IRQ_EINT(28), "KEY3", 0 },

 14     { IRQ_EINT(29), "KEY4", 0 },

 15 };

2)註冊中斷函式:

 71 /*

 72 **  driver init call func  

 73 */

 74 static int __init demo_init(void)

 75 {

 76     return register_keys();

 77 }

 

 30 /*

 31 **  register key irq devices  

 32 */

 33 static int register_keys(void)

 34 {

 35     int i;

 36     int ret;

 37 

 38     for (i = 0; i < ARRAY_SIZE(keys); ++i) {

 39         ret = request_irq(

 40             keys[i].irqnum,

 41             do_key_handler,

 42             IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,

 43             keys[i].name,

 44             &keys[i]    // irq handler's arg

 45         );

 46 

 47         if (ret < 0) {

 48             goto error0;

 49         }

 50     }

 51 

 52     return 0;

 53 

 54 error0:

 55     while (i--) {

 56         free_irq(keys[i].irqnum, &keys[i]);

 57     }

 58 

 59     return ret;

 60 }

3)中斷處理函式:

 17 static irqreturn_t do_key_handler(int irqnum, void *data)

 18 {

 19     struct millkey *pdev = data;

 20 

 21     printk();

 22     pdev->keycnt++;

 23 

 24     printk("%s is %s, keycnt = %d\n", pdev->name,

 25         pdev->keycnt%2?"press down":"release up", pdev->keycnt);

 26 

 27     return IRQ_HANDLED;

 28 }

 

4)中斷登出函式:

 62 static void unregister_keys(void)

 63 {

 64     int i;

 65 

 66     for (i = 0; i < ARRAY_SIZE(keys); ++i) {

 67         free_irq(keys[i].irqnum, &keys[i]);

 68     }

 69 }

 70 

 

 79 module_init(demo_init);

 80 

 81 static void __exit demo_exit(void)

 82 {

 83     unregister_keys();

 84 }

 

  • 測試程式程式碼

中斷是由硬體觸發,故無應用層的測試程式;

  • 測試結果

1插入驅動模組

[[email protected]_interrupt]# 

[[email protected]_interrupt]# insmod demo.ko 

[[email protected]_interrupt]# 

[[email protected]_interrupt]# 

[[email protected]_interrupt]# 

[[email protected]_interrupt]# find / -name KEY*

/proc/irq/445/KEY4

/proc/irq/444/KEY3

/proc/irq/443/KEY2

/proc/irq/442/KEY1

[[email protected]_interrupt]# 

[[email protected]_interrupt]# 

2)檢視當前已註冊的中斷,及其發生的情況

[[email protected]_03_interrupt]# cat /proc/interrupts

           CPU0       CPU1

28:      63591        472       GIC  MCT 

67:          0          0       GIC  dma-pl330.0

68:          0          0       GIC  dma-pl330.1

75:          0          0       GIC  abc4412-wdt

......

......

442:          0          0  exynos-eint  KEY1                                         

443:          0          0  exynos-eint  KEY2

444:          0          0  exynos-eint  KEY3

445:          0          0  exynos-eint  KEY4

3)按下按鍵,觸發中斷,可看到核心列印的日誌;

   中斷設定雙邊沿觸發,因按鍵有抖動,故實際按下1次,但彈道多次觸發列印

[[email protected]_interrupt]#

[[email protected]_interrupt]#

[[email protected]_interrupt]# [ 7945.490000] KEY1 is release up

[ 7945.625000] KEY1 is press down

[ 7945.625000] KEY1 is release up

[ 7946.225000] KEY2 is release up

[ 7946.350000] KEY2 is press down

[ 7946.940000] KEY3 is release up

[ 7947.110000] KEY3 is press down

[ 7947.665000] KEY4 is press down

[ 7947.665000] KEY4 is release up

[ 7947.665000] KEY4 is press down

[ 7947.665000] KEY4 is release up

[ 7947.790000] KEY4 is press down

[[email protected]_interrupt]#

 

  • 總結:

  • 遺留問題

 ret = request_irq(

 40             keys[i].irqnum,

 41             do_key_handler,

 42             IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,

 43             keys[i].name,

 44             &keys[i]    // irq handler's arg

 45         );

1)如上中斷註冊函式,中斷號 keys[i].irqnum 是如何和核心核心註冊的硬體中斷ID聯絡起來的?可參考如下文件,待分析;

https://blog.csdn.net/zqixiao_09/article/details/50926677

https://blog.csdn.net/zqixiao_09/article/details/50908125?locationNum=5&fps=1

https://blog.csdn.net/zqixiao_09/article/details/50916212

https://www.cnblogs.com/wang_yb/archive/2013/04/19/3030345.html《linux 核心設計與實現閱讀筆記

2)程式碼沒有消抖,故設定雙邊沿觸發時,會檢測到多次中斷,增加消抖處理機制;

定時器定時檢測消抖、delay(ms)死等延時消抖、中斷電平檢測消抖? 分析優缺點;

  1. 若應用程式需根據按鍵功能實現需求,則它如何知曉哪個按鍵被按下了呢?