RT-Thread学习笔记系列之OTA升级
前言
- 做一个产品,首先是需要设计后期可升级更新功能,否则没有升级功能则每次出现问题则需要寄回更新产品固件,因此会特别麻烦,所以使用到OTA升级功能
- RT-Thread推荐使用官方提供的Bootloader,具体使用方法请参考官方教程 (https://www.rt-thread.org/document/site/application-note/system/rtboot/an0028-rtboot/)
- 几个关键点需要注意,我因为粗心则折腾了挺久还成功
- 应用层的偏移地址要和制作的Bootloader的偏移地址一样
- 应用层的中断向量要和制作的Bootloader的偏移地址一样
- 固件打包器的压缩算法,加密算法等选项要和制作的Bootloader设置的一样,固件分区名一定是app(本人因为粗心以为是download区,导致升级不成功)
硬件介绍和Bootloader配置
本人使用的MCU是STM32F407ZGT6,其中有用到片外SPI Flash,SD卡。引脚兼容正点原子的F407的探索者开发板。Bootloader配置如下图
整个过程参考官方的教程还是比较顺利的,具体不做多介绍。
需求与功能设计
因为RT-Thread的OTA软件包只有使用串口和网络进行更新固件,如果是个人的话很少存在有个人的稳定服务器。串口升级则需要一个相对懂得操作的人才能执行。如果面对的使用对象是完全对这些不了解的人呢?那该咋办呢?
SD卡存储和拷贝文件对于日常生活中人们比较熟悉,所以将升级的固件拷贝SD卡再接入开发的设备进行升级。(本人的测试方案,后期可能会通过网络下载等方式直接下载到SD卡或SPI Flash设备中)
功能实现过程
该教程是直接使用RT-Thread Studio开发工具进行开发的,以下是软件包配置
board.h文件中使用以下宏定义
#define BSP_USING_SPI1
#define BSP_USING_SDIO
#define BSP_USING_ON_CHIP_FLASH
stm32f4xx_hal_conf.h文件中使用以下宏定义
#define HAL_MODULE_ENABLED
#define HAL_SD_MODULE_ENABLED
#define HAL_SPI_MODULE_ENABLED
#define HAL_FLASH_MODULE_ENABLED
使用STM32CUBEMX配置片外Flash和SD卡的接口,本人的配置如下
/**
* @brief SD MSP Initialization
* This function configures the hardware resources used in this example
* @param hsd: SD handle pointer
* @retval None
*/
void HAL_SD_MspInit(SD_HandleTypeDef* hsd)
{
GPIO_InitTypeDef GPIO_InitStruct = {
0};
if(hsd->Instance==SDIO)
{
/* USER CODE BEGIN SDIO_MspInit 0 */
/* USER CODE END SDIO_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_SDIO_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/**SDIO GPIO Configuration
PC8 ------> SDIO_D0
PC9 ------> SDIO_D1
PC10 ------> SDIO_D2
PC11 ------> SDIO_D3
PC12 ------> SDIO_CK
PD2 ------> SDIO_CMD
*/
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* USER CODE BEGIN SDIO_MspInit 1 */
/* USER CODE END SDIO_MspInit 1 */
}
}
/**
* @brief SD MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param hsd: SD handle pointer
* @retval None
*/
void HAL_SD_MspDeInit(SD_HandleTypeDef* hsd)
{
if(hsd->Instance==SDIO)
{
/* USER CODE BEGIN SDIO_MspDeInit 0 */
/* USER CODE END SDIO_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_SDIO_CLK_DISABLE();
/**SDIO GPIO Configuration
PC8 ------> SDIO_D0
PC9 ------> SDIO_D1
PC10 ------> SDIO_D2
PC11 ------> SDIO_D3
PC12 ------> SDIO_CK
PD2 ------> SDIO_CMD
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12);
HAL_GPIO_DeInit(GPIOD, GPIO_PIN_2);
/* USER CODE BEGIN SDIO_MspDeInit 1 */
/* USER CODE END SDIO_MspDeInit 1 */
}
}
/**
* @brief SPI MSP Initialization
* This function configures the hardware resources used in this example
* @param hspi: SPI handle pointer
* @retval None
*/
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_GPIOB_CLK_ENABLE();
/**SPI1 GPIO Configuration
PB3 ------> SPI1_SCK
PB4 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;
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(GPIOB, &GPIO_InitStruct);
/* SPI1 interrupt Init */
HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
}
else if(hspi->Instance==SPI2)
{
/* USER CODE BEGIN SPI2_MspInit 0 */
/* USER CODE END SPI2_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_SPI2_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**SPI2 GPIO Configuration
PC2 ------> SPI2_MISO
PC3 ------> SPI2_MOSI
PB13 ------> SPI2_SCK
*/
GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
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_SPI2;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_13;
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_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN SPI2_MspInit 1 */
/* USER CODE END SPI2_MspInit 1 */
}
}
/**
* @brief SPI MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param hspi: SPI handle pointer
* @retval None
*/
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi)
{
if(hspi->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspDeInit 0 */
/* USER CODE END SPI1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_SPI1_CLK_DISABLE();
/**SPI1 GPIO Configuration
PB3 ------> SPI1_SCK
PB4 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5);
/* SPI1 interrupt DeInit */
HAL_NVIC_DisableIRQ(SPI1_IRQn);
/* USER CODE BEGIN SPI1_MspDeInit 1 */
/* USER CODE END SPI1_MspDeInit 1 */
}
else if(hspi->Instance==SPI2)
{
/* USER CODE BEGIN SPI2_MspDeInit 0 */
/* USER CODE END SPI2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_SPI2_CLK_DISABLE();
/**SPI2 GPIO Configuration
PC2 ------> SPI2_MISO
PC3 ------> SPI2_MOSI
PB13 ------> SPI2_SCK
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_2|GPIO_PIN_3);
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13);
/* USER CODE BEGIN SPI2_MspDeInit 1 */
/* USER CODE END SPI2_MspDeInit 1 */
}
}
分区表配置如下
#define RT_APP_PART_ADDR 0x08020000
#define NOR_FLASH_DEV_NAME "W25Q128"
/* ===================== 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;
#define FLASH_SIZE_GRANULARITY_16K (4 * 16 * 1024)
#define FLASH_SIZE_GRANULARITY_64K (1 * 64 * 1024)
#define FLASH_SIZE_GRANULARITY_128K (1 * 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 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, "app", "onchip_flash_128k", 0, 896*1024, 0}, \
{FAL_PART_MAGIC_WORD, "download", NOR_FLASH_DEV_NAME, 0, 1024*1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */
中断向量偏移配置如下
static int ota_app_vtor_reconfig(void)
{
#define NVIC_VTOR_MASK 0x3FFFFF80
SCB->VTOR = RT_APP_PART_ADDR & NVIC_VTOR_MASK;
return 0;
}
INIT_BOARD_EXPORT(ota_app_vtor_reconfig);
片外SPI Flash挂载款设备(fal才能发现)如下
#include "spi_flash.h"
#include "spi_flash_sfud.h"
#include "drv_spi.h"
static int rt_hw_spi_flash_init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
rt_hw_spi_device_attach("spi1", "spi10", GPIOB, GPIO_PIN_14);
if (RT_NULL == rt_sfud_flash_probe("W25Q128", "spi10"))
{
return -RT_ERROR;
};
return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
SD卡挂载文件系统如下
#include "dfs_fs.h"
static int sd_mount(void)
{
rt_thread_mdelay(500);
if(dfs_mount("sd0", "/", "elm", 0, 0) == 0)
{
rt_kprintf("sd0 mount success\r\n");
}
else {
rt_kprintf("sd0 mount fail\r\n");
}
return RT_EOK;
}
INIT_APP_EXPORT(sd_mount);
此时编译下载就能发现设备和文件系统了
根据上面的需求加入一下代码则能实现从SD卡实现升级固件功能,该例子则使用finsh控制台进行命令选择文件升级(后期可以通过触摸屏幕直接在板子上直接选择文件升级)
#include <dfs_posix.h> /* 当需要使用文件操作时,需要包含这个头文件 */
void upgrade(int argc, char **argv)
{
fal_partition_t fal_dev = RT_NULL;
int fd, buffsize, writecnt, fal_addr, len;
unsigned char *ucData = RT_NULL;
char *filename = RT_NULL;
if (argc < 2)
{
rt_kprintf("upgrade [filename]\n");
return ;
}
filename = argv[1];
rt_kprintf("start upgrade %s\n", filename);
fd = open(filename, O_RDONLY);
if(fd < 0)
{
rt_kprintf("open %s file fail\n");
return ;
}
fal_dev = fal_partition_find("download");
if(fal_dev == RT_NULL)
{
rt_kprintf("can not find download partition\n");
return ;
}
if(fal_partition_erase_all(fal_dev) < 0)
{
rt_kprintf("erase download partition all fail\n");
return ;
}
buffsize = fal_dev->len;
ucData = rt_malloc(buffsize);//try malloc max buff
if (ucData == RT_NULL)
{
ucData = rt_malloc(4096);
if(ucData == RT_NULL)
{
rt_kprintf("No memory\n");
return;
}
buffsize = 4096;
}
writecnt = 0;
fal_addr = 0;
while(writecnt < fal_dev->len)
{
len = read(fd, ucData, buffsize);
if(len < 0)break;
if(fal_partition_write(fal_dev, fal_addr, ucData, buffsize) < 0)
{
rt_kprintf("fal write download partition addr:%x size:%d fail\n");
return ;
}
writecnt += buffsize;
fal_addr += buffsize;
}
rt_kprintf("write upgrade %s file success\n", filename);
rt_kprintf("reboot system\n");
NVIC_SystemReset();
return;
}
MSH_CMD_EXPORT(upgrade, a sd card upgrade sample);
编译下载后,将需要升级的固件拷贝到sd卡根目录中,设备接入SD卡重启进行驱动SD卡,输入ls命令看看升级固件是否存在
输入 upgrade rtthread.rbl选择固件进行升级
升级成功,接下来则可以继续开发其他应用功能了