RT-Thread學習筆記系列之OTA升級(1)

前言

  • 做一個產品,首先是需要設計後期可升級更新功能,否則沒有升級功能則每次出現問題則需要寄回更新產品固件,因此會特別麻煩,所以使用到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選擇固件進行升級
在這裏插入圖片描述
升級成功,接下來則可以繼續開發其他應用功能了



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章