• STM32 Bootloader开发记录 2


    在《stm32 bootloader开发记录.md》文档中,已经实现了Bootloader下的升级功能。可以在Bootloader启动时,进入升级模式,使用串口传输数据,来下载固件到flash中。

    但是,在实际应用中,一般是在应用运行过程中进行升级,而不是在Bootloader中进行升级。一般只有在开发阶段才需要在Bootloader中进行升级。所以,接下来,我将实现在app中进行升级操作,并且添加签名验签功能,保证升级过程中固件的安全性和完整性。

    1. 分区划分

    分区划分使用前面文档所说的flash map。应用分区有App1和App2,这里使用App1作为我们的应用分区,应用只会在App1中启动。而App2是固件存储分区,用来在App1运行过程中存储升级固件,固件下载到App2完成后,重启到Bootloader,然后让Bootloader把固件复制到App1中运行。

    (当然,也可以不拷贝App2到App1,可以直接从Bootloader启动App2,这种方式可以增加flash的使用寿命,并且升级过程较快。这里不使用这种方式的原因是,我们的固件有修改过中断向量表,如果可以在App2中启动,那么我们需要对每个固件是在哪个分区启动要提前知道,并设置正确的终端向量表地址。为了不这么麻烦,这里使用前面所述的复制固件到固定地址运行的方式。)

    image-20221014102840942

    2. 升级原理

    在App1中,我们依然使用串口接收数据,数据通信格式和Bootloader保持一致,这样,基本上可以不用修改dfu server程序的功能,就能完成升级了。在App1中接收固件,并将固件存储到App2分区,接收完成后,写入升级标致,然后重启。Bootloader在启动时,读取升级表示,识别到是App需要升级时,将App2分区的固件拷贝到App1分区中,如果复制过程没有问题了,清除写入标识,然后跳转到App1中运行新固件。

    针对Bootloader,基本上也不需要修改什么,加上一个识别升级标识的代码,然后增加一个拷贝固件的功能就够了。

    (签名验签功能, 需要使用到加密校验库,后面移植了mbedtls库再在拷贝固件的过程中加入验签即可。)

    应用程序,在串口接收到"ota_start"字符串后,进入升级模式,开始使用之前定义的通信格式进行通信。dfu server加入一个发送"ota_start"的App 升级模式即可。

    3. 代码

    参考代码地址

    3.1 bootloader

    拷贝固件

    void ota_copy_app()
    {
        uint32_t page_num = ota_info.header.firmware_size / 2048 + 1;
        uint8_t buffer_2k[2048] = {0};
        int i = 0, j = 0;
        uint32_t app2_addr = 0x8042000;
        uint8_t is_first = 1;
        
        flash_erase(FLASH_BANK_1, 32, 96);
        flash_erase(FLASH_BANK_2, 256, 4);
        
        for (; i < page_num; i++)
        {
            memcpy(buffer_2k, (uint32_t *)(app2_addr + i * 2048), 2048);
            flash_write_fw(buffer_2k, 2048, is_first);
            is_first = 0;
        }
    
        ota_info.status = OTA_NONE;
        ota_info_write(&ota_info);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    App升级模式

    int main(void)
    {
      HAL_Init();
        ......
        if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == GPIO_PIN_SET) {
            boot_to_ota = 1;
            printf("start ota\r\n");
        }
        
    	ota_info_read(&ota_info);
        ota_info_print(&ota_info);
        
        HAL_UART_Receive_IT(&huart1, uart_rx.data, 1);
        printf("start uart IT receive\r\n");
        
        if (boot_to_ota == 0 && ota_info.status == OTA_READY)
        {
            ota_copy_app();
            goto_application();
        }
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    3.2 Application

    串口接收

    void ota_uart_receive_it(UART_HandleTypeDef *huart)
    {
        HAL_UART_Receive_IT(&huart1, uart_rx.data + (++uart_rx.pos), 1);
        
        if (!ota_start_flag) 
        {
            if (uart_rx.pos == 8) {
                uart_rx.pos = -1;
            }
            else if (uart_rx.pos == 0)
            {
                char ota_start[] = "ota_start";
                if (strncmp((char *)uart_rx.data, ota_start, strlen(ota_start)) == 0)
                {
                    ota_start_flag = 1;
                    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
                }
            }
            return;
        }
    
        if (uart_rx.pos > 256) {
            uart_rx.pos = -1;
        } 
        else if (uart_rx.pos == 4) {
            uart_rx.data_size = uart_rx.data[2] | uart_rx.data[3] << 8;
        }
        else if (uart_rx.data_size + 8 == uart_rx.pos) {
            uart_rx_size = uart_rx.pos + 1;
            is_recv_frame_ok = 1;
            uart_rx.pos = -1;
        }
    }
    
    uint8_t ota_start_flag_get(void)
    {
        return ota_start_flag;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    主业务逻辑

    int main()
    {
        ......
    	while (1)
      {
          if (ota_start_flag_get() && start_ota() == 0) {
              HAL_Delay(100);
              HAL_NVIC_SystemReset();
          }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    3.3 dfu server

    增加一个发送"ota_start"字符串的步骤。

        while (1)
        {
            if (app_ota) {
                std::cout << "start app ota mode" < 0 && !dfu_start)
            {
                std::cout << "recv: " << std::string(data) << std::endl;
                dfu_start = true;
                sleep(2); //等待MCU启动
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4. 测试

    下载Bootloader、Application固件到开发板中。下载前,对整个flash进行擦除,防止升级标识错误导致测试失败。将PB2引脚拉低,防止Bootloader进入到下载模式。下载完成后,应用正常启动,应用版本号是5

    ----------------------------------------
          stm32l475 bootloader start
    ----------------------------------------
    firmware size: -1
    major: 0xff
    minor: 0xff
    status: OTA_RUNNING
    start uart IT receive
    haven't ota event
    starting application
    jump to application
    
    ----------------------------------------
          stm32l475 application start
    ----------------------------------------
    firmware version: 0x05
    hardware version: 0x01
    firmware size: -1
    major: 0xff
    minor: 0xff
    status: OTA_RUNNING
    start uart IT receive
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这个开启dfu server,进行应用升级。开启前,先关闭串口助手的串口,防止串口占用导致dfu server打开串口失败。

    .\dfu_server.exe G:\stm32\alentek_stm32l475_application\MDK-ARM\alentek_stm32l475_application\firmware6.bin COM4 app_ota
    
    • 1

    升级成功。

    ff ff ff ff jump to application
    
    ----------------------------------------
          stm32l475 application start
    ----------------------------------------
    firmware version: 0x06
    hardware version: 0x01
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    Oracle sqlnet.ora配置文件
    MyBatis入门详解
    【C++入门到精通】C++入门 —— 红黑树(自平衡二叉搜索树)
    【数据结构】顺序表
    抖音小程序开发教学系列(7)- 抖音小程序的发布与运营
    web3.0涉及的技术
    看完这篇 教你玩转渗透测试靶机vulnhub——FunBox11(Scriptkiddie)
    MIT6.s081/6.828 lectrue4:page tables 以及 Lab3 心得
    分布式锁的三种实现方式
    统计数字会撒谎
  • 原文地址:https://blog.csdn.net/duapple/article/details/127833851