ARM的中斷(S3C2440)
阿新 • • 發佈:2018-12-12
中斷要發生需要三部分同時工作:
- 中斷源
- 中斷控制器
- CPU使能中斷
中斷控制器的作用: 彙集各類中斷訊號併發給CPU。
中斷處理過程: 1.中斷控制器彙集各類中斷訊號併發給CPU。 2.CPU儲存當前程式的執行環境(各個暫存器),呼叫中斷服務程式ISR來處理中斷 3.在ISR中通過讀取中斷控制器、外設相關暫存器來識別哪個中斷,並處理。 4.清除中斷:通過讀寫中斷控制器和外設相關暫存器來實現。 5.最後恢復中斷程式的執行環境(即2中儲存的各個暫存器)。繼續執行。
看一下中斷控制器的內部結構框圖,瞭解中斷處理細節。 有些請求源是帶sub暫存器的,有些則不帶。 (1) 對於帶sub暫存器的,中斷源被觸發之後,SUBSRCPND暫存器相應位被置1,如果沒有被SUBMASK遮蔽的話,它在SRCPND的相應位也被置1,如果沒有被MASK遮蔽的話或者FIQ的話,它將被進一步處理。
(2) 對於不帶sub的,它在SRCPND的相應位被置1,如果沒有被MASK遮蔽的話或者FIQ的話,它將被進一步處理。
(3) 對於一般中斷,可能同時有幾個中斷被觸發,未被INTMASK遮蔽的中斷經比較後,選出優先順序最高的中斷,此中斷再INTPND的相應位被置1,然後CPU進入中斷模式進行處理。中斷服務子程式通過讀INTPND暫存器或者INTOFFSET暫存器來確定中斷源。
清中斷
- 指定中斷源的中斷服務程式中,必須通過清除 SRCPND 暫存器的相應位來正確的獲得來自相同源的中斷請求。如果從 ISR 中返回並且未清除相應位,則中斷控制器的操作就好像其它中斷請求已經從同一個源進入了。換句話說,如果 SRCPND 暫存器的指定位被設定為 1,其通常被認作一個有效中斷請求正在等待服務。 清除相應位的時間依賴於使用者的需要。如果希望收到來自相同冤源的其它有效請求,則應該首先清除相應位,並且接著使能中斷。 可以通過寫入一個數據到此暫存器來清除 SRCPND 暫存器的指定位。其只清除那些資料中被設定為 1 的相應位置的 SRCPND 位。那些資料中被設定為 0 的相應位置的位保持不變
- 就如 SRCPND 暫存器,必須在中斷服務程式中清除了 SRCPND 暫存器後清除此暫存器。可以通過寫入資料到此暫存器中來清除 INTPND 暫存器的指定位。只會清除資料中設定為 1 的相應 INTPND 暫存器位的位置。資料中設定為 0 的相應位的位置則保持不變。
- 可以通過寫入資料到此暫存器來清除 SUBSRCPND 暫存器的指定位。只有資料中那些被設定為 1 的相應SUBSRCPND 暫存器的位的位置才能被清除。資料中那些被設定為 0 的相應位的位置則保持不變。
程式設計
對於特定的中斷,不僅要設定中斷控制器,還要進行其它設定。
外部中斷:
- 設定中斷源,讓它能發出中斷訊號
- 設定中斷控制器
- 設定CPU,CPSR的 I 位,是中斷總開關
- 處理時要分辨中斷源
- 處理完要清中斷
彙編中的異常處理程式
.align 4
irq_eint:
ldr sp, =0x33d00000
/* 保護現場 */
stmdb sp!, {r0-r12,lr}
/* 處理異常 */
bl key_eint_isr
/* 恢復現場 */
ldmia sp!, {r0-r12,pc}^
相關C程式
/* 初始化中斷控制器 */
void interrupt_init(void)
{
INTMSK &= ~(1<<0); /* Service available */
}
/* 初始化中斷源,設定KEY為中斷源 */
void key_eint_init(void)
{
GPFCON &= ~(3<<0);
GPFCON |= (2<<0);
EXTINT0 |= (7<<0); /* 設定雙邊沿觸發 */
}
/* 按鍵外部中斷處理函式 */
void key_eint_isr(void)
{
/* 中斷處理 */
int val = GPFDAT;
if (val & (1<<4))
{
/* 當前狀態是滅 */
GPFDAT &= ~(1<<4);
}
else
{
GPFDAT |= (1<<4);
}
/* 清中斷 */
SRCPND |= (1<<0);
INTPND |= (1<<0);
}
定時器中斷:
- 設定時鐘
- 設定初值
- 載入初值,啟動Timer
- 設定自動載入
- 中斷相關 對於定時器部分,還要參考S3C2440的定時器功能。不再細述。
具體程式碼如下
#include "timer.h"
extern flag;
void interrupt_init(void)
{
INTMSK &= ~(1<<10); /* Service available */
}
/* 定時器輸入時鐘頻率 = PCLK / {預分頻值+1} / {分頻值} */
/* 31250 = 50000000/(99+1)/16 */
void timer_init(void)
{
TCFG0 = 99; //定時器0和1的預分頻值為99
TCFG1 &= ~(0xf<<0);
TCFG1 |= (3<<0); //定時器1的分頻值為1/16
TCNTB0 = 31250; //定時週期設為0.5s
TCON |= (1<<1); //手動更新
TCON &= ~(1<<1);
TCON |= ((1<<0) | (1<<3)); //開啟自動過載,開啟定時器
}
void timer_isr(void)
{
if (flag)
{
GPFDAT |= (1<<4);
GPFDAT |= (1<<5);
GPFDAT |= (1<<6);
flag = 0;
}
else
{
GPFDAT &= ~(1<<4);
GPFDAT &= ~(1<<5);
GPFDAT &= ~(1<<6);
flag = 1;
}
SRCPND |= (1<<10);
INTPND |= (1<<10);
}