STM32L073RZ (rev Z) IAP 跳转到引导加载程序(系统内存)
Posted
技术标签:
【中文标题】STM32L073RZ (rev Z) IAP 跳转到引导加载程序(系统内存)【英文标题】:STM32L073RZ (rev Z) IAP jump to bootloader (system memory) 【发布时间】:2017-06-20 15:02:42 【问题描述】:我使用的是 STM32L073RZ(Nucleo 64 板)。
我想在应用程序编程 (IAP) 中跳转到系统内存。
我的代码在 STM32L073 微控制器的版本 B 上工作,但在最新版本 Z 上失败。
我看了勘误表,没有给出细节,只是根据 BFB2 位固定在双启动机制到系统内存中的限制。
系统内存是否不再支持 IAP 跳转执行其代码(通过 USB 或 UART 刷新固件而不使用 BOOT0 引脚)?
该函数是我的主程序的第一行,它测试代码是否必须跳转到booloader:
void jumpBootLoader(void)
/* to do jump? */
if ( *((unsigned long *)0x20003FF0) == 0xDEADBEEF )
/* erase the label */
*((unsigned long *)0x20003FF0) = 0xCAFEFEED;
/* set stack pointer to the bootloader start address */
__set_MSP(*((uint32_t*)(0x1FF00000)));
/* system memory mapped at 0x00000000 */
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
/* jump to @bootloader + 4 */
((void (*)(void))(*((uint32_t*)(0x1FF00004))))();
我只要按下 BP1 按钮就调用这两行来触发复位 µC 后的跳转操作:
*((unsigned long *)0x20003FF0) = 0xDEADBEEF;
NVIC_SystemReset();
我使用 HSI 16Mhz 时钟源。
【问题讨论】:
如果您不向我们展示代码,很可能没有人可以帮助您... 【参考方案1】:解决方法是两次跳转到系统内存。 首先跳转到引导加载程序启动以初始化 RAM 中的数据,直到程序计数器将由 Dualbank 管理返回到 Flash。 第二跳转:跳转到Dualbank绕过地址
使用方法:用户必须先在Flash中初始化一个变量“Data_Address”(必须是偏移Flash扇区对齐地址),以区分第一次/第二次跳转。
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.PageAddress = Data_Address;
EraseInitStruct.NbPages = 1;
First_jump = *(__IO uint32_t *)(Data_Address);
if (First_jump == 0)
HAL_FLASH_Unlock();
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Data_Address, 0xAAAAAAAA);
HAL_FLASH_Lock();
/* Reinitialize the Stack pointer and jump to application address */
JumpAddress = *(__IO uint32_t *)(0x1FF00004);
if (First_jump != 0)
HAL_FLASH_Unlock();
HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);
HAL_FLASH_Lock();
/* Reinitialize the Stack pointer and jump to application address */
JumpAddress = (0x1FF00369);
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t *)(0x1FF00000));
Jump_To_Application();
【讨论】:
【参考方案2】:第一件重要的事情:您使用0x1FF0 0000
作为存储SP 的地址,这是正确的。然后使用0x1 FF00 0004
作为加载函数指针的地址。这是不正确的 - 一个零太多了。
请注意,如果您还使用 MSP 作为堆栈指针(您很可能是),那么使用 __set_MSP()
通常不是一个好主意。此函数的最新定义将“sp”标记为破坏寄存器,导致您的更改几乎立即恢复。顺便说一句,今天我正在做与您完全相同的事情,我发现了这个问题。在您的汇编清单中,您会看到 SP 在 msr msp, ...
指令之前保存到其他寄存器中,并在此之后立即恢复。
最后我是手动写的(STM32F4,所以地址不同):
constexpr uint32_t systemMemoryBase 0x1fff0000;
asm volatile
(
" msr msp, %[sp] \n"
" bx %[pc] \n"
:: [sp] "r" (*reinterpret_cast<const uint32_t*>(systemMemoryBase)),
[pc] "r" (*reinterpret_cast<const uint32_t*>(systemMemoryBase + 4))
);
顺便说一句 - 您无需为引导加载程序设置内存重新映射即可工作。
【讨论】:
对不起'0x1FF0 0 0004',我的代码复制/过去失败,我在帖子中更正了。它是 C++ 代码吗? 'constexpr' 和 'reinterpret_cast' 不能为我编译,我使用了 'const' 和一个简单的转换表达式 '(uint32_t*)' 代替。我在没有调用 __set_MSP() 的情况下测试了我的代码,它仍然有效... @hbevil - 是的,我发布的代码 sn-p 是 C++11,但您的更改也可以。至于您的其余评论 - “它仍然有效......”或者它仍然无法正常工作? (; 是的,它在 stm32l073 修订版 B 上“仍然有效”,并且在 rev Z 上“仍然无法工作”,尽管它是相同的代码......我看到引导加载程序版本不同,v4。 B 版为 0,Z 版为 4.1。 @hbevil - 即使跳转到引导加载程序是您在 main() 中执行的第一件事,但这并不意味着它是固件执行的第一件事。最有可能 - 如果您使用 HAL/SPL - RCC 中的时钟在 main() 之前初始化,执行引导加载程序的要求之一是在所有内容的“重置状态”下运行它,包括 RCC 时钟和 PLL 设置。还请在您的问题中发布更新的代码并验证程序集输出以查看它是否正确(您也可以在此处发布)。【参考方案3】:感谢您的帮助。我有我的答案!
v4.0 引导加载程序(初始版本)未实现双存储区机制,但 v4.1 支持此功能。
软件可以跳转到引导加载程序,但它会执行双引导机制。 因此,引导加载程序返回到 bank1(如果代码“有效”,则返回到 bank2)。
今天,我的配置无法绕过双组机制来执行引导加载程序: boot0 引脚复位,保护级别为 0(参见参考手册中的“表 11. Boot pin 和 BFB2 位配置”)。
【讨论】:
【参考方案4】:当您调用__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH()
时,您的程序计数器在哪里?
在同一区域外执行时重新映射内存区域会很糟糕!您可能需要将此代码重新定位到 SRAM 中,或者在 PC 设置为固定 FLASH 内存映射 (0x0800xxxx
) 的情况下执行此代码。
【讨论】:
以上是关于STM32L073RZ (rev Z) IAP 跳转到引导加载程序(系统内存)的主要内容,如果未能解决你的问题,请参考以下文章