好记性不如铅笔头

ARM, STM32, 操作系统

STM32CubeMX简单使用笔记:程序跳转

因为项目需要,最近重新拾起来STM32,正好趁着这个机会好好的梳理下遇到的知识细节。

一般我们使用STM32发布程序时,都需要考虑增加IAP功能。那么就要将程序分为bootloader和APP两个独立的程序,这里笔记下实现思路和跳转代码。
这里以STM32F407ZGT6为例,该芯片有1MB Flash大小,我们规划分区如下:

		|   Bootloader    |       APP         |    Reserved     |
		|  128K(0x20000)  |   640K(0xA0000)   |   256K(0x40000) |
		|                 |                   |                 |
     0x8000000         0x8020000           0x80C0000         0x8100000

我们首先修改Bootloader相关配置,主要为代码宏和链接脚本,如下图:

编译之后可以发现分区信息已经修改了,如下图:

然后同样修改APP的配置,如下图:

跳转代码如下:

void cortexMx_jumpto_app(uintptr_t start_address)
{
	/*
	 1  屏蔽中断响应,并且清除Pending的中断
	 2  清除所有中断使能
	 3  重设中断向量寄存器偏移(APP中会在SystemInit函数中重设)
	 4  重设MSP
	 5  跳转到指定位置执行
	 */

    uintptr_t nvic_addr  = start_address;
    uintptr_t stack_addr = *( (uintptr_t *)(nvic_addr) );
    uintptr_t pc_addr    = *( (uintptr_t *)(nvic_addr + sizeof(uintptr_t)) );

    /* 禁用外部中断 */
	__disable_irq();

	/* 停止ARM cortexM systick定时器中断 */
	SysTick->CTRL = 0x0;

	/* 停止STM32HAL库时钟的时基  */
	HAL_SuspendTick();

	for (uint32_t index = 0; index < sizeof(NVIC->ICPR) / sizeof(NVIC->ICPR[0]); ++index)
	{
		NVIC->ICER[index] = 0xFFFFFFFF;
		NVIC->ICPR[index] = 0xFFFFFFFF;
	}

    __set_MSP(stack_addr);
    __set_CONTROL(0x0);

    __DSB();
    __ISB();
//    __enable_irq();
    ( (void (*)(void))pc_addr )();
}

有两个需要注意的地方:
1 跳转代码中没有重设NVIC偏移,也没有重新放开外部中断,这里的做法是考虑到APP中会重新设置NVIC偏移,同时会开启中断。
2 在不考虑Flash跨程序修改时,可以修改FLASH_END宏防止程序误写了不该写的区段,但是如果业务上有Flash跨程序修改,比如IAP设计上Bootloader需要修改APP分区的Flash,那么FLASH_END就需要放大到所有可写地址,否则会有写入失败问题。原因很简单,STM32HAL库在Flash写入前对Flash地址做了范围校验:

#define IS_FLASH_ADDRESS(ADDRESS) ((((ADDRESS) >= FLASH_BASE) && ((ADDRESS) <= FLASH_END)) || \
                                   (((ADDRESS) >= FLASH_OTP_BASE) && ((ADDRESS) <= FLASH_OTP_END)))

 

Leave a Reply

4 × 2 =

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据