stm32L476RG - 如何从固件执行引导加载程序

Posted

技术标签:

【中文标题】stm32L476RG - 如何从固件执行引导加载程序【英文标题】:stm32L476RG - how to execute the bootloader from firmware 【发布时间】:2017-07-09 13:30:42 【问题描述】:

我正在开发 NUCLEO-L476RG 板,试图从我的固件代码启动引导加载程序,但它不适合我。这是我要执行的代码:

#include "stm32l4xx.h"
#include "stm32l4xx_nucleo.h"
#include "core_cm4.h"
#include "stm32l4xx_hal_uart.h"

GPIO_InitTypeDef GPIO_InitStructure;
UART_HandleTypeDef UartHandle;

UART_InitTypeDef UART_InitStructre;


void BootLoaderInit(uint32_t BootLoaderStatus)

    void (*SysMemBootJump)(void) = (void (*)(void)) (*((uint32_t *) 0x1FFF0004));

    if(BootLoaderStatus == 1) 
        HAL_DeInit(); // shut down running tasks

        // Reset the SysTick Timer
        SysTick->CTRL = 0;
        SysTick->LOAD = 0;
        SysTick->VAL =0;

        __set_PRIMASK(1); // Disable interrupts
        __set_MSP((uint32_t*) 0x20001000);

        SysMemBootJump();
    


int main(void)

     HAL_Init();

    __GPIOC_CLK_ENABLE();
    GPIO_InitStructure.Pin   = GPIO_PIN_13;
    GPIO_InitStructure.Mode  = GPIO_MODE_INPUT;
    GPIO_InitStructure.Pull  = GPIO_PULLUP;
    GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);

    while (1) 
        if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)) 
            BootLoaderInit(1);
        
    

    return 0;

执行固件后我希望得到的是我可以通过 UART 连接到板并从引导加载程序发送命令/获取响应。我尝试使用的命令来自这里:USART protocol used in the STM32 bootloader.

在与 UART 连接后,我没有看到电路板并没有响应。

【问题讨论】:

【参考方案1】:

以下是从this question 的答案中获得的一些想法。

HAL_RCC_DeInit();

这显然需要在重置后将时钟恢复到状态,正如引导加载程序所期望的那样。

__HAL_REMAPMEMORY_SYSTEMFLASH();

将系统引导加载程序映射到地址0x00000000

__ASM volatile ("movs r3, #0\nldr r3, [r3, #0]\nMSR msp, r3\n" : : : "r3", "sp");

从引导加载程序 ROM 设置堆栈指针。你的0x20001000 来自哪里?如果它是任意值,则堆栈可以破坏引导加载程序的变量。

然后有这个替代解决方案:

当我想跳转到引导加载程序时,我在其中一个中写入一个字节 备份寄存器,然后发出软复位。那么,当处理器 将重新启动,在程序的最开始,它会读取这个 注册。

请注意,您需要 LSI 或 LSE 时钟来访问备份寄存器。

【讨论】:

"请注意,您需要 LSI 或 LSE 时钟来访问备份寄存器。" - 这不是真的。这些时钟都不需要访问 STM32 中的任何内容。只有当外设明确选择它们作为时钟源时,它们才需要作为时钟源。【参考方案2】:

尽量避免使用__set_MSP(),因为此函数的当前实现不允许如果它也是您当前使用的堆栈指针(而且您很可能是),则允许您更改 MSP。原因是这个函数将“sp”标记为被破坏的寄存器,所以它会在之前保存,之后恢复。

请看这里 - STM32L073RZ (rev Z) IAP jump to bootloader (system memory)

【讨论】:

还是这样吗? __set_MSP() 的替代品是什么? @jonnor - 不,当前版本的 CMSIS 已修复。检查你的副本在clobber列表中是否有“sp”。 其实 STM32Cube_FW_L4_V1.13.0(相当新)附带的 CMSIS 仍然存在问题。感谢您的提醒!用于 L4 修复的版本 14 具有该修复。 github.com/STMicroelectronics/STM32CubeL4/blob/… @jonnor - 你总是可以直接从 ARM 用最新的 CMSIS 替换 Cube 包中的内容(;它可以正常工作。【参考方案3】:

从参考手册中找到您的引导加载程序起始地址。

然后使用下面的代码。

在此之前请确保您已清除并禁用中断。

/* Jump to different address */
JumpAddress = *(__IO uint32_t*) (BootloaderAddress + 4);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) ApplicationAddress);
Jump_To_Application();

也请查看Official STM32 AppNote。

【讨论】:

我试过这个,但它不适用于我的主板。您提供的链接适用于不同的 MCU - STM32F10xxx 。感谢您的尝试。

以上是关于stm32L476RG - 如何从固件执行引导加载程序的主要内容,如果未能解决你的问题,请参考以下文章

stm32l476 HAl GPIO Init将程序计数器发送到位置0000 0000

如何减少 STM32L4 HAL 库的 SPI 开销时间

STM32L476的RTC使用问题记录

stm32L476 - 擦除闪存

STM32L476 flash 页擦除没有效果

STM32L476 和 CubeMX 上带有 DMA 的 SD 卡没有中断