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

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

3天內不再提示

【i.MX6ULL】驅動(dòng)開(kāi)發(fā)8—中斷法檢測按鍵 Linux中斷和定時(shí)使用方法

碼農愛(ài)學(xué)習 ? 來(lái)源:碼農愛(ài)學(xué)習 ? 作者:碼農愛(ài)學(xué)習 ? 2022-05-25 09:09 ? 次閱讀

上篇,學(xué)習GPIO輸入功能的使用,本篇,來(lái)學(xué)習使用中斷的方式來(lái)檢測按鍵的按下。

1 Linux中斷介紹

1.1 中斷的上半部與下半部

中斷處理函數的執行,越快越好,但實(shí)際使用中,某些情況確實(shí)需要比較耗時(shí)是中斷過(guò)程,為此,Linux內核將中斷分為上半部和下半部?jì)蓚€(gè)處理部分

上半部:中斷處理函數,那些處理過(guò)程比較快,不會(huì )占用很長(cháng)時(shí)間的處理就可以放在上半部完成

下半部:如果中斷處理過(guò)程比較耗時(shí),那么就將這些比較耗時(shí)的代碼提出來(lái),交給下半部去執行,這樣中斷處理函數就會(huì )快進(jìn)快出

對于一個(gè)中斷,如何劃分出上下兩部分呢?

時(shí)間敏感,將其放在上半部

硬件相關(guān),將其放在上半部

要求不被其他中斷打斷,將其放在上半部

其他所有任務(wù),考慮放在下半部

1.2 下半部的3種實(shí)現方式

1.2.1 軟中斷

Linux內核使用softirq_action結構體表示軟中斷:

struct softirq_action 
{ 
	void (*action)(struct softirq_action *); 
}; 

一共有 10 個(gè)軟中斷

enum 
{ 
    HI_SOFTIRQ = 0,          /* 高優(yōu)先級軟中斷    */ 
    TIMER_SOFTIRQ,           /* 定時(shí)器軟中斷      */ 
    NET_TX_SOFTIRQ,          /* 網(wǎng)絡(luò )數據發(fā)送軟中斷 */ 
    NET_RX_SOFTIRQ,          /* 網(wǎng)絡(luò )數據接收軟中斷 */ 
    BLOCK_SOFTIRQ,           
    BLOCK_IOPOLL_SOFTIRQ,    
    TASKLET_SOFTIRQ,         /* tasklet 軟中斷   */ 
    SCHED_SOFTIRQ,           /* 調度軟中斷        */ 
    HRTIMER_SOFTIRQ,         /* 高精度定時(shí)器軟中斷 */ 
    RCU_SOFTIRQ,             /* RCU 軟中斷       */ 
        
    NR_SOFTIRQS 
};

要使用軟中斷,必須先使用open_softirq函數注冊對應的軟中斷處理函數:

/**
 * nr: 要開(kāi)啟的軟中斷
 * action: 軟中斷對應的處理函數
 * return: 無(wú)
 */
void open_softirq(int nr,  void (*action)(struct softirq_action *)) 

注冊好軟中斷以后需要通過(guò)raise_softirq函數觸發(fā):

/**
 * nr: 要觸發(fā)的軟中斷
 * return: 無(wú)
 */
void raise_softirq(unsigned int nr) 

1.2.2 tasklet

Linux內核使用tasklet_struct結構體來(lái)表示tasklet:

struct tasklet_struct 
{ 
	struct tasklet_struct *next;     /* 下一個(gè)tasklet      */ 
	unsigned long state;             /* tasklet狀態(tài)        */ 
	atomic_t count;                  /* 計數器, 記錄對tasklet的引用數 */ 
	void (*func)(unsigned long);     /* tasklet執行的函數  */ 
	unsigned long data;              /* 函數func的參數     */ 
}; 

要使用 tasklet,必須先定義一個(gè)tasklet,然后初始化:

/**
 * t: 要初始化的tasklet
 * func: tasklet的處理函數
 * data: 要傳遞給func函數的參數
 * return: 無(wú)
 */
void tasklet_init(struct tasklet_struct *t,
                void (*func)(unsigned long),  
                        unsigned long data); 

在上半部(中斷處理函數)中調用tasklet_schedule函數就能使tasklet在合適的時(shí)間運行:

/**
 * t: 要調度的tasklet
 * return: 無(wú)
 */
void tasklet_schedule(struct tasklet_struct *t)

1.2.3 工作隊列

工作隊列(work queue)是另外一種將中斷的部分工作推后的一種方式,它可以實(shí)現一些tasklet不能實(shí)現的工作,比如工作隊列機制可以睡眠。

Linux 內核使用work_struct結構體表示一個(gè)工作

struct work_struct { 
    atomic_long_t data;     
    struct list_head entry;  
    work_func_t func;        /* 工作隊列處理函數  */ 
};

這些工作組織成工作隊列,工作隊列使用workqueue_struct結構體表示。

在工作隊列機制中,將推后的工作交給一個(gè)稱(chēng)之為工作者線(xiàn)程(worker thread)的內核線(xiàn)程去完成。

1.3 中斷API函數

1.3.1 request_irq中斷請求函數

/**
 * irq: 要申請中斷的中斷號
 * handler: 中斷處理函數,當中斷發(fā)生以后就會(huì )執行此中斷處理函數
 * flags: 中斷標志
 * name: 中斷名字
 * dev: 設備結構體
 * return: 0-中斷申請成功, 其他負值-中斷申請失敗
 */
int request_irq(unsigned int irq,  
               irq_handler_t handler,  
               unsigned long flags, 
                  const char *name,  
                        void *dev) 

flags中斷標志,有下面幾種類(lèi)型

中斷標志 描述
IRQF_SHARED 多個(gè)設備共享一個(gè)中斷線(xiàn), 共享的所有中斷都必須指定此標志
IRQF_ONESHOT 單次中斷,中斷執行一次就結束
IRQF_TRIGGER_NONE 無(wú)觸發(fā)
IRQF_TRIGGER_RISING 上升沿觸發(fā)
IRQF_TRIGGER_FALLING 下降沿觸發(fā)
IRQF_TRIGGER_HIGH 高電平觸發(fā)
IRQF_TRIGGER_LOW 低電平觸發(fā)

1.3.2 free_irq中斷釋放函數

/**
 * irq: 要釋放中斷的中斷號
 * dev: 設備結構體
 * return: 無(wú)
 */
void free_irq(unsigned int irq,  
                      void *dev) 

1.3.3 irq_handler_t中斷處理函數

/**
 * int: 要處理的中斷號
 * void *: 通用指針, 需要與request_irq函數的dev參數保持一致
 * return: irqreturn_t枚舉類(lèi)型
 */
irqreturn_t (*irq_handler_t) (int, void *)

irqreturn_t枚舉類(lèi)型定義:

enum irqreturn { 
	IRQ_NONE          = (0 << 0), 
    IRQ_HANDLED       = (1 << 0), 
	IRQ_WAKE_THREAD   = (1 << 1), 
}; 

typedef enum irqreturn irqreturn_t; 

1.3.4 中斷使能/禁用函數

/**
 * int: 要使能的中斷號
 */
void enable_irq(unsigned int irq) 
    
/**
 * int: 要禁用的中斷號
 */
void disable_irq(unsigned int irq)

1.3.5 獲取中斷號

使用中斷時(shí),中斷信息先寫(xiě)到了設備樹(shù)里面,然后通過(guò)irq_of_parse_and_map函數從interupts屬性中提取到對應的中斷號

/**
 * dev: 設備節點(diǎn)
 * index: 索引號
 * return: 中斷號
 */
unsigned int irq_of_parse_and_map(struct device_node *dev, 
                                                 int index) 

2 軟件編寫(xiě)

仍使用上篇按鍵實(shí)驗中用到的兩個(gè)按鍵:

poYBAGKM90qAILxrAAFjbYSxyjc095.png

為了理解簡(jiǎn)單,本次程序暫不實(shí)現中斷的下半部邏輯,直接將整個(gè)中斷處理過(guò)程都放到中斷的上半部中處理。

2.1 修改設備樹(shù)文件

在上篇key實(shí)驗代碼的基礎上,修改imx6ull-myboard.dts,主要是修改key子節點(diǎn),添加中斷,修改后內容如下:

key { 
    #address-cells = <1>; 
    #size-cells = <1>; 
    compatible = "myboard-irq-key"; 
    pinctrl-names = "default"; 
    pinctrl-0 = <&pinctrl_key>; 
    key1-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;   /* SW2 */
    key2-gpio = <&gpio5 11 GPIO_ACTIVE_LOW>;   /* SW4 */
    interrupt-parent = <&gpio5>; 
    interrupts = <  1 IRQ_TYPE_EDGE_BOTH
                   11 IRQ_TYPE_EDGE_BOTH >;
    status = "okay"; 
}; 

2.2 按鍵中斷驅動(dòng)程序

2.2.1 硬件初始化與中斷配置

static int keyio_init(void)
{
    unsigned char i = 0;
    int ret = 0;
    
    /* 設備樹(shù)中獲取key節點(diǎn) */
    imx6uirq.nd = of_find_node_by_path("/key");
    if (imx6uirq.nd== NULL)
    {
        printk("key node not find!\r\n");
        return -EINVAL;
    } 

    /* 提取GPIO */
    imx6uirq.irqkeydesc[0].gpio = of_get_named_gpio(imx6uirq.nd ,"key1-gpio", 0);
    imx6uirq.irqkeydesc[1].gpio = of_get_named_gpio(imx6uirq.nd ,"key2-gpio", 0);
    if ((imx6uirq.irqkeydesc[0].gpio < 0)||(imx6uirq.irqkeydesc[1].gpio < 0))
    {
        printk("can't get key\r\n");
        return -EINVAL;
    }
    printk("key1_gpio=%d, key2_gpio=%d\r\n", imx6uirq.irqkeydesc[0].gpio, imx6uirq.irqkeydesc[1].gpio);
    
    /* 初始化key所使用的IO,并且設置成中斷模式 */
    for (i = 0; i < KEY_NUM; i++)
    {
        memset(imx6uirq.irqkeydesc[i].name, 0, sizeof(imx6uirq.irqkeydesc[i].name)); /* 緩沖區清零 */
        sprintf(imx6uirq.irqkeydesc[i].name, "key%d", i+1); /* 組合名字 */
        gpio_request(imx6uirq.irqkeydesc[i].gpio, imx6uirq.irqkeydesc[i].name);
        gpio_direction_input(imx6uirq.irqkeydesc[i].gpio);    
        imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(imx6uirq.nd, i); /* 取到對應的中斷號 */

        printk("key%d:gpio=%d, irqnum=%d\r\n",i+1,
                                              imx6uirq.irqkeydesc[i].gpio, 
                                              imx6uirq.irqkeydesc[i].irqnum);
    }
    /* 申請中斷 */
    imx6uirq.irqkeydesc[0].handler = key1_handler;
    imx6uirq.irqkeydesc[1].handler = key2_handler;
    imx6uirq.irqkeydesc[0].value = KEY1VALUE;
    imx6uirq.irqkeydesc[1].value = KEY2VALUE;
    
    for (i = 0; i < KEY_NUM; i++)
    {
        /* 中斷請求函數 */
        ret = request_irq(imx6uirq.irqkeydesc[i].irqnum,
                          imx6uirq.irqkeydesc[i].handler,
                          IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
                          imx6uirq.irqkeydesc[i].name,
                          &imx6uirq);
        if(ret < 0)
        {
            printk("irq %d request failed!\r\n", imx6uirq.irqkeydesc[i].irqnum);
            return -EFAULT;
        }
    }

    /* 創(chuàng  )建定時(shí)器 */
    init_timer(&imx6uirq.timer1);
    imx6uirq.timer1.function = timer1_function;
    init_timer(&imx6uirq.timer2);
    imx6uirq.timer2.function = timer2_function;
    return 0;
}

中斷檢測到按鍵按下后,為了消除按鍵抖動(dòng),這里使用定時(shí)器來(lái)進(jìn)行按鍵消抖,因為本次實(shí)驗用到兩個(gè)按鍵,所以就先也使用兩個(gè)定時(shí)器。

2.2.2 中斷服務(wù)函數

static irqreturn_t key1_handler(int irq, void *dev_id)
{
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)dev_id;

    dev->timer1.data = (volatile long)dev_id;
    mod_timer(&dev->timer1, jiffies + msecs_to_jiffies(10)); /* 10ms定時(shí) */
    
    return IRQ_RETVAL(IRQ_HANDLED);
}

中斷函數檢測到按鍵按下后,會(huì )開(kāi)啟一個(gè)10ms的定時(shí)器,用來(lái)按鍵消抖。

2.2.3 定時(shí)器服務(wù)函數

定時(shí)器的10ms到達之后,會(huì )觸發(fā)定時(shí)器服務(wù)函數,此時(shí)再次讀取按鍵的值,若仍為按下,則是按鍵真的按下了,若10ms后又檢測不到按鍵了,則說(shuō)明是按鍵抖動(dòng)導致的按鍵誤觸發(fā)。

void timer1_function(unsigned long arg)
{
    unsigned char value;
    struct irq_keydesc *keydesc;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;

    keydesc = &dev->irqkeydesc[0];

    value = gpio_get_value(keydesc->gpio); /* 讀取IO值 */
    if(value == 1) /* 按下按鍵 */
    {
        printk("get key1: high\r\n");
        atomic_set(&dev->keyvalue, keydesc->value);
    }
    else /* 按鍵松開(kāi) */
    {
        printk("key1 release\r\n");
        atomic_set(&dev->keyvalue, 0x80 | keydesc->value);
        atomic_set(&dev->releasekey, 1); /* 標記松開(kāi)按鍵,即完成一次完整的按鍵過(guò)程 */            
    }    
}

2.2.4 按鍵讀取函數

static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    unsigned char keyvalue = 0;
    unsigned char releasekey = 0;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;

    keyvalue = atomic_read(&dev->keyvalue);
    releasekey = atomic_read(&dev->releasekey);

    if (releasekey) /* 有按鍵按下 */
    {
        //printk("releasekey!\r\n");
        if (keyvalue & 0x80)
        {
            keyvalue &= ~0x80;
            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
        }
        else
        {
            goto data_error;
        }
        atomic_set(&dev->releasekey, 0); /* 按下標志清零 */
    }
    else
    {
        goto data_error;
    }
    return 0;
    
data_error:
    return -EINVAL;
}

2.3 按鍵中斷驅動(dòng)程序

按鍵中斷的應用程序,使用上篇的按鍵檢測的應用程序即可

3 實(shí)驗

編譯設備樹(shù)與驅動(dòng)文件(irqkey-BSp.ko),使用上篇的按鍵應用程序(key-App),按下按鍵,會(huì )打印get key,松開(kāi)按鍵,會(huì )打印key release。

pYYBAGKM91iAOQhOAACaYIadQzQ689.png

4 總結

本篇主要介紹了Linux中斷的使用方法,通過(guò)按鍵來(lái)進(jìn)行中斷實(shí)驗測試,并使用Linux定時(shí)器進(jìn)行按鍵去抖。

poYBAGKM916AeO0CAAD7m3tGRn8927.png

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

    關(guān)注

    5006

    文章

    18450

    瀏覽量

    292235
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11026

    瀏覽量

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

    關(guān)注

    5

    文章

    885

    瀏覽量

    41085
  • i.MX6
    +關(guān)注

    關(guān)注

    1

    文章

    36

    瀏覽量

    16234
收藏 人收藏

    評論

    相關(guān)推薦

    PLC中斷功能的作用及使用方法

    PLC控制系統中的一項關(guān)鍵技術(shù),對于提高系統的響應速度和實(shí)時(shí)性具有至關(guān)重要的作用。本文將對PLC中斷功能的作用及使用方法進(jìn)行詳細的闡述。
    的頭像 發(fā)表于 06-15 17:54 ?553次閱讀

    【GD32F303紅楓派開(kāi)發(fā)板使用手冊】第四講 GEXTI-按鍵中斷檢測實(shí)驗

    通過(guò)本實(shí)驗主要學(xué)習以下內容: ?EXTI中斷原理; ?按鍵中斷檢測原理;
    的頭像 發(fā)表于 05-31 10:13 ?119次閱讀
    【GD32F303紅楓派<b class='flag-5'>開(kāi)發(fā)</b>板使用手冊】第四講 GEXTI-<b class='flag-5'>按鍵</b><b class='flag-5'>中斷</b><b class='flag-5'>檢測</b>實(shí)驗

    【GD32F470紫藤派開(kāi)發(fā)板使用手冊】第三講 EXTI-按鍵中斷檢測實(shí)驗

    通過(guò)本實(shí)驗主要學(xué)習以下內容: EXTI中斷原理; 按鍵中斷檢測原理;
    的頭像 發(fā)表于 05-05 09:22 ?197次閱讀
    【GD32F470紫藤派<b class='flag-5'>開(kāi)發(fā)</b>板使用手冊】第三講 EXTI-<b class='flag-5'>按鍵</b><b class='flag-5'>中斷</b><b class='flag-5'>檢測</b>實(shí)驗

    單片機外部中斷定時(shí)中斷的區別和用法

    單片機外部中斷定時(shí)中斷在觸發(fā)來(lái)源、應用場(chǎng)景以及功能特點(diǎn)上存在明顯差異** **。
    的頭像 發(fā)表于 01-28 17:35 ?1778次閱讀

    獨立按鍵中斷系統的復習筆記

    獨立按鍵中斷系統的復習筆記
    發(fā)表于 12-05 09:08 ?0次下載

    Linux中斷處理淺析

    去繼續運行被暫時(shí)中斷的程序。Linux中通常分為外部中斷(又叫硬件中斷)和內部中斷(又叫異常)。 軟件對硬件進(jìn)行配置后,軟件期望等待硬件的某
    的頭像 發(fā)表于 11-09 16:46 ?371次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>中斷</b>處理淺析

    基于i.MX6ULL的掉電檢測設計與軟件測試

    基于i.MX6ULL的掉電檢測設計與軟件測試基于i.MX6ULL平臺設計實(shí)現掉電檢測功能,首先選擇一路IO,利用IO電平變化觸發(fā)中斷,在編寫(xiě)
    的頭像 發(fā)表于 11-09 10:40 ?542次閱讀
    基于<b class='flag-5'>i.MX6ULL</b>的掉電<b class='flag-5'>檢測</b>設計與軟件測試

    RK3568外部IO中斷示例

    ?外部IO中斷介紹本篇文章以萬(wàn)象奧科HD-RK3568-IOT評估板中GPIO30為例,介紹Linux內核中斷的注冊方法,使用中斷的方式
    的頭像 發(fā)表于 11-03 13:56 ?657次閱讀
    RK3568外部IO<b class='flag-5'>中斷</b>示例

    單片機定時(shí)中斷應用案例

    定時(shí)中斷,顧名思義就是在規定的時(shí)間內發(fā)送中斷請求,要實(shí)現定時(shí)中斷,單片機需要能夠正確地計算時(shí)間,單片機是如何進(jìn)行計時(shí)的呢?
    發(fā)表于 10-31 14:49 ?1009次閱讀
    單片機<b class='flag-5'>定時(shí)</b>器<b class='flag-5'>中斷</b>應用案例

    GPIO中斷例程實(shí)現按鍵檢測定時(shí)采樣,中斷無(wú)法返回

    我利用GPIO中斷例程實(shí)現按鍵檢測功能,同時(shí)加入了定時(shí)采樣后,發(fā)現GPIO中斷無(wú)法返回,不管是否清除了
    發(fā)表于 10-14 19:47

    定時(shí)中斷程序怎么寫(xiě)

    定時(shí)中斷程序怎么寫(xiě)? 在嵌入式系統中,定時(shí)器被廣泛應用于各種任務(wù)。它們在實(shí)時(shí)系統中可用于輪詢(xún),計時(shí)等任務(wù)。定時(shí)器可以被配置為周期性定時(shí)器,
    的頭像 發(fā)表于 09-01 10:17 ?1485次閱讀

    定時(shí)中斷是什么意思,定時(shí)中斷的工作原理

    定時(shí)中斷是由單片機中的定時(shí)器溢出而申請的中斷。51單片機中有兩個(gè)定時(shí)器T0和T1。STM32中共有11個(gè)
    的頭像 發(fā)表于 07-23 09:45 ?4766次閱讀

    STM32 SysTick中斷使用方法

    SysTick中斷屬于核內外設中斷器,中斷號為-1。
    的頭像 發(fā)表于 07-22 10:18 ?2356次閱讀
    STM32 SysTick<b class='flag-5'>中斷</b><b class='flag-5'>使用方法</b>

    Linux中斷情景分析

    在一個(gè)系統中,中斷時(shí)常發(fā)生,而且線(xiàn)程調度也是由一個(gè)硬件定時(shí)器時(shí)時(shí)刻刻發(fā)出中斷來(lái)支撐的??梢哉f(shuō)中斷就是linux系統的靈魂。
    發(fā)表于 06-23 14:22 ?427次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>中斷</b>情景分析

    中斷是什么,PLC中斷,單片機中斷,那中斷能干什么?

    中斷程序連接到定時(shí)中斷事件時(shí),啟用定時(shí)中斷并且開(kāi)始定時(shí)。連接期間,系統捕捉周期時(shí)間值,因此 SMB34 和 SMB35 的后續變化不會(huì )影響周期時(shí)間。要更改周期時(shí)間,必須修改周期時(shí)間值
    的頭像 發(fā)表于 06-20 14:11 ?1763次閱讀
    <b class='flag-5'>中斷</b>是什么,PLC<b class='flag-5'>中斷</b>,單片機<b class='flag-5'>中斷</b>,那<b class='flag-5'>中斷</b>能干什么?
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看