一、按键中断简介
裸机按键中断用到了外部中断:外部中断占了4个中断源(INT_EINT0 INT_EINT1 INT_EINT2 INT_EINT3 INT_EINT4)如下图所示
在用到外部中断时需要知道所用的按键GPIO引脚属于哪一个中断源或者属于哪一个组,Tiny6410按键用到了GPN0~GPN5 六个引脚,其中GPN0~GPN3 属于INT_EINT0, GPN4和GPN5属于INT_EINT1 。
二、向量中断与非向量中断简介
IRQ中断发生后,PC值根据VE值的不同而跳转到不同的地方执行中断服务函数
当VE=0时,PC跳转至0x18处执行中断服务函数
当VE=1时,PC跳转至对应的VICXVECTADDR指定的函数入口地址执行中断服务函数
具体的VE这一位在协处理器CP15的寄存器C1中,对应C1的第24位,设置VE=1可以用以下汇编语句:
mrc p15,0,r0,c1,c0,0 //将 CP15 的寄存器 C1 的值读到 r0 中
orr r0,r0,#(1<<24)" // 设置r0中的第24位为1
mcr p15,0,r0,c1,c0,0 //将 r0 的值写到 CP15 的寄存器 C1 中
本文主要讲VE=1时的程序设计。
二、start.S 文件
.global _start
_start:
reset:
// 把外设基地址告诉CPU
ldr r0, =0x70000000 // 6410内存地址0~0x60000000 外设地址0x70000000 ~ 0x7fffffff
orr r0,r0,#0x13 // 外设地址大小256M
mcr p15,0,r0,c15,c2,4 // 把r0的值(外设基地址和外设大小)写给CPU
// mcr:ARM寄存器到协处理寄存器的数据传送指令
// mrc:协处理寄存器到ARM寄存器的数据传送指令
// 关闭看门狗
ldr r0, =0x7E004000
mov r1, #0
str r1, [r0]
// 设置栈
ldr sp, =0x0c002000
//设置时钟
bl clock_init
bl irq_init
// 开中断
mrc p15,0,r0,c1,c0,0
orr r0,r0,#(1<<24) //这一块开中断和设置VE=1很重要
mcr p15,0,r0,c1,c0,0
mov r0, #0x53
msr CPSR_cxsf, r0
//main函数
ldr pc, =main
halt:
b halt
使用VE=1的好处:在start.S中不涉及任何有关的中断函数跳转,而将中断函数的调转交给了VICXVECTADDR寄存器(当发生中断时,PC自动跳转至寄存器中的函数入口地址处执行对应的中断服务函数)
三、外部中断初始化及中断服务函数
中断初始化:
void irq_init(void)
{
/* 配置GPN0~5引脚为中断功能 */
GPNCON &= ~(0xfff);
GPNCON |= 0xaaa;
/* 设置中断触发方式为: 上升沿触发 */
EINT0CON0 &= ~(0xfff);
EINT0CON0 |= 0x444;
/* 设置延时滤波寄存器*/
EINT0FLTCON = 0x00ffffff;
/* 禁止屏蔽中断 */
EINT0MASK &= ~(0x3f);
/* 在中断控制器里使能这些中断 */
VIC0INTENABLE |= (0x3); /* bit0: eint0~3, bit1: eint4~11 */
/* 注册中断处理函数*/
VIC0VECTADDR0 = (unsigned long)eint_irq; // INT_EINT0中断
VIC0VECTADDR1 = (unsigned long)eint_irq; // INT_EINT1中断
}
中断服务函数:中断服务函数需要加入保护现场和恢复现场的操作
//为了简便操作 六个按键(2个中断源)的中断服务函数的都相同
//中断服务函数执行led翻转操作
void eint_irq(void)
{
//保护现场
__asm__(
"sub lr, lr, #4\n"
"stmfd sp!, {r0-r12, lr}\n"
:
:
);
LedTriggle(); // 这是一个led翻转函数
/* 清中断 */
EINT0PEND = 0x3f;
VIC0ADDRESS = 0;
//恢复现场
__asm__(
"ldmfd sp!, {r0-r12, pc}^ \n"
:
:
);
}
以上为VE=1时的一些主要操作与下边内容没有任何关系。
四、对VE=0 时的非向量中断的一些理解
当VE=0时,PC会跳转至0x18处执行中断服务函数,所以需要在0x18处编写要执行的中断服务函数,具体在start.s中的实现如下:
.global _start
_start:
// 异常向量表
b reset /* 复位时,cpu跳到0地址0x00 */
b halt /* cpu遇到不能识别的指令时 0x04*/
b halt /* swi异常,进入svc模式 0x08*/
b halt /* 预取中止异常0x0b */
b halt /* 数据访问异常 0x10*/
b halt /* 没用到 0x14*/
b irq /* 中断异常 0x18*/
b halt /* 快中断异常 0x1b*/
reset:
// 外设地址告诉cpu
ldr r0, =0x70000000
orr r0, r0, #0x13
mcr p15,0,r0,c15,c2,4
// 关看门狗
ldr r0, =0x7E004000
mov r1, #0
str r1, [r0]
// 设置栈
ldr sp, =8*1024
// 初始化时钟
bl clock_init
// 初始化ddr
bl sdram_init
// 初始化nandflash
bl nand_init
// 初始化irq
bl irq_init
// 开中断
mov r0, #0x53
msr CPSR_cxsf, r0
on_ddr:
// 跳转
ldr pc, =main
// 中断异常
irq:
/* 保存现场 */
ldr sp, =0x54000000
sub lr, lr, #4
stmdb sp!, {r0-r12, lr}
/* 处理异常 */
bl do_irq
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^表示把spsr恢复到cpsr */
halt:
b halt