



以key按键功能为例,对于中断,首先先配置GPIO1_18引脚触发方式与使能引脚中断
// 配置按键引脚中断触发方式:下降沿触发
GPIO1->ICR2 |= (3 << 4);
// 使能引脚中断
GPIO1->IMR |= (1 << 18);
然后当按下按键后外设会产生对应的中断号99。(如下图所示,GPIO1组的16-31号引脚都为99,当产生中断号后,要查看具体引脚可通过GPIO的ISR状态寄存器查看)

产生中断号后,不仅在外设中保存,也会传给GIC控制器,在GIC中,将中断号写入IAR寄存器,再完成对应的工作之后,会通过上图的四条线路中的任意一条将中断送给ARM内核,ARM内核读取该寄存器里的中断号,确定是哪个外设引发的中断,执行中断处理函数,结束后将中断号写入EOIR寄存器,告诉GIC中断处理结束,同时外设的ISR寄存器也要记得清零。

一些细节:
首先在_reset_handler:函数中设置irq模式的中断开启,将bit7置0,
bic r0, r0, #(1 << 7) // irq
其次,初始化GIC
void system_interrupt_init(void)
{
// 初始化GIC控制器
GIC_Init();
}
同时,打开GIC中断
// 使能GIC中断
GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);
然后,要找到GIC中的IAR寄存器,参考下图,当知道GIC的基地址之后,加上偏移量即可得到寄存器地址,而要找到GIC基地址,则应借助coprocessor协处理器,在Cortex-A7内部,用于协助CPU完成特定任务,在内核中有cp0-cp15一共16个协处理器,功能各不相同,平时cp15使用最多,GIC的基地址也在此处。

而cp15协处理器有c0-c15一共16个寄存器组,下图以c0寄存器组的MIDR寄存器为例


分别表示ARM架构(相当于写死),内核主版本号,汇编指令集版本号,处理器型号,主版本号
而本次中断要配置的有c1中的SCTLR寄存器


将V位置0,表示可以通过VBAR寄存器更改启动代码的默认起始地址,置1则表示,起始地址为0XFFFF 0000,且不能再被重映射。在上文链接时更改了链接的起始地址为0x8780 0000,此时,由于系统默认只会从0x00地址处开始执行异常处理函数,当产生异常时,再去原先的地址则找不到异常函数,此时应进行重映射

默认从0x00处开始,而由于此时起始地址已经变为0x8780 0000,故应进行重映射,而SCTLR寄存器就是负责此功能

要操作协处理器的寄存器数据,引入以下两条指令:
mrc:读取cp15中寄存器的数据到通用寄存器
mcr:将通用寄存器中的数据写入到cp15中寄存器
使用格式:
mrc <coproc>,<opc1>,<Rt>,<CRn>,<CRm>,<opc2>对照参考手册即可,例如:
mrc p15,0,r0,c1,c0,0意思为将cp15中SCTLR寄存器值读到R0寄存器
此外12位应置1,表示将icache打开,指令处理速率变快(dcache尽量关闭,每次操作数据,若将数据放在dcache,则会忽略对寄存器本身的读取,不利于学习)
_enable_icache:
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #(1 << 13) //将cp15中SCTLR寄存器值读到R0寄存器
orr r0, r0, #(1 << 12)
mcr p15, 0, r0, c1, c0, 0
bx lr

此时只是将SCTLR配置了,此外还应配置VBAR寄存器,在c12组里

_set_vbar:
ldr r0, =0x87800000
mcr p15, 0, r0, c12, c0, 0
bx lr
最后在c15里的CBAR寄存器,可以读出GIC的基地址,不过可以通过Cortex-A7的固件库实现,即初始化GIC函数


inline:内联函数,本质是函数,调用内联函数前,必须有该函数的定义,编译时,内联函数会在函数调用位置被展开,类似宏,就地展开,不用跳转函数,提高函数执行效率,可在头文件中定义函数。
irq中断异常处理:

_irq_handler:
subs lr, lr, #4 //返回地址偏移量加了4,所以先减4
// GIC 基地址
mrc p15, 4, r1, c15, c0, 0 //读c15的CBAR寄存器里的基地址
add r1, r1, #0x2000 //基地址+偏移量
ldr r0, [r1, #0x0C] //r1再加到IAR寄存器的偏移量,将IAR里面的中断号放到r0
stmfd sp!, {r0-r12, lr}
stmfd sp!, {r0, r1} //中断号还要写到EOIR,所以单独保护
bl system_interrupt_handler //汇编调用c函数,参数小于4个时,通过r0-r3传值,所以r0的值就是中断号
ldmfd sp!, {r0, r1}
// 清除GIC中断标记位
str r0, [r1, #0x10]
ldmfd sp!, {r0-r12, pc}^
void system_interrupt_handler(int num)
{
if (num == GPIO1_Combined_16_31_IRQn)
{
if ((GPIO1->ISR & (1 << 18)) != 0)
{
led_nor();
}
GPIO1->ISR |= (1 << 18);
}
}
网硕互联帮助中心


评论前必须登录!
注册