基於三星 ARM Cortex-A9 Exynos4412處理器的底層實驗(外部中斷實驗)

【1】正常啓動流程

1.設備一上電,中斷向量表作爲第一段執行程序
2.初始化異常向量表基地址協處理器(默認是0,修改後異常跳轉時,PC值=基地址+向量表位置偏移)
3.禁用MMU內存管理單元
4.分配棧空間,初始化各個模式下的棧空間(user棧初始化放在最後)
5.跳轉到main執行程序

【2】IRQ中斷響應原理

假設產生中斷時,CPU當中正在執行一段程序(正常情況),突然之間產生外部中斷,整個CPU的相應以及處理流程:
1.CPU相應流程(CPU自己完成)
1)複製備份CPSR到SPSR_IRQ
2)配置CPSR寄存器
設置工作狀態爲arm態
修改工作模式爲IRQ模式
禁用相應中斷(IRQ)
3)保存返回地址到LR_IRQ
4)設置PC到異常向量表中的IRQ處理項(0x18)
2.根據異常向量表中的跳轉語句跳轉到IRQ_Handle處理程序(程序員寫的)
1)調整LR的值,爲異常結束返回做準備
2)入棧保護寄存器數據
3)跳轉到IRQ_Handle處理函數
4)出棧恢復數據
5)返回退出異常程序,並恢復CPSR

【3】中斷初始化配置

1.誰產生的中斷

2.什麼時候產生中斷

3.中斷怎麼到達SOC

4.產生的中斷交給哪個CPU去處理

5.同時產生多箇中斷時,怎麼處理

6.當一個IRQ中斷正在處理的時候,又來了同類型的一箇中斷怎麼辦

【4】GIC中斷控制器作用

中斷控制器GIC的作用
1.當多箇中斷同時產生的時候,中斷管理器將對這些中斷進行排隊,將優先級最高的轉發給CPU處理,其他再進行排隊等待
2.當一箇中斷正在處理的時候,外設又產生另一箇中斷,中斷管理器會對這個中斷進行記憶,等上一個響應完成後再去響應下一個
3**.爲每一箇中斷分配一個CPU去處理**
4.爲每個中斷選擇一箇中斷類型(FIQ或IRQ)
5.CPU不能區分是哪個外設產生的中斷,但CPU可以通過查詢中斷管理器來獲取當前中斷的中斷號
6.在中斷管理器中我們可以任意打開或關閉一箇中斷
7.爲每一箇中斷分配一個優先級

【5】按鍵K2控制蜂鳴器實驗(蜂鳴器實驗配置不做描述,前面有寫)

1.閱讀硬件原理圖,得到K2與SOC之間的對應連接關係:GPX1_1
在這裏插入圖片描述
在這裏插入圖片描述

2.查找用戶手冊,將GPX1_1配置爲中斷功能。
在這裏插入圖片描述

//選擇GPIO引腳功能爲EINT
	GPX1.CON |= (0xf<<4);

3.配置EXT_INT41CON寄存器,選擇觸發中斷條件(電平、邊沿)
在這裏插入圖片描述
在這裏插入圖片描述

//設置中斷信號檢測:↓下降沿
	EXT_INT41_CON &= ~(0x7<<4);
	EXT_INT41_CON |= (2<<4);

4.配置EXT_INT41_MASK寄存器,開啓按鍵中斷,使之能夠到達GIC
在這裏插入圖片描述

//使能GPIO對中斷的檢測
	EXT_INT41_MASK &= ~(1<<1);

5.配置GIC中斷管理器,對K2按鍵中斷進行管理配置(第9章節)
從9.1.1獲取到GIC支持的中斷類型和功能,從Figure 9-1 瞭解到中斷大致物理結構;從Table 9-2瞭解到GIC對每個中斷源的編號管理(ID);中斷原理部分閱讀9.4.1章節,具體配置看9.4.2章節.
在這裏插入圖片描述
在這裏插入圖片描述

//中斷白名單(中斷使能):SPI中斷號25,全局中斷號57
	ICDISER.ICDISER1 |= (1<<25);

1)使能中斷源
設置ICDISER寄存器使能對應ID中斷源,使之能夠到達GIC(9.4.1.2章節)
這裏要注意,ICDISER和ICDICER的功能是一樣,在ICDISER中寫是使能,在ICDICER寫1是禁用

2)中斷掛起狀態設置和清除(在本次實驗中不需要設置)
設置ICDISPR寄存器掛起中斷,設置ICDICPR寄存器清除掛起(每次結束中斷時需要清除)
3)選擇處理中斷CPU
配置ICDIPTR寄存器,選擇CPU0處理k2按鍵中斷。
在這裏插入圖片描述

//爲中斷分配處理的CPU0
	ICDIPTR.ICDIPTR14 &= ~(0xff<<8);
	ICDIPTR.ICDIPTR14 |= (1<<8);
5)設置中斷門檻
		配置ICCPMR寄存器,爲到達CPU的中斷設置一箇中斷優先級過濾門檻

在這裏插入圖片描述

//設置優先級別屏蔽門檻
	CPU0.ICCPMR = 255;

6)打開CPU0的接口中斷開關
ICCICR

//使能CPU接口中斷
	CPU0.ICCICR = 1;

7)打開GIC
ICDDCR
6.中斷處理函數
1)獲取中斷號
目的是讓CPU中運行處理程序識別是哪一個中斷源產生的中斷

int irq_num = 0;
	//讀取中斷號
	irq_num = CPU0.ICCIAR & 0x3ff;

2)根據中斷號選擇處理程序

switch(irq_num)
	{
	case 57: //按鍵K2
		//3、切換蜂鳴器狀態
		pwm_change();
		printf("in do_irq\n");
		//清除GPx1_1中斷標記
		EXT_INT41_PEND |= (1<<1);
		break;
	}

3)清除中斷標記
EXT_INT41_PEND寫1清零
ICDICPR寫1清0
ICCEOIR寫中斷號清除中斷

//清除GIC中斷標記
	CPU0.ICCEOIR |= irq_num;

代碼:

#include "exynos_4412.h"
void pwm_init(void)
{
	//1、選擇引腳功能pwmTOU0輸出功能
	GPD0.CON &= ~(0xf<<0);
	GPD0.CON |= (2<<0);

	//2、一級分頻:249
	PWM.TCFG0 &= ~(0xff<<0);
	PWM.TCFG0 |= (249<<0);

	//3、二級分頻:4
	PWM.TCFG1 &= ~(0xf<<0);
	PWM.TCFG1 |= (2<<0);

	//4、設置週期:1000Hz,佔空比50%
	PWM.TCNTB0 = 100;
	PWM.TCMPB0 = PWM.TCNTB0 / 2;

	//5、手動更新TCNT和TCMP
	PWM.TCON |= (1<<1);
	PWM.TCON &= ~(1<<1);

	//6、開啓自動重裝載
	PWM.TCON |= (1<<3);


}
void pwm_change(void)
{
	//7、啓動定時器
	PWM.TCON ^= (1<<0);

	if(PWM.TCON & 1)
		printf("pwm is open!\n");
	else
		printf("pwm is close!\n");
}


void key_init(void)
{

	/****************GPIO配置*****************/
	//1、選擇GPIO引腳功能爲EINT
	GPX1.CON |= (0xf<<4);

	//2、設置中斷信號檢測:↓下降沿
	EXT_INT41_CON &= ~(0x7<<4);
	EXT_INT41_CON |= (2<<4);

	//3、使能GPIO對中斷的檢測
	EXT_INT41_MASK &= ~(1<<1);

	/****************GIC配置*****************/
	//4、中斷白名單(中斷使能):SPI中斷號25,全局中斷號57
	ICDISER.ICDISER1 |= (1<<25);

	//5、使能GICD調度器檢測中斷信號
	ICDDCR = 1;

	//5、爲中斷分配處理的CPU0
	ICDIPTR.ICDIPTR14 &= ~(0xff<<8);
	ICDIPTR.ICDIPTR14 |= (1<<8);

	//6、設置優先級別屏蔽門檻
	CPU0.ICCPMR = 255;

	//7、使能CPU接口中斷
	CPU0.ICCICR = 1;

}

void delay(unsigned int time)
{
	int i;
	for(; time>0; time--)
	{
		for(i=2000; i>0; i--)
		{
			;
		}
	}
}

void do_irq(void)
{
	int irq_num = 0;


	//1、讀取中斷號
	irq_num = CPU0.ICCIAR & 0x3ff;

	//2、識別中斷源,選擇執行對應處理程序
	switch(irq_num)
	{
	case 57: //按鍵K2
		//3、切換蜂鳴器狀態
		pwm_change();
		printf("in do_irq\n");
		//清除GPx1_1中斷標記
		EXT_INT41_PEND |= (1<<1);
		break;
	}

	//清除GIC中斷標記
//	CPU0.ICCEOIR |= (CPU0.ICCIAR & 0x3ff);
	CPU0.ICCEOIR |= irq_num;

	delay(300);
}


int main(void)
{
	/*1初始化按鍵*/
	key_init();

	pwm_init();

	while(1)
	{
		;
	}

	return 0;
}