[国嵌攻略][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都要设置地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /******************************************************************** *名称: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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | /******************************************************************** *名称: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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术