1:s5pv210的中断步骤
(1):建立异常向量表;
(2):中断初始化;
(3):使能(如外部中断,写中断处理函数);
(4):建立中断号与中断处理函数的联系,使能中断;
当中断发生时,中断处理函数会自动处理中断;
流程如下:
2:建立异常向量表:
s5pv210异常向量表的基地址为0xD003_7400,下图为异常对于基地址的偏移量;
第一步建立异常向量表:
代码如下:
/*
* s5pv210 裸机
*
* 异常向量表初始化
*
*/
#define VECTOR_TABLE_BASE 0xD0037400
#define Reset_offset 0x0
#define Undef_offset 0x4
#define SVC_offset 0x8
#define Prectch_offset 0xC
#define Data_Abort_offset 0x10
#define IRQ_offset 0x18
#define FIQ_offset 0x1C
#define _PFUNC_Reset (*(unsigned int*)(VECTOR_TABLE_BASE+Reset_offset))
#define _PFUNC_Undef (*(unsigned int*)(VECTOR_TABLE_BASE+Undef_offset))
#define _PFUNC_SVC (*(unsigned int*)(VECTOR_TABLE_BASE+SVC_offset))
#define _PFUNC_Prectch (*(unsigned int*)(VECTOR_TABLE_BASE+Prectch_offset))
#define _PFUNC_Data_Abort (*(unsigned int*)(VECTOR_TABLE_BASE+Data_Abort_offset))
#define _PFUNC_IRQ (*(unsigned int*)(VECTOR_TABLE_BASE+IRQ_offset))
#define _PFUNC_FIQ (*(unsigned int*)(VECTOR_TABLE_BASE+FIQ_offset))
extern void IRQ_handle(void);
void Reset_handle(void)
{
}
void Undef_handle(void)
{
}
void SVC_handle(void)
{
}
void Prectch_handle(void)
{
}
void Data_Abort_handle(void)
{
}
void vector_table_init(void)
{
_PFUNC_Reset = (unsigned int)Reset_handle;
_PFUNC_Undef = (unsigned int)Undef_handle;
_PFUNC_SVC = (unsigned int)SVC_handle;
_PFUNC_Prectch = (unsigned int)Prectch_handle;
_PFUNC_Data_Abort = (unsigned int)Data_Abort_handle;
_PFUNC_IRQ = (unsigned int)IRQ_handle;
_PFUNC_FIQ = (unsigned int)IRQ_handle; //FIQ、IRQ都是采用IRQ中断
}
IRQ_handle要写在汇编IRQ_handle.S中;
代码如下:
/*
*
*
*
*
*/
#define IRQ_STACK 0xD0037F80
.global IRQ_handle
IRQ_handle:
//设置IRQ的栈
ldr sp, =IRQ_STACK
//由于三级流水线的存在,pc为此时的程序语句+8,保存的时候要把下一句保存到lr中
sub lr, lr, #4
//保存现场
stmd sp! {r0-r12, lr}
//跳转到中断处理函数
bl isr_handler
//恢复现场
ldmfd sp! {r0-r8, pc}^
ARM保存中断时为什么使用 sub lr, lr, #4
1. 首先要谈流水线,在arm执行过程中一般分为取指,译码,执行阶段
也就是假设当前 第一条指令在执行阶段
第二条指令在译码阶段
第三条指令在取指阶段
当前正在执行的指令地址为pc-8,第二条就为pc-4,而pc现在真正指向已处于pc位置
2. 一般pc在发生中断时lr保存的是当前的pc值,这里pc值是多少呢??
当发生中断肯定保存的pc是第三条指令,而我们从中断返回肯定不是执行第三条指令,而是紧接着的第二条指令,所以应该保存的 lr = pc - 4,
但是当执行到此位置时pc值已经改变,肯定不对,还好发生中断时 mov lr,pc 所以这里就可以直接使用 sub lr,lr,#4
————————————————————未完待续————————————————————————————
第二部:中断初始化
相关寄存器:
使能中断:
Disable中断
中断处理函数自动保存地址
中断处理函数存放地址
代码实战:
//清除4个中断处理函数
void clean_vicaddress(void)
{
_REG_VIC0ADDRESS = 0x0;
_REG_VIC1ADDRESS = 0x0;
_REG_VIC2ADDRESS = 0x0;
_REG_VIC3ADDRESS = 0x0;
}
void interrupt_init(void)
{
//第一步初始化中断之前要关闭所有中断
_REG_VIC0INTENCLEAR = 0xFFFFFFFF;
_REG_VIC1INTENCLEAR = 0xFFFFFFFF;
_REG_VIC2INTENCLEAR = 0xFFFFFFFF;
_REG_VIC3INTENCLEAR = 0xFFFFFFFF;
//第三步:设置中断为IRQ中断
_REG_VIC0INTSELECT = 0x0;
_REG_VIC1INTSELECT = 0x0;
_REG_VIC2INTSELECT = 0x0;
_REG_VIC3INTSELECT = 0x0;
//第三步:清中断处理函数地址
clean_vicaddress();
}
void int_disable(unsigned int num)
{
if (num < 32) {
_REG_VIC0INTENCLEAR = (0x1<<num);
}
else if (num < 64) {
_REG_VIC1INTENCLEAR = (0x1<<(num-32));
}
else if (num < 96) {
_REG_VIC2INTENCLEAR = (0x1<<(num-64));
}
else if (num < 128) {
_REG_VIC3INTENCLEAR = (0x1<<(num-96));
}
else {
}
}
void int_enable(unsigned int num)
{
if (num < 32) {
_REG_VIC0INTENABLE = (0x1<<num);
}
else if (num < 64) {
_REG_VIC1INTENABLE = (0x1<<(num-32));
}
else if (num < 96) {
_REG_VIC2INTENABLE = (0x1<<(num-64));
}
else if (num < 128) {
_REG_VIC3INTENABLE = (0x1<<(num-96));
}
else {
_REG_VIC0INTENABLE = 0xFFFFFFFF;
_REG_VIC1INTENABLE = 0xFFFFFFFF;
_REG_VIC2INTENABLE = 0xFFFFFFFF;
_REG_VIC3INTENABLE = 0xFFFFFFFF;
}
}
void creat_israddr(unsigned int num, void (*PIRQ_handler)(void))
{
if (num < 32) {
//*( (void (*)(void))(VIC0VECTADDR + 4*num) )= PIRQ_handler;
*( (volatile unsigned long *)(VIC0VECTADDR + 4*(num-0)) ) = (unsigned)PIRQ_handler;
}
else if (num < 64) {
//(void (*)(void))(VIC1VECTADDR + 4*(num-32))= PIRQ_handler;
*( (volatile unsigned long *)(VIC1VECTADDR + 4*(num-32)) ) = (unsigned)PIRQ_handler;
}
else if (num < 96) {
//(void (*)(void))(VIC2VECTADDR + 4*(num-64))= PIRQ_handler;
*( (volatile unsigned long *)(VIC2VECTADDR + 4*(num-64)) ) = (unsigned)PIRQ_handler;
}
else {
//(void (*)(void))(VIC3VECTADDR + 4*(num-96))= PIRQ_handler;
*( (volatile unsigned long *)(VIC3VECTADDR + 4*(num-96)) ) = (unsigned)PIRQ_handler;
}
}
//判断中断在哪个address中
static int check_int_addr(void)
{
if (_REG_VIC0IRQSTATUS) {
return 0;
}
else if (_REG_VIC1IRQSTATUS) {
return 1;
}
else if (_REG_VIC2IRQSTATUS) {
return 2;
}
else if (_REG_VIC3IRQSTATUS) {
return 3;
}
else {
return -1;
}
}
void isr_handler(void)
{
void (*p_isr)(void) = NULL;
int i;
i = check_int_addr();
switch (i) {
case 0 :
p_isr = (void (*)(void))_REG_VIC0ADDRESS;
break;
case 1 :
p_isr = (void (*)(void))_REG_VIC1ADDRESS;
break;
case 2 :
p_isr = (void (*)(void))_REG_VIC2ADDRESS;
break;
case 3 :
p_isr = (void (*)(void))_REG_VIC2ADDRESS;
break;
default :
break;
}
p_isr();
}
第三部:使能外部中断中断处理函数;
详细请看
void int_led_blink(void) { //中断处理函数
led_blink();
//清楚外部中断挂起,注意写1清挂起 clean_int_pend(); //清vicaddress
clean_vicaddress();
}
第四部:建立中断号与中断函数联系,使能中断
#include "interrupt.h" #include "stdio.h" extern void led_blink(void); extern void led1_on(void); extern void vector_table_init(void); extern void key_init(void); extern void uart_init(void); int main(void) { //按键初始化 key_inter_init(); //异常向量表初始化 vector_table_init(); //中断初始化 interrupt_init(); //创建函数 creat_israddr(NUM_EINT2, int_led_blink); creat_israddr(NUM_EINT3, int_led_blink); creat_israddr(NUM_EINT16_31, int_led_blink); //使能中断 int_enable(NUM_EINT2); int_enable(NUM_EINT3); int_enable(NUM_EINT16_31); while (1) { printf("a"); } }