外部中斷
什麼是外部中斷?資料手冊在哪裡?
(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
#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();
}