(1)保護現場
(2)cpu的pc指針自動跳轉到中斷向量表處找出對應的中斷處理入口,即中斷處理函數
(3)進入中斷處理函數後首先要分辨中斷源,然後進行對應的中斷處理
(4)中斷處理完,清除中斷
(5)返回現場
中斷程序的設置流程:
(1)建立中斷向量表
(2)初始化中斷:打開開關、設置優先級、中斷模式下的堆棧指針等
(3)建立中斷處理函數:彙編調用C函數
開始分析ARM920t的中斷處理過程,原理和51單片機類似直接開始分析我的實驗代碼。
進入代碼時間:
File:head.S
.extern main
.text
.global _start
_start:
@******************************************************************************
@ (1)建立中斷異常向量表:每一條指令佔用4個字節的空間
@******************************************************************************
b Reset @ 0x00:(有使用)啓動復位也是一種特殊的“異常”
HandleUndef:
b HandleUndef @ 0x04: 未定義指令中止模式的向量地址
HandleSWI:
b HandleSWI @ 0x08: 管理模式的向量地址,通過SWI指令進入此模式
HandlePrefetchAbort:
b HandlePrefetchAbort @ 0x0c: 指令預取終止導致的異常的向量地址
HandleDataAbort:
b HandleDataAbort @ 0x10: 數據訪問終止導致的異常的向量地址
HandleNotUsed:
b HandleNotUsed @ 0x14: 保留
b HandleIRQ @ 0x18: (有使用)中斷模式的向量地址
HandleFIQ:
b HandleFIQ @ 0x1c: 快中斷模式的向量地址
Reset:
ldr sp, =4096 @ 設置棧指針,以下都是C函數,調用前需要設好棧
bl disable_watch_dog @ 關閉WATCHDOG,否則CPU會不斷重啓
msr cpsr_c, #0xd2 @ 進入中斷模式,只有在中斷模式下纔可以設置中斷的堆棧指針
ldr sp, =3072 @ 設置中斷模式棧指針
msr cpsr_c, #0xd3 @ 進入(返回)管理模式
ldr sp, =4096 @ 設置管理模式棧指針,
@ 其實復位之後,CPU就處於管理模式,
@ 前面的“ldr sp, =4096”完成同樣的功能,此句可省略
bl init_led @ 初始化LED的GPIO管腳
bl init_irq @ 調用中斷初始化函數,在init.c中
msr cpsr_c, #0x53 @ 設置I-bit=0,開IRQ中斷
ldr lr, =halt_loop @ 進入c函數main前設置返回地址
ldr pc, =main @ 調用main函數
halt_loop:
b halt_loop
HandleIRQ: @ 定義中斷異常處理函數
sub lr, lr, #4 @ 計算返回地址
stmdb sp!, { r0-r12,lr } @ 保存使用到的寄存器
@ 注意,此時的sp是中斷模式的sp
@ 初始值是上面設置的3072
ldr lr, =int_return @ 設置調用ISR即EINT_Handle函數後的返回地址
ldr pc, =EINT_Handle @ 調用中斷服務函數,在interrupt.c中
int_return:
ldmia sp!, { r0-r12,pc }^ @ 中斷返回, ^表示將spsr的值複製到cpsr
代碼分析:慢慢體會和51單片機的一點差別
當一個異常中斷髮生時ARM920T的硬件自動處理的工作:保護一部分現場
(1)將前一個工作模式的“下一條即將執行的指令地址”即PC值保存到下一個異常模式下的R14即LR寄存器中(此時還未進入該異常模式)
(2)將前一個工作模式的下的CPSR保存到下一個異常模式下的SPSR中
(3)強制將下一個異常模式的CPSR工作模式位設置成對應值
(4)強制讓PC值等於中斷向量表中保存的跳轉地址
進入到中斷後軟件要完成的工作有:
(1)計算中斷返回地址保存到LR:上面我們講到硬件自動幫我們把
注意這裏有一點很有意思:根據2440使用手冊可知平時PC指針是指向當前執行指令的下兩條指令(即PC=“PC”+8),剛纔硬件幫我們保存的地址並不是我們中斷返回時真正想要的地址!!!
例如從IRQ返回前,我必須要矯正這個返回地址才行,即讓LR=PC+4,這很簡單,之前LR=PC+8,現在只要將LR=LR-4就可以了。
(2)保護剩餘的現場:將所有的工作寄存器,比如R0-R12、LR等
(3)調用異常處理函數並清除中斷
(4)恢復現場,將保存過得寄存器值恢復
【非常建議自己去看s3c2440英文版的使用手冊,裏面有講到中斷異常到來時是CPU是如何操作的】
附加文件:
(1)File:interrupt.c 真正中斷處理函數
#include "s3c24xx.h"
void EINT_Handle()
{
unsigned long oft = INTOFFSET;//判斷外部中斷源
unsigned long val;
switch( oft )
{
// S4被按下
case 0:
{
GPBDAT |= (0x7<<5); // 所有LED熄滅
GPBDAT &= ~(1<<5); // LED1點亮
break;
}
// S1被按下
case 1:
{
GPBDAT |= (0x7<<5); // 所有LED熄滅
GPBDAT &= ~(1<<6); // LED2點亮
break;
}
// K3被按下
case 2:
{
GPBDAT |= (0x7<<5); // 所有LED熄滅
GPBDAT &= ~(1<<7); // LED3點亮
break;
}
// K2被按下
case 4:
{
GPBDAT |= (0x7<<5); // 所有LED熄滅
GPBDAT &= ~(1<<8); // LED3點亮
break;
}
default:
break;
}
//清中斷
if( oft == 5 )
EINTPEND = (1<<11); // EINT8_23合用IRQ5
SRCPND = 1<<oft;
INTPND = 1<<oft;
}
(2)File:init.c 一些初始化函數,比較懶所以就用c函數代替
#include "s3c24xx.h"
#define GPB5_out (1<<(5*2))
#define GPB6_out (1<<(6*2))
#define GPB7_out (1<<(7*2))
#define GPB8_out (1<<(8*2))
#define GPB5_msk (3<<(5*2))
#define GPB6_msk (3<<(6*2))
#define GPB7_msk (3<<(7*2))
#define GPB8_msk (3<<(8*2))
#define GPF0_eint (0x2<<(0*2))
#define GPF1_eint (0x2<<(1*2))
#define GPF2_eint (0x2<<(2*2))
#define GPF4_eint (0x2<<(4*2))
#define GPF0_msk (3<<(0*2))
#define GPF1_msk (3<<(1*2))
#define GPF2_msk (3<<(2*2))
#define GPF4_msk (3<<(4*2))
void disable_watch_dog(void)
{
WTCON = 0; // 關閉WATCHDOG很簡單,往這個寄存器寫0即可
}
void init_led(void)
{
// LED1,LED2,LED4對應的3根引腳設爲輸出
GPFCON &= ~(GPF4_msk | GPF5_msk | GPF6_msk);
GPFCON |= GPF4_out | GPF5_out | GPF6_out;
}
/*
* 初始化GPIO引腳爲外部中斷
* GPIO引腳用作外部中斷時,默認爲低電平觸發、IRQ方式(不用設置INTMOD)
*/
void init_irq( )
{
//配置中斷引腳
GPFCON &= ~(GPF0_msk | GPF1_msk | GPF2_msk |GPF4_msk);
GPFCON |= GPF0_eint | GPF1_eint | GPF2_eint | GPF4_eint;
/*
* 設定優先級:
* ARB_MODE0 = 0, ARB_SEL0 = 00b
* ARB_MODE6 = 0, ARB_SEL6 = 00b
* ARB_MODE1、ARB_SEL1無需設置:因爲這一組仲裁器只用了一箇中斷源
* 優先級最終排名:EINT0 > EINT1 > EINT2 > EINT4
*/
PRIORITY = (PRIORITY & ( (~0x01) | (0x0<<7) | (~(0x1<<6))| (0x0<<19) ) );
// EINT0、EINT1、EINT2、EINT4 使能
INTMSK &= (~(1<<0)) & (~(1<<1)) & (~(1<<2)) & (~(1<<4));
}
代碼分析:
中斷優先級的設置:
ARM920t的中斷優先級主要由7個仲裁器arbiter來控制,每個仲裁器連接的中斷信號如圖,最終0-5仲裁器選出來的信號還要6仲裁器來進行最後一輪的排優。
PRIORITY寄存器共有有20bit,被分成兩部分功能:看2440手冊,記得是英文版
[0-6]ARB_MODE:設置仲裁寄存器的工作模式,主要有自動輪流優先級和關閉該功能。一個仲裁器使用1bit
[7-20]ARB_SEL:控制輸入中斷信號的優先級。一個仲裁器使用2bit
(3)File:main.c 等待中斷觸發
int main()
{
while(1);
return 0;
}
搞定