Boot loader更新及跳转App的三种方案

内心OS:emmm,画图画得好丑,详细讲述方案三

硬件环境:STM32F103RC为例

代码工具:STM32CubeMX

编译环境:Keil5

目录

方案一:运行的App位置固定,使用内部Flash

方案二:运行App位置固定,借助外部Flash

方案三:运行App可选,使用内部Flash

两份代码(Boot loader和App)同一种STM32CubeMX配置:

Boot loader代码:

App的Keil测试设置:

App代码段:

测试结果: 

有需要的话可评论,私发方案几的Boot loader代码以及App代码,均已经过测试


方案一:运行的App位置固定,使用内部Flash

个人不推荐使用这种方案(至于为什么,emm~,感觉上) 

注:只要Flash足够大,可以放置多个AppBuff,但是得提前规划好各个App的首地址和大小

流程:Boot loader检测Flag标志是否更新App,若需要更新App,将指定的AppBuff覆盖AppRun,然后跳转运行AppRun

优缺点(个人觉得):

        优点:App修改小,只修改VECT_TAB_OFFSET参数即可,工作都在Boot loader上操作

        缺点:1.如果只有一个AppBuff,则说明无后路可言,不能进行版本回滚

                2.更新情况下,需要在Boot loader内进行数据搬运,需要一定的时间(虽然时间很短)

                3.内部Flash读写次数有限,操作次数过多

                4.App大小受限,App.bin文件大小不能超过规定的App大小

方案二:运行App位置固定,借助外部Flash

流程:Boot loader读取外部/内部Flag标志是否更新App,若需要更新App,将指定存储在外部Flash的AppBuff读取覆盖App,然后跳转运行App

优缺点(还是个人觉得):

        优点:1.内部Flash擦写次数少

                2.除去Boot loader外,其余空间可以都是App的

        缺点:1.App和Boot loader的工程量都不小,应为涉及到外部Flash(例如SPI的W25Qx)

                2.更新情况下,在Boot loader操作的时间包括擦除内部Flash、读取外部Flash以及写内部Flash三部分操作

方案三:运行App可选,使用内部Flash

注:只要Flash足够大,你想放几个App都行,但是得提前规划好各个App的首地址和大小

流程:Boot loader检测Flag标志选择性跳转App,更新时可以选择性擦除更换App

优缺点(依旧是个人觉得):

        优点:1.Boot loader工程小

                2.内部Flash擦写次数少

                3.无需搬运擦写,只需实现跳转

        缺点:1.App上需要改动,且各个App大小及地址等必要信息需要与Boot loader一一对应

                2.App大小受限,App.bin文件大小不能超过规定的App大小,且App的个数与大小成反比

这里就只使用App1和App2为例子,且只实现跳转,擦除和更换只是对Flash的读写操作而已

两份代码(Boot loader和App)同一种STM32CubeMX配置:

RCC配置:只修改High Speed Clock (HSE)为Crystal/Ceramic Resonator,其他保持默认

SYS配置:配置Debug为Serial Wire

串口配置(UART1):Mode为Asynchronous

Boot loader代码:

// Bootloader.h

#define FLASH_ONEPAGE_SIZE  (0x800)   // 2k bytes
#define USER_BASE_ADDR    (0x08000000)
#define USER_BOOT_SIZE    (0x00005000)  // bootloader program size
#define USER_APP_PAGE     (0x32)        // 50 * 2k = 100k
#define USER_APP_SIZE     (USER_APP_PAGE * FLASH_ONEPAGE_SIZE)   // 50 * 2k = 100k
#define USER_APP1_ADDR    (USER_BASE_ADDR + USER_BOOT_SIZE)   // The first address of the user program
#define USER_APP2_ADDR    (USER_APP1_ADDR + USER_APP_SIZE)    // The second address of the user program

#define USER_FLASH_SIZE   (0x00040000)
#define USER_INFO_ADDR    (USER_BASE_ADDR + USER_FLASH_SIZE - FLASH_ONEPAGE_SIZE)

#define CLOSE_ALL_INT()   __set_PRIMASK(0x01)   // Turn off all interrupts

/* Exported types ------------------------------------------------------------*/
typedef void (*pFunc)();  // Defines a function pointer

void user_LoadApp(void);  // jump functions
// Bootloader.c

/**
 * @brief    Redirect pointer
 * @param    uint32_t Map addresses
 * @retval   void
 */
__asm void user_MSRMSP(uint32_t addr)
{
    MSR MSP, r0
    BX r14;
}

/**
 * @brief    Launch the app
 * @param    void
 * @retval   void
 */
void user_LoadApp(void)
{
    pFunc jumptoapp;
    uint32_t info = *(uint32_t *)USER_INFO_ADDR;
    uint32_t addr = ((info == 0x00000000) || (info == 0xFFFFFFFF)) ? USER_APP1_ADDR : USER_APP2_ADDR;

    if(((*(uint32_t *)addr) & 0x2FFF8000) == 0x20000000)
    {
        user_MSRMSP(addr);
        jumptoapp = (pFunc)*(volatile uint32_t *)(addr + 0x04);

        __set_MSP(*(volatile uint32_t *)addr);

        log_info("Ready to jump to the user program whose Flash is 0x%08X", addr);

        CLOSE_ALL_INT();

        SCB->VTOR = addr | (0x00 & (uint32_t)0xFFFFFE00);
        jumptoapp();
    }
    else
    {
        uint32_t data = (*(uint32_t *)addr);
        log_err("The user program with address 0x%08X could not be found", data);
    }
}
 // main.c里面int main(void)函数

 // while(1)前添加

  user_LoadApp();    // 只添加此函数即可

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

App的Keil测试设置:

App代码段:

// main.h(此处需要添加与Bootloader.h等同的宏定义)
// 唯一的差别就是最后一行的开关中断

#define FLASH_ONEPAGE_SIZE  (0x800)   // 2k bytes
#define USER_BASE_ADDR    (0x08000000)
#define USER_BOOT_SIZE    (0x00005000)  // bootloader program size
#define USER_APP_PAGE     (0x32)        // 50 * 2k = 100k
#define USER_APP_SIZE     (USER_APP_PAGE * FLASH_ONEPAGE_SIZE)   // 50 * 2k = 100k
#define USER_APP1_ADDR    (USER_BASE_ADDR + USER_BOOT_SIZE)   // The first address of the user program
#define USER_APP2_ADDR    (USER_APP1_ADDR + USER_APP_SIZE)    // The second address of the user program

#define USER_FLASH_SIZE   (0x00040000)
#define USER_INFO_ADDR    (USER_BASE_ADDR + USER_FLASH_SIZE - FLASH_ONEPAGE_SIZE)

#define OPEN_ALL_INT()   __set_PRIMASK(0x00)   // Turn off all interrupts
// main.c->int main(void)内HAL_Init();前

  uint32_t info = *(uint32_t *)USER_INFO_ADDR;
  uint32_t addr = ((info == 0x00000000) || (info == 0xFFFFFFFF)) ? USER_APP1_ADDR : USER_APP2_ADDR;

  SCB->VTOR = addr | (0x00 & (uint32_t)0xFFFFFE00);
  OPEN_ALL_INT();

测试结果: 

有需要的话可评论,私发方案几的Boot loader代码以及App代码,均已经过测试

  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
基于STM32F103的CAN Bootloader程序源码上位机是用于将固件文件通过CAN总线传输给目标设备的工具。下面是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> // 定义CAN消息结构体 typedef struct { uint8_t message_id; uint8_t data[8]; } CAN_Message; // 定义函数用于发送CAN消息 void send_can_message(uint8_t message_id, uint8_t* data, uint8_t length) { CAN_Message message; message.message_id = message_id; memcpy(message.data, data, length); // TODO: 使用STM32的CAN库发送CAN消息 // 示例代码中,假设使用CAN1发送消息 // CAN_Transmit(CAN1, &message); } int main() { uint8_t firmware_data[256]; // 存储固件数据的缓冲区 uint8_t firmware_length = 0; // 固件数据的长度 // TODO: 从上位机读取固件文件,将其保存到firmware_data缓冲区中 // 发送固件数据给目标设备 for (int i = 0; i < firmware_length; i += 8) { uint8_t length = (firmware_length - i < 8) ? firmware_length - i : 8; send_can_message(i/8, &firmware_data[i], length); // TODO: 等待一段时间,以允许目标设备接收和处理CAN消息 // 示例代码中,假设等待10ms // delay_ms(10); } return 0; } ``` 以上代码定义了一个函数`send_can_message`用于发送CAN消息,以及一个主函数`main`用于发送固件文件给目标设备。固件文件使用一个缓冲区`firmware_data`保存,并通过循环发送CAN消息,每次发送8字节的数据。在实际使用时,需要根据具体的硬件平台和需求来实现CAN发送函数,并在发送每个CAN消息后等待足够的时间以允许目标设备接收和处理CAN消息。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值