某个项目需要用到bootloader更新app到sdram内执行程序,不能选ARM9,领导又喜欢stm32,不差钱所以选了stm32f427这个芯片,最主要原因就是可以挂上sdram。
以前做过stm32F1系列的iap程序,基本就是bootloader跳转前关一下中断,app运行前设置一下中断向量表就ok了,这次换了427本以为也很简单,只不过是跳转位置从flash更改到sdram罢了,想想自己还是naive了一点,首先是按老套路设置,然后跳转立马死翘,debug半天无果,后来一顿百度谷歌翻芯片手册之后才发现要stm32f4要能在sdram运行用户程序,必须将sdram地址映射到0x90000000,因为芯片的默认sdram地址是不能执行指令的,所以修改一下跳转代码如下
void M3Jump( uint32_t dwJumpAddr )
{
volatile uint32_t JumpAddress;
SYSCFG->MEMRMP = SYSCFG->MEMRMP | 0x400; //SDRAM_BANK_ADDR = 0xD0000000 重映射后为 0x90000000 ,只有90000000的地址可以执行代码,所以必须remap
JumpAddress = *(vu32*) (0x90000000+4); //取入口地址
Jump_To_Application = (user_app) JumpAddress;
__set_MSP(*(vu32*) 0x90000000 ); //设置堆栈指针
Jump_To_Application(); //go !
}
(1)用户模式(usr , User Mode)。ARM处理器正常的程序执行状态。
(2)快速中断模式(fiq , Fast Interrupt Request Mode)。用于高速数据传输或通道处理。当触发快速中断时进入此模式。
(3)外部中断模式 ( irq , Interrupt Request Mode )。用于通用的中断处理。当触发外部中断时进入此模式。
(4)管理模式 ( svc , Supervisor Mode )。操作系统使用的保护模式。在系统复位或者执行软件中断指令SWI时进入。
(5)数据访问终止模式 ( abt , Abort Mode )。当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护。
(6)系统模式(sys , System Mode )。运行具有特权的操作系统任务。
(7)未定义指令中止模式 ( und , Undefined Mode )。当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真。
差不多就是上面这些意思,bootloader和app处于不同的运行模式,两种模式下的msp和psp是不一样的,因此直接由bootloader杀入app是会死翘的。所以我想了个笨办法,跳转前人为引发一个svc中断,然后在svc内强行切换系统模式,貌似强奸这种事情不太光彩,但是总算是有点眉目了,代码修改如下
void M3Jump( uint32_t dwJumpAddr )
{
volatile uint32_t JumpAddress;
__asm("SVC 0x0"); //这条指令很重要,引发一个svc中断,在里面切换cpu工作模式,从用户模式切换为特权模式,否则app跳转必死无疑
__asm("CPSID I"); //跳转前关中断
SYSCFG->MEMRMP = SYSCFG->MEMRMP | 0x400; //SDRAM_BANK_ADDR = 0xD0000000 重映射后为 0x90000000 ,只有90000000的地址可以执行代码,所以必须remap
JumpAddress = *(vu32*) (0x90000000+4);
Jump_To_Application = (user_app) JumpAddress;
__set_MSP(*(vu32*) 0x90000000 );
Jump_To_Application();
}
void SVC_Handler(void) //os系统执行在用户模式下,app跳转后要运行在特权模式才可以,因此用svc服务来切换用户模式到特权模式
{
__set_MSP(__get_PSP());//保存当前用户模式的PSP到MSP,因为svc中断返回后要使用MSP,如果没有这条指令,svc将找不到返回的路了,死机是必须的
__asm("MOV R14, #0xFFFFFFF9");//强行修改LR寄存器,让svc中断服务返回后使用MSP并且进入特权模式,这样app跳转后才能正常运行
}
修改跳转代码强行引发一个svc中断进入特权模式,只有在特权模式里面才能修改成其他模式,经过上面的修改后测试,debug查看各个寄存器数值,模式全部ok,尼玛还是不能运行啊,都快要怀疑人生了,没办法,一个一个下断点慢慢调试,最终断点下到remap语句后面的时候成功运行了,艹!原来remap也是需要执行时间的,remap没完成就jump也是会死的。最终jump函数修改如下
void M3Jump( uint32_t dwJumpAddr )
{
volatile uint32_t JumpAddress;
__asm("SVC 0x0"); //这条指令很重要,引发一个svc中断,在里面切换cpu工作模式,从用户模式切换为特权模式,否则app跳转必死无疑
__asm("CPSID I"); //跳转前关中断
SYSCFG->MEMRMP = SYSCFG->MEMRMP | 0x400; //SDRAM_BANK_ADDR = 0xD0000000 重映射后为 0x90000000 ,只有90000000的地址可以执行代码,所以必须remap
for(JumpAddress=0;JumpAddress<0xfffff;JumpAddress++); //等待映射完成才能读90000000地址,否则死翘
JumpAddress = *(vu32*) (0x90000000+4);
Jump_To_Application = (user_app) JumpAddress;
__set_MSP(*(vu32*) 0x90000000 );
Jump_To_Application();
}