一、arm异常工作模式介绍
page73
arm920T共有7种工作模式,共有31个通用的32位寄存器和6个程序状态寄存器(cpsr),共37个。其中标注三角的对应模式下特有的寄存器,其中cpsr控制工作模式。
page76
N:结果是否为负数 Z:计算结果是否为0 C:进位/借位/移位溢出 V:溢出标志
I:中断禁止位(1禁止中断,0允许) F:快中断禁止位(1禁止中断,0允许) T:cpu状态位(arm与THUMB) M0~M4:工作模式位
注意:不能直接设置T位,否则会发生不可预知的问题
工作模式位:
page78
当一个异常发生时,ARM920T 将完成:
1.在异常模式的lr(r14)中保存前一个工作模式的下一条,即将执行的指令地址,如下图。
2.cpsr复制到spsr。
3.设置cpsr工作模式位为要进入的异常工作状态。
4.将pc指针指向异常向量表。
如果要返回原来的工作模式,则:
1.将lr中的值减去适当的的值给pc
2.将spsr的值赋值给cpsr。
page80
page82
ARM920T一上电处于arm状态,并工作与管理模式(svc),并把pc指针赋值为0.
二、中断控制器介绍
基本工作流程:
page378
寄存器介绍:
(1)源等待寄存器
page384
读:为1表明有中断发生,0没有 写:1清除已经发生的中断,0无效
(2)中断模式寄存器
设置对应位为0处于IRQ模式,为1处于FIQ模式
(3)中断屏蔽寄存器
对应位为1屏蔽该中断,0激活该中断
(4)优先级寄存器
优先级的判断由6个一级仲裁器和一个二级仲裁器组成,每个仲裁器能仲裁REQ0~REQ5(如下图)。
page382
优先级寄存器的ARB_SELx用于选择中断的优先级顺序,REQ0和REQ5的顺序不能改变,标号小的优先级高。
ARB_MODEx选择仲裁器工作模式,为0时按照ARB_SELx配置工作,为1时按照被服务的REQx自动变化,例如REQ1发生中断,那么ARB_SELx自动变成01
(5)中断等待寄存器
page391
一个中断没有被屏蔽,发生的时候相应的位会自动置1,否则为0。我们要在清除SRCPND相应的位之后清除相应的INTPND相应的位,写1清除,0无效。
(6)中断偏移寄存器
表示INTPND中的哪位被置一了,例如INTPND第10被置1,则INTOFFSET的值为10,清除了SRCPND和INTPND,INTOFFSET自动被清除
寄存器配置大概流程:
使用中断步骤:
1.设置中断模式下的堆栈,即给中断模式下的sp指针赋值,最好在cpsr中将中断屏蔽。
2.设置中断异常向量(addr=0x18)跳转函数,在函数中确定中断源进行处理,读INTOFFSET即可判断。然后清除中断,从源头先清,例如先清EINTPEND,然后SRCPND,最后清除INTPND。
3.进入中断模式保护现场,退出中断模式还原现场。
进入:sub lr, lr, #4
stmdb sp!, {r0-r12,lr}
退出:ldmia sp!, {r0-r12,pc}^
4.设置具体引脚的模式,触发条件,是否被屏蔽等,在gpio那一章节。
5.确定中断方式为irq还是fiq,即配置INTMOD。
6.配置INMSK寄存器。
7.在cpsr中开启总中断。
实现同按不同的按键是led亮和灭
代码:
head.S
.text
.global _start
@ 0x00:
_start:
b rest
@ 0x04: 未定义指令中止模式的向量地址
HandleUndef:
b HandleUndef
@ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
HandleSWI:
b HandleSWI
@ 0x0c: 指令预取终止导致的异常的向量地址
HandlePrefetchAbort:
b HandlePrefetchAbort
@ 0x10: 数据访问终止导致的异常的向量地址
HandleDataAbort:
b HandleDataAbort
@ 0x14: 保留
HandleNotUsed:
b HandleNotUsed
@ 0x18: 中断模式的向量地址
b interrupt_server
@ 0x1c: 快中断模式的向量地址
HandleFIQ:
b HandleFIQ
rest:
mov sp, #4096
bl disable_watch_dog
msr cpsr_c, #0xd2 @进入irq模式并屏蔽总中断
mov sp, #3072 @设置irq模式下的堆栈
msr cpsr_c, #0xd3 @进入管理模式
bl init_led @初始化led
bl init_irq @初始化irq
msr cpsr_c, #0x53 @开启总中断
bl main
halt_loop:
b halt_loop
interrupt_server:
sub lr, lr, #4
stmdb sp!, {r0-r12,lr}
ldr lr, =return
ldr pc, =interrupt_handle
return:
ldmia sp!, {r0-r12,pc}^ @^代表将spsr的值赋值给cpsr
init.c
#include "s3c2440.h"
#define GPF_EINT0 1<<1
#define GPF_EINT0_MASK (~(3))
#define GPF_EINT2 1<<5
#define GPF_EINT2_MASK (~(3 << 4))
#define GPG_EINT11 1<<7
#define GPG_EINT11_MASK (~(3 << 6))
#define GPF4_OUT 1<<8
#define GPF4_OUT_MASK (~(0X03 << 8))
#define GPF5_OUT 1<<10
#define GPF5_OUT_MASK (~(0X03 << 10))
#define GPF6_OUT 1<<12
#define GPF6_OUT_MASK (~(0X03 << 12))
void disable_watch_dog(void)
{
WTCON = 0X00;
}
void init_led(void)
{
//设置gpf4、5、6为输出
GPFCON &= (GPF4_OUT_MASK & GPF5_OUT_MASK & GPF6_OUT_MASK);
GPFCON |= (GPF4_OUT | GPF5_OUT | GPF6_OUT);
GPFDAT |= (1<<4 | 1<<5 | 1<<6);
}
void init_irq(void)
{
//gpf0、gpf2、gpg3设置为中断模式,默认为低电平触发
GPFCON &= (GPF_EINT0_MASK & GPF_EINT2_MASK);
GPFCON |= (GPF_EINT0 | GPF_EINT2);
GPGCON &= GPG_EINT11_MASK;
GPGCON |= GPG_EINT11;
EINTMASK &= ~(1<<11); //使能EINT11
INTMOD &= (~1 & ~(1<<2) & ~(1<<5)); //irq模式
INTMSK &= (~1 & ~(1<<2) & ~(1<<5)); //开启相应的中断
}
interrupt.c
#include "s3c2440.h"
void interrupt_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 $@
arm-linux-objdump -D -m arm int_elf > int.dis
%.o:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $<
%.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $<
clean:
rm -f int.bin int_elf int.dis *.o
s3c2440.h
/* WOTCH DOG register */
#define WTCON (*(volatile unsigned long *)0x53000000)
/*GPIO registers*/
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
#define GPGCON (*(volatile unsigned long *)0x56000060)
#define GPGDAT (*(volatile unsigned long *)0x56000064)
/*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)