stm32f0 cortex m0 做bootloader中断向量表重映射
环境
-
MDK5
-
STM32F042K6Tx
-
Cortex-M0
问题
使用了一款Cortex-M0内核的芯片STM32F042K6Tx,发现它中断向量表的重映射方法与STM32F10x不同
STM32F10x HAL库使用
NVIC_SetVectorTable 或 void SystemInit (void) { SCB->VTOR = 0x08003800; }
但是STM32F042K6Tx对应M0并没有SCB->VTOR
如何确定M0没有呢?
库中没有
Drivers\CMSIS\Include\core_cm0.h
typedef struct { __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ uint32_t RESERVED0; __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ uint32_t RESERVED1; __IOM uint32_t SHP[2U]; /*!< Offset: 0x01C (R/W) System Handlers Priority Registers. [0] is RESERVED */ __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ } SCB_Type;
M0手册中没有
https://www.st.com/resource/en/reference_manual/DM00091010-.pdf
STM32F0X2参考手册中
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.
解决方法(把中断向量表拷贝到sram)
代码添加位置
可以在app也可以在bootloader但是要保证在boot关总中断后app打开中断之前
HAL库
memcpy((void *)0x20000000, (void *)0x08002800, VECTOR_SIZE); LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SRAM);
标准库
memcpy((void*)0x20000000, (void*)0x08002800, VECTOR_SIZE); /*/Libraries/STM32F0xx_StdPeriph_Driver/src/stm32f0xx_syscfg.c*/ SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM); /*0x2000 0000是SRAM的起始地址*/ /*0x08002800是应用程序的起址地址*/ /*VECTOR_SIZE是指中断向量表的大小*/
本次使用HAL库
bootloader
#define FLASH_START 0x08000000 #define IAP_SIZE (10*1024) #define ApplicationAddress (FLASH_START+IAP_SIZE) //0x08002800 void JumpToApp(void) { /* Check if valid stack address (RAM address) then jump to user application */ if (((* (volatile unsigned long*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000) { HAL_NVIC_DisableIRQ(USART1_IRQn); HAL_NVIC_DisableIRQ(EXTI4_15_IRQn); __disable_irq(); /* Jump to user application */ JumpAddress = *(volatile unsigned long*) (ApplicationAddress + 4); Jump_To_Application = (pFunction) JumpAddress; // memcpy((void *)RAM_START_ADDR, (void *)ApplicationAddress, VECTOR_SIZE); // LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SRAM); /* Initialize user application's Stack Pointer */ __set_MSP(*(volatile unsigned long*) ApplicationAddress); //NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x08003800); Jump_To_Application(); } }
APP
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ memcpy((void *)RAM_START_ADDR, (void *)ApplicationAddress, 0xc0); LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SRAM); /* USER CODE END Init */ __enable_irq();//使能中断 /* Configure the system clock */ SystemClock_Config(); }
如何确定VECTOR_SIZE大小
MDK-ARM\startup_stm32f042x6.s
Stack_Size EQU 0x400 AREA STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem SPACE Stack_Size __initial_sp ; <h> Heap Configuration ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> ; </h> Heap_Size EQU 0x200 AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base Heap_Mem SPACE Heap_Size __heap_limit PRESERVE8 THUMB ; Vector Table Mapped to Address 0 at Reset AREA RESET, DATA, READONLY EXPORT __Vectors EXPORT __Vectors_End EXPORT __Vectors_Size __Vectors DCD __initial_sp ; Top of Stack DCD Reset_Handler ; Reset Handler DCD NMI_Handler ; NMI Handler DCD HardFault_Handler ; Hard Fault Handler DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD SVC_Handler ; SVCall Handler DCD 0 ; Reserved DCD 0 ; Reserved DCD PendSV_Handler ; PendSV Handler DCD SysTick_Handler ; SysTick Handler ; External Interrupts DCD WWDG_IRQHandler ; Window Watchdog DCD PVD_VDDIO2_IRQHandler ; PVD through EXTI Line detect DCD RTC_IRQHandler ; RTC through EXTI Line DCD FLASH_IRQHandler ; FLASH DCD RCC_CRS_IRQHandler ; RCC and CRS DCD EXTI0_1_IRQHandler ; EXTI Line 0 and 1 DCD EXTI2_3_IRQHandler ; EXTI Line 2 and 3 DCD EXTI4_15_IRQHandler ; EXTI Line 4 to 15 DCD TSC_IRQHandler ; TS DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 DCD DMA1_Channel2_3_IRQHandler ; DMA1 Channel 2 and Channel 3 DCD DMA1_Channel4_5_IRQHandler ; DMA1 Channel 4 and Channel 5 DCD ADC1_IRQHandler ; ADC1 DCD TIM1_BRK_UP_TRG_COM_IRQHandler ; TIM1 Break, Update, Trigger and Commutation DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare DCD TIM2_IRQHandler ; TIM2 DCD TIM3_IRQHandler ; TIM3 DCD 0 ; Reserved DCD 0 ; Reserved DCD TIM14_IRQHandler ; TIM14 DCD 0 ; Reserved DCD TIM16_IRQHandler ; TIM16 DCD TIM17_IRQHandler ; TIM17 DCD I2C1_IRQHandler ; I2C1 DCD 0 ; Reserved DCD SPI1_IRQHandler ; SPI1 DCD SPI2_IRQHandler ; SPI2 DCD USART1_IRQHandler ; USART1 DCD USART2_IRQHandler ; USART2 DCD 0 ; Reserved DCD CEC_CAN_IRQHandler ; CEC and CAN DCD USB_IRQHandler ; USB __Vectors_End __Vectors_Size EQU __Vectors_End - __Vectors AREA |.text|, CODE, READONLY ; Reset handler routine Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit LDR R0, =__initial_sp ; set stack pointer MSR MSP, R0 ;;Check if boot space corresponds to test memory LDR R0,=0x00000004 LDR R1, [R0] LSRS R1, R1, #24 LDR R2,=0x1F CMP R1, R2 BNE ApplicationStart ;; SYSCFG clock enable LDR R0,=0x40021018 LDR R1,=0x00000001 STR R1, [R0] ;; Set CFGR1 register with flash memory remap at address 0 LDR R0,=0x40010000 LDR R1,=0x00000000 STR R1, [R0] ApplicationStart LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP ; Dummy Exception Handlers (infinite loops which can be modified) NMI_Handler PROC EXPORT NMI_Handler [WEAK] B . ENDP HardFault_Handler\ PROC EXPORT HardFault_Handler [WEAK] B . ENDP SVC_Handler PROC EXPORT SVC_Handler [WEAK] B . ENDP PendSV_Handler PROC EXPORT PendSV_Handler [WEAK] B . ENDP SysTick_Handler PROC EXPORT SysTick_Handler [WEAK] B . ENDP Default_Handler PROC EXPORT WWDG_IRQHandler [WEAK] EXPORT PVD_VDDIO2_IRQHandler [WEAK] EXPORT RTC_IRQHandler [WEAK] EXPORT FLASH_IRQHandler [WEAK] EXPORT RCC_CRS_IRQHandler [WEAK] EXPORT EXTI0_1_IRQHandler [WEAK] EXPORT EXTI2_3_IRQHandler [WEAK] EXPORT EXTI4_15_IRQHandler [WEAK] EXPORT TSC_IRQHandler [WEAK] EXPORT DMA1_Channel1_IRQHandler [WEAK] EXPORT DMA1_Channel2_3_IRQHandler [WEAK] EXPORT DMA1_Channel4_5_IRQHandler [WEAK] EXPORT ADC1_IRQHandler [WEAK] EXPORT TIM1_BRK_UP_TRG_COM_IRQHandler [WEAK] EXPORT TIM1_CC_IRQHandler [WEAK] EXPORT TIM2_IRQHandler [WEAK] EXPORT TIM3_IRQHandler [WEAK] EXPORT TIM14_IRQHandler [WEAK] EXPORT TIM16_IRQHandler [WEAK] EXPORT TIM17_IRQHandler [WEAK] EXPORT I2C1_IRQHandler [WEAK] EXPORT SPI1_IRQHandler [WEAK] EXPORT SPI2_IRQHandler [WEAK] EXPORT USART1_IRQHandler [WEAK] EXPORT USART2_IRQHandler [WEAK] EXPORT CEC_CAN_IRQHandler [WEAK] EXPORT USB_IRQHandler [WEAK] WWDG_IRQHandler PVD_VDDIO2_IRQHandler RTC_IRQHandler FLASH_IRQHandler RCC_CRS_IRQHandler EXTI0_1_IRQHandler EXTI2_3_IRQHandler EXTI4_15_IRQHandler TSC_IRQHandler DMA1_Channel1_IRQHandler DMA1_Channel2_3_IRQHandler DMA1_Channel4_5_IRQHandler ADC1_IRQHandler TIM1_BRK_UP_TRG_COM_IRQHandler TIM1_CC_IRQHandler TIM2_IRQHandler TIM3_IRQHandler TIM14_IRQHandler TIM16_IRQHandler TIM17_IRQHandler I2C1_IRQHandler SPI1_IRQHandler SPI2_IRQHandler USART1_IRQHandler USART2_IRQHandler CEC_CAN_IRQHandler USB_IRQHandler B . ENDP ALIGN ;******************************************************************************* ; User Stack and Heap initialization ;******************************************************************************* IF :DEF:__MICROLIB EXPORT __initial_sp EXPORT __heap_base EXPORT __heap_limit ELSE IMPORT __use_two_region_memory EXPORT __user_initial_stackheap __user_initial_stackheap LDR R0, = Heap_Mem LDR R1, =(Stack_Mem + Stack_Size) LDR R2, = (Heap_Mem + Heap_Size) LDR R3, = Stack_Mem BX LR ALIGN ENDIF END ;************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE*****
29行Vectors---到-->80行Vectors_End 每一个DCD都代表一个中断向量 ( 中断服务程序的入口地址 )
例如:
DCD USART1_IRQHandler ; USART1
这里的“USART1_IRQHandler"其实就是UART1中断服务程序USART1_IRQHandler这个函数,同时,它也代表这个函数的入口地址。
因为DCD对应的表示一个表中的元素,每个元素是一个长度为4字节的地址。除了第一个地址是SP(堆栈指针)外,其它的地址都是某个中断服务程序的入口地址。
使用Notepad计数发现有48个
所以:VECTOR_SIZE = 48*4 = 192 = 0xC0(字节)
使用WinHex查看位置(0xc0)
APP中RAM偏移
在执行完以上两行代码后,若发生中断,CPU就会去SRAM(即0x2000 0000处)取中断向量了,所以,以0x2000 0000作为起始地址之后的VECTOR_SIZE个字节就不能被改动了。为了达到这VECTOR_SIZE个字节不被修改的目的,如下两种方法可以实现。
总结
boot关闭中断开关---->跳转到app的bin地址+4位置(也就是rest)---->进入app以后把中断向量表拷贝到内存中,并使用LL_SYSCFG_SetRemapMemory告诉芯片到时候去内存0x20000000中找找中断--->打开中断开关