1. 程式人生 > >ARM的中斷(S3C2440)

ARM的中斷(S3C2440)

中斷要發生需要三部分同時工作:

  1. 中斷源
  2. 中斷控制器
  3. 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暫存器來確定中斷源。

清中斷

  1. 指定中斷源的中斷服務程式中,必須通過清除 SRCPND 暫存器的相應位來正確的獲得來自相同源的中斷請求。如果從 ISR 中返回並且未清除相應位,則中斷控制器的操作就好像其它中斷請求已經從同一個源進入了。換句話說,如果 SRCPND 暫存器的指定位被設定為 1,其通常被認作一個有效中斷請求正在等待服務。 清除相應位的時間依賴於使用者的需要。如果希望收到來自相同冤源的其它有效請求,則應該首先清除相應位,並且接著使能中斷。 可以通過寫入一個數據到此暫存器來清除 SRCPND 暫存器的指定位。其只清除那些資料中被設定為 1 的相應位置的 SRCPND 位。那些資料中被設定為 0 的相應位置的位保持不變
  2. 就如 SRCPND 暫存器,必須在中斷服務程式中清除了 SRCPND 暫存器後清除此暫存器。可以通過寫入資料到此暫存器中來清除 INTPND 暫存器的指定位。只會清除資料中設定為 1 的相應 INTPND 暫存器位的位置。資料中設定為 0 的相應位的位置則保持不變。
  3. 可以通過寫入資料到此暫存器來清除 SUBSRCPND 暫存器的指定位。只有資料中那些被設定為 1 的相應SUBSRCPND 暫存器的位的位置才能被清除。資料中那些被設定為 0 的相應位的位置則保持不變。

程式設計

對於特定的中斷,不僅要設定中斷控制器,還要進行其它設定。

外部中斷:

  1. 設定中斷源,讓它能發出中斷訊號
  2. 設定中斷控制器
  3. 設定CPU,CPSR的 I 位,是中斷總開關
  4. 處理時要分辨中斷源
  5. 處理完要清中斷

彙編中的異常處理程式

.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);
}

定時器中斷:

  1. 設定時鐘
  2. 設定初值
  3. 載入初值,啟動Timer
  4. 設定自動載入
  5. 中斷相關 對於定時器部分,還要參考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);	
}