一:框圖
這里通過按下按鍵,觸發中斷的例子,分析FS4412開發板 中斷的原理機制。
按下k2會觸發中斷信號 ---> GPIO管腳控制器 ---> 中斷使能 --> 分發使能 --> 接口使能 --> 優先級門檻 ---> ARM core。
FS4412開發板使用的CPU是exynos4412 ,它的中斷相對復雜,關鍵的是如何使得中斷信號如何通過重重關口,后到達 cpu核心。 這里有五個關口分別對應 GPIO管腳控制器(寄存器是EXT_INT41_MASK), 中斷使能(寄存器是ICDISER), 分發使能(ICDDCR),接口使能,
優先級門檻(ICCICR). 要在程序中分別打開這五個關口對應的寄存器位,過五關斬六將,才能把信號送入ARM 核心。
二. 代碼分析
1. Main.c (c語言主程序)
/*
功能: 按鍵K2按下后能觸發中斷輸出字符
作業: 實現K3 按下后能觸發中斷輸出字符
*/
//----------------uart
#define GPA1CON (*(volatile unsigned int *)0x11400020)
#define ULCON2 (*(volatile unsigned int *)0x13820000)
#define UCON2 (*(volatile unsigned int *)0x13820004)
#define UBRp2 (*(volatile unsigned int *)0x13820028)
#define UFRACVAL2 (*(volatile unsigned int *)0x1382002C)
#define UTXH2 (*(volatile unsigned int *)0x13820020)
#define UTRSTAT2 (*(volatile unsigned int *)0x13820010)
//---------------interrupt
#define GPX1CON (*(volatile unsigned int *)0x11000c20)
#define EXT_INT41CON (*(volatile int *)0x11000E04)
#define EXT_INT41_MASK (*(volatile int *)0x11000F04)
#define ICDISER1_CPU0 (*(volatile int *)0x10490104)
#define ICDIPTR14_CPU0 (*(volatile int *)0x10490838)
#define ICDDCR (*(volatile int *)0x10490000)
#define ICCICR_CPU0 (*(volatile int *)0x10480000)
#define ICCPMR_CPU0 (*(volatile int *)0x10480004)
#define EXT_INT41_PEND (*(volatile int *)0x11000f44)
#define ICCIAR_CPU0 (*(volatile int *)0x1048000C)
#define ICCEOIR_CPU0 (*(volatile int *)0x10480010)
#define ICDICPR1_CPU0 (*(volatile int *)0x10490284)
void interrupt_init(void)
{
//-----外: 配置管腳的工作模式
GPX1CON = (GPX1CON & ~(0xF<<4))|(0xF<<4); //配置 GPX1_1為中斷模式
EXT_INT41CON = (EXT_INT41CON & ~(0x7<<4))|(0x2<<4); //設置GPX1_1的觸發方式為 下降沿觸發
EXT_INT41_MASK = EXT_INT41_MASK & (~0x02); //GPX1_1 中斷使能
//-----內: 功能塊設置
ICDISER1_CPU0 = ICDISER1_CPU0 | (1<<25); //EINT9 (GPX1_1) GIC中斷使能
ICDIPTR14_CPU0 = 0x01010101; //參考例子背景,用默認設置
ICDDCR = ICDDCR|1; //GIC 分發總使能
ICCICR_CPU0 = 1; // CPU0 中斷使能
ICCPMR_CPU0 = 0XFF; //設置CPU0的優先級門檻為低
//KEY3
//外
GPX1CON = (GPX1CON & ~(0xF<<8))|(0xF<<8); //配置 GPX1_2為中斷模式
EXT_INT41CON = (EXT_INT41CON & ~(0x7<<8))|(0x2<<8); //設置GPX1_2的觸發方式未 下降沿觸發
EXT_INT41_MASK = EXT_INT41_MASK & (~0x04); //GPX1_2 中斷使能
//內
ICDISER1_CPU0 = ICDISER1_CPU0 | (1<<26); //EINT9 (GPX1_2) GIC中斷使能
}
void uart_init(void)
{
//-----外: 配置管腳的工作模式
GPA1CON = 0x22; //配置GPA1_1 GPA10 為uart串口模式
//-----內: 功能塊設置
//1. uart 通訊協議格式的設置
ULCON2 = 0x03; //設置協議格式( 無校驗位 1個停止位 8個數據位)
UCON2 = 0x05; //設置串口發送接收模式為polling模式
/*2. 設置uart 的速度為115200
p_VAL = (100000000/(115200 *16)) - 1 = 53.253
UBRpn = 53
UFRACVALn/16 = 0.253
Therefore, UFRACVALn = 4
*/
UBRp2 = 53;
UFRACVAL2 = 4;
}
void putc(char c)
{
while(1)
{
if(UTRSTAT2&0x02) //檢測發送是否為空
{
break;
}
}
UTXH2 = c; //發送字符
}
void do_irq(void )
{
int irq_num;
irq_num = ICCIAR_CPU0&0x3ff; ////中斷ID號
switch(irq_num)
{
case 57:
putc('k');
putc('2');
EXT_INT41_PEND = EXT_INT41_PEND|(1<<1); //清GPX1_1中斷標志
ICDICPR1_CPU0 = ICDICPR1_CPU0|(1<<25); //清GIC GPX1_1中斷標志
break;
case 58:
putc('k');
putc('3');
EXT_INT41_PEND = EXT_INT41_PEND|(1<<2); //清GPX1_2中斷標志
ICDICPR1_CPU0 = ICDICPR1_CPU0|(1<<26); //清GIC GPX1_2中斷標志
break;
default:
putc('e');
break;
}
ICCEOIR_CPU0 = (ICCEOIR_CPU0&0x3FF)|irq_num; //結束中斷 將處理完成的中斷ID號寫入該寄存器,則表示相應的中斷處理完成
}
int main(void)
{
uart_init();
interrupt_init();
while(1)
{
putc('f');
delay1s();
}
return 0;
}
2. Start.s 匯編程序(程序運行的第一條指令在該位置)
.global delay1s
.text
.global _start
_start:
b reset @0x00
ldr pc,_undefined_instruction @0x04
ldr pc,_software_interrupt
ldr pc,_prefetch_abort
ldr pc,_data_abort
ldr pc,_not_used
ldr pc,_irq @0x18
ldr pc,_fiq
_undefined_instruction: .word _undefined_instruction
_software_interrupt: .word _software_interrupt
_prefetch_abort: .word _prefetch_abort
_data_abort: .word _data_abort
_not_used: .word _not_used
_irq: .word irq_handler
_fiq: .word _fiq
irq_handler:
sub lr,lr,#4
stmfd sp!,{r0-r12,lr} @進棧保存現場
bl do_irq
irq_handler_end:
ldmfd sp!,{r0-r12,pc}^ @出;謴同F場
reset:
ldr r0,=0x40008000 @設置異常向量表的啟始地址為 0x40008000
mcr p15,0,r0,c12,c0,0 @ Vector Base Address Register
init_stack:
ldr r0,stacktop /*get stack top pointer*/
/********svc mode stack********/
mov sp,r0
sub r0,#128*4 /*512 byte for irq mode of stack*/
/****irq mode stack**/
msr cpsr,#0xd2
mov sp,r0
sub r0,#128*4 /*512 byte for irq mode of stack*/
/***fiq mode stack***/
msr cpsr,#0xd1
mov sp,r0
sub r0,#0
/***abort mode stack***/
msr cpsr,#0xd7
mov sp,r0
sub r0,#0
/***undefine mode stack***/
msr cpsr,#0xdb
mov sp,r0
sub r0,#0
/*** sys mode and usr mode stack ***/
msr cpsr,#0x10
mov sp,r0 /*1024 byte for user mode of stack*/
b main
delay1s:
ldr r4,=0xfffffff
delay1s_loop:
sub r4,r4,#1
cmp r4,#0
bne delay1s_loop
mov pc,lr
.align 4
/**** swi_interrupt handler ****/
stacktop: .word stack+4*512
.data
stack:
.space 4*512
.end
3.map.lds 鏈接腳本
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x40008000; /*指定鏈接的起始地址 */
. = ALIGN(4);
.text :
{
start.o(.text)
*(.text)
}
. = ALIGN(4);
.data :
{ *(.data) }
. = ALIGN(4);
.bss :
{ *(.bss) }
}
4. Makefile 編譯
all:
arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o start.o start.S
arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o main.o main.c
arm-none-linux-gnueabi-ld start.o main.o -Tmap.lds -o uart.elf
arm-none-linux-gnueabi-objcopy -O binary uart.elf uart.bin
arm-none-linux-gnueabi-objdump -D uart.elf > uart.dis
clean:
rm -rf *.bak start.o main.o uart.elf uart.bin uart.dis