固件升级,通常称为 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-fotaRT-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
这里列出了fm_area和df_area分区中RBL文件的主要信息项,便于开发者查询:
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 NanoMDK需要先获取 RT-Thread Nano pack 安装包并进行安装。RT-Thread Nano 离线安装包下载,下载结束后双击文件进行安装。RT-Thread Nano pack安装完成后,勾选 kernel和shell。
步骤3:将所有文件添加到工程
其中的drv_flash_l4.c要根据自己的工程,选择对应的文件,其他的都不需要改动。
步骤4:更改编译选项为AC6AC6的编译速度更快,尺寸更小。
步骤5:更改boart.c和rtconfig.hboart.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配置文件:
本项目更多的教程请参考博客内容:
https://blog.csdn.net/sinat_31039061/article/details/106344081
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有