【STM32】STM32F103RB基于Mbed OS的IAP固件升级

功能:STM32固件升级

副标题:对FW分包进行CRC校验

前提:

1、你的项目已移植好Mbed OS.

2、已封装好了对SPI FLASH操作的API.

场景:

        往往在升级和保存固件过程中,由于内置flash大小受限,不可能一次性将升级固件文件缓存在内置flash中,这个时候,就需要将固件分割成多个文件包进行处理,同时对每个分包进行CRC校验,以确保文件的完整性。

环境介绍:

MCU:STM32 F103RB

FLASH分区:Bootload:0x08000000 ~ 0x0800C800

                       UserAPP:0x0800C800 ~ 0x08020000

SPI Flash:W25Q80DV

|-------------------|   APPLICATION_ADDR + APPLICATION_SIZE == End of ROM
|                   |
...
|                   |
|    Application    |
|  (main program )  |
|                   |
+-------------------+   APPLICATION_ADDR == BOOTLOADER_ADDR + BOOTLOADER_SIZE
|                   |
|    Bootloader     |
|(my_bootloader.bin)|
|                   |
+-------------------+   BOOTLOADER_ADDR == Start of ROM

权威说明:

API:https://os.mbed.com/docs/mbed-os/v5.12/apis/flash-iap.html

示例:https://github.com/ARMmbed/mbed-os-example-bootloader/blob/master/main.cpp

注意:

我的IAP改造:

void apply_update(FILE *file, uint32_t address)

说明:由于我使用的MCU不在内置支持的范畴,所以就直接拿API过来用了。

           我外接了一个SPI Flash,专门用于升级,感觉还是有点奢侈。

           mbed os里的IAP写的还挺好的,只需要稍微改一下自己的读写接口即可。

简直灰常方便!

废话不多说了,直接上重点:

注意:下面的代码只做简单示例,更复杂的逻辑需根据自己业务进行添加。

第一部分:BootLoad

1、CRC校验 - Mbed Os源码中需要有下面API文件

并在"mbed.h"文件中包含:

2、涉及的函数

API一览表
namebriefparam
INT Chack_Spi_Flash()检测spi flash是否已连接void
U8 GetUpdateFW_Flag()获取升级固件标志void
VOID LoadFW_To_Flash(uint32_t address, uint32_t fw_size, uint32_t s_address)加载固件到内置flash

address - 内置flash中User APP的开始地址

fw_size - 固件大小 //这里我直接填的除Bootload                 的剩余地址

s_address - spi flash中存放升级FW的起始地址

void mbed_start_application(uintptr_t address)跳转到User APP开始运行address - 为User App的首地址

 

3、相关函数源码

void BootLoad_Jump(void)

/**
 * @name    : BootLoad_Jump
 * @brief   : 升级固件并跳转到user app
 * @param   : void
 * @retval  : void
 * @author  : atao
 */
void BootLoad_Jump(void)
{
	Get_MCU_Info();

	{
		if(Chack_Spi_Flash()&& (UPFW_FLAG == GetUpdateFW_Flag()))
		{
			printf("> Updata Firmware >>>\r\n");
			LoadFW_To_Flash(APPLICATION_ADDRESS, USER_FW_SIZE, SFLASH_FW_ADDRESS);
		}
		else
		{
			printf("> Is the latest firmware!\r\n");
		}
	}
	
	printf("> Jump To User App! >>>\r\n");
	wait_ms(100);
	mbed_start_application(APPLICATION_ADDRESS);
}	

VOID LoadFW_To_Flash(uint32_t address, uint32_t fw_size, uint32_t s_address)

/**
 * @name    : LoadFW_To_Flash
 * @brief   : 加载固件到内置flash
 * @param   : address - User App 首地址
              fw_size - 固件大小
              s_address - 升级固件所在的首地址
 * @retval  : void
 * @author  : atao
 */
VOID LoadFW_To_Flash(uint32_t address, uint32_t fw_size, uint32_t s_address)
{
    long len = fw_size;
    uint32_t SFLASH_CRC = 0;
    MbedCRC<POLY_32BIT_ANSI, 32> ct;
    uint32_t crc = 0;		

    printf("> Start load Firmware...\r\n");

    // page_offset = page_size - (addr % page_size);

    flash.init();

    const uint32_t page_size = flash.get_page_size();
    char *page_buffer = new char[page_size];
    uint32_t addr = address;
    uint32_t next_sector = addr + flash.get_sector_size(addr);
    bool sector_erased = false;
    size_t pages_flashed = 0;
    uint32_t percent_done = 0;
    int size_read = page_size;

    ct.compute_partial_start(&crc);
	
    while (true) {

        //led status
        Update_FW_led_status();
			
        // Read data for this page
        memset(page_buffer, 0, page_size);
        if (size_read > len) {
            break;
        }
       FLASH_ReadNByte((U8 *)page_buffer, s_address, page_size);

        // Erase this page if it hasn't been erased
        if (!sector_erased) {
            flash.erase(addr, flash.get_sector_size(addr));
            sector_erased = true;
        }

        // Program page
        flash.program(page_buffer, addr, page_size);
        // CRC Check data pkg
       if(size_read == USER_FW_SIZE)
       {
            ct.compute_partial((void *)page_buffer, page_size-4, &crc);
       }
       else
       {
            ct.compute_partial((void *)page_buffer, page_size, &crc);
       }

        addr += page_size;
        if (addr >= next_sector) {
            next_sector = addr + flash.get_sector_size(addr);
            sector_erased = false;
        }

        if (++pages_flashed % 3 == 0) {
            uint32_t percent_done_new = size_read * 100 / len;
            if (percent_done != percent_done_new) {
                percent_done = percent_done_new;
                printf("> Flashed %3d%%\r", percent_done);
            }
        }
                s_address += page_size;
                size_read += page_size;
    }
		
    ct.compute_partial_stop(&crc);
    flash.read((U8 *)&SFLASH_CRC, (APPLICATION_ADDRESS+USER_FW_SIZE-0x4), 4);
		
    printf("> CRC : 0x%X\r\n", crc);
    printf("> CheckNum[addr:0x%X] : 0x%X\r\n", (APPLICATION_ADDRESS+USER_FW_SIZE-0x4), SFLASH_CRC);
		
    if(crc == SFLASH_CRC)
    {
        ClearUpdateFW_Flag();
        printf("> Flashed 100%%\r\n");
    }
    else
    {
        printf("> FW Check Fail!\r\n");
        SoftReset();
    }

    delete[] page_buffer;

    flash.deinit();
}

划重点:

下面是官方的示例:

/*
 * @endcode
 * Example: Compute CRC with data available in parts
 * @code
 *
 *  #include "mbed.h"
 *  int main() {
 *      MbedCRC<POLY_32BIT_ANSI, 32> ct;
 *
 *      char  test[] = "123456789";
 *      uint32_t crc = 0;
 *
 *      printf("\nPolynomial = 0x%lx  Width = %d \n", ct.get_polynomial(), ct.get_width());
 *      ct.compute_partial_start(&crc);
 *      ct.compute_partial((void *)&test, 4, &crc);
 *      ct.compute_partial((void *)&test[4], 5, &crc);
 *      ct.compute_partial_stop(&crc);
 *      printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
 *      return 0;
 *  }
 */

第二部分:User App

对UserAPP部分的CRC校验参考前面的文章:https://blog.csdn.net/try_Catch27/article/details/95448914

这里就不做太多描述!

需要注意的地方:在User app初始化时一定要将中断地址进行偏移。

                            切记!切记!切记!

比较粗暴的改法:

void SystemInit (void)

若有更好的改法,望各位大侠指导,3Q!


附上一张效果图: 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值