s5pv210——中断实战

以下内容源于博客http://www.cnblogs.com/biaohc/p/6354068.html的学习,以及朱有鹏老师课程的学习,和网络资源的整理。


一、s5pv210的中断步骤

(1)建立异常向量表;

(2)中断初始化;

(3)使能(如外部中断,写中断处理函数);

(4)建立中断号与中断处理函数的联系,使能。

当中断发生时,中断处理函数会自动处理中断;

流程如下图:

s5pv210——中断实战_第1张图片

下面按上述步骤编写代码,内容细节见博客http://blog.csdn.net/oqqhutu12345678/article/details/71635611


二、代码

1、建立异常向量表

/*
 *	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}^//这里为什么不是恢复r0~r12

ARM保存中断时为什么使用 sub lr, lr, #4?

(1)首先要谈流水线,在arm执行过程中一般分为取指,译码,执行阶段;

  • 假设当前第一条指令在执行阶段,第二条指令在译码阶段,第三条指令在取指阶段;
  • 若当前正在执行的指令地址为pc-8,那第二条就为pc-4,而pc指向取址。

(2)一般pc在发生中断时lr保存的是当前的pc值,这里pc值是多少呢?

  • 当发生中断时,肯定保存的pc是第三条指令,而我们从中断返回肯定不是执行第三条指令,而是紧接着的第二条指令,所以应该保存的 lr = pc - 4,(但是当执行到此位置时pc值已经改变,肯定不对,还好发生中断时 mov lr,pc ,)所以这里就可以直接使用 sub lr,lr,#4,即lr=pc-4。

2、中断初始化

//清除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<

3、使能外部中断中断处理函数

void int_led_blink(void)
{
   //中断处理函数 
  led_blink();
  //清楚外部中断挂起,注意写1清挂起
    clean_int_pend();
   //清vicaddress
  clean_vicaddress(); 
}

4、建立中断号与中断函数联系,使能中断

#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");
    }
    
}















小总结:

一、S5PV210中断处理的编程实践

2、中断控制器初始化

  • 主要工作有:第一阶段绑定异常向量表到异常处理程序;禁止所有中断源;选择所有中断类型为IRQ;清理VICnADDR寄存器为0.

3、中断的使能与禁止

  • 思路是先根据中断号判断这个中断属于VIC几,然后在用中断源减去这个VIC的偏移量,得到这个中断号在本VIC中的偏移量,然后1<

4、绑定自己实现的isr到VICnVECTADDR

  • 搞清楚2个寄存器的区别:VICnVECTADDR和VICnADDR
  • VICVECTADDR寄存器一共有4×32个,每个中断源都有一个VECTADDR寄存器,将自己为这个中断源写的isr地址丢到这个中断源对应的VECTADDR寄存器中即可。

5、真正的中断处理程序如何获取isr

  • 发生中断时,硬件自动把相应中断源的isr地址从VICnVECTADDR寄存器推到VICnADDR寄存器中,所以只需要到相应的VICnADDR中去拿出isr地址,调用执行即可。
  • 第4步绑定isr地址到VICnVECTADDR,以及第5步中断发生时第二阶段的第二阶段如何获取isr地址,这两步是相关的。这两个的结合技术,就是x210的硬件自动寻找isr的机制。


二、整个中断的流程梳理:

整个中断的工作分为2部分:

第一部分是我们为中断响应而做的预备工作:

1. 初始化中断控制器

2. 绑定写好的isr到中断控制器

3. 相应中断的所有条件使能

第二部分是当硬件产生中断后如何自动执行isr:

1. 第一步,经过异常向量表跳转入IRQ/FIQ的入口

2. 第二步,做中断现场保护(在start.S中),然后跳入isr_handler

3. 第三步,在isr_handler中先去搞清楚是哪个VIC中断了,然后直接去这个VIC的ADDR寄存器中取isr来执行即可。

4. 第四步,isr执行完,中断现场恢复,直接返回继续做常规任务。



你可能感兴趣的:(ARM裸机)