天天看點

S5PV210系列 (裸機十)之按鍵和CPU的中斷系統(二)

外部中斷

什麼是外部中斷?資料手冊在哪裡?

(1)SoC支援的中斷類型中有一類叫外部中斷。内部中斷就是指的中斷源來自于SoC内部(一般是内部外設),譬如序列槽、定時器等部件産生的中斷;外部中斷是SoC外部的裝置,通過外部中斷對應的GPIO引腳産生的中斷。

(2)按鍵在SoC中就使用外部中斷來實作。具體實作方法是:将按鍵電路接在外部中斷的GPIO上,然後将GPIO配置為外部中斷模式。此時人通過按按鍵改變按鍵電路的電壓高低,這個電壓高低會觸發GPIO對應的外部中斷,通過引腳傳進去給CPU處理。

(3)外部中斷相關的介紹和寄存器都在2.2.6章節(屬于GPIO部分)

電平觸發和邊沿觸發

(1)外部中斷的觸發模式主要有2種:電平觸發和邊沿觸發。

(2)電平觸發就是說GPIO上的電平隻要滿足條件,就會不停觸發中斷。電平觸發分為高電平觸發和低電平觸發。電平觸發的特點是,隻要電平滿足條件就會不停觸發中斷。

(3)邊沿觸發分為上升沿觸發、下降沿觸發和雙邊沿觸發三種。邊沿觸發不關心電平正常狀态,隻關心電平變化的瞬間(邊沿觸發不關心電平本身是高還是低,隻關心變化是從高到低還是從低到高的這個過程)。

分析按鍵的工作:如果我們關注的是按鍵按下和彈起這兩個事件本身,那麼應該用邊沿觸發來處理按鍵;如果我們關心的是按鍵按下/彈起的那一段時間,那麼應該用電平觸發。

關鍵寄存器:CON、PEND、MASK

(1)外部中斷的主要配置寄存器有3個:EXT_CON、EXT_PEND、EXT_MASK

(2)EXT_CON配置外部中斷的觸發方式。觸發方式就是說外部電平怎麼變化就能觸發中斷,也就是說這個外部中斷産生的條件是什麼

(3)EXT_PEND寄存器是中斷挂起寄存器。這個寄存器中每一位對應一個外部中斷,平時沒有中斷時值為0。當發生了中斷後,硬體會自動将這個寄存器中該中斷對應的位置1,我們去處理完這個中斷後應該手工将該位置0。這個PEND寄存器的位就相當于是一個标志,如果發生了中斷但是我們暫時忙來不及去處理時,這個位一直是1(這就是挂起),直到我有空了去處理了這個中斷才會手工清除(寫代碼清除)這個挂起位表示這個中斷被我處理了。

(4)EXT_MASK 寄存器就是各個外部中斷的使能 / 禁止開關。

分析X210開發闆的按鍵對應的EINT編号:

EINT2、EINT3、EINT16、EINT17、EINT18、EINT19

S5PV210系列 (裸機十)之按鍵和CPU的中斷系統(二)
S5PV210系列 (裸機十)之按鍵和CPU的中斷系統(二)
#include "stdio.h"
#include "main.h"

// 定義操作寄存器的宏
#define GPH0CON     0xE0200C00
#define GPH0DAT     0xE0200C04
#define GPH2CON     0xE0200C40
#define GPH2DAT     0xE0200C44

#define rGPH0CON    (*(volatile unsigned int *)GPH0CON)
#define rGPH0DAT    (*(volatile unsigned int *)GPH0DAT)
#define rGPH2CON    (*(volatile unsigned int *)GPH2CON)
#define rGPH2DAT    (*(volatile unsigned int *)GPH2DAT)

#define EXT_INT_0_CON   0xE0200E00
#define EXT_INT_2_CON   0xE0200E08
#define EXT_INT_0_PEND  0xE0200F40
#define EXT_INT_2_PEND  0xE0200F48
#define EXT_INT_0_MASK  0xE0200F00
#define EXT_INT_2_MASK  0xE0200F08

#define rEXT_INT_0_CON  (*(volatile unsigned int *)EXT_INT_0_CON)
#define rEXT_INT_2_CON  (*(volatile unsigned int *)EXT_INT_2_CON)
#define rEXT_INT_0_PEND (*(volatile unsigned int *)EXT_INT_0_PEND)
#define rEXT_INT_2_PEND (*(volatile unsigned int *)EXT_INT_2_PEND)
#define rEXT_INT_0_MASK (*(volatile unsigned int *)EXT_INT_0_MASK)
#define rEXT_INT_2_MASK (*(volatile unsigned int *)EXT_INT_2_MASK)



//------------------------輪詢方式處理按鍵---------------------------
// 初始化按鍵
void key_init(void)
{
    // 設定GPHxCON寄存器,設定為輸入模式
    // GPH0CON的bit8~15全部設定為0,即可
    rGPH0CON &= ~(<<);
    // GPH2CON的bit0~15全部設定為0,即可
    rGPH2CON &= ~(<<);
}

static void delay20ms(void)
{
    // 這個函數作用是延時20ms
    // 因為我們這裡是裸機程式,且重點不是真的要消抖,而是教學
    // 是以我這裡這個程式隻是象征性的,并沒有實體
    // 如果是研發,那就要花時間真的調試出延時20ms的程式
    int i, j;

    for (i=; i<; i++)
    {
        for (j=; j<; j++)
        {
            i * j;
        }
    }
}

void key_polling(void)
{
    // 依次,挨個去讀出每個GPIO的值,判斷其值為1還是0.如果為1則按鍵按下,為0則彈起

    // 輪詢的意思就是反複循環判斷有無按鍵,隔很短時間
    while ()
    {
        // 對應開發闆上标着LEFT的那個按鍵
        if (rGPH0DAT & (<<))
        {
            // 為1,說明沒有按鍵
            led_off();
        }
        else
        {
            // 添加消抖
            // 第一步,延時
            delay20ms();
            // 第二步,再次檢驗按鍵狀态
            if (!(rGPH0DAT & (<<)))
            {
                // 為0,說明有按鍵
                led1();
                printf("key left.\n");
            }
        }

        // 對應開發闆上标着DOWN的那個按鍵
        if (rGPH0DAT & (<<))
        {
            // 為1,說明沒有按鍵
            led_off();
        }
        else
        {
            // 為0,說明有按鍵
            led2();
            printf("key down.\n");
        }

        // 對應開發闆上标着UP的那個按鍵
        if (rGPH2DAT & (<<))
        {
            // 為1,說明沒有按鍵
            led_off();
        }
        else
        {
            // 為0,說明有按鍵
            led3();
        }
    }
}


//-----------------------中斷方式處理按鍵-----------------------------------
// 以中斷方式來處理按鍵的初始化
void key_init_interrupt(void)
{
    // 1. 外部中斷對應的GPIO模式設定
    rGPH0CON |= <<;        // GPH0_2 GPH0_3設定為外部中斷模式
    rGPH2CON |= <<;      // GPH2_0123共4個引腳設定為外部中斷模式

    // 2. 中斷觸發模式設定
    rEXT_INT_0_CON &= ~(<<);  // bit8~bit15全部清零
    rEXT_INT_0_CON |= ((<<)|(<<));     // EXT_INT2和EXT_INT3設定為下降沿觸發
    rEXT_INT_2_CON &= ~(<<);
    rEXT_INT_2_CON |= ((<<)|(<<)|(<<)|(<<));   

    // 3. 中斷允許
    rEXT_INT_0_MASK &= ~(<<);            // 外部中斷允許
    rEXT_INT_2_MASK &= ~(<<);

    // 4. 清挂起,清除是寫1,不是寫0
    rEXT_INT_0_PEND |= (<<);
    rEXT_INT_2_PEND |= (<<);
}

// EINT2通道對應的按鍵,就是GPH0_2引腳對應的按鍵,就是開發闆上标了LEFT的那個按鍵
void isr_eint2(void)
{
    // 真正的isr應該做2件事情。
    // 第一,中斷處理代碼,就是真正幹活的代碼
    printf("isr_eint2_LEFT.\n");
    // 第二,清除中斷挂起
    rEXT_INT_0_PEND |= (<<);
    intc_clearvectaddr();
}

void isr_eint3(void)
{
    // 真正的isr應該做2件事情。
    // 第一,中斷處理代碼,就是真正幹活的代碼
    printf("isr_eint3_DOWN.\n");
    // 第二,清除中斷挂起
    rEXT_INT_0_PEND |= (<<);
    intc_clearvectaddr();
}

void isr_eint16171819(void)
{
    // 真正的isr應該做2件事情。
    // 第一,中斷處理代碼,就是真正幹活的代碼
    // 因為EINT16~31是共享中斷,是以要在這裡再次去區分具體是哪個子中斷
    if (rEXT_INT_2_PEND & (<<))
    {
        printf("eint16\n");
    }
    if (rEXT_INT_2_PEND & (<<))
    {
        printf("eint17\n");
    }
    if (rEXT_INT_2_PEND & (<<))
    {
        printf("eint18\n");
    }
    if (rEXT_INT_2_PEND & (<<))
    {
        printf("eint19\n");
    }

    // 第二,清除中斷挂起
    rEXT_INT_2_PEND |= (<<);
    intc_clearvectaddr();
}















           

繼續閱讀