最近使用了一款Cortex-M0內核的芯片STM32F030CC,發現它中斷向量表的重映射方法與STM32F10x系列的有所區別,在這里記錄與分享一下。
由於需要通過IAP進行固件升級,所以芯片的FLASH里面要燒錄兩份代碼:一個Boot loader, 一個用戶應用程序。理所當然的,在用戶應用程序中,必須得重新映射中斷向量表。
可是在ST提供的固件庫里,我卻沒有發現類似於stm32f10x固件庫中的void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset)接口。
瀏覽了一下Cortex-M0的Programming manual,原來M0並沒有SCB->VTOR這個寄存器,難怪ST的庫里沒有提供NVIC_SetVectorTable這個接口。
這下要怎么辦?在網絡上搜索了一下,受到網友findaway123這篇文章的啟發,我在STM32F030CC的Reference manual中找到以下說明:
Physical remap
Once the boot mode is selected, the application software can modify the memory accessible in the code area.This modification is performed by programming the MEM_MODE bits in the SYSCFG configuration register 1 (SYSCFG_CFGR1). Unlike Cortex® M3 and M4, the M0 CPU does not support the vector table relocation. For application code which is located in a different address than 0x0800 0000, some additional code must be added in order to be able to serve the application interrupts. A solution will be to relocate by software the vector table to the internal SRAM:
• Copy the vector table from the Flash (mapped at the base of the application load address) to the base address of the SRAM at 0x2000 0000.
• Remap SRAM at address 0x0000 0000, using SYSCFG configuration register 1.
• Then once an interrupt occurs, the Cortex®-M0 processor will fetch the interrupt handler start address from the relocated vector table in SRAM, then it will jump to execute the interrupt handler located in the Flash.
This operation should be done at the initialization phase of the application. Please refer to AN4065 and attached IAP code from www.st.com for more details.
OK,解決方法找到了!
在用戶應用程序中,按照以上方法,添加以下兩行代碼:
memcpy((void*)0x20000000, (void*)0x08004000, VECTOR_SIZE); SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM);
其中,0x2000 0000是SRAM的起始地址,這個不需要改動。
而之后的兩個參數需要根據實際情況作出修改。0x0800 4000是應用程序的起址地址,從這里開始的VECTOR_SIZE字節,存放是的應用程序的中斷向量表。VECTOR_SIZE是指中斷向量表的大小,具體多大可以在startup.s文件里計算得到。以下以startup_stm32f030.s為例作說明:
1 Stack_Size EQU 0x00000400 2 3 AREA STACK, NOINIT, READWRITE, ALIGN=3 4 Stack_Mem SPACE Stack_Size 5 __initial_sp 6 7 8 ; <h> Heap Configuration 9 ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> 10 ; </h> 11 12 Heap_Size EQU 0x00000200 13 14 AREA HEAP, NOINIT, READWRITE, ALIGN=3 15 __heap_base 16 Heap_Mem SPACE Heap_Size 17 __heap_limit 18 19 PRESERVE8 20 THUMB 21 22 23 ; Vector Table Mapped to Address 0 at Reset 24 AREA RESET, DATA, READONLY 25 EXPORT __Vectors 26 EXPORT __Vectors_End 27 EXPORT __Vectors_Size 28 29 __Vectors DCD __initial_sp ; Top of Stack 30 DCD Reset_Handler ; Reset Handler 31 DCD NMI_Handler ; NMI Handler 32 DCD HardFault_Handler ; Hard Fault Handler 33 DCD 0 ; Reserved 34 DCD 0 ; Reserved 35 DCD 0 ; Reserved 36 DCD 0 ; Reserved 37 DCD 0 ; Reserved 38 DCD 0 ; Reserved 39 DCD 0 ; Reserved 40 DCD SVC_Handler ; SVCall Handler 41 DCD 0 ; Reserved 42 DCD 0 ; Reserved 43 DCD PendSV_Handler ; PendSV Handler 44 DCD SysTick_Handler ; SysTick Handler 45 46 ; External Interrupts 47 DCD WWDG_IRQHandler ; Window Watchdog 48 DCD 0 ; Reserved 49 DCD RTC_IRQHandler ; RTC through EXTI Line 50 DCD FLASH_IRQHandler ; FLASH 51 DCD RCC_IRQHandler ; RCC 52 DCD EXTI0_1_IRQHandler ; EXTI Line 0 and 1 53 DCD EXTI2_3_IRQHandler ; EXTI Line 2 and 3 54 DCD EXTI4_15_IRQHandler ; EXTI Line 4 to 15 55 DCD 0 ; Reserved 56 DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 57 DCD DMA1_Channel2_3_IRQHandler ; DMA1 Channel 2 and Channel 3 58 DCD DMA1_Channel4_5_IRQHandler ; DMA1 Channel 4 and Channel 5 59 DCD ADC1_IRQHandler ; ADC1 60 DCD TIM1_BRK_UP_TRG_COM_IRQHandler ; TIM1 Break, Update, Trigger and Commutation 61 DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare 62 DCD 0 ; Reserved 63 DCD TIM3_IRQHandler ; TIM3 64 DCD 0 ; Reserved 65 DCD 0 ; Reserved 66 DCD TIM14_IRQHandler ; TIM14 67 DCD TIM15_IRQHandler ; TIM15 68 DCD TIM16_IRQHandler ; TIM16 69 DCD TIM17_IRQHandler ; TIM17 70 DCD I2C1_IRQHandler ; I2C1 71 DCD I2C2_IRQHandler ; I2C2 72 DCD SPI1_IRQHandler ; SPI1 73 DCD SPI2_IRQHandler ; SPI2 74 DCD USART1_IRQHandler ; USART1 75 DCD USART2_IRQHandler ; USART2 76 77 __Vectors_End 78 79 __Vectors_Size EQU __Vectors_End - __Vectors 80 81 AREA |.text|, CODE, READONLY 82 83 ; Reset handler routine 84 Reset_Handler PROC 85 EXPORT Reset_Handler [WEAK] 86 IMPORT __main 87 IMPORT SystemInit 88 89 90 91 LDR R0, =__initial_sp ; set stack pointer 92 MSR MSP, R0 93
我們只需關注其中的一小部分。從29行開始,直到75行,每一個DCD都代表一個中斷向量(所謂中斷向量,說得明白點,其實就是某個中斷服務程序的入口地址)。例如第74行的:
DCD USART1_IRQHandler ; USART1
這里的“USART1_IRQHandler"其實就是UART1中斷服務程序USART1_IRQHandler這個函數,同時,它也代表這個函數的入口地址。
以上代碼即定義了這樣一張表,這張表包括45個元素,每個元素是一個長度為4字節的地址。除了第一個地址是SP(堆棧指針)外,其它的地址都是某個中斷服務程序的入口地址。
那么,回到我們要解決的問題上來,之前memcpy函數中的第三個參數VECTOR_SIZE,針對本例,就應該是45*4=180(0xB4)個字節。
在執行完以上兩行代碼后,若發生中斷,CPU就會去SRAM(即0x2000 0000處)取中斷向量了,所以,以0x2000 0000作為起始地址之后的VECTOR_SIZE個字節就不能被改動了。為了達到這VECTOR_SIZE個字節不被修改的目的,如下兩種方法可以實現。
•在工程文件內修改SRAM的起始地址及長度,如下圖

•如果使用了分散加載文件,則在分散加載文件中修改SRAM的起始地址及長度也能達到目的。
至此,STM32F0系列Cortex-M0內核芯片中斷向量表重映射的問題已解決。
