ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ARM裸机开发-中断

ARM裸机开发-中断 以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置0bic r0, r0, #(1 7) // irq其次初始化GICvoid 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 p150r0c1c00意思为将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); } }最后可创建一个interrupt的.c文件用于存放中断相关函数typedef void (*irq_handler_t)(void); // 函数指针类型 irq_handler_t interrupt_vector_table[160]; // 函数地址 void system_interrupt_init(void) { // 初始化GIC控制器 GIC_Init(); } void system_interrupt_register(int num, irq_handler_t handler) { interrupt_vector_table[num] handler; } void system_interrupt_handler(int num) { if (interrupt_vector_table[num] ! NULL) { interrupt_vector_table[num](); } }在key.c中void key_interrupt_handler(void) { if ((GPIO1-ISR (1 18)) ! 0) { led_nor(); beep_nor(); } GPIO1-ISR | (1 18); } void key_init(void) { // 引脚复用功能 IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0); // 引脚电气属性 IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF0B0); // 按键引脚初始化 GPIO_Type_t t { .direction gpio_input }; gpio_init(GPIO1, 18, t); // 配置按键引脚中断触发方式下降沿触发 GPIO1-ICR2 | (3 4); // 使能引脚中断 GPIO1-IMR | (1 18); // 注册按键中断 system_interrupt_register(GPIO1_Combined_16_31_IRQn, key_interrupt_handler); // 设置按键中断优先级 GIC_SetPriority(GPIO1_Combined_16_31_IRQn, 0); // GIC使能按键中断 GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn); }
返回列表