【RT-Thread】STM32F4片外Flash实现Bootloader


前言

上一篇文章介绍了基于STM32F103精英开发板使用片内Flash实现bootloader功能,本文介绍基于STM32F429芯片使用片内Flash+W25Q128芯片实现bootloader功能。阅读本文之前建议先了解上一篇文章介绍的bootloader文件在线制作方法和OTA文件打包方法。

上一篇:【RT-Thread】STM32片内Flash实现Bootloader:https://blog.csdn.net/u010440719/article/details/127721095


1. 开发环境搭建

  • 硬件平台:STM32F429VET6
  • 软件版本:rt-thread-v4.1.0
  • 环境:RT-Thread Studio
  • 串口工具:Xshell 7
  • 烧录工具:J-Link

2. 配置片内Flash

在board.h中开启宏定义:#define BSP_USING_ON_CHIP_FLASH

提示:基于开发板创建工程时,可以直接在“硬件”设置菜单中使能,而基于芯片创建工程时没有这一选项,所以需要手动添加宏定义。

boad.h文件:原文注释中描述了使能片上flash的方法。
/*-------------------------- ON_CHIP_FLASH CONFIG BEGIN --------------------------*/
/** if you want to use on chip flash you can use the following instructions.
 * STEP 1 define macro related to the on chip flash
 *                 such as    BSP_USING_ON_CHIP_FLASH
 * STEP 2, modify your stm32xxxx_hal_config.h file to support on chip flash peripherals. define macro related to the peripherals
 *                 such as     #define HAL_FLASH_MODULE_ENABLED
 */
#define BSP_USING_ON_CHIP_FLASH
/*-------------------------- ON_CHIP_FLASH CONFIG END --------------------------*/

在stm32f4xx_hal_conf.h中开启宏定义:#define HAL_FLASH_MODULE_ENABLED

stm32f4xx_hal_conf.h文件:
...
#define HAL_FLASH_MODULE_ENABLED
...

3. 配置SPI总线驱动

在board.h文件中对SPI驱动程序配置方法有详细的说明,具体步骤如下:

  • 第1步:RTT Setting配置使用SPI总线/设备驱动程序;
  • 第2步:board.h文件添加SPI1宏定义;
  • 第3步:stm32xxxx_hal_msp.c文件SPI1驱动代码移植;
  • 第4步:stm32f4xx_hal_config.h文件使能SPI1驱动库。

1)配置使用SPI总线/设备驱动程序:使用串行SFUD通用驱动程序(Serial Flash Universal Driver) ,其它使用默认参数即。

在这里插入图片描述

配置SPI设备驱动后,在<rtconfig.h>头文件自动添加以下宏定义:

在这里插入图片描述
配置SPI设备驱动后,在conponents目录下自动增加spi驱动程序文件 :

在这里插入图片描述

2)修改board.h头文件:添加SPI1宏定义#define BSP_USING_SPI1

board.h文件:原文注释中描述了SPI驱动程序配置步骤。
/*-------------------------- SPI CONFIG BEGIN --------------------------*/
/** if you want to use spi bus you can use the following instructions.
 * STEP 1, open spi driver framework support in the RT-Thread Settings file
 * STEP 2, define macro related to the spi bus
 *                 such as     #define BSP_USING_SPI1
 * STEP 3, copy your spi init function from stm32xxxx_hal_msp.c generated by stm32cubemx to the end of board.c file
 *                 such as     void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
 * STEP 4, modify your stm32xxxx_hal_config.h file to support spi peripherals. define macro related to the peripherals
 *                 such as     #define HAL_SPI_MODULE_ENABLED
 */
#define BSP_USING_SPI1
/*#define BSP_USING_SPI2*/
/*#define BSP_USING_SPI3*/
/*-------------------------- SPI CONFIG END --------------------------*/

3)cubemx工具配置SPI1功能引脚,工作模式为:Full-Duplex Master。SPI_CS引脚建议配置为普通IO的输出模式。

在这里插入图片描述

使用cubemx工具生代码后,将stm32xxxx_hal_msp.c文件中SPI1驱动代码移植到board.c中:

board.c文件:

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hspi->Instance==SPI1)
  {
  /* USER CODE BEGIN SPI1_MspInit 0 */

  /* USER CODE END SPI1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_SPI1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PA5     ------> SPI1_SCK
    PA6     ------> SPI1_MISO
    PA7     ------> SPI1_MOSI
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  /* USER CODE BEGIN SPI1_MspInit 1 */
  /* USER CODE END SPI1_MspInit 1 */
  }
}

注:避免重复定义,stm32f4xx_hal_msp.c原文件中的函数添加__WEAK关键字:

stm32f4xx_hal_msp.c文件:
__WEAK void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)

4)使能stm32f4xx_hal_config.h文件中的SPI模块驱动:

stm32f4xx_hal_config.h文件:
...
/* #define HAL_MMC_MODULE_ENABLED   */
#define HAL_SPI_MODULE_ENABLED
/* #define HAL_TIM_MODULE_ENABLED   */
#define HAL_UART_MODULE_ENABLED
/* #define HAL_USART_MODULE_ENABLED   */
...

编译并下载程序,通过list_device命令验证SPI总线驱动程序添加成功。

在这里插入图片描述


4. 挂载SPI总线设备

添加SPI总线驱动程序后,需要手挂载SPI总线设备。因此,新建一个bsp_w25q.c文件,通过INIT_COMPONENT_EXPORT方法挂载SPI总线设备。

bsp_w25q.c文件完整源码:

#include "drv_spi.h"
#include "spi_flash_sfud.h"

#define W25Q_SPI_BUS_NAME        "spi1"    /* cubemx配置的SPI1*/
#define W25Q_SPI_DEVICE_NAME     "spi10"   /* SPI1总线上0号设备*/

/* spi flash设备挂载(参考官方文档) */
static int rt_hw_spi_flash_init(void)
{
    rt_hw_spi_device_attach(W25Q_SPI_BUS_NAME, W25Q_SPI_DEVICE_NAME, GPIOA, GPIO_PIN_4);

    if (RT_NULL == rt_sfud_flash_probe("W25Q128", W25Q_SPI_DEVICE_NAME))
    {
        return -RT_ERROR;
    };

    SFUD_DEBUG("spi flash w25q128 initailize success.");

    return RT_EOK;
}

/* 导出组件初始化(或者在main初始化中调用) */
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);

编译并下载程序,通过list_device命令验证SPI总线设备挂载成功。

在这里插入图片描述

官方参考挂载SPI设备方法:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/spi/spi


5. 配置FAL抽象层

1)RTT Setting配置FAL

使用FAL(FLASH ABSTRACTION LAYER)抽象层对Flash存储空间进行分区。根据实际使用的Flash芯片,自定义设备使用FAL设备名称,如:W25Q128。

在这里插入图片描述

在conponents目录自动添加fal模块程序:

在这里插入图片描述
2)修改fal_cfg.h文件:

此时编译程序将提示"找不到fal_cfg.h文件",这个文件是对Flash块设备进行分区定义,需要用户自定义。

在这里插入图片描述
在自动生成的示例程序中包含fal_cfg.h和fal_flash_sfud_port.c文件,分别将其移到相应的头文件和源文件目录下。

在这里插入图片描述

再次编译,提示“drv_flash_f4.c文件中相关的变量未定义”。此文件下的片上16k、64k、128k表示不同Flash扇区大小的芯片。

在这里插入图片描述

提示:对于Cortex-M4内核芯片,其Flash扇区的容量大小分布如下:前0~3扇区大小为16k,第4扇区为64k,第5扇区之后的为128k。由于前128k用于bootloader程序,不会对其执行擦除操作,因此只需要擦除或写入大小为128k的扇区。

在这里插入图片描述

根据以上分析,将FAL分为三个区:

  1. onchip_flash_16k:片内第0扇区(大小16k)开始的128k用于bootloader程序;
  2. onchip_flash_128k:片内第5扇区(大小128k)开始的384k用于app程序;
  3. FAL_USING_NOR_FLASH_DEV_NAME:片外W25Q128芯片从0开始的1M用于download区;(也可以设置download和app的大小一样。)
#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_

#include <rtconfig.h>
#include <board.h>

#define FLASH_SIZE_GRANULARITY_16K   (4 * 16 * 1024)
#define FLASH_SIZE_GRANULARITY_64K   (64 * 1024)
#define FLASH_SIZE_GRANULARITY_128K  (7 * 128 * 1024)

#define STM32_FLASH_START_ADRESS_16K  STM32_FLASH_START_ADRESS
#define STM32_FLASH_START_ADRESS_64K  (STM32_FLASH_START_ADRESS_16K + FLASH_SIZE_GRANULARITY_16K)
#define STM32_FLASH_START_ADRESS_128K (STM32_FLASH_START_ADRESS_64K + FLASH_SIZE_GRANULARITY_64K)

/* ===================== Flash device Configuration ========================= */
extern const struct fal_flash_dev stm32_onchip_flash_16k;
extern const struct fal_flash_dev stm32_onchip_flash_64k;
extern const struct fal_flash_dev stm32_onchip_flash_128k;
extern struct fal_flash_dev nor_flash0;

/* flash device table */
#define FAL_FLASH_DEV_TABLE                                          \
{                                                                    \
    &stm32_onchip_flash_16k,                                         \
    &stm32_onchip_flash_64k,                                         \
    &stm32_onchip_flash_128k,                                        \
    &nor_flash0,                                                     \
}

/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */
#define FAL_PART_TABLE                                                               \
{                                                                                    \
    {FAL_PART_MAGIC_WORD,      "boot",     "onchip_flash_16k",              0,          128*1024, 0}, \
    {FAL_PART_MAGIC_WORD,       "app",     "onchip_flash_128k",             128*1024,   384*1024, 0}, \
    {FAL_PART_MAGIC_WORD,  "download",      FAL_USING_NOR_FLASH_DEV_NAME,   0,          1024*1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */

#endif /* _FAL_CFG_H_ */

官方参考源文件:…\rt-thread-v4.1.0\bsp\stm32\stm32f429-atk-apollo\board\ports\fal_cfg.h

3)修改fal_flash_sfud_port.c文件:

根据实际使用的Flash芯片容量大小,修改W25Q128芯片容量为16M:

fal_flash_sfud_port.c文件:
...
struct fal_flash_dev nor_flash0 =
{
    .name       = FAL_USING_NOR_FLASH_DEV_NAME,
    .addr       = 0,
    .len        = 16 * 1024 * 1024,
    .blk_size   = 4096,
    .ops        = {init, read, write, erase},
    .write_gran = 1
};
...

6. 中断向量重映射

1)修改board.c文件:

官方教程使用INIT_BOARD_EXPORT的方法实现中断向量重映射,在此建议使用函数调用方式在board_init()初始化之前手动调用该函数。

board.c文件源码:

#include <rtthread.h>
#include <board.h>
#include <drv_common.h>

/* 中断向量重映射 */
static int rt_app_vector_init(void)
{
    #define APP_PART_ADDR    0x08020000     /* app起始地址 */
    #define NVIC_VTOR_MASK   0x3FFFFF80     /* 中断向量有效位 */
    /* Set the Vector Table base location by user application firmware definition */
    SCB->VTOR = APP_PART_ADDR & NVIC_VTOR_MASK;
    return 0;
}


RT_WEAK void rt_hw_board_init()
{
    extern void hw_board_init(char *clock_src, int32_t clock_src_freq, int32_t clock_target_freq);

    /* Heap initialization */
#if defined(RT_USING_HEAP)
    rt_system_heap_init((void *) HEAP_BEGIN, (void *) HEAP_END);
#endif

    /* 中断向量重映射 */
    rt_app_vector_init();

    hw_board_init(BSP_CLOCK_SOURCE, BSP_CLOCK_SOURCE_FREQ_MHZ, BSP_CLOCK_SYSTEM_FREQ_MHZ);

    /* Set the shell console output device */
#if defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE)
    rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif

    /* Board underlying hardware initialization */
#ifdef RT_USING_COMPONENTS_INIT
    rt_components_board_init();
#endif

}

2)修改链接脚本link.lds文件:

修改链接脚本文件link.lds中的ROM起始地址为app分区的起始地址:0x0802000,大小于为384k。此外RAM的地址可能和芯片实际大小并一致,可能是RTT Studio软件需要优化的地方,改为和芯片实际大小一致即可。

在这里插入图片描述

3)修改main.c文件:
在main.c文件中调用fal初始化,并添加打印app程序版本号V1.0.0。

main.c文件:

#include <rtthread.h>
#include "fal.h"

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

int main(void)
{
    fal_init();
    LOG_I("APP firmware version: %s", "V1.0.0");
    
    while (1)
    {
        rt_thread_mdelay(1000);
    }
    return RT_EOK;
}

7. Bootloader功能验证

1)烧录bootloader程序:

将准备好的boot_f4.bin文件烧录(制作方法见上一篇文章)。

2)烧录app程序:

程序先从bootloader程序启动,再跳转到app程序,以下为运行结果:

在这里插入图片描述


8. Ymodem OTA升级

1)配置OTA功能:

在RTT Setting中使能ota_downloader功能,使用Ymodem OTA协议。重新编译并下载app程序,此时V1.0.0版本具有Ymodem OTA功能。

在这里插入图片描述

2)打包app固件程序:

修改app程序为V2.0.0版本,将编译产生的bin文件进行OTA固件打包。

在这里插入图片描述
3)Ymodem OTA升级:

执行Ymodem OTA升级后程序将自动重启,过程中有等待时间。以下为Ymodem OTA升级结果:

在这里插入图片描述


总结

本文基于STM32F429芯片实现片外Flash的bootloader功能,其原理和使用片内Flash基本类型,只是增加SPI总线设备的挂载。此外,在实际操作过程中,发现使用RTT Studio基于开发板新建工程和基于芯片创建工程时,产生的RTT Setting菜单有所差异。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32F4Bootloader是一种启动引导程序,用于在STM32F4微控制器上进行固件升级和启动。STM32F4Bootloader分为原厂Bootloader和自定义Bootloader两种类型。 原厂Bootloader是由STM原厂提供的引导程序,一般用于通过串口、CAN或USB等接口进行固件的自举升级。在STM32F4中,原厂Bootloader存储在内部存储器的0x1fff 0000 - 0x1fff 77ff地址范围内,大小为30K。通过设置boot引脚,即将boot0设置为1,boot1设置为0,可以进入原厂Bootloader。然而,使用原厂Bootloader进行固件升级会覆盖原有的程序。 自定义Bootloader是用户根据需要自行开发的引导程序,用于制作IAP(In-Application Programming),实现跳转到不同的flash区域执行代码。自定义Bootloader可以满足不同的应用需求。 在STM32F4中,通常需要更改烧写首地址来加载Bootloader1、Bootloader2、APP1和APP2四份代码。可以通过在APP1中将Bootloader1、flag和Bootloader2以boot数组表的形式存在,从而简化固件的烧写过程。例如,可以使用bootflag数组存储升级运行标志和变量表偏移长度,使用bootloader1数组存储Bootloader1的代码。通过这种方式,可以在APP1中通过引用数组的方式来加载和执行Bootloader1的代码。 综上所述,stm32F4Bootloader是一种用于固件升级和启动的引导程序,可以分为原厂Bootloader和自定义Bootloader两种类型,具体使用和实现方式可以参考STM32F407的中文参考手册。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [STM32F4单片机bootloader及在线升级IAP基本原理](https://blog.csdn.net/weixin_43058521/article/details/125355343)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值