必看链接:试图搞懂MDK程序下载到flash(二)–分散加载文件scatter
参考链接:STM32F10x单片机Flash写操作导致中断不响应问题
最近本人在写STM32F0系列的FLASH写操作的代码时突然发现一个问题,MCU往内部FLASH写入数据的过程中,中断程序是无法响应的,若此时发生中断,MCU是不能跳转到相应的中断程序上去的。
官方手册《STM32F10xxx闪存编程手册.en》Page11描述如下:
MCU在执行内部flash写操作时,又必须响应中断,这要怎么办?
其实MCU执行的代码一般是放在FLASH地址上,MCU运行代码又必须向FLASH读取代码,但是向内部FLASH写数据的过程中,就不能继续读取FLASH中的程序,所以我们可以把中断程序(里面中断调用的所有)函数都放在RAM中,并把中断向量表(FLASH:0x08000000)映射到RAM(0x02000000),若在FLASH写数据期间发生中断,MCU会从RAM运行中断程序,从而保证中断程序正常执行。现在以STM32F0系列的例程讲解:
相关链接:STM32中断向量表的位置,重定向
一、映射中断向量表至RAM地址0x02000000
注:F0和F1系列MCU,映射中断向量表到RAM地址0x0200000方法不同,但是F1的方法好像不适用于F0
void IAP_Set()
{
u32 i=0;
/* Relocate by software the vector table to the internal SRAM at 0x20000000 ***/
/* Copy the vector table from the Flash (mapped at the base of the application
load address 0x08000000) to the base address of the SRAM at 0x20000000. */
for(i = 0; i < 48; i++)//先将"应用程序"的中断向量表拷贝到sram中
{
*((uint32_t*)(0x20000000 + (i << 2)))=*(__IO uint32_t*)(0x08000000 + (i<<2));
}
/* Enable the SYSCFG peripheral clock*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
/* Remap SRAM at 0x00000000 */
/*然后再配置SYSCFG寄存器的最低两位,将sram映射到地址0,这样,在发生中断后,cpu从地址0取
中断向量,这样实际上就是从SRAM中取的中断向量,而SRAM中的中断向量表又是之前从"应用程序"
的中断向量表拷贝过来的,所以,最终其实是取的“应用程序”的中断向量表*/
SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM);
}
二、将RAM的起始地址偏移0xC0个字节,KEIL工程设置如下:
三、将中断程序用到的所有相关代码、包括用到的库函数全部编译进RAM区域
参考链接:STM32 进阶教程 11 - RAM中运行程序
这里我采用上述链接方法四方案,按照如下步骤配置KEIL工程(里面的文件是我新建文件所写,也可以改原本工程的文件)
按照如下灵活编辑:
LR_IROM1 0x08000000 0x00010000 { ; load region size_region
ER_IROM1 0x08000000 0x00010000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x200000C0 0x00002000 { ; RW data
*.o (RESET_ram, +First)
*.o (RAMCODE)
startup_stm32f030_inram.o(+RO)
SMG.o(+RO +RW)
stm32f0xx_it.o(+RO +RW)
stm32f0xx_flash.o(+RO +RW)
stmflash.o(+RO +RW)
system_stm32f0xx.o(.data, +ZI +RW)
.ANY (+RW +ZI)
}
}
注:因为由用到了FLASH,所以要把响应的固件库也要包含进去,以及存放中断函数的it.c文件和所有调用到的函数
四、新建并修改启动文件,并添加到工程内
PRESERVE8
THUMB
; Vector Table Mapped to Address 0 at Reset
AREA RESET_ram, DATA, READONLY
EXPORT __Vectors_ram
EXPORT __Vectors_End_ram
EXPORT __Vectors_Size_ram
__Vectors_ram DCD 0 ; Top of Stack
DCD 0 ; 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 0 ; Reserved
DCD RTC_IRQHandler ; RTC through EXTI Line
DCD FLASH_IRQHandler ; FLASH
DCD RCC_IRQHandler ; RCC
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 0 ; Reserved
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 0 ; Reserved
DCD TIM3_IRQHandler ; TIM3
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD TIM14_IRQHandler ; TIM14
DCD TIM15_IRQHandler ; TIM15
DCD TIM16_IRQHandler ; TIM16
DCD TIM17_IRQHandler ; TIM17
DCD I2C1_IRQHandler ; I2C1
DCD I2C2_IRQHandler ; I2C2
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
__Vectors_End_ram
__Vectors_Size_ram EQU __Vectors_End_ram - __Vectors_ram
AREA |.text|, CODE, READONLY
IMPORT NMI_Handler ;NMI Handler
IMPORT HardFault_Handler ;Hard Fault Handler
IMPORT SVC_Handler ;SVCall Handler
IMPORT PendSV_Handler ;PendSV Handler
IMPORT SysTick_Handler ;SysTick Handler
IMPORT WWDG_IRQHandler [WEAK]
IMPORT RTC_IRQHandler [WEAK]
IMPORT FLASH_IRQHandler [WEAK]
IMPORT RCC_IRQHandler [WEAK]
IMPORT EXTI0_1_IRQHandler [WEAK]
IMPORT EXTI2_3_IRQHandler [WEAK]
IMPORT EXTI4_15_IRQHandler [WEAK]
IMPORT DMA1_Channel1_IRQHandler [WEAK]
IMPORT DMA1_Channel2_3_IRQHandler [WEAK]
IMPORT DMA1_Channel4_5_IRQHandler [WEAK]
IMPORT ADC1_IRQHandler [WEAK]
IMPORT TIM1_BRK_UP_TRG_COM_IRQHandler [WEAK]
IMPORT TIM1_CC_IRQHandler [WEAK]
IMPORT TIM3_IRQHandler [WEAK]
IMPORT TIM14_IRQHandler [WEAK]
IMPORT TIM15_IRQHandler [WEAK]
IMPORT TIM16_IRQHandler [WEAK]
IMPORT TIM17_IRQHandler [WEAK]
IMPORT I2C1_IRQHandler [WEAK]
IMPORT I2C2_IRQHandler [WEAK]
IMPORT SPI1_IRQHandler [WEAK]
IMPORT SPI2_IRQHandler [WEAK]
IMPORT USART1_IRQHandler [WEAK]
IMPORT USART2_IRQHandler [WEAK]
END
编译成功后,查看“工程名.map文件”(文件夹内搜索就行),可以看到相关的函数都已经编译到RAM地址中了
一下scatter文件是我个人写的,放出来提供给大家参考参考:
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
; ";"为注释
LR_IROM1 0x08002000 0x00010000 { ; 代码加载区,表示从此处开始执行
ER_IROM1 0x08002000 0x00010000 { ; 代码执行区要与代码加载区地址一致
*.o (RESET, +First) ; 将中断向量表放在开头
*(InRoot$$Sections) ; 根据各种地址生成启动代码,实现对映像(程序)的加载
.ANY (+RO) ; 只读变量、代码本身放在ROM区域
}
RW_IRAM1 0x200000C0 0x00000600 { ; 可读可写区域,空间大小为0x00000600
.ANY (+RW) ; 可读可写变量存放在0x200000C0开头处,0x20000000-0x200000C0存放的是拷贝至RAM里面的中断向量表
}
RAM_CODE 0x20000600 0x00002000 { ; 需要运行在RAM的程序、初始化为0(.ZI)变量的区域
SMG.o ; SMG.c文件编译成SMG.o文件,里面的函数防止在该段区域
timer.o ; 同上
stm32f0xx_it.o
stm32f0xx_flash.o
stmflash.o
system_stm32f0xx.o(.data, +ZI)
.ANY (+ZI)
}
}
; 上面分区域只是为了更好的观察.map文件
以下为生成的.map文件的局部截图: