目录
前言
上一篇文章介绍了基于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分为三个区:
- onchip_flash_16k:片内第0扇区(大小16k)开始的128k用于bootloader程序;
- onchip_flash_128k:片内第5扇区(大小128k)开始的384k用于app程序;
- 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菜单有所差异。