由Cortex-M3权威指南可以知道,中断向量表可以实现重定位,因此尝试将中断向量表放在RAM区,从而实现动态调整中断处理流程(ISR)。
首先自己定义主堆栈区域:
点击(此处)折叠或打开
- #define MSP_STACK_SIZE (0x400)
- uint32_t MSPStack[1024] @ ".noinit";
- __vector_table
- DCD MSPStack;sfe(CSTACK)
- DCD Reset_Handler
- DCD NMI_Handler
- DCD HardFault_Handler
- DCD MemManage_Handler
- DCD BusFault_Handler
- DCD UsageFault_Handler
由于Cortex-M3使用的是“向下生长的满栈”,堆栈指针SP指向最后一个被压入堆栈的32位数值,在下一次压栈时,SP先自减4,再存入新的数值。因此需要对自定义的堆栈进行初始化,将SP指向正确的位置。(在此之前尽量不要进行函数调用)
点击(此处)折叠或打开
- Reset_Handler
- LDR R0, =MSPStack
- MOV R1, #MSP_STACK_SIZE
- LSL R1, R1, #2 ;each element is 4 bytes
- ADD R0, R0, R1
- MSR MSP, R0
- LDR R0, =SystemInit
- BLX R0
- LDR R0, =__iar_program_start
- BX R0
这样SP就指向了正确的位置,并可以进行函数调用了(系统复位后,默认进入特权级的线程模式,参考下图);由下图可知,系统复位后默认使用的时MSP。
在进入main函数之后,通过修改VTOR寄存器,实现向量表的重定位;
点击(此处)折叠或打开
- NVIC_SetVectorTable(0x20000000, 0);
- void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset)
- {
- SCB->VTOR = NVIC_VectTab | (Offset & (uint32_t)0x1FFFFF80);
- }
0x20000000对应VTOR的第29位为1,故向量表位于RAM区,偏移为0表示位于RAM区的首地址。
接下来应该为该RAM区重建新的中断向量表了
点击(此处)折叠或打开
- typedef void (*NVIC_IRQ_HANDLE)();
- #pragma location=0x20000000 // #pragma location用于下面变量的绝对地址定位
- //__root 保证没有使用的函数或者变量也能够包含在目标代码中
- __root NVIC_IRQ_HANDLE handle[] =
- {
- (NVIC_IRQ_HANDLE)MSPStack,
- Reset_Handler,
- NMI_Handler,
- HardFault_Handler,
- MemManage_Handler,
- BusFault_Handler,
- UsageFault_Handler,
- //__vector_table_0x1c
- 0,
- 0,
- 0,
- 0,
- SVC_Handler,
- DebugMon_Handler,
- 0,
- PendSV_Handler,
- SysTick_Handler,
- DMA_IRQHandler , //0: DMA Interrupt
- GPIO_EVEN_IRQHandler , //1: GPIO_EVEN Interrupt
- TIMER0_IRQHandler , //2: TIMER0 Interrupt
- USART0_RX_IRQHandler , //3: USART0_RX Interrupt
- USART0_TX_IRQHandler , //4: USART0_TX Interrupt
- USB_IRQHandler , //5: USB Interrupt
- ACMP0_IRQHandler , //6: ACMP0 Interrupt
- ADC0_IRQHandler , //7: ADC0 Interrupt
- DAC0_IRQHandler , //8: DAC0 Interrupt
- I2C0_IRQHandler , //9: I2C0 Interrupt
- I2C1_IRQHandler , //10: I2C1 Interrupt
- GPIO_ODD_IRQHandler , //11: GPIO_ODD Interrupt
- TIMER1_IRQHandler , //12: TIMER1 Interrupt
- TIMER2_IRQHandler , //13: TIMER2 Interrupt
- TIMER3_IRQHandler , //14: TIMER3 Interrupt
- USART1_RX_IRQHandler , //15: USART1_RX Interrupt
- USART1_TX_IRQHandler , //16: USART1_TX Interrupt
- LESENSE_IRQHandler , //17: LESENSE Interrupt
- USART2_RX_IRQHandler , //18: USART2_RX Interrupt
- USART2_TX_IRQHandler , //19: USART2_TX Interrupt
- UART0_RX_IRQHandler , //20: UART0_RX Interrupt
- UART0_TX_IRQHandler , //21: UART0_TX Interrupt
- UART1_RX_IRQHandler , //22: UART1_RX Interrupt
- UART1_TX_IRQHandler , //23: UART1_TX Interrupt
- LEUART0_IRQHandler , //24: LEUART0 Interrupt
- LEUART1_IRQHandler , //25: LEUART1 Interrupt
- LETIMER0_IRQHandler , //26: LETIMER0 Interrupt
- PCNT0_IRQHandler , //27: PCNT0 Interrupt
- PCNT1_IRQHandler , //28: PCNT1 Interrupt
- PCNT2_IRQHandler , //29: PCNT2 Interrupt
- RTC_IRQHandler, //30: RTC Interrupt
- BURTC_IRQHandler , //31: BURTC Interrupt
- CMU_IRQHandler , //32: CMU Interrupt
- VCMP_IRQHandler , //33: VCMP Interrupt
- LCD_IRQHandler , //34: LCD Interrupt
- MSC_IRQHandler , //35: MSC Interrupt
- AES_IRQHandler , //36: AES Interrupt
- EBI_IRQHandler , //37: EBI Interrupt
- EMU_IRQHandler , //38: EMU Interrupt
- 0 , //39: Reserved Interrupt
- };
由于该handle中的中断处理函数偏移固定,故可以很轻松的重定义该处理函数;
由于RAM起始的0x100大小用于新的中断向量表,故在链接文件中需要做如下修改:
点击(此处)折叠或打开
- define symbol __ICFEDIT_region_RAM_start__ = 0x20000100;
- define symbol __ICFEDIT_region_RAM_end__ = (0x20000100+0x00008000-1 - 0x100);
注意事项:
复位后,对于所有优先级可编程的异常,其优先级都被初始化为0(最高)。同时为了确认具体使用了几位表示优先级,可以先往一个优先级寄存器中写入0xFF, 读出多少个1,就表示使用多少位表示优先级。
中断重定位后,新的中断向量表中的前4字节不起作用,不会影响当前的MSP。