要实现STM32的IAP(在应用编程),需要分别建立bootloader和app工程。这里的bootloader程序是用户自己实现的,与STM32内置的bootloader无关。例如,我们可以把bootloader程序放在0x8000000处,把app程序放在0x80001000处,上电时首先进入bootloader程序执行,检查是否需要更新app,执行完毕后再跳转到app程序处执行。
为了实现bootLoader到app的跳转,首先需要弄清楚STM32程序执行的流程。
STM32 flash程序执行通常由地址0x8000000开始。首4个字节存放主栈顶指针MSP地址,其值是由编译器编译后决定的。比如用户用到了多少全局变量以及静态变量,在RAM中就需要预留出相应的空间,从而影响栈顶指针的位置,而局部变量在RAM中的存取就需要栈顶指针和栈大小来约束。上电时STM32的MSP寄存器即被初始化。
__Vectors DCD __initial_sp ; Top of StackDCD Reset_Handler ; Reset HandlerDCD NMI_Handler ; NMI HandlerDCD HardFault_Handler ; Hard Fault HandlerDCD MemManage_Handler ; MPU Fault HandlerDCD BusFault_Handler ; Bus Fault HandlerDCD UsageFault_Handler ; Usage Fault HandlerDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD SVC_Handler ; SVCall HandlerDCD DebugMon_Handler ; Debug Monitor HandlerDCD 0 ; ReservedDCD PendSV_Handler ; PendSV HandlerDCD SysTick_Handler ; SysTick Handler
在栈顶指针地址后面紧接着是以Reset_Handler
开始的中断向量表,该表一一指向了对应的中断函数地址。
上电或复位后,STM32的PC被赋予0x8000000+4的值,也就是跳转到中断向量表中Reset_Handler
向量指向的中断程序
Reset_Handler
所指向的程序如下,可以看出此处执行了SystemInit
和main
两个函数,也就是说通过复位中断后程序最终流向了main
函数的while(1)
死循环,从而执行用户代码。
Reset_Handler PROCEXPORT Reset_Handler [WEAK]IMPORT SystemInitIMPORT __mainLDR R0, =SystemInitBLX R0LDR R0, =__mainBX R0ENDP
在执行main函数的过程中,每当有中断发生时,程序就会跳转到中断向量表中搜索对应的中断程序,然后跳转至中断函数处执行,最后返回main函数继续执行。
通过上面的分析可知,STM32在上电或复位后把0x8000000
处的值赋给了MSP寄存器来初始化主堆栈指针地址,把0x8000000+4
处的值赋给了PC寄存器使程序进入复位中断从而顺利执行。
所以,要实现bootloader到app的跳转,需要我们需要改变MSP和PC这两个寄存器。bootloader中跳转代码的实现如下:
#define __IO volatile /*!< defines 'read / write' permissions */
#define ApplicationAddress 0x8003000
pFunction Jump_To_Application;
uint32_t JumpAddress;
typedef void (*pFunction)(void);/*** @brief Set the Main Stack Pointer** @param topOfMainStack Main Stack Pointer** Assign the value mainStackPointer to the MSP * (main stack pointer) Cortex processor register*/
__ASM void __set_MSP(uint32_t mainStackPointer)
{msr msp, r0bx lr
}
void Jump2App(void)
{/* Test if user code is programmed starting from address "ApplicationAddress" */if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000){ /* Jump to user application */JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);Jump_To_Application = (pFunction) JumpAddress;/* Initialize user application's Stack Pointer */__set_MSP(*(__IO uint32_t*) ApplicationAddress);Jump_To_Application();}
}
ApplicationAddress
是用户定义的app程序首地址,如0x8000000+0x1000。通过if条件来判断该地址中的值是否在指定的RAM空间范围内,如果是则从0x8000000+0x1000+4地址(即app程序的Reset_Handler
向量)中取出要跳转的地址JumpAddress
,并通过空函数指针Jump_To_Application
指向该地址。
完成好准备工作后,接下来就是最重要的一步。通过__set_MSP()
函数设置MSP寄存器,通过调用空函数指针Jump_To_Application
使得PC跳转至地址JumpAddress
。如此一来,MSP和PC的值由bootloader空间更新为app空间,转而运行app部分的代码。
但是,配置工作还没有结束。上面提到了当执行main函数发生中断时,会跳转至中断向量表,由于我们没有修改中断向量表,中断向量表指向的仍是bootloader空间的中断函数,所以最后中断返回bootloader的main函数执行。而我们希望映射的中断向量表是app空间的中断向量表,因此,需要在app的main函数开头添加重映射语句:
SCB->VTOR = ApplicationAddress;
1.要实现IAP功能,需要建立bootloader和app两个工程,bootloader地址从0x8000000开始,而app地址由用户指定(不能与bootloader冲突),编译好工程后分别下载到STM32;
2.在bootloader中设置MSP和PC以跳转至app执行,进入app后首先要重映射中断向量表,使得中断执行app空间的中断函数。
上一篇:几种输入输出数据的方法
下一篇:【C++进阶】哈希表的实现