注:以下内容學習于韋東山老師arm裸機第一期視訊教程
一.中斷的處理
1.1 中斷初始化
1.1.1 設定中斷源,讓他能夠發出中斷信号
a.以按鍵中斷為例,按鍵原理圖如下,4個按鍵分别接到EINT0,EINT2,EINT11,ENIT19對應GPF0,GPF2,GPG3,GPG11
b.配置GPFCON,GPGCON使得GPF0,GPF2,GPG3,GPG11被配置為外部中斷引腳
c.配置EXTINTX寄存器設定中斷觸發方式為雙邊沿觸發
其中EXTINT0寄存器對應EINT0-EINT7
EXTINT1寄存器對應EINT8-EINT15
EXTINT2寄存器對應EINT16-EINT23
d.配置EINTMASK寄存器允許EINT0,EINT2,EINT11,ENIT19向中斷控制器發生中斷
其中EINT0-EINT3的中斷信号不需要配置,可以直接到達中斷控制器
相關碼如下:
/* 初始化按鍵, 設為中斷源 */
void key_eint_init(void)
{
/* 配置GPIO為中斷引腳 */
GPFCON &= ~((3<<0) | (3<<4));
GPFCON |= ((2<<0) | (2<<4)); /* S2,S3被配置為中斷引腳 */
GPGCON &= ~((3<<6) | (3<<22));
GPGCON |= ((2<<6) | (2<<22)); /* S4,S5被配置為中斷引腳 */
/* 設定中斷觸發方式: 雙邊沿觸發 */
EXTINT0 |= (7<<0) | (7<<8); /* S2,S3 */
EXTINT1 |= (7<<12); /* S4 */
EXTINT2 |= (7<<12); /* S5 */
/* 設定EINTMASK使能eint11,19 */
EINTMASK &= ~((1<<11) | (1<<19));
}
1.1.2 設定中斷控制器,讓他可以向CPU發出中斷
根據下圖進行相關的配置
a.多個中斷産生經過優先級隻會有一個通知CPU,可以讀取INTPND寄存器來判斷是哪一個中斷産生了
bit0-eint0 1-中斷産生,需要進行清除
bit2-eint2
bit5-eint8-23
b.Priority表示中斷優先級,暫時不設定
c.MODE用來設定某個中斷為快中斷或者普通的中斷,我們使用預設值,發出IRQ信号
d.需要設定MASK寄存器,某一位被設定為1,會屏蔽掉這個中斷
bit0-eint0
bit2-eint2
bit5-eint8-23
e.對于中斷源,有的中斷源包括子中斷源,例如序列槽中斷包括接受中斷,發送中斷和出錯的中斷
假設序列槽0産生了TX0子中斷SUBSRCPND寄存器的某一位就會被置1,然後進行過濾,通過SUBMASK進行處理,
SUBMASK寄存器每一位對應一子中斷源,如果我們将這裡面的某一位置1,就會過濾掉這一位對應的中斷
f.如果是沒有子中斷源的中斷,會直接進入SRCPND,用來顯示某一個中斷是否發生了,執行完成之後需要清除SRCPND寄存器
外部中斷可以沒有子中斷,會直接到達SRCPND,對于外部中斷其對應關系如下
bit0-eint0 1-中斷産生,需要清除
bit2-eint2
bit5-eint8-23
某一位等于1時,表示中斷發生了,對于eint8-23需要再次讀取EINTPEND寄存器判斷哪個中斷發生了
g.INTOFFSET寄存器,顯示哪一個中斷正在等待處理,即用來顯示INTPND中哪一位被設定為1,可以讀取INTOFFSET寄存器或者INTPND來判斷中斷源,對應關系如下
這個寄存器不需要清除,在清除SRCPND和INTPND時會自動清除
相關代碼如下:
/* SRCPND 用來顯示哪個中斷産生了, 需要清除對應位
* bit0-eint0
* bit2-eint2
* bit5-eint8_23
*/
/* INTMSK 用來屏蔽中斷, 1-masked
* bit0-eint0
* bit2-eint2
* bit5-eint8_23
*/
/* INTPND 用來顯示目前優先級最高的、正在發生的中斷, 需要清除對應位
* bit0-eint0
* bit2-eint2
* bit5-eint8_23
*/
/* INTOFFSET : 用來顯示INTPND中哪一位被設定為1
*/
/* 初始化中斷控制器 */
void interrupt_init(void)
{
INTMSK &= ~((1<<0) | (1<<2) | (1<<5));
}
1.1.3 設定CPU,CPSR中的I位是中斷的總開關,需要清0才能打開中斷
相關代碼如下:
mrs r0, cpsr /* 讀出cpsr */
bic r0, r0, #0xf /* 修改M4-M0為0b10000, 進入usr模式 */
bic r0, r0, #(1<<7) /* 清除I位, 使能中斷 */
msr cpsr, r0
1.2 進行中斷
1.2.1 分辨中斷源
讀取INTOFFSET寄存器,表示INTPND中的值哪一位被設定為1
0-eint0
2-eint2
5-eint8-23
1.2.2 調用對應的處理函數,并且清除中斷源
清中斷時想EINTPND寫入1清除對應的中斷(讀的時候1表示中斷發生,寫入1清除中斷,很奇怪,2440手冊上是這麼描述的,見下圖)
相關代碼如下:
void key_eint_irq(int irq)
{
unsigned int val = EINTPEND;
unsigned int val1 = GPFDAT;
unsigned int val2 = GPGDAT;
if (irq == 0) /* eint0 : s2 控制 D12 */
{
...
}
else if (irq == 2) /* eint2 : s3 控制 D11 */
{
...
}
else if (irq == 5) /* eint8_23, eint11--s4 控制 D10, eint19---s5 控制所有LED */
{
if (val & (1<<11)) /* eint11 */
{
...
}
else if (val & (1<<19)) /* eint19 */
{
...
}
}
EINTPEND = val;
}
1.3 處理完畢後要清中斷
清除中斷時從前向後清,即先清SRCPND,再清INTPND。否則前面的還會影響後面
相關代碼如下:
void handle_irq_c(void)
{
/* 分辨中斷源 */
int bit = INTOFFSET;
/* 調用對應的處理函數 */
if (bit == 0 || bit == 2 || bit == 5) /* eint0,2,eint8_23 */
{
key_eint_irq(bit); /* 進行中斷, 清中斷源EINTPEND */
}
/* 清中斷 : 從源頭開始清 */
SRCPND = (1<<bit);
INTPND = (1<<bit);
}
interrupt.c檔案代碼如下
#include "s3c2440_soc.h"
/* SRCPND 用來顯示哪個中斷産生了, 需要清除對應位
* bit0-eint0
* bit2-eint2
* bit5-eint8_23
*/
/* INTMSK 用來屏蔽中斷, 1-masked
* bit0-eint0
* bit2-eint2
* bit5-eint8_23
*/
/* INTPND 用來顯示目前優先級最高的、正在發生的中斷, 需要清除對應位
* bit0-eint0
* bit2-eint2
* bit5-eint8_23
*/
/* INTOFFSET : 用來顯示INTPND中哪一位被設定為1
*/
/* 初始化中斷控制器 */
void interrupt_init(void)
{
INTMSK &= ~((1<<0) | (1<<2) | (1<<5));
}
/* 初始化按鍵, 設為中斷源 */
void key_eint_init(void)
{
/* 配置GPIO為中斷引腳 */
GPFCON &= ~((3<<0) | (3<<4));
GPFCON |= ((2<<0) | (2<<4)); /* S2,S3被配置為中斷引腳 */
GPGCON &= ~((3<<6) | (3<<22));
GPGCON |= ((2<<6) | (2<<22)); /* S4,S5被配置為中斷引腳 */
/* 設定中斷觸發方式: 雙邊沿觸發 */
EXTINT0 |= (7<<0) | (7<<8); /* S2,S3 */
EXTINT1 |= (7<<12); /* S4 */
EXTINT2 |= (7<<12); /* S5 */
/* 設定EINTMASK使能eint11,19 */
EINTMASK &= ~((1<<11) | (1<<19));
}
/* 讀EINTPEND分辨率哪個EINT産生(eint4~23)
* 清除中斷時, 寫EINTPEND的相應位
*/
void key_eint_irq(int irq)
{
unsigned int val = EINTPEND;
unsigned int val1 = GPFDAT;
unsigned int val2 = GPGDAT;
if (irq == 0) /* eint0 : s2 控制 D12 */
{
if (val1 & (1<<0)) /* s2 --> gpf6 */
{
/* 松開 */
GPFDAT |= (1<<6);
}
else
{
/* 按下 */
GPFDAT &= ~(1<<6);
}
}
else if (irq == 2) /* eint2 : s3 控制 D11 */
{
if (val1 & (1<<2)) /* s3 --> gpf5 */
{
/* 松開 */
GPFDAT |= (1<<5);
}
else
{
/* 按下 */
GPFDAT &= ~(1<<5);
}
}
else if (irq == 5) /* eint8_23, eint11--s4 控制 D10, eint19---s5 控制所有LED */
{
if (val & (1<<11)) /* eint11 */
{
if (val2 & (1<<3)) /* s4 --> gpf4 */
{
/* 松開 */
GPFDAT |= (1<<4);
}
else
{
/* 按下 */
GPFDAT &= ~(1<<4);
}
}
else if (val & (1<<19)) /* eint19 */
{
if (val2 & (1<<11))
{
/* 松開 */
/* 熄滅所有LED */
GPFDAT |= ((1<<4) | (1<<5) | (1<<6));
}
else
{
/* 按下: 點亮所有LED */
GPFDAT &= ~((1<<4) | (1<<5) | (1<<6));
}
}
}
EINTPEND = val;
}
void handle_irq_c(void)
{
/* 分辨中斷源 */
int bit = INTOFFSET;
/* 調用對應的處理函數 */
if (bit == 0 || bit == 2 || bit == 5) /* eint0,2,eint8_23 */
{
key_eint_irq(bit); /* 進行中斷, 清中斷源EINTPEND */
}
/* 清中斷 : 從源頭開始清 */
SRCPND = (1<<bit);
INTPND = (1<<bit);
}
二.定時器(參考2440第10章)
2.1 工作原理如下圖
2.1.1 每次來一個時鐘信号,TCNTn減1
2.1.2 damh TCNTn == TCMPn時,不會産生中斷,可以讓對應的PWM引腳翻轉
2.1.3 TCNTn繼續減1,當TCNTn等于0時可以産生中斷,PWM引腳再次翻轉
TCNTn和TCMPn的初值來自于TCNTBn和TCMPn寄存器
2.1.4 TCNTn等于0時,可以自動加載初值
2.2 定時器的使用
2.2.1 設定時鐘
2.2.2 設定初值
2.2.3 加載初值,啟動Timer
2.2.4 設定為自動加載
2.2.5 設定中斷
2.3 代碼示例
/* timer.c */
#include "s3c2440_soc.h"
void TimerFunc(int irq)
{
static int cnt = 3;
#if 0
if (GPFDAT & (1 << 4))
GPFDAT &= ~(1 << 4);
else
GPFDAT |= (1 << 4);
#endif
cnt++;
GPFDAT &= ~(1 << cnt);
if (cnt == 7)
{
cnt = 3;
GPFDAT |= (0x7 << 4);
}
}
void TimerInit(void)
{
/* 1.Set clock */
/*
* Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value}
* = 50000000 / (99 + 1) / 16
* = 31250
*/
TCFG0 = 99; /* Prescaler 0 = 99 */
TCFG1 |= 0x3; /* divider value = 16 */
/* 2.Set TCNTn init val */
TCNTB0 = 15625; /* 0.5s */
/* 3.Load init val*/
TCON |= (1 << 1);
/* 4.Set Auto update, and start timer */
TCON &= ~(1 << 1);
TCON |= (1 << 0) | (1 << 3);
/* timer0的中斷号是10 */
register_irq(10, TimerFunc);
}
/* interrupt.c */
#include "s3c2440_soc.h"
#include "interrupt.h"
void handle_irq(void)
{
/* 1.Resolved interrupt source */
int bit = INTOFFSET;
/* 2.handle irq */
irq_arr[bit](bit);
/* 3.clear irq */
SRCPND = (1 << bit);
INTPND = (1 << bit);
}
/* 以中斷号為下标放到irq_arr指針數組中去 */
void register_irq(int irq, irq_func func)
{
irq_arr[irq] = func;
INTMSK &= ~(1 << irq);
}