某些特定场景下,需要自定义中断向量表,同时又维持 SDK 现有的功能,比如, 响应未开放的中断(如 SysTick)、精确统计中断处理的开销等等, 此时可以考虑覆写 platform 的中断向量表。

下面介绍具体的步骤。

1. 获取芯片的最大中断号

找到 platform_irq_callback_type_t 里的最末一个中断名称,通过 platform_read_info 转换为底层中断号(记为 N)。或者直接取一个足够大的值,如 N = 64。

2. 定义完整的中断向量表

打开初始化文件,修改 __Vectors 的定义:

__Vectors       DCD     0                 ; Top of Stack
        DCD     Reset_Handler             ; Reset Handler
        DCD     Override_Handler          ; NMI Handler
        DCD     Override_Handler          ; Hard Fault Handler
        DCD     Override_Handler          ; MPU Fault Handler
        DCD     Override_Handler          ; Bus Fault Handler
        DCD     Override_Handler          ; Usage Fault Handler
        DCD     0                         ; Reserved
        DCD     0                         ; Reserved
        DCD     0                         ; Reserved
        DCD     0                         ; Reserved
        DCD     Override_Handler          ; SVCall Handler
        DCD     Override_Handler          ; Debug Monitor Handler
        DCD     0                         ; Reserved
        DCD     Override_Handler          ; PendSV Handler
        DCD     Override_Handler          ; SysTick Handler

        ; 重复 N 次
        DCD Override_Handler
        ....
        DCD Override_Handler

__Vectors_End

3. 用汇编实现 Override_Handler

Override_Handler PROC
                IMPORT UserHandler

                ; int irq = SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk;
                LDR     r0, =0xE000ED04
                LDR     r1, [r0]
                BFC     r1,#9,#23

                ; if (irq > 15) call UserHandler; else call default
                CMP     r1, #0x0F
                BGT     CALL_USER_ISR

                ; TODO: 响应 Exception

                ; 继续调用 platform 内的 Exception 处理函数
                ; f_irq_handler isr = (f_irq_handler)io_read(PLATFORM_NVIC_VECT + irq * 4);
                ; 以 ING916xx 为例,platform 内中断向量表起始位置为 0x02002000
                LDR     r0, =0x02002000
                LDR     r0, [r0, r1, LSL #2]
                BX      r0

CALL_USER_ISR
                MOV     r0, r1
                LDR     r1, =UserHandler
                BX      r1

				ENDP

其中 PLATFORM_NVIC_VECT 可通过 platform_get_vector_table_address() 获取。

4. 实现 UserHandler

void UserHandler(int irq)
{
    typedef void (*f_irq_handler)(void);

    f_irq_handler isr = (f_irq_handler)io_read(PLATFORM_NVIC_VECT + irq * 4);

    // TODO: 实现所需功能

    // 继续调用 platform 内的 ISR
    isr();
}

4. 替换中断向量表

在需要的位置(比如 app_main)里设置中断向量表:

int app_main()
{
    extern uint32_t __Vectors;
    SCB->VTOR = (uint32_t)&__Vectors;

    //....
}