赞
踩
固件升级,通常称为 OTA(Over the Air)升级或者 FOTA(Firmware Over-The-Air)升级,即固件通过空中下载进行升级的技术。
bootloader 的升级模式常见有以下两种:
RT-Thread OTA 使用的是 bootloader 升级模式 2, bootloader 分区 + app 分区 + download 分区的组合。
为了能让开发者快速掌握 OTA 升级这把利器,RT-Thread 开发团队提供了通用的Bootloader。开发者通过该 Bootloader 即可直接使用 RT-Thread OTA 功能,轻松实现对设备端固件的管理、升级与维护。
下图展示了 RT-Thread 通用 Bootloader 的软件框架:
RT-Thread 开发团队的官方Bootloader以bin文件形式提供, 在线获取地址: http://iot.rt-thread.com
通常嵌入式系统程序是没有文件系统的,而是将 Flash 分成不同的功能区块,从而形成不同的功能分区。
要具备 OTA 固件升级能力,通常需要至少有两个程序运行在设备上。其中负责固件校验升级的程序称之为 bootloader,另一个负责业务逻辑的程序称之为 app。它们负责不同的功能,存储在 Flash 的不同地址范围,从而形成了 bootloader 分区和 app 分区。
但多数情况下嵌入式系统程序是运行在 Flash 中的,下载升级固件的时候不会直接向 app 分区写入新的固件,而是先下载到另外的一个分区暂存,这个分区就是 download 分区,也有称之为 app2 分区,这取决于 bootloader 的升级模式。
bootloader 分区、 app 分区、 download 分区及其他分区一起构成了分区表。分区表标识了该分区的特有属性,通常包含分区名、分区大小、分区的起止地址等。
通用 Bootloader 中的分区表包含如下三个分区:
Ymodem 是一种文本传输协议,在 OTA 应用中为空中下载技术提供文件传输的支持。基于 Ymodem协议的固件升级即为 OTA 固件升级的一个具体应用实例。
当系统需要升级固件时,Bootloader 将从 download 分区将固件搬运到 app 分区,主要功能流程如下所示:
Bootloader 工作过程如下图所示:
当系统中的固件损坏,Bootloader 将从 factory 分区将固件搬运到 app 分区,主要功能流程如下所示:
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配置相关组件即可。
RT-FOTA的作者虽然使用的是rtthread nano版本,但是添加了完整版特有的 device 框架和finsh组件,这样的优点是很方便扩展更多的软件包,但是相应的也增加了nano的尺寸,并且对于使用者需要移植的地方相应的也会增加,如果用户使用RT-Thread的完整版搭载的话,移植虽然比较方便,但是占用flash空间就更大了。所以为了使RT-FOTA的占用空间更小,更方便用户移植,我对RT-FOTA做了一下改进:
对RT-FOTA重新移植后,在不影响原有功能的情况下,所占flash空间减小到42K。
重新移植后的RT-FOTA项目地址:https://gitee.com/Aladdin-Wang/RT-FOTA-STM32L431
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
使用过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;
可以看出使用了RTT的SFUD和FAL组件,同时列出了分区表信息。这个地方与原作者的使用方式稍微做了更改,原作者的rt-fota是在开机5秒钟内,按下Enter键,即0x0d,就可以进入命令行模式。我改为了开机检测到按键有效,但小于十秒,就进入命令行模式,如果检测到有效电平大于十秒,就进入恢复固件功能,如果开机没检测到有效电平,进入正常模式。演示如下:
检测到有效电平小于十秒:
进入finsh模式:
检测到有效电平大于十秒:
进入出厂固件恢复:
开机没有检测到有效电平:
正常启动:
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
ymdown命令:
ymdown是基于Ymodem协议的下载命令,使用RTT的ymodem和ymodem _ ota组件实现,其中将ymodem _ ota.c中的DEFAULT_DOWNLOAD_PART设置为需要默认使用分区名,即在使用ymdown不带参数的情况下就下载到DEFAULT_DOWNLOAD_PART分区,也可加分区名作为参数指定下载位置。
步骤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配置文件:
关注公众号,后续有精彩内容会第一时间发送给您!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。