功能: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
void apply_update(FILE *file, uint32_t address)
说明:由于我使用的MCU不在内置支持的范畴,所以就直接拿API过来用了。
我外接了一个SPI Flash,专门用于升级,感觉还是有点奢侈。
mbed os里的IAP写的还挺好的,只需要稍微改一下自己的读写接口即可。
简直灰常方便!
废话不多说了,直接上重点:
注意:下面的代码只做简单示例,更复杂的逻辑需根据自己业务进行添加。
第一部分:BootLoad
1、CRC校验 - Mbed Os源码中需要有下面API文件
并在"mbed.h"文件中包含:
2、涉及的函数
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 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 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
这里就不做太多描述!
比较粗暴的改法:
void SystemInit (void)
若有更好的改法,望各位大侠指导,3Q!
附上一张效果图: