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);
}

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();
    }
    ......
}
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;
}

主业务逻辑

int main()
{
    ......
	while (1)
  {
      if (ota_start_flag_get() && start_ota() == 0) {
          HAL_Delay(100);
          HAL_NVIC_SystemReset();
      }
  }
}
3.3 dfu server

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

    while (1)
    {
        if (app_ota) {
            std::cout << "start app ota mode" <<std::endl;
            boost::system::error_code ec;
            char ota_start[] = "ota_start";
            port.write_some(boost::asio::buffer(ota_start, strlen(ota_start)), ec);
        }

        size_t n = port.read_some(boost::asio::buffer(data, sizeof(data)));

        if (n > 0 && !dfu_start)
        {
            std::cout << "recv: " << std::string(data) << std::endl;
            dfu_start = true;
            sleep(2); //等待MCU启动

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

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

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

升级成功。

ff ff ff ff jump to application

----------------------------------------
      stm32l475 application start
----------------------------------------
firmware version: 0x06
hardware version: 0x01
posted @ 2022-11-13 16:38  duapple  阅读(52)  评论(0编辑  收藏  举报  来源