[国嵌攻略][050][2440按键中断编程]
程序结构优化
1.把不同的功能放到不同的文件里面
2.一般在bootloader中不使用MMU,所以关掉MMU
按键初始化
1.打开开发板的原理图,找到按键
2.把对应的GPIO配置成中断,对中断源进行初始化
初始化中断控制器
1.SUBMASK和MASK必须要保证没有屏蔽中断,GPIO是不属于子中断,所以不用设置SUBMASK,MODE和Priority保存默认值即可,设置INTMSK(0x4A000008)
2.EINT4_7对应4个中断,分开设置需要设置EINTMASK(0x560000A4)。注意,设置EINTMASK寄存器必须要在INTMASK之前设置
3.在核心预处理时,关闭了中断,在这里需要打开中断
中断处理
1.当中断产生时,会跳转到中断向量表,执行ldr pc, _irq
2.保存环境
2.1.当前lr指向pc+8的位置,当中断返回时需要返回到lr-4的位置,算出lr的值,sub lr, lr, #4
2.2.保存寄存器到堆栈,stmfd sp!, {r0-r12, lr}
2.3.调用中断处理函数,handle_int
2.4.从堆栈恢复寄存器,ldmfd sp!, {r0-r12, pc}^
2.5.handle_int需要做:1.判断中断源 2.根据中断源,执行不同的中断处理。INTOFFSET(0x4A000014)标明了那个中断源产生了中断
3.清除中断,在恢复环境之前先要清除SRCPND(0x4A000000)和INTPND(0x4A000010)对应位的请求,保证下次中断能被处理。如果下次中断到来,发现SRCPND和INTPND对应位为1,表示上次中断还没有被处理,忽略这次中断请求。如果用到是子中断需要清零SUBSRCPND,如果用到的是EINT4-ENT23那么要清除EINTPEND(0x560000A8)。注意,要清除中断时要往对应位上写入1
4.注意在irq模式中设置的sp栈指针是r13_irq,而初始化栈sp指针是r13_svc。所以在初始化栈指针是要对r13_irq和r13_svc都要设置地址
/******************************************************************** *名称:irq *功能:中断处理 *********************************************************************/ irq: //保存环境 sub lr, lr, #4 //三级流水线,pc指向指定的地址与当前执行指令的地址偏移+8 stmfd sp!, {r0-r12, lr} //保存r0-r12和lr寄存器的值到栈中 //处理中断 bl process_int //恢复环境 ldmfd sp!, {r0-r12, pc}^ //从栈中载入r0-r12和lr寄存器的值,^表示把spsr恢复到cpsr
/******************************************************************** *名称:int *作者:D *时间:2015.11.10 *功能:中断控制器 ********************************************************************/ /******************************************************************** * 宏定义 ********************************************************************/ #define SRCPND 0X4A000000 //中断源请求寄存器 #define INTPND 0X4A000010 //中断请求寄存器 #define INTMSK 0x4A000008 //中断掩码寄存器 #define EINTMASK 0x560000A4 //外部中断掩码寄存器 #define EINTPEND 0x560000A8 //外部中断请求寄存器 .text /******************************************************************** *名称:init_int *功能:初始化中断 *********************************************************************/ .global init_int init_int: //屏蔽中断 ldr r0, =INTMSK mvn r1, #0 //屏蔽中断,设置INTMSK[31-0]:0xFFFFFFFF str r1, [r0] //关闭中断 mrs r0, cpsr orr r0, r0, #0x000000C0 //关闭中断,设置cpsr[7-6]:11 msr cpsr, r0 mov pc, lr /******************************************************************** *名称:enable_int *功能:打开中断 *********************************************************************/ .global enable_int enable_int: //使能中断 ldr r0, =EINTMASK ldr r1, [r0] bic r1, r1, #0x00000900 //使能外部中断,设置外部中断掩码EINT8:0 EINT11:0 str r1, [r0] ldr r0, =INTMSK ldr r1, [r0] bic r1, r1, #0x00000020 //使能中断,设置中断掩码EINT8_23:0 str r1, [r0] //打开中断 mrs r0, cpsr bic r0, r0, #0x000000C0 //打开中断,设置cpsr[7-6]:00 msr cpsr, r0 mov pc, lr /******************************************************************** *名称:process_int *功能:处理中断 *********************************************************************/ .global process_int process_int: mov ip, lr //保存链接寄存器,嵌套调用保存返回地址 //读取中断源 ldr r0, =EINTPEND //读取外部中断请求,如果中断从INTOFFSET中读取,子中断从SUBSRCPND ldr r1, [r0] //处理中断源 cmp r1, #0x00000100 //如果是EINT8,调用EINT8中断处理 beq eint8_process cmp r1, #0x00000800 //如果是EINT11,调用EINT11中断处理 beq eint11_process b end_process_int //如果都不是,结束中断处理 eint8_process: bl on_led //点亮led b end_process_int eint11_process: bl off_led //熄灭led b end_process_int end_process_int: //清除中断源 ldr r0, =EINTPEND ldr r1, [r0] str r1, [r0] //清除外部中断请求 ldr r0, =SRCPND ldr r1, [r0] orr r1, r1, #0x00000020 //清除中断源请求,设置EINT8_23:0,如果是子中断还要清除SUBSRCPND str r1, [r0] ldr r0, =INTPND ldr r1, [r0] orr r1, r1, #0x00000020 //清除中断请求,设置EINT8_23:0 str r1, [r0] mov lr, ip //恢复链接寄存器,嵌套调用恢复返回地址 mov pc, lr
注意:在函数嵌套调用中要先把lr保存到ip中,在函数中被调用其他函数时会改变,当外部函数返回时,需要从ip中恢复lr,用于返回到外部函数被调用处
r0-r15寄存器作用
r0-r3
用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。被调用函数在返回之前不必恢复 r0-r3。如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。
r4-r11
被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。
r12
是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复 r12。
r13
是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。
r14
是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复
r15
是程序计数器 PC。它不能用于任何其它用途。
注意:在中断程序中,所有的寄存器都必须保护,编译器会自动保护R4~R11