嵌入式学习-uboot-lesson11-按键中断

一、中断处理流程分析

1.中断概念

CPU在工作的过程中,经常需要与外设进行交互,交互的方式包括”轮询方式”,”中断方式”。
轮询方式
CPU不断地查询设备的状态。该方式实现比较简单,但CPU利用率很低,不适合多任务的系统。
中断方式
CPU在告知硬件开始一项工作后,就去做别的事去了,当硬件完成了该项工作后,向CPU发送一个信号,告知CPU它已经完成了这项工作。

2.中断生命周期

嵌入式学习-uboot-lesson11-按键中断_第1张图片

事件会送到中断控制器里面,中断控制器对其进行过滤,然后将其送到CPU核
嵌入式学习-uboot-lesson11-按键中断_第2张图片

3.中断源

在中断的生命周期中,中断源的作用是负责产生中断信号。
S3C2440支持60个中断源;
S3C6410支持64个中断源;
S5PV210支持93个中断源;
6410的中断:
6410一共有个两个中断组,为VIC0和VIC1,每个VIC支持32个中断源,一共64个中断源,如下图所示

嵌入式学习-uboot-lesson11-按键中断_第3张图片

嵌入式学习-uboot-lesson11-按键中断_第4张图片
从上面三幅图可以看到6410的64个中断源,同时可以看到有的中断源例如 INT_EINT0 包含了4个中断,主要是为了节约中断,就用这一个中断源,当 这四个中断某一个发生时,中断源INT_EINT0 就会产生中断,然后会查询相应的寄存器来判断是发生了那个中断。

4.中断处理方式

4.1非向量方式(2440)

嵌入式学习-uboot-lesson11-按键中断_第5张图片
如上图,非向量中断,当中断产生的时候,会跳转到一个统一的入口,然后在这个入口函数中判断是那个中断发生了,然后再去执行相应的中断处理程序处去

4.2向量方式(6410/210)

嵌入式学习-uboot-lesson11-按键中断_第6张图片
向量中断,就是当中断发生的时候,硬件自动判断到是那个中断产生,然后就会跳转到相应的中断寄存器里面,中断寄存器里面存放着这个中断的处理程序的地址,然后根据这个地址,就是我们的中断处理程序了。

二、6410中断编程分析

其总体流程如下所示:
嵌入式学习-uboot-lesson11-按键中断_第7张图片

1.初始化按键

嵌入式学习-uboot-lesson11-按键中断_第8张图片
从上面两幅图可以看到,ok6410的6个按键对应的GPIO分别为GPN0 ~ GPN5
嵌入式学习-uboot-lesson11-按键中断_第9张图片
根据上图,对按键1和按键6进行操作,需要将控制寄存器设为外部中断模式,因此要设置为0b10

#define GPNCON (volatile unsigned long*)0x7f008830
void button_init()
{
    *(GPNCON) = 0b10 | (0b10<<10);        //设置按键1与按键6 外部中断
}

2.初始化中断控制器

2.1配置为下降沿触发

嵌入式学习-uboot-lesson11-按键中断_第10张图片

#define EXT_INT_0_CON *((volatile unsigned int *)0x7f008900) 

嵌入式学习-uboot-lesson11-按键中断_第11张图片
将EINT0 和 EINT5 设置为下降沿触发
EXT_INT_0_CON = (0b010)|(0b010<<8); /* 配置按键1和按键6为下降沿触发 */

2.2取消中断屏蔽

嵌入式学习-uboot-lesson11-按键中断_第12张图片

#define EXT_INT_0_MASK *((volatile unsigned int *)0x7f008920) 

嵌入式学习-uboot-lesson11-按键中断_第13张图片
使能中断,则使寄存器的值为0即可

EXT_INT_0_MASK = 0; /* 取消屏蔽外部中断 */ 

2.3使能中断向量控制器

这里写图片描述

#define VIC0INTENABLE *((volatile unsigned int *)0x71200010) 

这里写图片描述
根据上面两幅图,使能中断寄存器需要设置其值为1,同时要设置EINT0 EINT5,则需要使能中断源INT_EINT0 INT_EINT1

VIC0INTENABLE |= (0b1)|(0b10); /* 使能外部中断*/ 

2.4 设置CPSR状态寄存器,打开总中断

    __asm__( 
    "mrs r0,cpsr\n"               /*读出值*/
    "bic r0, r0, #0x80\n"
    "msr cpsr_c, r0\n"           /*写入*/ 
    : 
    : 

2.5使能向量中断

向量中断的方式,当中断产生的时候,并没有统一的入口,6410的64个中断有64个相对应的寄存器,每个寄存器里面存放着相对应的中断处理程序的地址,当中断产生的时候,硬件自己判断发生了那个中断,cpu就直接从发生中断对应的寄存器中取出处理程序的地址,从而处理中断。

嵌入式学习-uboot-lesson11-按键中断_第14张图片


#define EINT0_VECTADDR *((volatile unsigned int *)0x71200100) 
#define EINT5_VECTADDR *((volatile unsigned int *)0x71200104) 

    EINT0_VECTADDR = (int)key1_handle;         
    EINT5_VECTADDR = (int)key6_handle;

使能向量中断,6410也可向下兼容2440非向量中断方式,因此需要设置其为向量中断方式

   __asm__( 

    "mrc p15,0,r0,c1,c0,0\n"    /*使能向量中断*/
    "orr r0,r0,#(1<<24)\n"        
    "mcr p15,0,r0,c1,c0,0\n"
    : 
    : 
  );

3.中断处理

3.1保存环境

保存环境变量到栈上

    __asm__( "sub lr, lr, #4\n" "stmfd sp!, {r0-r12, lr}\n" : : );

3.2中断处理

led_on(); //点亮led

3.3清除中断


此寄存器的功能是当发生中断后,就会产生计数,从0变为1

当处理完中断之后,需要清除掉这次中断,使其恢复到原始的状态。

#define EXT_INT_0_PEND *((volatile unsigned int *)0x7f008924) 
EXT_INT_0_PEND = ~0x0;  

当产生中断的时候,中断寄存器里面的地址也会放在VIC0ADDRESS 里面,因此在处理完中断的时候,也需要将其清零。

#define VIC0ADDRESS *((volatile unsigned int *)0x71200f00) 
#define VIC1ADDRESS *((volatile unsigned int *)0x71300f00)
    VIC0ADDRESS = 0; 
    VIC1ADDRESS = 0; 

3.4恢复环境

    __asm__( "ldmfd sp!, {r0-r12, pc}^ \n" : : );

4.栈的处理

中断环境下的栈和SVC模式下的栈有所不同,因此需要设置中断模式下的栈

init_stack:
    msr cpsr_c, #0xd2 @进入中断模式
    ldr sp, =0x53000000  @初始化r13_irq

    msr cpsr_c, #0xd3 @进入svc模式
    ldr sp,=0x54000000   @初始化r13_svc 50000000 + 64 MB

    mov pc,lr

贴上这次改动过的代码:
button.c

/********************************************
*file name: button.c
*author   : stone
*date     : 2016.7.3
*function : 按键中断进行相关的操作
*********************************************/

#define GPNCON (volatile unsigned long*)0x7f008830

void button_init()
{
 *(GPNCON) = 0b10 | (0b10<<10); //设置按键1与按键6 为外部中断模式
}

interrupt.c

/******************************************** *file name: interrupt.c *author : stone *date : 2016.7.3 *function : 中断处理 *********************************************/
#define EXT_INT_0_CON       *((volatile unsigned int *)0x7f008900) //外部中断配置寄存器
#define EXT_INT_0_MASK      *((volatile unsigned int *)0x7f008920) //外部中断屏蔽寄存器
#define EXT_INT_0_PEND      *((volatile unsigned int *)0x7f008924) //外部中断0悬挂寄存器

#define VIC0INTENABLE       *((volatile unsigned int *)0x71200010)  //中断使能寄存器

#define EINT0_VECTADDR      *((volatile unsigned int *)0x71200100)  //向量地址0寄存器
#define EINT5_VECTADDR      *((volatile unsigned int *)0x71200104)  //向量地址1寄存器
#define VIC0ADDRESS         *((volatile unsigned int *)0x71200f00)  //向量地址寄存器
#define VIC1ADDRESS         *((volatile unsigned int *)0x71300f00)

//按键1处理程序
void key1_handle()
{
    __asm__(                  /*3.1保存环境*/

    "sub lr, lr, #4\n"  
    "stmfd sp!, {r0-r12, lr}\n"       
    : 
    : 
   );

    //3.2中断处理程序
    led_on();

    //3.3清除中断 
    EXT_INT_0_PEND = ~0x0;  
    VIC0ADDRESS = 0; 
    VIC1ADDRESS = 0; 

   //3.4恢复环境
    __asm__( 
    "ldmfd sp!, {r0-r12, pc}^ \n"       
    : 
    : 
  );

}

//按键6处理程序
void key6_handle()
{
    __asm__( 

    "sub lr, lr, #4\n"  
    "stmfd sp!, {r0-r12, lr}\n"       
    : 
    : 
  );
    led_off();

    //清除中断 
    EXT_INT_0_PEND = ~0x0; 
    VIC0ADDRESS = 0; 
    VIC1ADDRESS = 0;   

    __asm__( 
    "ldmfd sp!, {r0-r12, pc}^ \n"       
    : 
    : 
  ); 
}

//中断初始化
void init_irq()
{ 
    EXT_INT_0_CON = (0b010)|(0b010<<8);         /* 2.1 配置按键1和按键6为下降沿触发 */  

    EXT_INT_0_MASK = 0;                         /* 2.2取消屏蔽外部中断 */  

    VIC0INTENABLE |= (0b1)|(0b10);              /* 2.3使能外部中断*/  

    EINT0_VECTADDR = (int)key1_handle;          /* 2.5用户按下key时,CPU就会自动的将VIC0VECTADDR0的值赋给VIC0ADDRESS并跳转到这个地址去执 */  
    EINT5_VECTADDR = (int)key6_handle;


    __asm__( 

    "mrc p15,0,r0,c1,c0,0\n"                   /*2.5使能向量中断*/
    "orr r0,r0,#(1<<24)\n"
    "mcr p15,0,r0,c1,c0,0\n"

    "mrs r0,cpsr\n"                            /*2.4设置cpsr状态寄存器*/
    "bic r0, r0, #0x80\n"
    "msr cpsr_c, r0\n"            
    : 
    : 
  );
}

main.c

/********************************************
*file name: main.c
*author   : stone
*date     : 2016.7.3
*function : 总程序
*********************************************/

int gboot_main()
{

 /*mmu 初始化,暂时不用*/
 #ifdef MMU_ON
 mmu_init(); 
 #endif

 /*led初始化*/
 led_init();

 /*中断初始化*/
 init_irq();

 /*按键初始化*/
 button_init();

 //led_off();

 while(1);

 return 0; 
}

mmu.c

/******************************************** *file name: mmu.c *author : stone *date : 2016.7.3 *function : mmu的初始化 *********************************************/
#define MMU_FULL_ACCESS (3 << 10) /* 访问权限 [11:10]*/
#define MMU_DOMAIN (0 << 5) /* 属于哪个域 [8:5]*/
#define MMU_SPECIAL (1 << 4) /* 必须是1 [4]*/
#define MMU_CACHEABLE (1 << 3) /* cacheable [3]*/
#define MMU_BUFFERABLE (1 << 2) /* bufferable [2]*/
#define MMU_SECTION (2) /* 表示这是段描述符 [1:0]*/
#define MMU_SECDESC (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_SECTION)
#define MMU_SECDESC_WB (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)

void creat_page_table()
{
    unsigned long *ttb = (unsigned long *)0x50000000; //表在内存的基地址处
    unsigned long vaddr; //虚拟地址
    unsigned long paddr; //物理地址

    vaddr = 0xa0000000; //虚拟地址
    paddr = 0x7F000000;
    *(ttb + (vaddr >> 20)) = (paddr&0xfff00000) | MMU_SECDESC;
    //*(ttb + (vaddr >> 20)) 为表项的位置 
    //(paddr&0xfff00000) 获取高12位数据
    //MMU_SECDESC 访问led的gpio很简单,就不需要cache和buffer

    //映射内存
    vaddr = 0x50000000;
    paddr = 0x50000000;  //其虚拟地址和物理地址是一致的
    while (vaddr < 0x54000000)  //映射64mb
    {
        *(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC_WB;
        vaddr += 0x100000;
        paddr += 0x100000;
    }   

}

void mmu_enable()
{
    __asm__(

    /*设置TTB*/
    "ldr r0, =0x50000000\n"     /* 页表的基地址 */         
    "mcr p15, 0, r0, c2, c0, 0\n"    

    /*不进行权限检查*//*cp15 c3 domain 控制权限*/
    "mvn r0, #0\n"                   
    "mcr p15, 0, r0, c3, c0, 0\n"    

   /*使能MMU*/
    "mrc p15, 0, r0, c1, c0, 0\n"    
    "orr r0, r0, #0x0001\n"          
    "mcr p15, 0, r0, c1, c0, 0\n"    
    : 
    : 
  );
}
void mmu_init()
{
    create_page_table();
    mmu_enable();
}

菜鸟一枚,如有错误,多多指教。。。

你可能感兴趣的:(中断,uboot,按键,OK6410,向量中断)