一、ARM中斷體系結構
arm有7中異常工作模式
使用者模式、快中斷模式、管理模式、資料通路終止模式、中斷模式、系統模式、未定義指令終止模式。

幾種模式有什麼不同呢,
1、不同的寄存器
2、不同的權限
3、觸發條件
對于不同的寄存器,ARM920T有31個通用的32位寄存器和6個程式狀态寄存器。這37個寄存器分為7組,進入某個工作模式時就使用他那組的寄存器。有些寄存器,不同的工作模式下有他的副本,當切換到另一個工作模式時,那個工作模式的寄存器副本将被使用:這些寄存器被稱為備份寄存器(圖中灰色部分的寄存器)
在ARM狀态下,每種工作模式都有16個通用的寄存器和一個(或兩個,這取決于不同的工作模式)程式狀态寄存器。圖中R0-R15可以直接通路,這些寄存器除了R15外都是通用寄存器,即他們既可以儲存資料又可以儲存位址。另外R13-R15稍有特殊。R13是棧指針寄存器,通常被用來儲存棧指針。14被稱為程式連接配接寄存器,當執行BL子程式調用指令時,R14中得到R15(程式計數器PC)的備份。而當發生中斷或異常時,對應的R14_svc、R14_irq、R14_fiq、R14_abt、R14_und中儲存R15的傳回值。
快速中斷模式有7個備份寄存器R8-R14,這使得進入快速中斷模式執行很大部分程式時,隻要不改變R0-R7,不用儲存任何寄存器。七種工作模式都有自己獨占的寄存器副本R13和R14,這使得每個模式都有自己的棧指針寄存器和連接配接寄存器。
每種工作模式除了R0-R15外,還有第17個寄存器CPSR,即目前程式狀态寄存器。CPSR中一些位被用來辨別各種狀态,一些位被用來辨別目前處于什麼工作模式。
如圖CPSR
CPSR各位的意義:
(1)T位:置位時,CPU處Thumb狀态,否則處于ARM狀态
(2)中斷禁止位:I位和F位屬于中斷禁止位。他們被置位時IRQ中斷和FIQ中斷分别被禁止。
(3)工作模式位:辨別CPU目前處于什麼工作模式。可以編寫這些位,使CPU進入指定的工作模式。
除CPSR外,還有快速中斷模式、中斷模式、管理模式、資料通路終止模式和未定義指令終止魔獸等5中工作模式都有獨占的寄存器SPSR,即程式狀态儲存寄存器。當切換進入這些工作模式時,在SPSR中儲存前一個工作模式的CPSR的值,這樣,當傳回前一個工作模式時,可以将SPSR中的值恢複到CPSR中。
綜上所述,當一個異常發生時,将切換進入相應的工作模式,這時ARM920T CPU核将自動完成如下的事情:
(1)、在異常工作模式的連接配接寄存器R14中儲存前一個工作模式的下一條即将執行指令的位址,對于ARM狀态,這個值是目前pc值加4或加8。
(2)、将CPSR的值複制到異常模式的SPSR
(3)、将CPSR的工作模式位設定為這個異常模式對應的工作模式。
(4)、令PC值等于這個異常模式在異常向量表的位址。即跳轉去執行異常向量表中的相應的指令。
相反地、從異常工作模式退出傳回之前的工作模式時,需要通過軟體完成以下事情:
(1)、前面進入異常工作模式時,連接配接寄存器儲存了前一工作模式的一個指令位址,将他減去一個适當的值後賦給PC寄存器。
(2)、将SPSR的值複制回CPSR。
CPSR模式位設定如下圖:
二、s3c2440的中斷控制器
CPU工作的過程中如何知道各類外設發生了某些不預期的事件呢,比如序列槽接收到了新資料、USB接口插入了裝置、按下了某個按鍵等。主要有兩種方法:
(1)是查詢方式:程式循環地查詢各裝置的狀态并作出相應的反應。實作比較簡單,常用在功能相對單一的系統中,比如在一個溫度檢測系統對溫度的不斷檢查。缺點是占用CPU資源,不适用于多任務系統。
(2)、中斷方式:當某事件發生時,硬體會設定某個寄存器;CPU在每執行完一個指令時,通過硬體檢視這個寄存器,如果發現所關注的時
間發生了,則中斷目前程式流程,跳轉到一個固定的位址處理這件事,最後傳回繼續執行被中斷的程式。他的實作相對複雜,但是效率很高,是常用的方法。
中斷體系外設、内部外設與CPU核的硬體框圖:
不論何種CPU,中斷的處理是相似的。
(1)、中斷控制器彙集各類外設發出的中斷信号,然後告訴CPU。
(2)、CPU儲存目前程式的運作環境,調用中斷服務程式來處理這些中斷。
(3)、在ISR中通過讀取中斷控制器,外設的相關寄存器來識别這是哪個中斷,并進行相應的處理。
(4)、清楚中斷:通過讀寫中斷控制器和外設的相關寄存器來實作。
(5)、最後恢複被中斷程式的運作環境(即上面儲存的各個寄存器等),繼續執行。
對于不同的CPU而言,中斷的處理隻是細節的不同。
中斷處理框圖:
從中斷控制器結構圖看出
SUBSRCPND和SRCPND寄存器表明有哪些中斷被觸發,正在等待處理;SUBMASK(INTSUBMASK寄存器)和MASK(INTMASK寄存器)用于屏蔽某些中斷。
圖中的“Request sources(with sub-register)”表示INT_RXD0、INT_TXD0等中斷源(這類中斷源S3C2440有15個)。他們不同于“Request sources(without sub-register”。
附圖s3c2440異常向量表:
綜上所述,使用中斷的步驟如下:
(1)設定好中斷模式和快速中斷模式下的棧:當發生中斷IRQ時,CPU進入中斷模式,這時使用中斷模式下的棧;當發生快速中斷時,CPU進入快速中斷模式,這時使用快速中斷模式下的棧。
(2)準備好中斷處理函數。
a、異常向量:
在異常向量表中設定好進入中斷模式或快速中斷模式時的跳轉函數,他們的異常向量分别是0x00000018、0x0000001c。
b、中斷服務程式(ISR)
IRQ、FIQ的跳轉函數,具體将調用具體中斷的服務函數、
對于IRQ,讀取INTPND寄存器或INTOFFSET寄存器的值來判斷中斷源,然後分别處理。
對FIQ,由于隻有一個中斷可以設為FIQ,無需判斷中斷源。
c、清除中斷:如果不清除中斷,則CPU會誤以為這個中斷又一次發生。
清除中斷時從源頭開始:首先,需要的話,操作具體外設清除中斷信号,比如EINTPEND;其次,清除SUBSRCPND(用到的話)、SRCPND寄存器中的相應的位;最後清除INTPND寄存器中相應的位。
(3)、進入、退出中斷模式或快速中斷模式時,需要儲存、恢複被中斷程式的運作環境。
(4)、根據具體中斷設定相關外設,比如GPIO中斷,需要将相應的引腳功能設定為外部中斷、設定中斷觸發條件等,一些中斷有自己的屏蔽寄存器,還要開啟它,比如,GPIO的外設中斷,EINTMASK。
(5)、對于“Request sources(without sub-register)”的中斷,将INTSUBMASK寄存器中相應的位設為0.
(6)、确定使用中斷的方式:FIQ或IRQ
a、如果是FIQ,則在INTMOD寄存器中設定相應位為1
b、如果是IRQ,則在RIORITY寄存器中設定優先級
(7)、如果是IRQ,将INTMAK寄存器中相應的位設為0(FIQ不受INITMSK寄存器的控制)。
(8)、設定CPSR寄存器中的I位(對于IRQ)或F位(對于FIQ)為0,使能IRQ或FIQ、
在s3c2440中中斷控制器相關的8個寄存器:
1、SUBSRCPND
SUBSRCPND被用來辨別INT_RXD0、INT_TXD0等中斷是否已經發生,每位對應一個中斷。當這些中斷發生且沒有被INTSUBMSK寄存器屏蔽,則他們的若幹位将彙集出現在SRCPND寄存器的一位上,比如、隻要INT_RXD0、INT_TXD0或INT_ERR0一個發生且沒有被屏蔽,則SRCPND寄存器INT_UART0位被置1.
要清除中斷時,往SUBSRCPND中相應的位寫1
2、INTSUBMSK
用來屏蔽SUBSRCPND寄存器所辨別的中斷
3、SRCPND
SRCPND寄存器每一位用來辨別一個或一類中斷是否已經發生,清除中斷時與SUBSRCPND類似。
4、INTMSK
INTMSK寄存器用來屏蔽SRCPND寄存器所辨別的中斷。
5、INTMOD
當INTMOD寄存器中某位寫入1時,它對應的中斷被設為FIQ,同一時間裡,INTMOD寄存器中隻能有一位寫入1。
6、PRIORITY
優先級寄存器,有多個中斷同時發生時,中斷控制器将選出優先級最高的中斷源
7、INTPND
經過優先級仲裁器選出優先級最高的中斷後,這個中斷在INTPND寄存器中相應的位被置1,随後,CPU将進入中斷模式處理它。同一時間段裡,此寄存器隻有一位被置1:在ISR中,可以根據這個位确定是哪個中斷。清除中斷時,往這個位寫1.
8、INTOFFSET寄存器
這個寄存器被用來表示INTPND寄存器中哪個位被置1了,即INTPND寄存器中位【x】位1時,INTOFFSET寄存器的值為x(x為0-31)。
在清除SRCPND、INTPND寄存器時,INTOFFSET寄存器被自動清除。
三、實作按鍵中斷,led燈亮程式
head.S
@******************************************************************************
@ File:head.S
@ 功能:初始化,設定中斷模式、管理模式的棧,設定好中斷處理函數
@******************************************************************************
.extern main
.text
.global _start
_start:
@******************************************************************************
@ 中斷向量,本程式中,除Reset和HandleIRQ外,其它異常都沒有使用
@******************************************************************************
b Reset
@ 0x04: 未定義指令中止模式的向量位址
HandleUndef:
b HandleUndef
@ 0x08: 管理模式的向量位址,通過SWI指令進入此模式
HandleSWI:
b HandleSWI
@ 0x0c: 指令預取終止導緻的異常的向量位址
HandlePrefetchAbort:
b HandlePrefetchAbort
@ 0x10: 資料通路終止導緻的異常的向量位址
HandleDataAbort:
b HandleDataAbort
@ 0x14: 保留
HandleNotUsed:
b HandleNotUsed
@ 0x18: 中斷模式的向量位址
b HandleIRQ
@ 0x1c: 快中斷模式的向量位址
HandleFIQ:
b HandleFIQ
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, #0x5f @ 設定I-bit=0,開IRQ中斷
ldr lr, =halt_loop @ 設定傳回位址
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
init.c
/*
* init.c: 進行一些初始化
*/
#include "s3c24xx.h"
/*
* LED1,LED2,LED4對應GPF4、GPF5、GPF6
*/
#define GPF4_out (1<<(4*2))
#define GPF5_out (1<<(5*2))
#define GPF6_out (1<<(6*2))
#define GPF4_msk (3<<(4*2))
#define GPF5_msk (3<<(5*2))
#define GPF6_msk (3<<(6*2))
/*
* S2,S3,S4對應GPF0、GPF2、GPG3
*/
#define GPF0_eint (0x2<<(0*2))
#define GPF2_eint (0x2<<(2*2))
#define GPG3_eint (0x2<<(3*2))
#define GPF0_msk (3<<(0*2))
#define GPF2_msk (3<<(2*2))
#define GPG3_msk (3<<(3*2))
/*
* 關閉WATCHDOG,否則CPU會不斷重新開機
*/
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( )
{
// S2,S3對應的2根引腳設為中斷引腳 EINT0,ENT2
GPFCON &= ~(GPF0_msk | GPF2_msk);
GPFCON |= GPF0_eint | GPF2_eint;
// S4對應的引腳設為中斷引腳EINT11
GPGCON &= ~GPG3_msk;
GPGCON |= GPG3_eint;
// 對于EINT11,需要在EINTMASK寄存器中使能它
EINTMASK &= ~(1<<11);
/*
* 設定優先級:
* ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ3,即EINT0 > EINT2
* 仲裁器1、6無需設定
* 最終:
* EINT0 > EINT2 > EINT11即K2 > K3 > K4
*/
PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ;
// EINT0、EINT2、EINT8_23使能
INTMSK &= (~(1<<0)) & (~(1<<2)) & (~(1<<5));
}
s3c2440.h
/* WOTCH DOG register */
#define WTCON (*(volatile unsigned long *)0x53000000)
/* SDRAM regisers */
#define MEM_CTL_BASE 0x48000000
#define SDRAM_BASE 0x30000000
/* NAND Flash registers */
#define NFCONF (*(volatile unsigned int *)0x4e000000)
#define NFCMD (*(volatile unsigned char *)0x4e000004)
#define NFADDR (*(volatile unsigned char *)0x4e000008)
#define NFDATA (*(volatile unsigned char *)0x4e00000c)
#define NFSTAT (*(volatile unsigned char *)0x4e000010)
/*GPIO registers*/
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
#define GPFUP (*(volatile unsigned long *)0x56000058)
#define GPGCON (*(volatile unsigned long *)0x56000060)
#define GPGDAT (*(volatile unsigned long *)0x56000064)
#define GPGUP (*(volatile unsigned long *)0x56000068)
#define GPHCON (*(volatile unsigned long *)0x56000070)
#define GPHDAT (*(volatile unsigned long *)0x56000074)
#define GPHUP (*(volatile unsigned long *)0x56000078)
/*UART registers*/
#define ULCON0 (*(volatile unsigned long *)0x50000000)
#define UCON0 (*(volatile unsigned long *)0x50000004)
#define UFCON0 (*(volatile unsigned long *)0x50000008)
#define UMCON0 (*(volatile unsigned long *)0x5000000c)
#define UTRSTAT0 (*(volatile unsigned long *)0x50000010)
#define UTXH0 (*(volatile unsigned char *)0x50000020)
#define URXH0 (*(volatile unsigned char *)0x50000024)
#define UBRDIV0 (*(volatile unsigned long *)0x50000028)
/*interrupt registes*/
#define SRCPND (*(volatile unsigned long *)0x4A000000)
#define INTMOD (*(volatile unsigned long *)0x4A000004)
#define INTMSK (*(volatile unsigned long *)0x4A000008)
#define PRIORITY (*(volatile unsigned long *)0x4A00000c)
#define INTPND (*(volatile unsigned long *)0x4A000010)
#define INTOFFSET (*(volatile unsigned long *)0x4A000014)
#define SUBSRCPND (*(volatile unsigned long *)0x4A000018)
#define INTSUBMSK (*(volatile unsigned long *)0x4A00001c)
/*external interrupt registers*/
#define EINTMASK (*(volatile unsigned long *)0x560000a4)
#define EINTPEND (*(volatile unsigned long *)0x560000a8)
interrupt.c
#include "s3c24xx.h"
void EINT_Handle()
{
unsigned long oft = INTOFFSET;
unsigned long val;
switch( oft )
{
// S2被按下
case 0:
{
GPFDAT |= (0x7<<4); // 所有LED熄滅
GPFDAT &= ~(1<<4); // LED1點亮
break;
}
// S3被按下
case 2:
{
GPFDAT |= (0x7<<4); // 所有LED熄滅
GPFDAT &= ~(1<<5); // LED2點亮
break;
}
// K4被按下
case 5:
{
GPFDAT |= (0x7<<4); // 所有LED熄滅
GPFDAT &= ~(1<<6); // LED4點亮
break;
}
default:
break;
}
//清中斷
if( oft == 5 )
EINTPEND = (1<<11); // EINT8_23合用IRQ5
SRCPND = 1<<oft;
INTPND = 1<<oft;
}
main.c
int main()
{
while(1);
return 0;
}
Makefile
objs := head.o init.o interrupt.o main.o
int.bin: $(objs)
arm-linux-ld -Ttext 0x00000000 -o int_elf $^
arm-linux-objcopy -O binary -S int_elf [email protected]
arm-linux-objdump -D -m arm int_elf > int.dis
%.o:%.c
arm-linux-gcc -Wall -O2 -c -o [email protected] $<
%.o:%.S
arm-linux-gcc -Wall -O2 -c -o [email protected] $<
clean:
rm -f int.bin int_elf int.dis *.o
轉載于:https://www.cnblogs.com/Ye-Jason/p/7374726.html