【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一覽表
name brief param
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!


附上一張效果圖: 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章