0 参考资料
STM32MP13xx参考手册.pdf(RM0475)
ARM Generic Interrupt Controller Architecture version 2.0 - Architecture Specification.pdf
1 基于Cortex-A7的STM32MP135的IRQ初始化及处理流程分析
熟悉基于Cortex-M内核的stm32系列MCU的一定对中断向量表非常熟悉,在Cortex-M中断向量表存放了所有中断服务函数的地址,使用前需要设置中断向量偏移地址确定中断向量基地址,一旦产生异常或中断处理器就会自动跳转到相应的中断服务函数中执行中断(例如EXTI0_IRQHandler、USART1_IRQHandler等)。如下是基于Cortex-M7的stm32H743的部分中断向量表:
在Cortex-A7中也有中断向量表,不过并没有包含所有的IRQ中断,而是将所有中断均归类到IRQ中。中断向量表如下:
因此发生中断时不会直接跳转到相应的中断服务函数内执行,而是跳转到IRQ中断服务函数统一处理。下面分析STM32MP135的IRQ初始化及处理流程。
1.1 初始化向量表基地址
/* Set Vector Base Address Register (VBAR) to point to this application's vector table */
"LDR R0, =Vectors \n"
"MCR p15, 0, R0, c12, c0, 0 \n"
"ISB \n"
这里通过设置VBAR(向量基地址寄存器)初始化向量基地址。
Vectors函数定义如下:
/*----------------------------------------------------------------------------
Exception / Interrupt Vector Table
*----------------------------------------------------------------------------*/
void Vectors(void) {
__asm__ volatile(
".align 7 \n"
"LDR PC, =Reset_Handler \n"
"LDR PC, =Undef_Handler \n"
"LDR PC, =SVC_Handler \n"
"LDR PC, =PAbt_Handler \n"
"LDR PC, =DAbt_Handler \n"
"LDR PC, =Rsvd_Handler \n"
"LDR PC, =IRQ_Handler \n"
"LDR PC, =FIQ_Handler \n"
);
}
编译之后的地址如下:
正好和Cortex-A7中定义的异常/中断偏移地址相对应,当发生IRQ中断时,PC指针便移动到相对于中断向量基地址+0x18的位置,然后执行语句“LDR PC, =IRQ_Handler”,将IRQ_Handler函数的地址放入PC指针,便开始执行IRQ_Handler函数。
需要注意的是,Reset_Handler函数被定义在代码段最开始的位置,这样当从BOOT跳转到APP或者stm32内部bootloader跳转时,只需要将PC指针指向代码段首地址便可以实现跳转。
注:0x2ffe0000是代码段首地址。
1.2 IRQ_Handler函数处理中断流程
IRQ_Handler函数如下:
void __attribute__((interrupt("IRQ"))) IRQ_Handler(void)
{
#elif defined(__ICCARM__)
__irq __arm void IRQ_Handler(void)
{
#endif
uint32_t ItId;
IRQHandler_t handler;
while (1U)
{
/* Get highest pending Interrupt Id from GIC driver*/
ItId = (uint32_t)IRQ_GetActiveIRQ();
if (ItId <= GIC_HIGHEST_INTERRUPT_VALUE) /* Highest value of GIC Valid Interrupt */
{
/* Check validity of IRQ */
if (ItId >= (uint32_t)MAX_IRQ_n)
{
SystemInit_IRQ_ErrorHandler();
}
else
{
/* Find appropriate IRQ Handler (Require registration before!) */
handler = IRQ_GetHandler((IRQn_ID_t)ItId);
if (handler != NULL)
{
/* Call IRQ Handler */
handler();
}
else
{
/* Un register Handler , error ! */
SystemInit_IRQ_ErrorHandler();
}
}
/* End Acknowledge interrupt */
IRQ_EndOfInterrupt((IRQn_ID_t)ItId);
}
else
{
/* Normal case: whenever there is no more pending IRQ , IAR returns ACKNOWLEDGE special IRQ value */
if (ItId == GIC_ACKNOWLEDGE_RESPONSE)
{
break;
}
/* Spurious IRQ Value (1022) ... */
else
{
SystemInit_IRQ_ErrorHandler();
}
}
}
}
执行步骤如下:
(1)获取激活/挂起的最高优先级中断ID
(2)查找中断ID对应的中断服务函数
(3)跳转到中断服务函数执行中断服务
(4)发送中断处理结束信号,本次中断处理完毕
(5)继续步骤(1)直到中断全部处理完毕
从以上步骤可以看到一个和Corex-M处理中断明显不同的地方,那就是高优先级中断已经无法抢占低优先级中断,这个在后面的实验中可以看到。
2 总结
(1)基于Cortex-A7的STM32MP135的IRQ不支持高优先级中断抢占低优先级中断执行