RT-Thread學習筆記系列之OTA升級
- 前言
- 硬體介紹和Bootloader配置
- 需求與功能設計
- 功能實作程序
前言
- 做一個產品,首先是需要設計后期可升級更新功能,否則沒有升級功能則每次出現問題則需要寄回更新產品韌體,因此會特別麻煩,所以使用到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選擇韌體進行升級

升級成功,接下來則可以繼續開發其他應用功能了
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/225426.html
標籤:其他
上一篇:分享我的華為面經,華為OD崗筆試+面試心得,本人已成功入職!
下一篇:C#封裝多個執行緒
