STM32通用Bootloader——FOTA

1.固件升級簡述

固件升級,通常稱爲 OTA(Over the Air)升級或者 FOTA(Firmware Over-The-Air)升級,即固件通過空中下載進行升級的技術。

1.1 bootloader 升級模式

bootloader 的升級模式常見有以下兩種:

  1. bootloader 分區 + app1 分區 + app2 分區模式
    該模式下, bootloader 啓動後,檢查 app1 和 app2 分區,哪個固件版本最新就運行哪個分區的固件。當有新版本的升級固件時,固件下載程序會將新的固件下載到另外的一個沒有運行的 app 分區,下次啓動的時候重新選擇執行新版本的固件。
    優點:無需固件搬運,啓動速度快。
    缺點: app1 分區和 app2 分區通常要大小相等,佔用 Flash 資源;且 app1 和 app2 分區都只能存放app 固件,不能存放其他固件(如 WiFi 固件)。
  2. bootloader 分區 + app 分區 + download 分區模式
    該模式下, bootloader 啓動後,檢查 download 分區是否有新版本的固件,如果 download 分區內有新版本固件,則將新版本固件從 download 分區搬運到 app 分區,完成後執行 app 分區內的固件;如果 download 分區內沒有新版本的固件,則直接執行 app 分區內的固件。當有新版本的升級固件時,固件下載程序會將新的固件下載到 download 分區內,重啓後進行升級。
    優點: download 分區可以比 app 分區小很多(使用壓縮固件),節省 Flash 資源,節省下載流量;download 分區也可以下載其他固件,從而升級其他的固件,如 WiFi 固件、 RomFs。
    缺點:需要搬運固件,首次升級啓動速度略慢。

RT-Thread OTA 使用的是 bootloader 升級模式 2, bootloader 分區 + app 分區 + download 分區的組合。

2.RT-OTA簡介

爲了能讓開發者快速掌握 OTA 升級這把利器,RT-Thread 開發團隊提供了通用的Bootloader。開發者通過該 Bootloader 即可直接使用 RT-Thread OTA 功能,輕鬆實現對設備端固件的管理、升級與維護。

下圖展示了 RT-Thread 通用 Bootloader 的軟件框架:
在這裏插入圖片描述
RT-Thread 開發團隊的官方Bootloader以bin文件形式提供, 在線獲取地址: http://iot.rt-thread.com

3.Flash 分區簡述

通常嵌入式系統程序是沒有文件系統的,而是將 Flash 分成不同的功能區塊,從而形成不同的功能分區。
要具備 OTA 固件升級能力,通常需要至少有兩個程序運行在設備上。其中負責固件校驗升級的程序稱之爲 bootloader,另一個負責業務邏輯的程序稱之爲 app。它們負責不同的功能,存儲在 Flash 的不同地址範圍,從而形成了 bootloader 分區和 app 分區。
但多數情況下嵌入式系統程序是運行在 Flash 中的,下載升級固件的時候不會直接向 app 分區寫入新的固件,而是先下載到另外的一個分區暫存,這個分區就是 download 分區,也有稱之爲 app2 分區,這取決於 bootloader 的升級模式。
bootloader 分區、 app 分區、 download 分區及其他分區一起構成了分區表。分區表標識了該分區的特有屬性,通常包含分區名、分區大小、分區的起止地址等。
通用 Bootloader 中的分區表包含如下三個分區:
在這裏插入圖片描述

4.Ymodem文件傳輸協議

Ymodem 是一種文本傳輸協議,在 OTA 應用中爲空中下載技術提供文件傳輸的支持。基於 Ymodem協議的固件升級即爲 OTA 固件升級的一個具體應用實例。

5.RT-OTA功能說明

5.1 升級固件功能

當系統需要升級固件時,Bootloader 將從 download 分區將固件搬運到 app 分區,主要功能流程如下所示:

  1. Bootloader 啓動時檢查 download 分區和 app 分區中的固件版本。
  2. 如果兩個固件版本相同,則跳轉到 app 分區,Bootloader 運行結束。
  3. 固件版本不同則將 download 分區中的固件搬運到 app 分區。
  4. 在搬運的過程中 Bootloader 可以對固件進行校驗、解密、解壓縮等操作。
  5. 搬運完畢後,刪除 download 分區中存儲的固件。
  6. 重啓系統跳轉到 app 分區中的固件運行,Bootloader 運行結束。

Bootloader 工作過程如下圖所示:
在這裏插入圖片描述

5.2 恢復固件功能

當系統中的固件損壞,Bootloader 將從 factory 分區將固件搬運到 app 分區,主要功能流程如下所示:

  1. Bootloader 啓動時檢查觸發固件恢復的引腳是否爲有效電平。
  2. 如果有效電平持續超過 10S 則將 factory 分區中的固件搬運到 app 分區中。
  3. 如果有效電平沒有持續超過 10S 則繼續進行 2.2 小節中介紹的啓動步驟。
  4. 在搬運的過程中 Bootloader 可以對固件進行校驗、解密、解壓縮等操作。
  5. 搬運完畢後,保持 factory 分區中的固件不變。
  6. 重啓系統跳轉到 app 分區中的固件運行,Bootloader 運行結束。

6.RT-FOTA簡介

RT-Thread官方推出了STM32系列單片機的通用bootloader,在其網站可以通過網頁配置就可以生成bootloader的燒錄文件,使廣大嵌入式工程師不用編寫一行代碼,就能夠輕鬆完成自己產品的bootloader功能。但是由於RTT官方的bootloader軟件RT-OTA是商用性質,不公開源碼,不僅僅限制了在其他平臺的移植,而且也不方便加入產品的特定功能。所以就有了RT-FOTA的由來。
RT-FOTA兼容RTThread官方OTA的所有功能,爲了與官方的RT-OTA作於區分,所以取名爲RT-FOTA。
RT-FOTA的項目地址:https://gitee.com/spunky_973/rt-fota
RT-FOTA可以直接使用在RT-Thread的完整版搭載,只需要將rt _ fota.c、rt _ fota.h和rt _ fota_crc.c放入工程中即可實現,然後用env配置相關組件即可。

7.RT-FOTA功能說明

  1. 支持RTT官方的RBL打包軟件,使用方式也一致。目前支持包括CRC32、AES256、quicklz和fastlz功能;
  2. 支持命令行模式(FINSH組件)和出廠固件恢復;
  3. 支持FLASH分區(FAL組件);
  4. 增加fota和ymdown命令;
  5. 其他功能可自行方便擴展;

8.RT-FOTA應用示例

RT-FOTA的作者雖然使用的是rtthread nano版本,但是添加了完整版特有的 device 框架和finsh組件,這樣的優點是很方便擴展更多的軟件包,但是相應的也增加了nano的尺寸,並且對於使用者需要移植的地方相應的也會增加,如果用戶使用RT-Thread的完整版搭載的話,移植雖然比較方便,但是佔用flash空間就更大了。所以爲了使RT-FOTA的佔用空間更小,更方便用戶移植,我對RT-FOTA做了一下改進:

  1. 編譯環境改爲Arm Compiler 6.13,優化等級改爲-Oz
  2. 去除device 框架,使用精簡版的rtthread nano版本,只添加finsh組件
  3. 使用STM32CubeMX生成對應的工程,以方便移植到其他STM32平臺

對RT-FOTA重新移植後,在不影響原有功能的情況下,所佔flash空間減小到42K。
重新移植後的RT-FOTA項目地址:https://gitee.com/Aladdin-Wang/RT-FOTA-STM32L431

8.1 工程目錄

在這裏插入圖片描述

8.2 軟件配置說明

RT-FOTA的軟件配置仍然集中在rtconfig.h中,把所有根據不同需求,需要修改的宏都集中在了rtconfig.h中,其中需要用戶修改的部分有:

...
#define STM32_FLASH_START_ADRESS     ((uint32_t)0x08000000)
#define STM32_FLASH_SIZE             (256 * 1024)
#define STM32_FLASH_END_ADDRESS      ((uint32_t)(STM32_FLASH_START_ADRESS + STM32_FLASH_SIZE))

#define STM32_SRAM1_START              (0x20000000)      
#define STM32_SRAM1_END                (STM32_SRAM1_START + 64 * 1024)   // 結束地址 = 0x20000000(基址) + 64K(RAM大小)


// <<< end of configuration section >>>
/* On-chip Peripheral Drivers */
#define BSP_USING_ON_CHIP_FLASH
#define BSP_USING_LPUART1
#define BSP_USING_SPI2
/* Onboard Peripheral Drivers */
#define BSP_DATAFALSH_CS_GPIOX       GPIOB
#define BSP_DATAFALSH_CS_GPIO_PIN    GPIO_PIN_12

#define RT_FOTA_SIGNAL_LED
#define RT_FOTA_SIGNAL_LED_GPIOX     GPIOB
#define RT_FOTA_SIGNAL_LED_GPIO_PIN  GPIO_PIN_1
#define RT_FOTA_SIGNAL_LED_ON_LEVEL  GPIO_PIN_RESET

#define RT_FOTA_DEFAULT_KEY
#define RT_FOTA_DEFAULT_KEY_CHK_TIME 10
#define RT_FOTA_DEFAULT_KEY_GPIOX	 GPIOA
#define RT_FOTA_DEFAULT_KEY_GPIO_PIN GPIO_PIN_7
#define RT_FOTA_DEFAULT_KEY_LEVEL    GPIO_PIN_SET
/* package */
#define PKG_USING_FAL
#define FAL_DEBUG 1
#define FAL_PART_HAS_TABLE_CFG
#define FAL_PART_TABLE                                                               \
{  																						\
    {FAL_PART_MAGIC_WROD, "app",    "onchip_flash", 64*1024,       192*1024, 0}, \
	{FAL_PART_MAGIC_WROD, "ef", FAL_USING_NOR_FLASH_DEV_NAME, 0 , 1024 * 1024, 0}, \
	{FAL_PART_MAGIC_WROD, "download", FAL_USING_NOR_FLASH_DEV_NAME, 1024 * 1024 , 512 * 1024, 0}, \
	{FAL_PART_MAGIC_WROD, "factory", FAL_USING_NOR_FLASH_DEV_NAME, (1024 + 512) * 1024 , 512 * 1024, 0}, \
}
#define FAL_USING_SFUD_PORT
#define FAL_USING_NOR_FLASH_DEV_NAME "w25q64"

#define PKG_USING_YMODEM_OTA
#define TINY_CRYPT_AES
#define PKG_USING_QUICKLZ
#define QLZ_COMPRESSION_LEVEL 3

/* RT-FOTA module define */
#define RT_FOTA_SW_VERSION      "1.0.0"
/* FOTA application partition name */
#ifndef RT_FOTA_APP_PART_NAME
#define RT_FOTA_APP_PART_NAME   "app"
#endif

/* FOTA download partition name */
#ifndef RT_FOTA_FM_PART_NAME
#define RT_FOTA_FM_PART_NAME    "download"
#endif

/* FOTA default partition name */
#ifndef RT_FOTA_DF_PART_NAME
#define RT_FOTA_DF_PART_NAME    "factory"
#endif

/* AES256 encryption algorithm option */
#define RT_FOTA_ALGO_AES_IV  	"0123456789ABCDEF"
#define RT_FOTA_ALGO_AES_KEY 	"0123456789ABCDEF0123456789ABCDEF"

#define SOC_SERIES_STM32L4
#endif

8.3 RBL文件說明

使用過RTT官方的RT-OTA組件的朋友都知道,下載的不是bin文件,而是需要通過RTT打包軟件“裝飾”成rbl文件之後,才能被RT-OTA識別。
在這裏插入圖片描述
RTT的打包軟件可以設置代碼加密和壓縮,其配置信息都存在rbl文件前96字節中:

rt-fota />fota show download 0 96
00000000: 52 42 4C 00 02 02 00 00 59 34 CB 5E 61 70 70 00 
00000010: 00 00 00 00 00 00 00 00 00 00 00 00 30 2E 30 2E 
00000020: 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00000030: 00 00 00 00 30 2E 30 2E 33 00 00 00 00 00 00 00 
00000040: 00 00 00 00 00 00 00 00 00 00 00 00 25 51 9A 76 
00000050: 9D F9 7E DE 78 99 02 00 10 AE 01 00 5A AA C4 BE 

其具體含義如下:

typedef struct {
	char type[4];				/* RBL字符頭 */ 
	rt_uint16_t fota_algo;		/* 算法配置: 表示是否加密或者使用了壓縮算法 */
	rt_uint8_t fm_time[6];		/* 原始bin文件的時間戳, 6位時間戳, 使用了4字節, 包含年月日信息 */
	char app_part_name[16];		/* app執行分區名 */
	char download_version[24];	/* 固件代碼版本號 */
	char current_version[24];	/* 這個域在rbl文件生成時都是一樣的,我用於表示app分區當前運行固件的版本號,判斷是否固件需要升級 */
	rt_uint32_t code_crc;		/* 代碼的CRC32校驗值,它是的打包後的校驗值,即rbl文件96字節後的數據 */
	rt_uint32_t hash_val;		/* 估計這個域是指的原始代碼本身的校驗值,但不知道算法,無法確認,故在程序中未使用 */
	rt_uint32_t raw_size;		/* 原始代碼的大小 */
	rt_uint32_t com_size;		/* 打包代碼的大小 */
	rt_uint32_t head_crc;		/* rbl文件頭的CRC32校驗值,即rbl文件的前96字節 */
} rt_fota_part_head, *rt_fota_part_head_t;

8.4 開機界面

在這裏插入圖片描述
可以看出使用了RTT的SFUD和FAL組件,同時列出了分區表信息。這個地方與原作者的使用方式稍微做了更改,原作者的rt-fota是在開機5秒鐘內,按下Enter鍵,即0x0d,就可以進入命令行模式。我改爲了開機檢測到按鍵有效,但小於十秒,就進入命令行模式,如果檢測到有效電平大於十秒,就進入恢復固件功能,如果開機沒檢測到有效電平,進入正常模式。演示如下:

檢測到有效電平小於十秒:
進入finsh模式:
在這裏插入圖片描述
檢測到有效電平大於十秒:
進入出廠固件恢復:
在這裏插入圖片描述
開機沒有檢測到有效電平:
正常啓動:
在這裏插入圖片描述

8.5 命令行模式

RT-FOTA的命令行模式使用的RTT的FINSH組件, 除了RTT系統自帶命令外,還增加fota和ymdown命令:
fota命令
鍵入fota命令後回車即可看到幫助命令:

rt-fota />fota
Usage:
fota probe                       - probe RBL file of partiton
fota show partition addr size    - show 'size' bytes starting at 'addr'
fota clone des_part src_part     - clone src partition to des partiton
fota exec                        - execute application program

  1. probe參數可以打印出當分區的RBL信息:
    在這裏插入圖片描述
    這裏列出了fm_area和df_area分區中RBL文件的主要信息項,便於開發者查詢:
  • App partition name: 指的是RTT打包文件時設置的分區名
  • Algorithm mode : 指的是RTT打包文件使用那些算法:AES256/Quicklz/Fastlz
  • Firmware version : 指的是RTT打包文件設置的固件版本號
  • Code raw size : 指的代碼原始大小
  • Code package size : 指的代碼打包後的大小
  • Build Timestamp : 指的代碼生成的時間戳
  1. show參數可以顯示分區的具體實際數據,方便調試與檢查:
    在這裏插入圖片描述
  2. clone參數是實現分區數據克隆:
    這裏是將factory分區數據完整的克隆到download分區中。
    在這裏插入圖片描述
  3. exec參數是用於執行app分區的應用代碼:
    在這裏插入圖片描述

ymdown命令:
ymdown是基於Ymodem協議的下載命令,使用RTT的ymodem和ymodem _ ota組件實現,其中將ymodem _ ota.c中的DEFAULT_DOWNLOAD_PART設置爲需要默認使用分區名,即在使用ymdown不帶參數的情況下就下載到DEFAULT_DOWNLOAD_PART分區,也可加分區名作爲參數指定下載位置。

  1. ymodem_ota命令
    在這裏插入圖片描述
  2. ymodem_ota -p命令
    將固件下載到factory分區:
    在這裏插入圖片描述

9.如何移植

步驟1:通過STM32CubMX生成工程:
RT-Thread 操作系統重定義 HardFault_Handler、PendSV_Handler、SysTick_Handler 中斷函數,爲了避免重複定義的問題,在生成工程之前,需要在中斷配置中,代碼生成的選項中,取消選擇三個中斷函數(對應註釋選項是 Hard fault interrupt, Pendable request, Time base :System tick timer),最後點擊生成代碼,具體操作如下圖中步驟:
在這裏插入圖片描述
步驟2:基於 Keil MDK 移植 RT-Thread Nano
MDK需要先獲取 RT-Thread Nano pack 安裝包並進行安裝。RT-Thread Nano 離線安裝包下載,下載結束後雙擊文件進行安裝。
RT-Thread Nano pack安裝完成後,勾選 kernel和shell。
在這裏插入圖片描述
步驟3:將所有文件添加到工程
在這裏插入圖片描述
其中的drv_flash_l4.c要根據自己的工程,選擇對應的文件,其他的都不需要改動。
在這裏插入圖片描述
步驟4:更改編譯選項爲AC6
AC6的編譯速度更快,尺寸更小。
在這裏插入圖片描述
在這裏插入圖片描述
步驟5:更改boart.c和rtconfig.h
boart.c可以直接複製使用

/*
 * Copyright (c) 2006-2019, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2017-07-24     Tanek        the first version
 * 2018-11-12     Ernest Chen  modify copyright
 */
 
#include <stdint.h>
#include <rthw.h>
#include <rtthread.h>
#include "main.h"
#define _SCB_BASE       (0xE000E010UL)
#define _SYSTICK_CTRL   (*(rt_uint32_t *)(_SCB_BASE + 0x0))
#define _SYSTICK_LOAD   (*(rt_uint32_t *)(_SCB_BASE + 0x4))
#define _SYSTICK_VAL    (*(rt_uint32_t *)(_SCB_BASE + 0x8))
#define _SYSTICK_CALIB  (*(rt_uint32_t *)(_SCB_BASE + 0xC))
#define _SYSTICK_PRI    (*(rt_uint8_t  *)(0xE000ED23UL))

// Updates the variable SystemCoreClock and must be called 
// whenever the core clock is changed during program execution.
extern void SystemCoreClockUpdate(void);
extern void SystemClock_Config(void);
extern void MX_GPIO_Init();
// Holds the system core clock, which is the system clock 
// frequency supplied to the SysTick timer and the processor 
// core clock.
extern uint32_t SystemCoreClock;

static uint32_t _SysTick_Config(rt_uint32_t ticks)
{
    if ((ticks - 1) > 0xFFFFFF)
    {
        return 1;
    }
    
    _SYSTICK_LOAD = ticks - 1; 
    _SYSTICK_PRI = 0xFF;
    _SYSTICK_VAL  = 0;
    _SYSTICK_CTRL = 0x07;  
    
    return 0;
}

#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)

#if defined(__CC_ARM) || defined(__CLANG_ARM)
extern int Image$$RW_IRAM1$$ZI$$Limit;                   // RW_IRAM1,需與鏈接腳本中運行時域名相對應
#define HEAP_BEGIN      ((void *)&Image$$RW_IRAM1$$ZI$$Limit)
#endif

#define HEAP_END                       STM32_SRAM1_END
#endif

/**
 * This function will initial your board.
 */
void rt_hw_board_init()
{

	HAL_Init();
	SystemClock_Config();
	/* System Clock Update */
    SystemCoreClockUpdate();

    /* System Tick Configuration */
    _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
    MX_GPIO_Init();
	extern int uart_init(void);
	uart_init();
    /* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INIT
    rt_components_board_init();
#endif
    extern void rt_fota_print_log(void);
    rt_fota_print_log();
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
    rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);
#endif
}

void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}



rtconfig.h配置文件:
在這裏插入圖片描述
在這裏插入圖片描述

10.注意事項

  1. 如果APP部分已經使用了Ymodem或者其他文件傳輸方式,bootloader可以不使能Ymodem
  2. app也可以使用裸機開發,對系統無依賴,對於app只需要更改中斷向量表部分,IAP可以由bootloader的Ymodem完成
  3. 本項目示例代碼中使用的硬件有lpuart1、spi2(W25Q64)、PA7(key)、PB1(led)、PB12(片選)
  4. 本項目地址:https://gitee.com/Aladdin-Wang/RT-FOTA-STM32L431

歡迎關注本人公衆號:
在這裏插入圖片描述

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