ITPub博客

首页 > 数据库 > 数据库开发技术 > 分享一个单片机的多系统引导方案

分享一个单片机的多系统引导方案

原创 数据库开发技术 作者:Rust嵌入式 时间:2024-02-24 00:12:00 0 删除 编辑

前言

多系统引导,通常指的是在计算机中安装多个操作系统,在开机时,由开机引导程序选择默认加载的操作系统,或通过键盘选择需要加载的操作系统,然后运行相应的操作系统。 在嵌入式单片机应用中,由于受限于Flash的资源大小,通常只有bootloader和App固件保留在单片机内部Flash,但随着MCU的发展,也有更大的Flash,运行频率更快的单片机可以选择。因此也可以用来实现多系统快速引导。本文将分享一种基于STM32F407开发的多系统引导的方案。 本工程就实现引导不同的3D打印固件,分别是Marlin、RepRap、Klipper三种固件,为了节省Flash空间,默认将他们保存在SD卡,在引导时如果当前Flash中的系统与选中的系统不同,则从SD卡中搬运App覆盖Flash中运行。

多系统引导的实现

Bootloader信息段

为了加快启动的速度,在BootLoader需要保留当前默认的系统的信息,如存放地址偏移,固件大小,固件CRC值等信息,因此定义以下结构体标记系统和引导的信息。

typedef struct {
    /* 固件类型 */
    firmware_type_t type;
    /* 固件的大小 */
    uint32_t size;
    /* 存放flash的地址 */
    uint32_t offset;
    /* 固件CRC值 */
    uint32_t crc;
    /* 固件版本 */
    uint8_t version[16];
    /* 升级的固件是否加密, 0:不加密, 1加密 */
    uint32_t is_encryption;
}firmware_board_info_t;

/* 板载信息段 */
typedef struct _board_info_t{
    /* 信息头部的魔数, 恒定为:"CRBD" */
    uint32_t magic;
    /* bootload版本 */
    uint8_t  bootload_version[32];
    /* 主板的版本号 */
    uint8_t  hardware_version[24];
    /* 当前分区的序号 */
    firmware_type_t run_number;
    /* 固件状态 */
    firmware_board_info_t firmware[5];
    
    /* 保留字段,保持结构体总大小为512字节 */
    //uint8_t reserve[336]; 
    /* 信息头crc */
    uint32_t info_crc;
}board_info_t;

/* 板子错误状态,
        0x00:正常
        bit0 = 1: app1升级中
        bit1 = 1: app2升级中
        bit2 = 1: app3升级中
        bit3    : 保留
        bit4 = 1: app1破坏
        bit5 = 1: app2破坏
        bit6 = 1: app3破坏
        bit7 :保留
        uint32_t board_err;
     */

    
typedef struct {
    /* 固件类型 */
    firmware_type_t type;
    /* 在文件中的偏移值 */
    uint32_t start;
    /* 固件的大小 */
    uint32_t size;
    /* 存放flash的地址 */
    uint32_t offset;
    /* 固件CRC值 */
    uint32_t crc;
    /* 固件版本 */
    uint8_t version[16];
    uint8_t reserve[24];   /* 保持结构体总大小64字节 */
    /* 升级的固件是否加密, 0:不加密, 1加密 */
    uint32_t is_encryption; 
}firmware_pack_info_t;   

/* 多系统包的文件信息头,也是单片机的固件信息段 */
typedef struct {
    /* 魔数标志, "HYFW" */
    uint32_t magic;
    /* 总文件长度 */
    uint32_t len;
    /* 首先引导的地址 */
    uint32_t runboot; 
    firmware_pack_info_t firmware[3];
    /* 支持的硬件版本号,最多支持8种 */
    uint8_t  hardware_version[8][24];
   
    /* 保留字段,保持结构体总大小为512字节 */
    uint8_t reserve[112];          
    /* 信息头的crc */
    uint32_t info_crc;
}firmware_info_t;

引导配置文件

引导选择,通过一个upgrade.ini配置文件来决定当前需要引导哪个系统,upgrade.ini文件内容如下:

;整包的自动升级检查
[All]
auto_upgrade=false
upgrade_addr=0x8008000
file_name=default
    
;RepRap的自动升级检查
[RepRap]
auto_upgrade=true
upgrade_addr=0x8008000
file_name=default

;Marlin的自动升级检查
[Marlin]
auto_upgrade=false
;upgrade_addr=0x8008000
file_name=default
    
;Klpper的自动升级检查
[Klipper]
auto_upgrade=false
upgrade_addr=0x8008000
file_name=default
    
[Other]
auto_upgrade=true
upgrade_addr=0x8008000
file_name=default

[Pack]
auto_upgrade=true
upgrade_addr=0x8008000
file_name=default
    
;引导顺序,升级配置优先级
[boot]
boot0=RepRap
boot1=Marlin
Boot2=Klipper
Boot3=none

;配置的优先级:
;[Other] > [All] > [RepRap] > [Marlin] > [Klipper]

引导控制

单片机上电时立即运行Bootloder程序,检查SD卡是否存在,如果不存在则运行默认地址的程序,否则继续检查upgrade.ini文件中的配置,然后从SD卡中读取当前需要引导的固件,计算CRC,如果与Flash中的系统的CRC一致,则直接运行Flash中的系统,否则根据配置内容搬运新的固件到Flash,达到升级的目的。然后运行新的系统。实现逻辑如下:

#include "update.h"
#include "config.h"
#include <stdint.h>
#include <stdbool.h>
#include "debug.h"
#include "uart.h"
#include "stm32f4xx_hal.h"
#include "fatfs.h"
#include "iniparser.h"

#define BOOTLOAD_VERSION            0.0.1
#define HARDWARE_VERSION            HY_DB_V0.1

#define FIRMWARE_DIR                0:/system/           /* 升级文件的目录 */
#define FIRMWARE_FILE_REPRAP        RepRap.bin           /* RepRap升级文件 */
#define FIRMWARE_FILE_MARLIN        Marlin.bin           /* Marlin升级文件 */
#define FIRMWARE_FILE_KLIPPER       Klipper.bin          /* Klipper升级文件 */
#define FIRMWARE_FILE_ALL_FIRM      firmware_All.bin     /* 整包多系统升级文件 */
#define FIRMWARE_FILE_OTHER         firmware.bin         /* 未知的系统包 */
#define FIRMWARE_UPGRADE_LOG        upgrade.log          /* 升级日志文件 */
#define FIRMWARE_UPGRADE_SET        upgrade.ini          /* 自动升级、启动顺序配置文件 */ 
#define BOARD_FILE_INFO             brd.dat              /* 板载信息 */

#define _STR(str)                   #str
#define _STR_MACR(str)              _STR(str)

#define _FIRMWARE_PATH(firmware)    _STR_MACR(FIRMWARE_DIR)\
                                        _STR_MACR(FIRMWARE_FILE_##firmware)

#define RepRap_upgrade_path         _FIRMWARE_PATH(REPRAP)
#define Marlin_upgrade_path         _FIRMWARE_PATH(MARLIN)
#define Klipper_upgrade_path        _FIRMWARE_PATH(KLIPPER)
#define All_firm_upgrade_path       _FIRMWARE_PATH(ALL_FIRM)
#define Other_upgrade_path          _FIRMWARE_PATH(OTHER)
#define Board_file_info_path        _STR_MACR(FIRMWARE_DIR)\
                                        _STR_MACR(BOARD_FILE_INFO)
#define BOARD_UPGRADE_SET_FILE_INI  _STR_MACR(FIRMWARE_DIR)\
                                        _STR_MACR(FIRMWARE_UPGRADE_SET)                                 
                                        
#define BOOTLOAD_FIRMWARE_VERSION   _STR_MACR(BOOTLOAD_VERSION)
#define BOARD_HARDWARE_VERSION      _STR_MACR(HARDWARE_VERSION)

#define BOOT_SET_ALL_PACK      All
#define BOOT_SET_REPRAP        RepRap
#define BOOT_SET_MARLIN        Marlin
#define BOOT_SET_KLIPPER       Klipper
#define BOOT_SET_OTHER         Other

#define BOOT_SET_DEFAULT_ADDR  0x8008000      /*  默认bootload 的大小为48K */

#define BOOT_SET_AUTO_UPG      auto_upgrade
#define BOOT_SET_ADDR          upgrade_addr
#define BOOT_SET_FILE          file_name
#define BOOT_SET_DEFAULT       default
   
#define BOOT_SET_DEFAULT_STR   _STR_MACR(BOOT_SET_DEFAULT)
#define BOOT_SET_ALL_PACK_STR  _STR_MACR(BOOT_SET_ALL_PACK)
#define BOOT_SET_REPRAP_STR    _STR_MACR(BOOT_SET_REPRAP)
#define BOOT_SET_MARLIN_STR    _STR_MACR(BOOT_SET_MARLIN)
#define BOOT_SET_KLIPPER_STR   _STR_MACR(BOOT_SET_KLIPPER)
#define BOOT_SET_OTHER_STR     _STR_MACR(BOOT_SET_OTHER)

#define BOOT_SET_FALSE_STR     "false"
#define BOOT_SET_TRUE_STR      "true"
#define BOOT_SET_NONE_STR      "none"

#define BOOT_SET_DEFAULT_ADDR_STR      _STR_MACR(BOOT_SET_DEFAULT_ADDR)

#define BOOT_SET_KEY(sec, key) _STR_MACR(sec)":"\
                                _STR_MACR(key)    

#define BOARD_INFO_MAGIC        (0x1234)
#define BOARD_INFO_SIZE         (512)
#define FIRMWARE_INFO_MAGIC     (0x5678)
#define FIRMWARE_INFO_SIZE      (512)

typedef enum{
    UPGRADE_OK = 0,                 /* 升级成功 */
    UPGRADE_FILE_NOT_EXIST,         /* 没有查找到升级文件 */
    UPGRADE_FILE_CRC_ERROR,         /* 文件CRC校验错误 */
    UPGRADE_FILE_OPEN_ERROR,        /* 文件打开错误 */
    UPGRADE_FILE_READ_ERR,          /* 文件读取错误 */
    UPGRADE_FILE_READ_HALT,         /* 文件读取错误 */
    UPGRADE_FILE_SIZE_ERR,          /* 文件大小太大 */
    UPGRADE_FLASH_ERASE_ERR,        /* flash擦除错误 */
    UPGRADE_FLASH_WRITE_ERR,        /* flash写错误 */
    UPGRADE_FLASH_VERIFY_ERR,       /* flash校验错误 */
    UPGRADE_FIRMWARE_RUN_ERR,       /* 固件运行错误 */
    FIRMWARE_FILE_INFO_NOT_EXIST,   /* 固件信息文件读取错误 */
    FIRMWARE_NONEED_UPGRADE,        /* 固件相同,无需升级 */
}upgrade_result_t;

typedef enum {
    Other,
    AllPack = 0,
    RepRap,
    Marlin,
    Klipper,
}firmware_type_t;
    
static uint32_t crc32_default(uint8_t *pszBuf,uint32_t ulLength);
static uint32_t crc32_pend(uint32_t pre, uint8_t *bytes, uint32_t len);
static uint32_t get_file_crc(const char *file_name);
static int get_file_size(const char *file_name);
static dictionary * firmware_settings_load(const char *settings_name);

bool goto_app(void)
{
    uint32_t sp;
    void (*app_run_fun)(void);
    if(SD_PRESENT == BSP_SD_IsDetected()){
        board_info_t *bd_info;
        bd_info = board_info_get_from_sd(Board_file_info_path);
        sp = bd_info->firmware[bd_info->run_number].offset;
    }
    else{
        sp = BOOT_SET_DEFAULT_ADDR;
    }
    /* 固件第一个字为堆栈指针 */
    /* 检查栈顶地址是否合法. */
    if(((*(volatile uint32_t *)sp) & 0x2FFE0000) == 0x20000000){
        debug("app jump(%x)...\n", sp);

        app_run_fun = (void (*)(void))*(volatile uint32_t *)(sp + 4);
        /* 关闭所以中断 */
        __disable_irq();
        /* 设置堆栈指针 */
        __set_MSP(*(volatile uint32_t *)sp);
        /* 进入app */
        app_run_fun();
    }

    /* if app run success, never run to there */
    return false;
}

static const board_info_t s_default_board_info = {
    /* 信息头部的魔数, 恒定为:"CRBD" */
    .magic = BOARD_INFO_MAGIC,
    /* bootload版本 */
    .bootload_version = BOOTLOAD_FIRMWARE_VERSION,
    /* 主板的版本号 */
    .hardware_version = BOARD_HARDWARE_VERSION,
    /* 首次直接启动的地址,必须为app的地址 */
    .run_number = RepRap,
    /* 固件状态 */
    .firmware = {
        {
            .type = AllPack,
            .size = 0,
            .offset = BOOT_SET_DEFAULT_ADDR,
            .crc = 0,
            .version = "V0.0.1",
            .is_encryption = 0,
        },
        {
            .type = Other,
            .size = 0,
            .offset = BOOT_SET_DEFAULT_ADDR,
            .crc = 0,
            .version = "V0.0.1",
            .is_encryption = 0,
        },
        {
            .type = Marlin,
            .size = 0,
            .offset = BOOT_SET_DEFAULT_ADDR,
            .crc = 0,
            .version = "V0.0.1",
            .is_encryption = 0,
        },
        {
            .type = Klipper,
            .size = 0,
            .offset = BOOT_SET_DEFAULT_ADDR,
            .crc = 0,
            .version = "no firmware",
            .is_encryption = 0,
        }
    },
    
    /* 保留字段,保持结构体总大小为512字节 */
    //.reserve = {0},
    /* 信息头crc */
    .info_crc = 0,
};

/* 板载信息 */
static board_info_t s_board_info;

bool board_info_save_to_sd(board_info_t *board_info, const char *board_info_name)
{
    FRESULT file_op_res;
    FIL file;
    UINT cnt;

    board_info->info_crc = crc32_default((uint8_t *)board_info, 
                            sizeof(board_info_t) - sizeof(uint32_t));
    
    /* 创建板载文件 */
    file_op_res = f_open(&file, board_info_name, FA_CREATE_ALWAYS|FA_WRITE);
    if(FR_OK != file_op_res){
        std_printf("fail to create file %s: result: %s\n", board_info_name, 
            file_op_err_to_str(file_op_res));
        return false;
    }
    /* 重新写入默认的信息 */ 
    file_op_res = f_write(&file, (const void *)board_info, 
                    sizeof(board_info_t), &cnt);
    if(FR_OK != file_op_res){
        std_printf("fail to write file %s, result: %s\n", board_info_name, 
            file_op_err_to_str(file_op_res));
        f_close(&file);
        return false;
    }
    f_close(&file);
    
    std_printf("%s crc: %x\n", board_info_name, get_file_crc(board_info_name));
    
    return true;
}

/* 从sd卡的板载信息文件中读取板子的信息 */
board_info_t *board_info_get_from_sd(const char *board_info_name)
{
    /* 获取文件属性(大小) */
    FILINFO file_info;
    FRESULT file_op_res;
    FIL file;
    UINT cnt;
    do{
        file_op_res = f_stat (board_info_name, &file_info);
        if(file_op_res != FR_OK){
            std_printf("file %s read info error: %s\n",board_info_name, 
                file_op_err_to_str(file_op_res));
            break;
        }
        
        /* 文件大小不对,则放弃解析文件,重新生成 */
        if(file_info.fsize != sizeof(board_info_t)){
            std_printf("file size not match board info, whill reset it...\n");
            break;
        }
        
        /* 走到这里说明文件存在,文件大小正常,读取内容 */
        file_op_res = f_open(&file, board_info_name, FA_READ);
        if(FR_OK != file_op_res){
            std_printf("fail to open file %s: result: %s\n", board_info_name, 
                file_op_err_to_str(file_op_res));
            break;
        }
        
        file_op_res = f_read(&file, &s_board_info, sizeof(board_info_t), &cnt);
        /* 文件读取错误,读取不了指定大小的内容 */
        if(sizeof(board_info_t) != cnt){
            std_printf("file read error: %s", file_op_err_to_str(file_op_res));
            f_close(&file);
            break;
        }
        
        /* 校验CRC */
        uint32_t crc;
        crc = crc32_default((uint8_t *)&s_board_info, sizeof(board_info_t) - sizeof(uint32_t));

        if(crc != s_board_info.info_crc){
            std_printf("board info file crc err\n");
            f_close(&file);
            break;
        }
        
#if 0
        std_printf("magic: %x\n", s_board_info.magic);
        std_printf("bootload_version: %s\n", s_board_info.bootload_version);
        std_printf("runboot: %x\n", s_board_info.runboot);
        std_printf("firmware.type: %x\n", s_board_info.firmware[0].type);
        std_printf("crc: %x\n", s_board_info.info_crc);
        std_printf("%s crc: %x\n", board_info_name, get_file_crc(board_info_name));
#endif
        
        f_close(&file);
        return &s_board_info;
    }while(0);

    s_board_info = s_default_board_info;
    s_board_info.info_crc = crc32_default((uint8_t *)&s_board_info, 
                            sizeof(board_info_t) - sizeof(uint32_t));
    
    /* 创建板载文件 */
    file_op_res = f_open(&file, board_info_name, FA_CREATE_ALWAYS|FA_WRITE);
    if(FR_OK != file_op_res){
        std_printf("fail to create file %s: result: %s\n", board_info_name, 
            file_op_err_to_str(file_op_res));
        return &s_board_info;
    }
    /* 重新写入默认的信息 */ 
    file_op_res = f_write(&file, (const void *)&s_board_info, 
                    sizeof(board_info_t), &cnt);
    if(FR_OK != file_op_res){
        std_printf("fail to write file %s, result: %s\n", board_info_name, 
            file_op_err_to_str(file_op_res));
    }
    f_close(&file);
    
    std_printf("%s crc: %x\n", board_info_name, get_file_crc(board_info_name));
    return &s_board_info;
}


/* 导入固件配置检查 */
static dictionary * firmware_settings_load(const char *settings_name)
{
    dictionary * ini; 

    //std_printf("iniparser: file%s\n", settings_name);
    ini = iniparser_load(settings_name);

#if 0
    std_printf("n: %d\n", ini->n);
    std_printf("size:%d\n", ini->size);
    std_printf("sec cnt: %d\n", iniparser_getnsec(ini));
    std_printf("Reprap auto upgrade: %s\n", iniparser_getstring(ini, 
                BOOT_SET_KEY(BOOT_SET_REPRAP, BOOT_SET_AUTO_UPG), 
                BOOT_SET_FALSE_STR));
    
    std_printf("Reprap file name: %s\n", iniparser_getstring(ini, 
                BOOT_SET_KEY(BOOT_SET_REPRAP, BOOT_SET_FILE_STR), 
                BOOT_SET_DEFAULT_STR));
    
    std_printf("Reprap addr name: %s\n", iniparser_getstring(ini, 
                BOOT_SET_KEY(BOOT_SET_REPRAP, BOOT_SET_ADDR), 
                BOOT_SET_DEFAULT_ADDR_STR));
#endif

    return ini;
}

bool firmware_upgrade_check(void)
{
    dictionary * ini;
    uint32_t upgrade_addr;    
    
    /* 导入ini文件 */
    ini = firmware_settings_load(BOARD_UPGRADE_SET_FILE_INI);
    
    /* 配置出错 */
    if(ini == NULL){
        return true;
    }

    /* 默认包升级自动 */
    if(0 == strcmp(BOOT_SET_TRUE_STR, 
                    iniparser_getstring(ini, 
                        BOOT_SET_KEY(BOOT_SET_OTHER, BOOT_SET_AUTO_UPG), 
                        BOOT_SET_FALSE_STR))){
        upgrade_addr = iniparser_getlongint(ini, 
                            BOOT_SET_KEY(BOOT_SET_OTHER, BOOT_SET_ADDR), 
                            BOOT_SET_DEFAULT_ADDR);
        firmware_upgrade(Other_upgrade_path, upgrade_addr, Other);                    
        return true;
    }
                        
#if 0    
    /* 整包自动升级 */
    if(0 == strcmp(BOOT_SET_TRUE, 
                    iniparser_getstring(ini, 
                        BOOT_SET_KEY(BOOT_SET_ALL_PACK, BOOT_SET_AUTO_UPG), 
                        BOOT_SET_FALSE_STR))){
        upgrade_addr = iniparser_getlongint(ini, 
                            BOOT_SET_KEY(BOOT_SET_ALL_PACK, BOOT_SET_ADDR), 
                            BOOT_SET_DEFAULT_ADDR);
        firmware_upgrade(RepRap_upgrade_path, upgrade_addr, AllPack);                       
        return true;
    }
#endif                  
    /* RepRap自动升级 */
    if(0 == strcmp(BOOT_SET_TRUE_STR, 
                    iniparser_getstring(ini, 
                        BOOT_SET_KEY(BOOT_SET_REPRAP, BOOT_SET_AUTO_UPG), 
                        BOOT_SET_FALSE_STR))){
        upgrade_addr = iniparser_getlongint(ini, 
                            BOOT_SET_KEY(BOOT_SET_REPRAP, BOOT_SET_ADDR), 
                            BOOT_SET_DEFAULT_ADDR);
        firmware_upgrade(RepRap_upgrade_path, upgrade_addr, RepRap);                       
        return true;
    }
    
    /* Marlin自动升级 */
    if(0 == strcmp(BOOT_SET_TRUE_STR, 
                    iniparser_getstring(ini, 
                        BOOT_SET_KEY(BOOT_SET_MARLIN, BOOT_SET_AUTO_UPG), 
                        BOOT_SET_FALSE_STR))){
        upgrade_addr = iniparser_getlongint(ini, 
                            BOOT_SET_KEY(BOOT_SET_MARLIN, BOOT_SET_ADDR), 
                            BOOT_SET_DEFAULT_ADDR);
        firmware_upgrade(Marlin_upgrade_path, upgrade_addr, Marlin);                        
        return true;
    }
    
    /* Klipper自动升级 */
    if(0 == strcmp(BOOT_SET_TRUE_STR, 
                    iniparser_getstring(ini, 
                        BOOT_SET_KEY(BOOT_SET_KLIPPER, BOOT_SET_AUTO_UPG), 
                        BOOT_SET_FALSE_STR))){
                            
        return true;
    }
    
    return true;
}

/* 获得文件的crc */
static int get_file_size(const char *file_name)
{
    /* 获取文件属性(大小) */
    FILINFO file_info;
    FRESULT file_op_res;

    file_op_res = f_stat (file_name, &file_info);
    if(file_op_res == FR_OK){
        return file_info.fsize;
    }
    std_printf("file read error: %d", file_op_err_to_str(file_op_res));
    return 0;
}

/* 计算文件的crc */
static uint32_t get_file_crc(const char *file_name)
{
    FIL file;
    FRESULT res;
    
    res = f_open(&file,file_name,FA_READ);
    if(FR_OK != res){
        std_printf("fail to open file %s: result: %x\n", file_name, res);
    }

    uint8_t buff[32];
    UINT cnt;
    uint32_t crc;
    crc = 0;
    do{
        /* 每次读取32个字节依次计算crc */
        res = f_read(&file, buff, sizeof(buff), &cnt);
        if(cnt != 0){
            crc = crc32_pend(crc, buff, cnt);
        }
    }while(cnt);
    
    f_close(&file);
    return crc;
}

typedef struct _sector_table{
    uint32_t sector_offset;
    uint32_t sector_size;
}sector_table_t;

/* STM32F407的flash扇区表 */
static const sector_table_t s_sector_table[] = {
        FLASH_BASE + (16 << 10)*0,(16 << 10),     /*sector 0  16KB*/
        FLASH_BASE + (16 << 10)*1,(16 << 10),     /*sector 1  16KB*/
        FLASH_BASE + (16 << 10)*2,(16 << 10),     /*sector 2  16KB*/
        FLASH_BASE + (16 << 10)*3,(16 << 10),     /*sector 3  16KB*/
        FLASH_BASE + (16 << 10)*4,(64 << 10),     /*sector 4  64KB*/
        FLASH_BASE + (64 << 10)*2,(128 << 10),     /*sector 5  128KB*/
        FLASH_BASE + (128 << 10)*2,(128 << 10),     /*sector 6  128KB*/
        FLASH_BASE + (128 << 10)*3,(128 << 10),     /*sector 7  128KB*/
        FLASH_BASE + (128 << 10)*4,(128 << 10),     /*sector 8  128KB*/
        FLASH_BASE + (128 << 10)*5,(128 << 10),     /*sector 9  128KB*/
        FLASH_BASE + (128 << 10)*6,(128 << 10),     /*sector 10 128KB*/
        FLASH_BASE + (128 << 10)*7,(128 << 10),     /*sector 11 128KB*/
    };

static uint32_t flash_addr_to_sector(uint32_t addr)
{
    assert_param((addr >= FLASH_BASE) && (addr < FLASH_END));
    
    int table_cnt;
    table_cnt = sizeof(s_sector_table)/sizeof(sector_table_t);
    for(int i = 0; i < table_cnt; i++){
        if(addr >= s_sector_table[table_cnt - i - 1].sector_offset){
            return table_cnt - i - 1;
        }
    }
    /* make gcc happy */
    return 0;
}

static uint32_t sector_count(uint32_t addr, uint32_t len)
{
    uint32_t sector;
    uint32_t sector_sz;
    uint32_t sector_cnt;
    
    sector_cnt = 1;
    sector = flash_addr_to_sector(addr);
    sector_sz += (addr - s_sector_table[sector].sector_offset);
    while((sector_sz < len) && 
        (sector < (sizeof(s_sector_table)/sizeof(sector_table_t)) - 1)){
        sector_sz += s_sector_table[sector + 1].sector_size;
        sector++;
        sector_cnt++;
    }
    return sector_cnt;
}

upgrade_result_t firmware_upgrade(const char *file_name, uint32_t addr, 
                                    firmware_type_t firm_type)
{
    /* 检查参数 */
    assert_param(file_name != NULL);
    assert_param((addr >= FLASH_BASE) && (addr < FLASH_END));
    /* 地址必须要4字节对齐 */
    assert_param((addr & 0x03) == 0);
    
    std_printf("firmware: %s\n", file_name);
    /* 检查文件是否存在 */
    
    /* 检查文件crc */
    uint32_t file_size;
    uint32_t file_crc;
    board_info_t *board_info;
    
    FRESULT file_op_res;
    
    /* 读取板载信息 */
    board_info = board_info_get_from_sd(Board_file_info_path);
    file_crc = get_file_crc(file_name);
    file_size = get_file_size(file_name); 
    
    //std_printf("file size: %u, crc: %x\n", file_size, file_crc);
    /* 当前系统没有变化,则检查文件crc是否一致 */
    if(board_info->run_number == firm_type){
        /* 文件相同,不需要升级 */
        if(board_info->firmware[firm_type].crc == file_crc){
            std_printf("firmware is new, boot...\n");
            return FIRMWARE_NONEED_UPGRADE;
        }
    }
    
    /* flash解锁 */
    HAL_FLASH_Unlock();
    
    /* 擦除flash */
    uint32_t sector;
    uint32_t sector_cnt;
    sector = flash_addr_to_sector(addr);
    sector_cnt = sector_count(addr, file_size);
 
    FLASH_EraseInitTypeDef erase_info;
    erase_info.Banks = FLASH_BANK_1;
    erase_info.NbSectors = sector_cnt;
    erase_info.Sector = sector;
    erase_info.TypeErase = FLASH_TYPEERASE_SECTORS;
    erase_info.VoltageRange = FLASH_VOLTAGE_RANGE_3; /* 2.7~3.6V */
    uint32_t err;
    HAL_StatusTypeDef erase_res;

#if 0
    std_printf("need erase addr: %x~%x\n", addr, addr + file_size);
    std_printf("erase sector start: %u, need erase sector cnt: %u\n", sector, sector_cnt);
#endif
    std_printf("erase ...\n");
    
    erase_res = HAL_FLASHEx_Erase(&erase_info, &err);
    
    /* 擦除失败 */
    if(erase_res != HAL_OK){
        std_printf("erase result: %d\n",erase_res);
        return UPGRADE_FLASH_ERASE_ERR;
    }
    
    std_printf("erase ok\n");
    
    FIL file;
    
    /* 打开固件文件,读取内容 */
    file_op_res = f_open(&file,file_name, FA_READ);
    if(FR_OK != file_op_res){
        std_printf("fail to open file %s: result: %x\n", file_name, file_op_res);
        return UPGRADE_FILE_OPEN_ERROR;
    }
    
    /* 读文件内容-写flash */
    HAL_StatusTypeDef st;
    uint32_t data;
    UINT cnt;
    /* 进度显示 */
    uint32_t process, process_cnt;
    int idx, i;
    process = file_size/10;
    process_cnt = 0;

    for(i = 0, idx = 0; i < file_size; i += 4){
        file_op_res = f_read(&file, &data, sizeof(data), &cnt);
        if(cnt == 0){
            std_printf("file read error: %d", file_op_res);
            f_close(&file);
            return UPGRADE_FILE_READ_ERR;
        }
        
        st = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i, (uint64_t)data);
        if(st != HAL_OK){
            std_printf("write to flash fail(%d/%d) %d\n",i, file_size, st);
            return UPGRADE_FLASH_WRITE_ERR;
        }
        
        if((process_cnt += 4) > process){
            idx += 10;
            std_printf("process: %d%%\n", i*100/file_size);
            process_cnt = 0;
        }
    }
    std_printf("process: 100%%\n");
    
    f_close(&file);
    std_printf("upgrade success!\n");
    
        /* 更新固件标志 */
    board_info->run_number = firm_type;
    board_info->firmware[firm_type].crc = file_crc;
    board_info->firmware[firm_type].size = file_size;
    board_info->firmware[firm_type].offset = addr;
    board_info->firmware[firm_type].is_encryption = 0;
    board_info->firmware[firm_type].type = firm_type;
    
    board_info_save_to_sd(board_info, Board_file_info_path);
    
    HAL_FLASH_Lock();
    
    return UPGRADE_OK;
}

static uint32_t Reflect(uint32_t ref, uint8_t ch)
{
 uint32_t value = 0;

 for(int i = 1; i < (ch + 1); i++){
  if (ref & 1)
   value |= 1 << (ch - i);
  ref >>= 1;
 }

 return value;
}

static uint32_t s_crc_table[256] = {0x123456};

static void crc_init(void)
{
    uint32_t ulPolynomial = 0x04C11DB7;
    for (int i = 0; i <= 0xFF; i++){
  s_crc_table[i] = Reflect(i, 8) << 24;
  for (int j = 0; j < 8; j++)
   s_crc_table[i] = (s_crc_table[i] << 1) ^ 
                    (s_crc_table[i] & ((uint32_t)1 << 31) ? ulPolynomial : 0);
  s_crc_table[i] = Reflect(s_crc_table[i], 32);
 }
}

static uint32_t crc32_default(uint8_t *pszBuf, uint32_t ulLength)
{
 uint32_t Result = 0xFFFFFFFF;
    /* 初始化crc表 */
    if(s_crc_table[0] == 0x123456){
        crc_init();
    }
    
 while (ulLength--){
  Result = (Result >> 8) ^ s_crc_table[(Result & 0xFF) ^ *pszBuf++];
    }

 Result ^= 0xFFFFFFFF;

 return Result;
}

static uint32_t crc32_pend(uint32_t pre, uint8_t *bytes, uint32_t len)
{
    pre ^= 0xFFFFFFFF;
    
    /* 初始化crc表 */
    if(s_crc_table[0] == 0x123456){
        crc_init();
    }
    while(len--){
        pre = (pre >> 8) ^ s_crc_table[(pre & 0xFF) ^ *bytes++];
    }
    pre ^= 0xFFFFFFFF;
    return pre;
}

int main(void)
{
    HAL_Init();
    /* 时钟初始化 */
    SystemClock_Config();
    uart_init();
    /* sdio外设初始化 */
    sdio_sd_init();
    /* 文件系统初始化 */
    MX_FATFS_Init();
    /* led 状态初始化 */
    run_led_init();

#if 0
    /* 延时进入app */
    for(int i = 0; i < 1; i++){
        HAL_Delay(1000);
        run_led_trun();
        std_printf("wait %d\n", i);
    }
#endif

    run_led_on();
    /* 插卡了则检查升级配置 */
    if(SD_PRESENT == BSP_SD_IsDetected()){
        firmware_upgrade_check();
    }
    while (1){
        std_printf("goto app...\n");
        HAL_Delay(500);

        if(goto_app() == false){
            std_printf("fail to jump app\n");
        }
    }
}


原文作者:Rust嵌入式;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2023-11-13

  • 博文量
    64
  • 访问量
    45582