大家就当看个乐。
代码已经补上。
Bootloader + A区方案
设计说明
● 存储分区:
○ Bootloader区:存储引导加载程序,负责启动流程、固件验证和升级逻辑。
○ A区:存储应用程序固件,运行时由Bootloader跳转到A区执行。
● 升级流程:
a. Bootloader接收新固件(通过串口、CAN、OTA等方式)。
b. 新固件直接写入A区,覆盖原有固件。
c. Bootloader验证新固件(例如通过CRC或哈希校验)。
d. 验证通过后,重启并跳转到A区运行新固件。
优势
● 简单性:设计简单,存储分区少,Bootloader逻辑清晰,易于实现。
● 存储效率高:只需要Bootloader和A区,适合Flash空间有限的MCU。
劣势
● 无回滚机制:升级失败(如新固件损坏或不完整)会导致系统不可用,需通过外部工具恢复。
● 升级风险高:直接覆盖A区,若升级中断(掉电等),可能导致设备变砖。
Bootloader + A区 + 缓存区
设计说明
● 存储分区:
○ Bootloader区:负责启动、固件验证和升级管理。
○ A区:存储当前运行的应用程序固件。
○ 缓存区:临时存储新固件,升级前进行完整性验证。
● 升级流程:
a. Bootloader接收新固件并存储到缓存区。
b. 在缓存区完成新固件的完整性验证(CRC、MD5等)。
c. 验证通过后,将缓存区内容复制到A区。
d. 重启并跳转到A区运行新固件。
优势
● 升级安全性提升:新固件先存储到缓存区,验证后再覆盖A区,降低升级失败风险。
● 支持完整性检查:缓存区允许在写入A区前进行完整性验证,减少无效固件覆盖风险。
劣势
● 存储开销大:需要额外的缓存区,占用Flash空间,通常需要Flash容量是固件大小的2倍以上。
● 无回滚机制:升级失败后仍无法回滚到旧固件,需外部恢复。
● 升级时间较长:需要先写入缓存区再复制到A区,增加了升级耗时。
Bootloader + A区 + B区
设计说明
● 存储分区:
○ Bootloader区:负责启动、固件验证、升级管理和分区切换逻辑。
○ A区:存储当前运行的应用程序固件。
○ B区:存储备用固件(新固件或旧固件),用于升级或回滚。
● 升级流程:
a. Bootloader接收新固件并写入B区(假设A区为当前运行固件)。
b. 在B区完成新固件的完整性验证。
c. 验证通过后,Bootloader更新启动标志,标记B区为活动分区。
d. 重启并跳转到B区运行新固件。
e. 若新固件运行失败,Bootloader可切换回A区(回滚)。
优势
● 支持回滚:A区和B区可互为备份,升级失败可回滚到旧固件,大幅提升可靠性。
● 高可靠性:新固件在B区验证通过后再切换,降低变砖风险。
● 支持无缝升级:升级过程不影响当前运行(A区),适合高可用性场景。
劣势
● 存储开销大:需要两倍应用程序大小的Flash空间,适合Flash容量较大的MCU。
● 开发复杂性高:Bootloader需实现分区切换、状态管理和回滚逻辑,开发和测试成本较高。
● 升级时间较长:需要完整写入B区并验证,耗时较多。
目前项目中使用的是第三种方式,已经实现,分区如下:
区域 起始地址 结束地址 大小 用途
Bootloader 0x08000000 0x08007FFF 32KB 引导程序
回退标志存储区 0x08008000 0x080087FF 2KB 集中回退标志
选择标志存储区 0x08008800 0x08008FFF 2KB 选择运行区域
A 区程序 0x08009000 0x08026FFF 120KB 应用程序 A(主版本)
B 区程序 0x08027000 0x08044FFF 120KB 应用程序 B(备份版本)
升级协议及代码后续慢慢更新,等我。
回来了
以现在项目为例,目前项目中是需要Boot+A+B的情况,我将以截图和代码的形式呈现,若有疑问,可以留言评论,看到会回复。感谢。
BOOT部分
下面是keil的设置起始地址:
下面是代码部分
#define APPLICATION_SELECT_ADDRESS ((uint32_t)0x08008800U)
#define APPLICATION1_ADDRESS ((uint32_t)0x08009000U)
#define APPLICATION2_ADDRESS ((uint32_t)0x08027000U)
int main(void)
{
u16 us_app_flag = 0;
NVIC_SetVectorTable(NVIC_VectTab_FLASH,0U);
while (1)
{
us_app_flag = *(unsigned short *)APPLICATION_SELECT_ADDRESS;
switch(us_app_flag)
{
case 0xFFFFU:
{
FLASH_Unlock();
FLASH_EraseOnePage(APPLICATION_SELECT_ADDRESS);
FLASH_ProgramWord(APPLICATION_SELECT_ADDRESS, 0x00000001);
FLASH_Lock();
break;
}
case 0x0001U:
{
Ctrl_Program_Jump(APPLICATION1_ADDRESS);
break;
}
case 0x0002U:
{
Ctrl_Program_Jump(APPLICATION2_ADDRESS);
break;
}
default:
{
break;
}
}
}
}
void Ctrl_Program_Jump(u32 un_app_address)
{
u32 JumpAddress = 0;
pFunction Jump_To_Application;
if (((*(__IO u32*)un_app_address) & 0x2FFE0000 ) == 0x20000000)
{
/* Jump to user application */
JumpAddress = *(__IO u32*) (un_app_address + 4);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO u32*) un_app_address);
/* Jump to application */
Jump_To_Application();
}
}
上述代码可以判断跳转的情况。如果需要整个工程的代码,可以私聊我,发送,都是免费。
APP1部分
下面是keil的截图
APP2部分
下面是keil的截图
这两个是设置的APP运行的地址,其实这个设置都无所谓,我主要是想让大家在调试的时候,记住好自己的地址,实际上使用bin文件是没有地址,只会根据应用程序写入的标准判断。
下面是部分代码
#define PARAMETER_SAVED_ADDRESS ((uint32_t)0x0807F800U) /*参数存储地址*/
#define BACKUP_REGION_FLAG_ADDRESS ((uint32_t)0x08008000U) /*程序回退标志存储地址*/
#define APPLICATION_SELECT_ADDRESS ((uint32_t)0x08008800U) /*程序选择标志存储地址*/
#define APPLICATION1_ADDRESS ((uint32_t)0x08009000U) /*程序1存储地址*/
#define APPLICATION2_ADDRESS ((uint32_t)0x08027000U) /*程序2存储地址*/
#define UPDATE_FILE_SIZE_MAX ((uint32_t)0x1E000U) /* 升级文件最大120KB */
#define INTER_FLASH_PAGE_SIZE ((uint32_t)0x800U) /* 1 page 2kB*/
#define STM_SECTOR_SIZE 2048
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // 主优先级4bit,子优先级0bit
// 获取系统时钟树时钟
RCC_ClocksType RCC_ClockFreq;
RCC_GetClocksFreqValue(&RCC_ClockFreq);
delay_init(144);
usAppRunSpaceFlag = *((u32 *)APPLICATION_SELECT_ADDRESS);
/* 处理默认值(首次启动/标志未写入)*/
// bootloader 已经做了,这边就不处理了
if (APP_RUN_IN_SPACE_A == usAppRunSpaceFlag)
{
NVIC_SetVectorTable(NVIC_VectTab_FLASH,APPLICATION1_ADDRESS);
}
else if (APP_RUN_IN_SPACE_B == usAppRunSpaceFlag)
{
NVIC_SetVectorTable(NVIC_VectTab_FLASH,APPLICATION2_ADDRESS);
}
else
{
while (1)
; /* 出错, boot程序未烧录 */
}
...........
.........
//后面就是升级的协议内容,主要把固件获取,然后放到flash中,最后在相应的存储区标志即可
//如有问题,可以私聊我,感谢。
}
感谢各位的阅读时间,如果有需要沟通交流,可以私聊我,看到一定回复。