Bootstrap

AT32 bootloader程序与上位机程序

        从8051到stm32, 从串口下载到JLINK调试,从keil到arm-none-eabi-gcc,从"Hello wrold"到通信协议,一路起来已学会很多,是时候写一下bootloader了。

基本原理

  • 单片机代码编译完后可以生成".hex"和".bin"文件,而bin文件写入到单片机的FLASH里面,单片机就能正常运行了。
  • 单片机启动后,会从0x0800 0000地址开始运行。bootloader的程序从0x0800 000开始。
  • bootloader功能就是开机在固定时内(比如100ms内)检测是否需要升级,否则跳转到APP地址上。

如何升级

  • 上位机只要把bin文件通过通讯接口(串口、USB、WIFI...)发送到单片机,单机将收到的bin文件数据存到FLASH里面就能完成升级。
  • bin文件很大,单片机RAM不够大,不能一次性接收,需要将bin拆包。
  • 单片机擦除FLASH时需要一定的时间,写入速度较快。所以最好只擦除bin需要的大小。
  • 单片机需要确保收到的数据是正确的,因此需要数据检验。
  • 上位机需要查询、写入、擦除、读取、跳转等的功能,每次通讯都需要回应以确保成功。

数据包格式

[4byte:CRC][1byte:CMD][1byte:~CMD][2byte:dataSize][nbyte:data]

因为使用空闲中断来收到数据,并且通讯都是独占的不会中断,因此不需要帧头和帧尾,统一使用crc32来校验,所是32位的,放前面可以更方便来做不定长数据包FRAME_DATA_SIZE 一般为flash页大小,因为需要按页擦除,因此擦一页发一页。不同单片机的页大小是不一样的。

struct pack_info
{
    union
    {
        struct
        {
            uint32_t crc;
            uint32_t addr;
            uint32_t cmd1 : 8;
            uint32_t cmd2 : 8;
            uint32_t data_size : 16;
            uint8_t data[FRAME_DATA_SIZE];
        };
        uint8_t buffer[FRAME_DATA_SIZE + 12];
    };
    uint32_t buffer_size;
};

固件信息

  • 代码可不能随便刷的,否则容易变砖。如果不担心变砖可以不需要这个!
  • 因此我们需要知道bin文件是针对哪个型号的,单片机又是什么型号的。
  • 最好还能知道已经刷进去的程序是什么版本的等等,因此需要把固件信息存下来。
  • 所以要在单片机上分配块FLASH页面,而bin文件上又保存这些信息呢,总不能上传时再输入吧。
  • typedef union bootloader
    {
        struct
        {
            uint32_t infoCrc;//固件信息校验
            uint32_t magic;//固件信息标志,固定为0x5A1234A5,代码里写入
            uint32_t start_addr;//固件起始地址
            uint32_t size;//固件大小
            uint32_t FWcrc;//固件校验
            uint32_t version;//版本号,格式为:主版本号.子版本号.编译号.序号
            uint32_t pid;//产品ID,格式为:主板ID.核心ID.产品ID.功能ID
            uint32_t date;//编译日期
            uint32_t bootTimes;//启动次数
            AT32_MCU_TYPE whoAmI;
            char name[256];//固件名称,代码里写入,后面再改也行
        };
        uint8_t buffer[FLASH_SectorSize];
    } firmware_info_t;

FLASH分区

        以AT32F413RCT7为例,它FLASH大小为256kb,页大小为2kb。数据手册上有。

代码从0x0800 000开始,如果只用串口通讯,则bootloader程序约为4kb,如果使用USB通讯则会有14kb左右,以编译器为准。因为FLASH划分为如下:

【bootloader区】【固件信息区】【用户代码区】【用户配置区0】【用户配置区1】

/**
 * @brief
 * TOTAL FLASH SIZE: 256Kb
 * [Bloader:0-14Kb][FWINFO:14-16Kb][APP:16-252Kb][CONFIG0:252-254Kb][CONFIG2:254-256Kb]
 */
#define FLASH_SECTOR_COUNT 128
#define FLASH_SectorSize (uint32_t)2048
#define FLASH_Base_Addr (uint32_t)0x08000000
#define FLASH_IAP_Size (uint32_t)0x4000 // 0x08004000~0x0800FFFF
#define FLASH_APP_ADDR (uint32_t)(FLASH_Base_Addr | FLASH_IAP_Size)

#define FLASH_TOTAL_SIZE (FLASH_SECTOR_COUNT * FLASH_SectorSize)
#define FLASH_END_ADDR (FLASH_Base_Addr + FLASH_TOTAL_SIZE)

#define FLASH_APP_END_ADDR (FLASH_END_ADDR - FLASH_SectorSize * 2)
#define FLASH_APP_SIZE (FLASH_TOTAL_SIZE - FLASH_SectorSize * 3 - FLASH_IAP_Size)

#define FLASH_FWINFO_ADDR (FLASH_APP_ADDR - FLASH_SectorSize)
#define FLASH_CONFIG_ADDR0 (FLASH_APP_END_ADDR + FLASH_SectorSize)
#define FLASH_CONFIG_ADDR1 (FLASH_CONFIG_ADDR0 + FLASH_SectorSize)

*.ld文件数据分区

        ld文件是编译器编译代码时用来划分数据对应flash位置的,gcc编译器才有的。可以将代码的数据安排到指定FLASH位置上。

bootlader的*.ld文件

        只要改变memory就分配完了,整个bootlaoder程序不能超过16Kb.如下:

/* Specify the memory areas */
MEMORY
{
FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 14K
FWINFO (rx)     : ORIGIN = 0x08003800, LENGTH = 2K
RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 32K
SPIM (rx)       : ORIGIN = 0x08400000, LENGTH = 16384K
}

APP的*.ld文件

分配flash

/* Program Entry, set to mark it as "used" and avoid gc */
MEMORY
{
FWINFO (rx) : ORIGIN =0x08003800,LENGTH =2k
ROM (rx)    : ORIGIN =0x08004000,LENGTH =236k
RAM (rw)    : ORIGIN =0x20000000,LENGTH =32k
}

将变量分配到flash上

SECTIONS
{
    .firemware_info : ALIGN(4)
    {
        KEEP(*(firemware_info))
    } > FWINFO

 定义固件信息

#include "bootloader.h"
const firmware_info_t zino_firemware_info __attribute__((section("firemware_info")))= {
    .infoCrc = ~FLASH_FW_MAGIC,
    .magic = FLASH_FW_MAGIC,
    .start_addr = 0x08004000,
    .size = 0,
    .FWcrc = 0,
    .version = VESION2INT(0,0,0,1),
    .pid =PID2INT(1,2,3,4),
    .name = "ZINO Digital Power Supply V0.1",
    .date = DATE2INT(2024,11,19),
    .whoAmI.ID = 0x30240,
    .whoAmI.Serial = AT32F413xxx7,
    .whoAmI.Model=__AT32F413RCT7,
    .whoAmI.Flash = _256KB,
    .whoAmI.Footprint = LQFP64,
};

 这样编译好后,bin文件的前2kb就是固件信息的结构体数据 ,bin文件可以查看

 

 添加固件校验值

        bin文件被编译完成后,并不会自己计算固件信息里面的固件CRC,因此需要另一个程序来计算这个值,并重新写入到固件里面。这样就能得到一个带有校验值、MCU型号、程序版本等等的固件了。

固件生成工具

        只需要会一点c语言就行,fopen bin文件,读取前面一部分固件信息,再把后一部分真正的代码计算出CRC32,然后再写回去生成一个新的文件。。。用gcc编译超方便的 ~~~

/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */
#include "bootloader.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define CRC32_INIT_VALUE 0xFFFFFFFF
#define CRC32_XOR_VALUE 0xFFFFFFFF

uint32_t crc32_cal(const uint8_t *pdata, int32_t len, uint32_t initial, uint32_t finalXor, bool inputReflected, bool resultReflected);

/**
 * @brief
 *  make -f ./win_iap_tool/firminfo.mk
 * ./win_iap_tool/makefirmware.exe ./win_iap_tool/ZINO_FC_V4_V1.0.1_20241115.bin test
 * @param argc
 * @param argv
 * @return int
 */
int main(int argc, char *argv[])
{
    char *newName = NULL;
    if (argc < 2)
    {
        printf("Usage: %s <*.bin> <newName(optional)> %d\n", argv[0], argc);
        return -1;
    }
    if (argc == 3)
    {
        newName = argv[2];
    }
    firmware_info_t firmInfo;
    at32_mcu_init_model(&firmInfo.whoAmI, argv[1]);
    FILE *fw = fopen(argv[1], "rb");
    if (fw == NULL)
    {
        printf("open file error\n");
        return -1;
    }

    size_t thisRead = fread(&firmInfo, 1, sizeof(firmInfo), fw);
    if (thisRead != sizeof(firmInfo))
    {
        printf("read firmInfo size error\n");
        return -1;
    }
    printf("name:%s\n", firmInfo.name);
    printf("infoCrc: 0x%08X\n", firmInfo.infoCrc);
    printf("magic: 0x%08X\n", firmInfo.magic);
    printf("startAddr: 0x%08X\n", firmInfo.start_addr);
    printf("size: 0x%08X\n", firmInfo.size);
    printf("version: 0x%08X\n", firmInfo.version);
    printf("pid: 0x%08X\n", firmInfo.pid);
    printf("date: 0x%08X\n", firmInfo.date);
    printf("Serial: 0x%08X\n", firmInfo.whoAmI.Serial);
    printf("ID: 0x%08X\n", firmInfo.whoAmI.ID);
    printf("Model: 0x%08X, %s\n", firmInfo.whoAmI.Model, at32_mcu_mode2str(&firmInfo.whoAmI));
    printf("Flash: 0x%08X, %s\n", firmInfo.whoAmI.Flash, at32_mcu_flash2str(&firmInfo.whoAmI));
    printf("Footprint: 0x%08X, %s\n", firmInfo.whoAmI.Footprint, at32_mcu_footprint2str(&firmInfo.whoAmI));

    if (firmInfo.magic != FLASH_FW_MAGIC && firmInfo.infoCrc != ~FLASH_FW_MAGIC)
    {
        printf("firmware magic error\n");
        return -1;
    }
    if (firmInfo.whoAmI.Model >= AT32_MCU_MODEL_COUNT)
    {
        printf("mcu model unknow!\n");
        return -1;
    }
    AT32_MCU_TYPE mcuType = AT32_MCU_LIST[firmInfo.whoAmI.Model];
    if (mcuType.Model != firmInfo.whoAmI.Model)
    {
        printf("mcu model error!\n");
        return -1;
    }
    if (mcuType.Flash != firmInfo.whoAmI.Flash)
    {
        printf("mcu flash error!\n");
        return -1;
    }
    if (mcuType.Footprint != firmInfo.whoAmI.Footprint)
    {
        printf("mcu footprint error!\n");
        return -1;
    }
    if (mcuType.Serial != firmInfo.whoAmI.Serial)
    {
        printf("mcu Serial error!\n");
        return -1;
    }
    if (mcuType.ID != firmInfo.whoAmI.ID)
    {
        printf("mcu ID error!\n");
        return -1;
    }

    fseek(fw, 0, SEEK_SET);
    fseek(fw, 0, SEEK_END);
    size_t fwSize = ftell(fw);
    size_t fwTotalsize = fwSize - sizeof(firmInfo);
    size_t fwEndAddr = firmInfo.start_addr + firmInfo.size;
    fseek(fw, 0, SEEK_SET);
    printf("fwTotalsize(without fwInfo): 0x%08X\n", fwTotalsize);
    size_t flashToalSize = at32_mcu_flash_size[firmInfo.whoAmI.Flash];
    size_t flashEndAddr = FLASH_Base_Addr + flashToalSize;
    printf("flashToalSize: %d,[0x%08X-0x%08X]\n", flashToalSize, FLASH_Base_Addr, flashEndAddr);
    if (fwEndAddr > flashEndAddr)
    {
        printf("firmware size overflow!\n");
        return -1;
    }
    firmInfo.size = fwTotalsize;
    uint8_t *fwBuf = (uint8_t *)malloc(fwSize);
    thisRead = fread(fwBuf, 1, fwSize, fw);
    if (thisRead != fwSize)
    {
        printf("firmware read fwSize error!\n");
        return -1;
    }
    /**use new name */
    if (newName)
    {
        memset(firmInfo.name, 0, sizeof(firmInfo.name));
        memcpy(firmInfo.name, newName, strlen(newName));
        printf("get new name: %s\n", firmInfo.name);
    }
    char *newFwname;
    size_t newFwnameLen = strlen(firmInfo.name);
    const char *src= firmInfo.name;
    if(strcmp(&src[newFwnameLen - 4], ".bin") != 0)
    {
        newFwname = (char *)malloc(newFwnameLen + 5);
        strcpy(newFwname, src);
        strcat(newFwname, ".bin");
        printf("newFwname: %s\n", newFwname);
    }
    else
    {
        newFwname = (char *)malloc(newFwnameLen + 1);
        strcpy(newFwname, src);
        printf("newFwname: %s\n", newFwname);
    }

    /**update fw crc, info crc */
    firmInfo.FWcrc = crc32_cal(&fwBuf[FLASH_SectorSize], fwTotalsize, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    firmInfo.infoCrc = crc32_cal((const uint8_t *)&firmInfo+4, sizeof(firmInfo) - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    printf("FWcrc: 0x%08X, infoCrc:0x%08X\n", firmInfo.FWcrc, firmInfo.infoCrc);
    /**updata firmInfo with crc*/
    memcpy(fwBuf, &firmInfo, sizeof(firmInfo));
    /**write new firmware */



    FILE *fwOut = fopen(newFwname, "wb");
    if (!fwOut)
    {
        printf("new firmware creat error!\n");
        return -1;
    }
    size_t thisWrite = fwrite(fwBuf, 1, fwSize, fwOut);
    if (thisWrite != fwSize)
    {
        printf("firmware write fwSize error!\n");
        return -1;
    }
    printf("Success! New: %s\n", newFwname);
    fclose(fwOut);
    free(fwBuf);
    return 0;
}

 运行结果:

make firmare
./UploadTool/makeFirmware.exe ./build/ZINO_POWER_V1.0.1_20250110.bin ZINO_DPS_ZINO_POWER_V1.0.1_20250110.bin
name:ZINO Digital Power Supply V0.1
infoCrc: 0xA5EDCB5A
magic: 0x5A1234A5
startAddr: 0x08004000
size: 0x00000000
version: 0x00000001
pid: 0x01020304
date: 0x07E80B13
Serial: 0x00000047
ID: 0x00030240
Model: 0x0000000F, AT32F413RCT7
Flash: 0x00000002, 256KB
Footprint: 0x00000003, LQFP64
fwTotalsize(without fwInfo): 0x00015EFC
flashToalSize: 262144,[0x08000000-0x08040000]
get new name: ZINO_DPS_ZINO_POWER_V1.0.1_20250110.bin
newFwname: ZINO_DPS_ZINO_POWER_V1.0.1_20250110.bin
FWcrc: 0xFF0BC36F, infoCrc:0x40693BE8
Success! New: ZINO_DPS_ZINO_POWER_V1.0.1_20250110.bin

单片机型号信息

  •  at32单片机上,有一个装置存储了具体的型号信息,可以读出来. 文档找不到了,可找他们工程师要。
  • 我把他们当时的型号都搞进来了。可以通过读flash识别出来型号。可以通过型号来定义单片机。
  • 固件生成时,通过型号来定义单片机,那么固件信息就带型号了。
  • 烧录时,通过读flash来识别型号,那可以知道固件和单片机匹配与否了。
/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */
/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */

#pragma once

#ifdef __cplusplus
 extern "C" {
#endif

#include <stdint.h>
typedef uint32_t AT32_MCU_ID;
typedef enum
{
    AT32F403xxx6 = 0x27,  //0X27,
    AT32F413xxx7 = 0x47,  //0X47,
    AT32F415xxx7 = 0x57,  //0X57,
    AT32F403Axxx7 = 0x77, //0X77,
    AT32F407xxx7 = 0x87,  //0X87,
} AT32_MCU_Serial;

typedef enum AT32_MCU_MODEL_LIST
{
    __AT32F403ZCT6 = 0,
    __AT32F403VCT6,
    __AT32F403RCT6,
    __AT32F403CCT6,
    __AT32F403ZGT6,
    __AT32F403VGT6,
    __AT32F403RGT6,
    __AT32F403CGT6,
    __AT32F403ZET6,
    __AT32F403VET6,
    __AT32F403RET6,
    __AT32F403CET6,
    __AT32F403CGU6,
    __AT32F403CEU6,
    __AT32F403CCU6,
    __AT32F413RCT7,
    __AT32F413RBT7,
    __AT32F413CCT7,
    __AT32F413CBT7,
    __AT32F413KCU7_4,
    __AT32F413KBU7_4,
    __AT32F413C8T7,
    __AT32F413CCU7,
    __AT32F413CBU7,
    __AT32F415RCT7,
    __AT32F415CCT7,
    __AT32F415KCU7_4,
    __AT32F415RCT7_7,
    __AT32F415RBT7,
    __AT32F415CBT7,
    __AT32F415KBU7_4,
    __AT32F415RBT7_7,
    __AT32F415R8T7,
    __AT32F415C8T7,
    __AT32F415R8T7_7,
    __AT32F415K8U7_4,
    __AT32F415CBU7,
    __AT32F415CCU7,
    __AT32F403AVCT7,
    __AT32F403ARCT7,
    __AT32F403ACCT7,
    __AT32F403ACCU7,
    __AT32F403AVGT7,
    __AT32F403ARGT7,
    __AT32F403ACGT7,
    __AT32F403ACGU7,
    __AT32F403AVET7,
    __AT32F403ARET7,
    __AT32F403ACET7,
    __AT32F403ACEU7,
    __AT32F407VCT7,
    __AT32F407RCT7,
    __AT32F407VGT7,
    __AT32F407RGT7,
    __AT32F407VET7,
    __AT32F407RET7,
    AT32_MCU_MODEL_COUNT,
} AT32_MCU_MODEL;
typedef enum
{
    _64KB = 0,
    _128KB,
    _256KB,
    _512KB,
    _1024KB,
    AT32_MCU_FLASH_COUNT,
} AT32_MCU_FLASH;
typedef enum
{
    QFN32 = 0,
    QFN48,
    LQFP48,
    LQFP64,
    LQFP100,
    LQFP144,
    AT32_MCU_Footprint_COUNT,
} AT32_MCU_Footprint;
typedef struct mcuInfo
{
    AT32_MCU_Serial Serial:8;
    AT32_MCU_ID ID;
    AT32_MCU_MODEL Model:8;
    AT32_MCU_FLASH Flash:8;
    AT32_MCU_Footprint Footprint:8;
    uint8_t UID[12];
} AT32_MCU_TYPE;
typedef enum
{
    Cortex_M0_r0p0 = 0,
    Cortex_M0plus_r0p0,
    Cortex_M1_r0p0,
    Cortex_M1_r0p1,
    Cortex_M1_r1p0,
    Cortex_M3_r0p0,
    Cortex_M3_r1p0,
    Cortex_M3_r1p1,
    Cortex_M3_r2p0,
    Cortex_M3_r2p1,
    Cortex_M4_r0p0,
    Cortex_M4_r0p1,
    Cortex_count,
}AT32_MCU_Cortex;

typedef struct at32_mcu_core_info
{
    AT32_MCU_Cortex core;
    uint32_t id;
}at32_mcu_core;

#define AT32MCU_FOREACH(x) for (uint8_t(x) = 0; (x) < AT32_MCU_MODEL_COUNT; (x)++)

#define MCU_UNIQUE_96BIT_ID_BASE_ADDR_31_0  ((uint32_t)0x1FFFF7E8)
#define MCU_UNIQUE_96BIT_ID_BASE_ADDR_63_32 ((uint32_t)0x1FFFF7EC)
#define MCU_UNIQUE_96BIT_ID_BASE_ADDR_95_64 ((uint32_t)0x1FFFF7F0)
#define AT32_MCU_SERIAL_ADDR 0x1FFFF7F3
#define AT32_MCU_PID_BASE_ADDR 0xE0042000
#define AT32_MCU_SERIAL_ID (*(uint8_t*)AT32_MCU_SERIAL_ADDR)
#define AT32_MCU_PID (*(uint32_t*)AT32_MCU_PID_BASE_ADDR)


extern const char AT32_MCU_Cortex_str[Cortex_count+1][16];
extern const AT32_MCU_TYPE AT32_MCU_LIST[AT32_MCU_MODEL_COUNT];
extern const char AT32_MCU_Model_str[AT32_MCU_MODEL_COUNT + 1][16];
extern const char AT32_MCU_FLASH_str[AT32_MCU_FLASH_COUNT + 1][8];
extern const char AT32_MCU_Footprint_str[AT32_MCU_Footprint_COUNT + 1][8];
extern const uint32_t at32_mcu_flash_size[AT32_MCU_FLASH_COUNT];
extern AT32_MCU_TYPE whoAmI;
// char *at32_mcu_core2str(at32_mcu_core *core);
char *at32_mcu_mode2str(AT32_MCU_TYPE *mcu);
char *at32_mcu_flash2str(AT32_MCU_TYPE *mcu);
char *at32_mcu_footprint2str(AT32_MCU_TYPE *mcu);
char *at32_mcu_core2str(uint32_t id);
uint32_t at32_mcu_get_coreID(void);
uint8_t at32_mcu_who_am_i(AT32_MCU_TYPE *mcu);
uint8_t at32_mcu_init_model(AT32_MCU_TYPE *mcu, char *model);
#ifdef __cplusplus
 }
#endif
/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */
/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */
#include "AT32Models.h"
#include <string.h>
// #include "USART_Configuration.h"
const AT32_MCU_TYPE AT32_MCU_LIST[AT32_MCU_MODEL_COUNT] = {
    {0x27, 0x50240, __AT32F403ZCT6, _256KB, LQFP144},
    {0x27, 0x50241, __AT32F403VCT6, _256KB, LQFP100},
    {0x27, 0x50242, __AT32F403RCT6, _256KB, LQFP64},
    {0x27, 0x50243, __AT32F403CCT6, _256KB, LQFP48},
    {0x27, 0x50344, __AT32F403ZGT6, _1024KB, LQFP144},
    {0x27, 0x50345, __AT32F403VGT6, _1024KB, LQFP100},
    {0x27, 0x50346, __AT32F403RGT6, _1024KB, LQFP64},
    {0x27, 0x50347, __AT32F403CGT6, _1024KB, LQFP48},
    {0x27, 0x502C8, __AT32F403ZET6, _512KB, LQFP144},
    {0x27, 0x502C9, __AT32F403VET6, _512KB, LQFP100},
    {0x27, 0x502CA, __AT32F403RET6, _512KB, LQFP64},
    {0x27, 0x502CB, __AT32F403CET6, _512KB, LQFP48},
    {0x27, 0x5034C, __AT32F403CGU6, _1024KB, QFN48},
    {0x27, 0x502CD, __AT32F403CEU6, _512KB, QFN48},
    {0x27, 0x5024E, __AT32F403CCU6, _256KB, QFN48},
    {0x47, 0x30240, __AT32F413RCT7, _256KB, LQFP64},
    {0x47, 0x301C1, __AT32F413RBT7, _128KB, LQFP64},
    {0x47, 0x30242, __AT32F413CCT7, _256KB, LQFP48},
    {0x47, 0x301C3, __AT32F413CBT7, _128KB, LQFP48},
    {0x47, 0x30244, __AT32F413KCU7_4, _256KB, QFN32},
    {0x47, 0x301C5, __AT32F413KBU7_4, _128KB, QFN32},
    {0x47, 0x30106, __AT32F413C8T7, _64KB, LQFP48},
    {0x47, 0x30247, __AT32F413CCU7, _256KB, QFN48},
    {0x47, 0x301C0, __AT32F413CBU7, _128KB, QFN48},
    {0x57, 0x30240, __AT32F415RCT7, _256KB, LQFP64},
    {0x57, 0x30241, __AT32F415CCT7, _256KB, LQFP48},
    {0x57, 0x30242, __AT32F415KCU7_4, _256KB, QFN32},
    {0x57, 0x30243, __AT32F415RCT7_7, _256KB, LQFP64},
    {0x57, 0x301C4, __AT32F415RBT7, _128KB, LQFP64},
    {0x57, 0x301C5, __AT32F415CBT7, _128KB, LQFP48},
    {0x57, 0x301C6, __AT32F415KBU7_4, _128KB, QFN32},
    {0x57, 0x301C7, __AT32F415RBT7_7, _128KB, LQFP64},
    {0x57, 0x30108, __AT32F415R8T7, _64KB, LQFP64},
    {0x57, 0x30109, __AT32F415C8T7, _64KB, LQFP48},
    {0x57, 0x3010B, __AT32F415R8T7_7, _64KB, QFN32},
    {0x57, 0x3010A, __AT32F415K8U7_4, _64KB, LQFP64},
    {0x57, 0x301CD, __AT32F415CBU7, _128KB, QFN48},
    {0x57, 0x3024C, __AT32F415CCU7, _256KB, QFN48},
    {0x77, 0x50240, __AT32F403AVCT7, _256KB, LQFP100},
    {0x77, 0x50241, __AT32F403ARCT7, _256KB, LQFP64},
    {0x77, 0x50242, __AT32F403ACCT7, _256KB, LQFP48},
    {0x77, 0x50243, __AT32F403ACCU7, _256KB, QFN48},
    {0x77, 0x50344, __AT32F403AVGT7, _1024KB, LQFP100},
    {0x77, 0x50345, __AT32F403ARGT7, _1024KB, LQFP64},
    {0x77, 0x50346, __AT32F403ACGT7, _1024KB, LQFP48},
    {0x77, 0x50347, __AT32F403ACGU7, _1024KB, QFN48},
    {0x77, 0x502CD, __AT32F403AVET7, _512KB, LQFP100},
    {0x77, 0x502CE, __AT32F403ARET7, _512KB, LQFP64},
    {0x77, 0x503CF, __AT32F403ACET7, _512KB, LQFP48},
    {0x77, 0x503D0, __AT32F403ACEU7, _512KB, QFN48},
    {0x87, 0x50249, __AT32F407VCT7, _256KB, LQFP100},
    {0x87, 0x5024A, __AT32F407RCT7, _256KB, LQFP64},
    {0x87, 0x5034B, __AT32F407VGT7, _1024KB, LQFP100},
    {0x87, 0x5034C, __AT32F407RGT7, _1024KB, LQFP64},
    {0x87, 0x502D1, __AT32F407VET7, _512KB, LQFP100},
    {0x87, 0x502D2, __AT32F407RET7, _512KB, LQFP64},
};
const uint32_t at32_mcu_flash_size[AT32_MCU_FLASH_COUNT] =
{
    0x10000,
    0x20000,
    0x40000,
    0x80000,
    0x100000,
};
const char AT32_MCU_Model_str[AT32_MCU_MODEL_COUNT + 1][16] =
    {
        "AT32F403ZCT6",
        "AT32F403VCT6",
        "AT32F403RCT6",
        "AT32F403CCT6",
        "AT32F403ZGT6",
        "AT32F403VGT6",
        "AT32F403RGT6",
        "AT32F403CGT6",
        "AT32F403ZET6",
        "AT32F403VET6",
        "AT32F403RET6",
        "AT32F403CET6",
        "AT32F403CGU6",
        "AT32F403CEU6",
        "AT32F403CCU6",
        "AT32F413RCT7",
        "AT32F413RBT7",
        "AT32F413CCT7",
        "AT32F413CBT7",
        "AT32F413KCU7-4",
        "AT32F413KBU7-4",
        "AT32F413C8T7",
        "AT32F413CCU7",
        "AT32F413CBU7",
        "AT32F415RCT7",
        "AT32F415CCT7",
        "AT32F415KCU7-4",
        "AT32F415RCT7-7",
        "AT32F415RBT7",
        "AT32F415CBT7",
        "AT32F415KBU7-4",
        "AT32F415RBT7-7",
        "AT32F415R8T7",
        "AT32F415C8T7",
        "AT32F415R8T7-7",
        "AT32F415K8U7-4",
        "AT32F415CBU7",
        "AT32F415CCU7",
        "AT32F403AVCT7",
        "AT32F403ARCT7",
        "AT32F403ACCT7",
        "AT32F403ACCU7",
        "AT32F403AVGT7",
        "AT32F403ARGT7",
        "AT32F403ACGT7",
        "AT32F403ACGU7",
        "AT32F403AVET7",
        "AT32F403ARET7",
        "AT32F403ACET7",
        "AT32F403ACEU7",
        "AT32F407VCT7",
        "AT32F407RCT7",
        "AT32F407VGT7",
        "AT32F407RGT7",
        "AT32F407VET7",
        "AT32F407RET7",
        "unknown",
};
const char AT32_MCU_FLASH_str[AT32_MCU_FLASH_COUNT + 1][8] =
    {
        "64KB",
        "128KB",
        "256KB",
        "512KB",
        "1024KB",
        "unknown",
};
const char AT32_MCU_Footprint_str[AT32_MCU_Footprint_COUNT + 1][8] =
    {
        "QFN32",
        "QFN48",
        "LQFP48",
        "LQFP64",
        "LQFP100",
        "LQFP144",
        "unknown",
};
const char AT32_MCU_Cortex_str[Cortex_count + 1][16] =
    {
        "Cortex-M0-r0p0",
        "Cortex-M0+-r0p0",
        "Cortex-M1-r0p1",
        "Cortex-M1-r0p1",
        "Cortex-M1-r1p0",
        "Cortex-M3-r0p0",
        "Cortex-M3-r1p0",
        "Cortex-M3-r1p1",
        "Cortex-M3-r2p0",
        "Cortex-M3-r2p1",
        "Cortex-M4-r0p0",
        "Cortex-M4-r0p1",
        "unknown",
};
const uint32_t AT32_MCU_CORE_ID[Cortex_count] =
    {
        0x410CC200,
        0x410CC600,
        0x410CC210,
        0x410CC211,
        0x411CC210,
        0x410FC230,
        0x410FC231,
        0x411FC231,
        0x412FC230,
        0x412FC231,
        0x410FC240,
        0x410FC241,

};
char *at32_mcu_mode2str(AT32_MCU_TYPE *mcu)
{
    if (mcu->Model < AT32_MCU_MODEL_COUNT)
        return (char *)AT32_MCU_Model_str[mcu->Model];
    else
        return (char *)AT32_MCU_Model_str[AT32_MCU_MODEL_COUNT];
}
char *at32_mcu_flash2str(AT32_MCU_TYPE *mcu)
{
    if (mcu->Flash < AT32_MCU_FLASH_COUNT)
        return (char *)AT32_MCU_FLASH_str[mcu->Flash];
    else
        return (char *)AT32_MCU_FLASH_str[AT32_MCU_FLASH_COUNT];
}
char *at32_mcu_footprint2str(AT32_MCU_TYPE *mcu)
{
    if (mcu->Footprint < AT32_MCU_Footprint_COUNT)
        return (char *)AT32_MCU_Footprint_str[mcu->Footprint];
    else
        return (char *)AT32_MCU_Footprint_str[AT32_MCU_Footprint_COUNT];
}
uint8_t at32_mcu_who_am_i(AT32_MCU_TYPE *mcu)
{
    uint8_t serial = AT32_MCU_SERIAL_ID;
    uint32_t id = AT32_MCU_PID;
    serial = ((id >> 28) | (serial << 4));
    id = id & 0xFFFFF;
    // usartPrintf(">>s:%d - id:%d\n\n", serial, id);
    AT32MCU_FOREACH(i)
    {
        // usartPrintf(">>s:%d - %d\n\n", AT32_MCU_LIST[i].Serial, AT32_MCU_LIST[i].ID);
        if ((AT32_MCU_LIST[i].Serial == serial) && (AT32_MCU_LIST[i].ID == id))
        {
            mcu->Serial = serial;
            mcu->ID = id;
            mcu->Model = AT32_MCU_LIST[i].Model;
            mcu->Flash = AT32_MCU_LIST[i].Flash;
            mcu->Footprint = AT32_MCU_LIST[i].Footprint;
            return 0;
        }
    }
    mcu->Serial = serial;
    mcu->ID = id;
    mcu->Model = AT32_MCU_MODEL_COUNT;
    mcu->Flash = AT32_MCU_Footprint_COUNT;
    mcu->Footprint = AT32_MCU_Footprint_COUNT;

    return 1;
}
uint32_t at32_mcu_get_coreID(void)
{
    /*
    内核的系统控制块(SCB) 中存在一个名为CPU ID基本寄存器的寄存器, 它是只读的, 其中包括处
    理器类型和版本号。该寄存器的地址为0XE000ED00(只支
    持特权访问),在程序中, 可以利用SCB->CPUID访问该寄存器,也可通过绝对地址访问,如
    *(uint32_t *)0xE000ED00。
    */
    return *(uint32_t *)0xE000ED00;
}
char *at32_mcu_core2str(uint32_t id)
{
    for (uint16_t i = 0; i < Cortex_count; i++)
    {
        if (id == AT32_MCU_CORE_ID[i])
            return (char *)AT32_MCU_Cortex_str[i];
    }
    return (char *)AT32_MCU_Cortex_str[Cortex_count];
}
uint8_t at32_mcu_init_model(AT32_MCU_TYPE *mcu, char *model)
{
    for(uint16_t i = 0; i < AT32_MCU_MODEL_COUNT; i++)
    {
        if(strcmp(model, AT32_MCU_Model_str[i]) == 0)
        {
            memcpy(mcu, &AT32_MCU_LIST[i], sizeof(AT32_MCU_TYPE));
            return 0;
        }
    }
    mcu->Serial = 0;
    mcu->ID = 0;
    mcu->Model = AT32_MCU_MODEL_COUNT;
    mcu->Flash = AT32_MCU_Footprint_COUNT;
    mcu->Footprint = AT32_MCU_Footprint_COUNT;
    return 1;
}

 

        

BOOTLOADER程序

通过以上信息整理,我们就能知道bootloader需要一些什么操作了:

typedef struct bootloader_info
{
    uint32_t version;
    uint32_t currentAddr;
    uint32_t totalSize;
    uint32_t SizeLeft;
    uint32_t timeOut;
    uint32_t dt;
    uint32_t appAddr;
    uint32_t appSize;
    uint32_t fwInfoAddr;
    uint32_t sectorSize : 16;
    uint32_t sectorCount : 16;
    uint16_t (*read)(uint8_t *buf);
    uint16_t (*write)(uint8_t *buf, uint16_t len);
    uint32_t (*millis)(void);
    void (*flashLock)(void);
    void (*flashUnlock)(void);
    void (*flashSectorErase)(uint32_t addr);
    void (*flashRead)(uint32_t addr, uint8_t *buf, uint32_t len);
    void (*flashWrite)(uint32_t addr, uint8_t *buf, uint32_t len);
    void (*jumpToAddr)(uint32_t addr);
    void (*reset)(void);
    struct pack_info pack;
    firmware_info_t fw_info;
    AT32_MCU_TYPE whoAmI;
    bool synecd;
} bl_info_t;

 

程序跳转

我这里用了usb,需要关闭usb,不然usb硬件还在和电脑连接,cpu又不在了,会出大事。

typedef void (*iapfun)(void);
volatile iapfun jump2app;
void IAP_Load_APP(uint32_t appxaddr)
{
	SysTick->CTRL = 0; // stop systick
	usbd_disconnect(&usb_core_dev);
	crm_periph_clock_enable(CRM_USB_PERIPH_CLOCK, FALSE);
	crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, FALSE);
	nvic_irq_disable(USBFS_L_CAN1_RX0_IRQn);
	__disable_irq();
	jump2app = (iapfun) (* (volatile uint32_t *)(appxaddr + 4)); // 用户代码区第二个字为程序开始地址(复位地址)
	__set_MSP(*(volatile uint32_t *)appxaddr);
	__set_PSP(*(volatile uint32_t *)appxaddr);							//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
	__set_CONTROL(0);
	jump2app(); // 跳转到APP.
}

单片机bootloader代码

/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */

#ifndef __BOOTLOADER_H
#define __BOOTLOADER_H
#include <stdint.h>
#include <stdbool.h>
#include "AT32Models.h"

#define Version_ID1 0x10
#define Version_ID2 0x1C



/**
 * @brief
 * TOTAL FLASH SIZE: 256Kb
 * [Bloader:0-14Kb][FWINFO:14-16Kb][APP:16-252Kb][CONFIG0:252-254Kb][CONFIG2:254-256Kb]
 */
#define FLASH_SECTOR_COUNT 128
#define FLASH_SectorSize (uint32_t)2048
#define FLASH_Base_Addr (uint32_t)0x08000000
#define FLASH_IAP_Size (uint32_t)0x4000 // 0x08004000~0x0800FFFF
#define FLASH_APP_ADDR (uint32_t)(FLASH_Base_Addr | FLASH_IAP_Size)

#define FLASH_TOTAL_SIZE (FLASH_SECTOR_COUNT * FLASH_SectorSize)
#define FLASH_END_ADDR (FLASH_Base_Addr + FLASH_TOTAL_SIZE)

#define FLASH_APP_END_ADDR (FLASH_END_ADDR - FLASH_SectorSize * 2)
#define FLASH_APP_SIZE (FLASH_TOTAL_SIZE - FLASH_SectorSize * 3 - FLASH_IAP_Size)

#define FLASH_FWINFO_ADDR (FLASH_APP_ADDR - FLASH_SectorSize)
#define FLASH_CONFIG_ADDR0 (FLASH_APP_END_ADDR + FLASH_SectorSize)
#define FLASH_CONFIG_ADDR1 (FLASH_CONFIG_ADDR0 + FLASH_SectorSize)

#define FRAME_DATA_SIZE 2048
#define FLASH_FW_MAGIC 0x5A1234A5

#define VESION2INT(MAIN, SUB, BUILD, NUM) (uint32_t)(((MAIN) << 24) | ((SUB) << 16) | ((BUILD) << 8) | (NUM))
#define PID2INT(board, core, product, function) (uint32_t)(((board) << 24) | ((core) << 16) | ((product) << 8) | (function))
#define DATE2INT(YEAR, MONTH, DAY) (uint32_t)(((YEAR) << 16) | ((MONTH) << 8) | (DAY))
enum
{
    CMD_SYNC = 0XF4,
    CMD_ACK = 0XAF,
    CMD_NACK = 0XFC,
    CMD_VERSION = 0X01,
    CMD_GETID = 0X02,
    CMD_WRITE = 0X03,
    CMD_READ = 0X04,
    CMD_RESET = 0X05,
    CMD_JUMP = 0X06,
    CMD_ERASE = 0X07,
    CMD_W_FW_INFO = 0X08,
    CMD_R_FW_INFO = 0X09,
    CMD_LOG = 0X0A,
};

typedef union bootloader
{
    struct
    {
        uint32_t infoCrc;//固件信息校验
        uint32_t magic;//固件信息标志,固定为0x5A1234A5,代码里写入
        uint32_t start_addr;//固件起始地址
        uint32_t size;//固件大小
        uint32_t FWcrc;//固件校验
        uint32_t version;//版本号,格式为:主版本号.子版本号.编译号.序号
        uint32_t pid;//产品ID,格式为:主板ID.核心ID.产品ID.功能ID
        uint32_t date;//编译日期
        uint32_t bootTimes;//启动次数
        AT32_MCU_TYPE whoAmI;
        char name[256];//固件名称,代码里写入,后面再改也行
    };
    uint8_t buffer[FLASH_SectorSize];
} firmware_info_t;

struct pack_info
{
    union
    {
        struct
        {
            uint32_t crc;
            uint32_t addr;
            uint32_t cmd1 : 8;
            uint32_t cmd2 : 8;
            uint32_t data_size : 16;
            uint8_t data[FRAME_DATA_SIZE];
        };
        uint8_t buffer[FRAME_DATA_SIZE + 12];
    };
    uint32_t buffer_size;
};
typedef struct bootloader_info
{
    uint32_t version;
    uint32_t currentAddr;
    uint32_t totalSize;
    uint32_t SizeLeft;
    uint32_t timeOut;
    uint32_t dt;
    uint32_t appAddr;
    uint32_t appSize;
    uint32_t fwInfoAddr;
    uint32_t sectorSize : 16;
    uint32_t sectorCount : 16;
    uint16_t (*read)(uint8_t *buf);
    uint16_t (*write)(uint8_t *buf, uint16_t len);
    uint32_t (*millis)(void);
    void (*flashLock)(void);
    void (*flashUnlock)(void);
    void (*flashSectorErase)(uint32_t addr);
    void (*flashRead)(uint32_t addr, uint8_t *buf, uint32_t len);
    void (*flashWrite)(uint32_t addr, uint8_t *buf, uint32_t len);
    void (*jumpToAddr)(uint32_t addr);
    void (*reset)(void);
    struct pack_info pack;
    firmware_info_t fw_info;
    AT32_MCU_TYPE whoAmI;
    bool synecd;
} bl_info_t;

void bl_pack_clear(bl_info_t *bl);
uint32_t bl_pack_recv(bl_info_t *bl);
void bl_pack_send(bl_info_t *bl, uint8_t cmd, uint32_t addr, uint8_t *data, uint16_t data_size);

void bootloader_handle(bl_info_t *bl);
#endif
/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */
// #include "at32f413.h"
#include <stdbool.h>
#include "systick.h"
#include <string.h>
#include "bootloader.h"
#include "crc_func_def.h"
#include "CRC32.h"


#define SERIAL_BUG 0
#define CRC32_INIT_VALUE 0xFFFFFFFF
#define CRC32_XOR_VALUE 0xFFFFFFFF

uint32_t crc32_cal(const uint8_t *pdata, uint32_t len, uint32_t initial, uint32_t finalXor, bool inputReflected, bool resultReflected)
{
    uint32_t crc = initial;
    uint32_t temp1 = 0, temp2 = 0, pos = 0;
    const uint32_t *pTable = crc32__table;
    for (int i = 0; i < len; i++)
    {
        uint32_t curByte = pdata[i];
        if (inputReflected)
        {
            curByte = Reflect8(pdata[i]);
        }
        temp1 = (crc ^ (curByte << (32 - 8)));
        pos = (temp1 >> (32 - 8)) & 0xFF;
        temp2 = (temp1 << 8);
        crc = (temp2 ^ pTable[pos]);
    }
    if (resultReflected)
    {
        crc = Reflect32(crc);
    }
    return (crc ^ finalXor);
}

void bl_pack_clear(bl_info_t *bl)
{
    memset(bl->pack.buffer, 0, sizeof(bl->pack.buffer));
    bl->pack.buffer_size = 0;
}
void bl_pack_send(bl_info_t *bl, uint8_t cmd, uint32_t addr, uint8_t *data, uint16_t data_size)
{
    uint32_t crc = 0;
    bl_pack_clear(bl);
    bl->pack.cmd1 = cmd;
    bl->pack.cmd2 = (uint8_t)~cmd;
    bl->pack.addr = addr;
    memcpy(bl->pack.data, data, data_size);
    bl->pack.data_size = data_size;
    bl->pack.buffer_size = 12 + bl->pack.data_size;
    crc = crc32_cal(bl->pack.buffer + 4, bl->pack.buffer_size - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    bl->pack.crc = crc;
    bl->write(bl->pack.buffer, bl->pack.buffer_size);
    bl->write((uint8_t *)&crc, 0); /**FIXME USB-CDC BUG! */
#if (SERIAL_BUG)
    /**FIXME  长度是64的倍数时,会卡住不发送 */

#endif
}
uint32_t bl_pack_recv(bl_info_t *bl)
{
    uint32_t crc = -1;
    bl_pack_clear(bl);
    bl->pack.buffer_size = bl->read(bl->pack.buffer);
    if (bl->pack.buffer_size > 0)
    {
        uint32_t crcLen = bl->pack.buffer_size - 4;
#if (SERIAL_BUG)
        crcLen -= 4;
#endif
        crc = crc32_cal(bl->pack.buffer + 4, crcLen, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
        if (crc == bl->pack.crc)
        {
            return 0;
        }
    }
    return crc;
}
void bl_write_handle(bl_info_t *bl)
{
    if (bl->pack.addr >= bl->appAddr && bl->pack.addr <= (bl->appAddr + bl->appSize))
    {
        bl->flashWrite(bl->pack.addr, bl->pack.data, bl->pack.data_size);
        bl_pack_send(bl, CMD_ACK, 0, NULL, 0);
    }
    else
    {
        bl_pack_send(bl, CMD_NACK, 0, NULL, 51);
    }
}
void bl_read_handle(bl_info_t *bl)
{
    uint8_t readBuffer[FLASH_SectorSize];
    if (bl->pack.addr >= bl->appAddr && bl->pack.addr < (bl->appAddr + bl->appSize))
    {
        uint32_t sizeRemain = bl->appAddr + bl->appSize - bl->pack.addr;
        uint32_t readSize = bl->pack.data[0] | (bl->pack.data[1] << 8) | (bl->pack.data[2] << 16) | (bl->pack.data[3] << 24);
        readSize = readSize > sizeRemain ? sizeRemain : readSize;

        bl->flashRead(bl->pack.addr, readBuffer, readSize);
        bl_pack_send(bl, CMD_ACK, bl->pack.addr, readBuffer, readSize);
    }
    else
    {
        bl_pack_send(bl, CMD_NACK, 0, NULL, 41);
    }
}

void bl_erase_handle(bl_info_t *bl)
{
    if (bl->pack.addr >= bl->appAddr && bl->pack.addr < (bl->appAddr + bl->appSize))
    {
        uint32_t sizeRemain = bl->appAddr + bl->appSize - bl->pack.addr;
        uint32_t eraseSize = 0;

        eraseSize = bl->pack.data[0] | (bl->pack.data[1] << 8) | (bl->pack.data[2] << 16) | (bl->pack.data[3] << 24);
        eraseSize = eraseSize > sizeRemain ? sizeRemain : eraseSize;
        uint32_t endAddr = bl->pack.addr + eraseSize;
        bl->flashUnlock();
        uint32_t eraseAddr = bl->pack.addr;
        for (; eraseAddr < endAddr; eraseAddr += bl->sectorSize)
        {
            bl_pack_send(bl, CMD_ACK, eraseAddr, (uint8_t *)&endAddr, 4);
            bl->flashSectorErase(eraseAddr);
        }
        bl_pack_send(bl, CMD_ACK, eraseAddr, (uint8_t *)&endAddr, 4);
        bl->flashLock();
    }
    else
    {
        bl_pack_send(bl, CMD_NACK, 0, NULL, 21);
    }
}

void bl_jump_handle(bl_info_t *bl)
{
    if (bl->pack.addr >= bl->appAddr && bl->pack.addr < (bl->appAddr + bl->appSize))
    {
        bl_pack_send(bl, CMD_ACK, bl->pack.addr, NULL, 0);
        if (bl->jumpToAddr)
            bl->jumpToAddr(bl->pack.addr);
    }
    else
    {
        bl_pack_send(bl, CMD_NACK, bl->appAddr, NULL, 31);
    }
}
void bl_W_fwInfo_handle(bl_info_t *bl)
{
    int err = 0;
    memset(&bl->fw_info, 0, sizeof(firmware_info_t));
    memcpy(&bl->fw_info, bl->pack.data, bl->pack.data_size);
    uint32_t crc = crc32_cal((const uint8_t *)&bl->fw_info + 4, sizeof(firmware_info_t) - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    if (crc != bl->fw_info.infoCrc)
    {
        err = 11;
        goto NackExit;
    }
    if (bl->fw_info.magic != FLASH_FW_MAGIC)
    {
        err = 12;
        goto NackExit;
    }
    if (bl->fw_info.size > bl->appSize || bl->fw_info.size == 0)
    {
        err = 13;
        goto NackExit;
    }
    if (bl->fw_info.start_addr < bl->appAddr || bl->fw_info.start_addr >= (bl->appAddr + bl->appSize))
    {
        err = 14;
        goto NackExit;
    }
    if (bl->fw_info.whoAmI.ID != bl->whoAmI.ID || bl->fw_info.whoAmI.Serial != bl->whoAmI.Serial)
    {
        err = 15;
        goto NackExit;
    }
    bl->flashUnlock();
    bl->flashSectorErase(bl->fwInfoAddr);
    bl->flashWrite(bl->fwInfoAddr, (uint8_t *)&bl->fw_info, sizeof(firmware_info_t));
    bl->flashLock();
    bl_pack_send(bl, CMD_ACK, 0, NULL, 0);
    return;

NackExit:
    bl_pack_send(bl, CMD_NACK, 0, NULL, err);
}
void bl_R_fwInfo_handle(bl_info_t *bl)
{

    uint16_t dataSize = bl->pack.addr != 0 ? bl->pack.addr : sizeof(firmware_info_t);
    bl->flashRead(bl->fwInfoAddr, (uint8_t *)&bl->fw_info, sizeof(firmware_info_t));

    bl_pack_send(bl, CMD_ACK, bl->fwInfoAddr, (uint8_t *)&bl->fw_info, dataSize);
}
uint32_t bl_check_fw(bl_info_t *bl)
{
    uint32_t crc = 0;

    bl->flashRead(bl->fwInfoAddr, (uint8_t *)&bl->fw_info, sizeof(firmware_info_t));
    crc = crc32_cal((uint8_t *)&bl->fw_info + 4, sizeof(firmware_info_t) - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    if (bl->fw_info.magic == FLASH_FW_MAGIC && bl->fw_info.size < bl->appSize && crc == bl->fw_info.infoCrc)
    {
        const uint8_t *fwData = (const uint8_t *)bl->fw_info.start_addr;
        if (bl->fw_info.bootTimes == 0)
        {
            /**First time,  check crc*/
            crc = crc32_cal(fwData, bl->fw_info.size, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
            if (crc == bl->fw_info.FWcrc)
            {
                // bl_pack_send(bl, CMD_LOG, bl->appAddr, (uint8_t *)"bl->jumpToAddr\r\n", 17);
                bl->jumpToAddr(bl->appAddr);
                return 0;
            }
        }
        else if (bl->fw_info.bootTimes == 0xFFFFFFFF)
        {
            // bl_pack_send(bl, CMD_LOG, bl->fwInfoAddr, (uint8_t *)"system failure\r\n", 17);
            /**system failure*/
            return 0;
        }
        else
        {
            bl->jumpToAddr(bl->appAddr);
            return 0;
        }
    }
    // bl_pack_send(bl, CMD_LOG, bl->fwInfoAddr, (uint8_t *)"fw crc error\r\n", 15);
    return 1;
}

void bootloader_handle(bl_info_t *bl)
{
#if 1
    bl->pack.buffer_size = bl->read(bl->pack.buffer);
    if (bl->pack.buffer_size != 0)
    {
        uint32_t crcLen = bl->pack.buffer_size - 4;
        uint32_t crc = crc32_cal(bl->pack.buffer + 4, crcLen, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
        uint8_t cmd2 = ~bl->pack.cmd2;
        if (bl->pack.crc == crc)
        {
            if (bl->pack.cmd1 == cmd2)
            {
                if(!bl->synecd)
                {
                    if(bl->pack.cmd1 == CMD_SYNC)
                    {
                        bl->synecd = true;
                        bl_pack_send(bl, CMD_ACK, 0, NULL, 0);
                    }
                    else
                    {
                        bl_pack_send(bl, CMD_NACK, 0, NULL, 3);
                    }
                }
                else
                {
                    /**NOTE SYNC */
                    switch (bl->pack.cmd1)
                    {
                    case CMD_SYNC:
                        bl->synecd = true;
                        bl_pack_send(bl, CMD_ACK, 0, NULL, 0);
                        break;
                    case CMD_VERSION:
                        bl_pack_send(bl, CMD_ACK, 0, (uint8_t *)&bl->version, 4);
                        break;
                    case CMD_GETID:
                        bl_pack_send(bl, CMD_ACK, 0, (uint8_t *)&bl->whoAmI, sizeof(bl->whoAmI));
                        break;
                    case CMD_WRITE:
                        bl_write_handle(bl);
                        break;
                    case CMD_READ:
                        bl_read_handle(bl);
                        break;
                    case CMD_RESET:
                        bl_pack_send(bl, CMD_ACK, 0, NULL, 0);
                        if (bl->reset)
                            bl->reset();
                        break;
                    case CMD_JUMP:
                        bl_jump_handle(bl);
                        break;
                    case CMD_ERASE:
                        bl_erase_handle(bl);
                        break;
                    case CMD_W_FW_INFO:
                        bl_W_fwInfo_handle(bl);
                        break;
                    case CMD_R_FW_INFO:
                        bl_R_fwInfo_handle(bl);
                        break;
                    default:
                        /** unknown cmd */
                        bl_pack_send(bl, CMD_NACK, 0, NULL, 1);
                        break;
                    }
                }
            }
            else
            {
                /**NOTE cmd error */
                bl_pack_send(bl, CMD_NACK, 0, NULL, 2);
            }
        }
        else
        {
            /**NOTE crc error */
            bl_pack_send(bl, CMD_NACK, 0, NULL, 3);
        }
        memset(bl->pack.buffer, 0, bl->pack.buffer_size);
        bl->pack.buffer_size = 0;
    }
#endif
    if (bl->synecd == false)
    {
        if (bl->millis() > (bl->dt + 100))
        {
            // NOTE IAP timeout. Exit.
            bl_check_fw(bl);
            bl->dt = bl->millis();
        }
    }
}

 

上位机代码

(使用Linux gcc编译,或者cygwin编译):

/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include "bootloader.h"
#include "AT32Models.h"

#define CRC32_INIT_VALUE 0xFFFFFFFF
#define CRC32_XOR_VALUE 0xFFFFFFFF

uint32_t crc32_cal(const uint8_t *pdata, int32_t len, uint32_t initial, uint32_t finalXor, bool inputReflected, bool resultReflected);

firmware_info_t firmware;
bl_info_t bootloader;
bl_info_t *bl = &bootloader;
uint8_t *firmware_data = NULL;
int f_com = 0;
uint16_t SetSize = 0;
int readout = 0;

uint16_t bl_write(uint8_t *buff, uint16_t len)
{
    if (f_com == 0)
    {
        printf("bl_write error, f_com: 0x%08X\n", f_com);
        return 0;
    }
    uint16_t ret = write(f_com, buff, len);
    // tcflush(f_com, TCIOFLUSH);
    if (ret != len)
        printf("bl_write error, ret:%d\n", ret);
    return ret;
}
uint16_t bl_read(uint8_t *buff)
{
    if (!f_com)
    {
        printf("bl_read error, f_com: 0x%08X\n", f_com);
        return 0;
    }

    uint16_t len = 0;
    for (;;)
    {
        if (read(f_com, buff, 1) == 1)
        {
            buff++;
            len++;
        }
        else
        {
            return len;
        }
    }
    return len;
}
int firmware_extract(FILE *f, firmware_info_t *i)
{
    if (!f)
        return -1;
    size_t readsize = fread(i, 1, sizeof(firmware_info_t), f);
    if (readsize != sizeof(firmware_info_t))
    {
        printf("firmware_extract error, readsize:%d\n", readsize);
        return -1;
    }

    // printf("readsize:%d\n",readsize);
    uint32_t crc = crc32_cal((const uint8_t *)i + 4, sizeof(firmware_info_t) - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    if (crc != i->infoCrc)
    {
        printf("firmware_extract error, crc: 0x%08X, infoCrc: 0x%08X\n", crc, i->infoCrc);
        return -1;
    }
    printf(" *name     : %s\n", i->name);
    printf(" *infoCrc  : 0x%08X\n", i->infoCrc);
    printf(" *magic    : 0x%08X\n", i->magic);
    printf(" *startAddr: 0x%08X\n", i->start_addr);
    printf(" *size     : 0x%08X\n", i->size);
    printf(" *FWcrc    : 0x%08X\n", i->FWcrc);
    printf(" *version  : 0x%08X\n", i->version);
    printf(" *pid      : 0x%08X\n", i->pid);
    printf(" *date     : 0x%08X\n", i->date);
    printf(" *Serial   : 0x%08X\n", i->whoAmI.Serial);
    printf(" *ID       : 0x%08X\n", i->whoAmI.ID);
    printf(" *Model    : 0x%08X\n", i->whoAmI.Model);
    printf(" *Flash    : 0x%08X\n", i->whoAmI.Flash);
    printf(" *Footprint: 0x%08X\n", i->whoAmI.Footprint);

    firmware_data = (uint8_t *)malloc(i->size);
    if(!firmware_data)
    {
        printf("firmware_extract error, malloc failed\n");
        return -1;
    }
    readsize = fread(firmware_data, 1, i->size, f);

    // FILE *fw = fopen("./write.bin", "wb");
    // fwrite(firmware_data, 1, i->size, fw);
    // fclose(fw);

    if (readsize != i->size)
    {
        printf("firmware_extract error, readsize:%d/%d\n", readsize, i->size);
        return -1;
    }
    crc = crc32_cal(firmware_data, i->size, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    if (crc != i->FWcrc)
    {
        printf("firmware_extract error, crc:%x, FWcrc:%x\n", crc, i->FWcrc);
        return -1;
    }
    printf("FWcrc: 0x%08X\n", crc);
    return 0;
}
void delay_ms(uint32_t ms)
{
    usleep(ms * 1000);
}
int serial_init(int *fcom, char *com, int baudrate)
{
    /**get com num */

    char linuxCom[12] = "/dev/ttyS";
    int comIndex = -1;

    if (strncmp(com, "COM", 3) == 0)
    {
        comIndex = atoi(com + 3) - 1;
        if (comIndex < 9)
            linuxCom[9] = comIndex + '0';
        else if (comIndex < 100)
        {
            linuxCom[9] = comIndex / 10 + '0';
            linuxCom[10] = comIndex % 10 + '0';
        }
        else
        {
            printf("com error!\n");
            return -1;
        }
        printf("\ncomIndex = %d\n", comIndex);
        printf("com = %s\n", linuxCom);
    }
    else
    {
        printf("com error!\n");
        return -1;
    }

    /**get baudrate */
    printf("baudrate:%d \n", baudrate);
    /**open and init com port*/
    *fcom = open(linuxCom, O_RDWR | O_NOCTTY | O_NDELAY);
    if (*fcom == -1)
    {
        printf("com port open error! %s\n", strerror(errno));
        return -1;
    }
    printf("serial open\n");

    struct termios options;
    tcgetattr(*fcom, &options);
    switch (baudrate)
    {
    case 9600:
        cfsetispeed(&options, B9600); // 设置输入波特率为921600
        cfsetospeed(&options, B9600); // 设置输出波特率为921600
        break;
    case 115200:
        cfsetispeed(&options, B115200); // 设置输入波特率为921600
        cfsetospeed(&options, B115200); // 设置输出波特率为921600
        break;
    case 921600:
        cfsetispeed(&options, B921600); // 设置输入波特率为921600
        cfsetospeed(&options, B921600); // 设置输出波特率为921600
        break;

    default:

        cfsetispeed(&options, B921600); // 设置输入波特率为921600
        cfsetospeed(&options, B921600); // 设置输出波特率为921600
        break;
    }
    options.c_cflag |= (CLOCAL | CREAD);                // 忽略调制解调器控制线,启用接收器
    options.c_cflag &= ~PARENB;                         // 无奇偶校验
    options.c_cflag &= ~CSTOPB;                         // 1位停止位
    options.c_cflag &= ~CSIZE;                          // 清除数据位设置
    options.c_cflag |= CS8;                             // 设置数据位为8位
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 非规范模式,禁止
    options.c_oflag &= ~OPOST;                          // 原始输出
    options.c_cc[VTIME] = 1;                            // 设置超时1
    options.c_cc[VMIN] = 1;                             // 设置最小字符为1
    tcsetattr(*fcom, TCSANOW, &options);

    printf("serial init done\n");
    return 0;
}
int dev_sync(bl_info_t *bl)
{
    int sendtry = 0;
    printf("\nTry to sync");
    while (1)
    {
        tcflush(f_com, TCIOFLUSH);
        bl_pack_send(bl, CMD_SYNC, 0, NULL, 0);
        // printf("\nsending:");
        // for (int i = 0; i < bl->pack.buffer_size; i++)
        // {
        //     if (i % 16 == 0)
        //         printf("\n");
        //     printf("%02X ", bl->pack.buffer[i]);
        // }
        // printf("\n");
        delay_ms(1);
        if (bl_pack_recv(bl) == 0 && bl->pack.cmd1 == CMD_ACK)
        {
            printf("Sync success!\n");
            // break;
            return 0;
        }
        else
        {
            delay_ms(10);
            sendtry++;
            if (sendtry >= 300)
            {
                printf("sync timeout! exit!\n");
                return -2;
            }
        }
        printf("\nrev: %d", bl->pack.buffer_size);
        for (int i = 0; i < bl->pack.buffer_size; i++)
        {
            if (i % 16 == 0)
                printf("\n");
            printf("%02X ", bl->pack.buffer[i]);
        }
        printf("\n");
    }
    printf("\n");
    return -1;
}
int getVersion(bl_info_t *bl)
{
    printf("\nget version\n");
    int sendtry = 0;
    while (1)
    {
        tcflush(f_com, TCIOFLUSH);
        bl_pack_send(bl, CMD_VERSION, 0, NULL, 0);
        // printf("\nsending:");
        // for (int i = 0; i < bl->pack.buffer_size; i++)
        // {
        //     if (i % 16 == 0)
        //         printf("\n");
        //     printf("%02X ", bl->pack.buffer[i]);
        // }
        // printf("\n");
        delay_ms(1);
        if (bl_pack_recv(bl) == 0 && bl->pack.cmd1 == CMD_ACK)
        {
            if (bl->pack.cmd1 == CMD_ACK)
            {
                printf("get version success\n");
                memcpy(&bl->version, bl->pack.data, bl->pack.data_size);
                printf("version:0x%08X\n", bl->version);
                return 0;
            }
        }
        delay_ms(1);
        if (sendtry++ >= 3)
        {
            printf("get version timeout! exit!\n");
            return -1;
        }
        printf(".");
    }
    printf("\n");
}
int getId(bl_info_t *bl)
{
    printf("\nget id...\n");
    int sendtry = 0;
    while (1)
    {
        tcflush(f_com, TCIOFLUSH);
        bl_pack_send(bl, CMD_GETID, 0, NULL, 0);
        // printf("\nsending:");
        // for (int i = 0; i < bl->pack.buffer_size; i++)
        // {
        //     if (i % 16 == 0)
        //         printf("\n");
        //     printf("%02X ", bl->pack.buffer[i]);
        // }
        // printf("\n");
        delay_ms(1);
        if (bl_pack_recv(bl) == 0 && bl->pack.cmd1 == CMD_ACK)
        {
            if (bl->pack.cmd1 == CMD_ACK)
            {
                printf("get id success\n");
                memcpy(&bl->whoAmI, bl->pack.data, bl->pack.data_size);
                printf(" *Serial   : 0x%08X\n", bl->whoAmI.Serial);
                printf(" *ID       : 0x%08X\n", bl->whoAmI.ID);
                printf(" *Model    : 0x%08X\n", bl->whoAmI.Model);
                printf(" *Flash    : 0x%08X\n", bl->whoAmI.Flash);
                printf(" *Footprint: 0x%08X\n", bl->whoAmI.Footprint);
                printf(" *UID      : ");
                for (int i = 0; i < sizeof(bl->whoAmI.UID); i++)
                {
                    printf("%02X", bl->whoAmI.UID[i]);
                }
                printf("\n");
                int mcuMatch = 0;
                if (bl->whoAmI.Serial != firmware.whoAmI.Serial)
                {
                    printf("whoAmI Serial not match!\n");
                    mcuMatch++;
                }
                if (bl->whoAmI.Model != firmware.whoAmI.Model)
                {
                    printf("whoAmI Model not match!\n");
                    mcuMatch++;
                }
                if (bl->whoAmI.Flash != firmware.whoAmI.Flash)
                {
                    printf("whoAmI Flash not match!\n");
                    mcuMatch++;
                }
                if (mcuMatch != 0)
                {
                    printf("MCU not match!\n");
                    return -1;
                }
                printf(" - - - MCU match! - - - \n\n\n");
                // memcpy(firmware.whoAmI.UID, bl->whoAmI.UID, sizeof(bl->whoAmI.UID));
                return 0;
            }
        }
        delay_ms(1);
        if (sendtry++ >= 3)
        {
            printf("get version timeout! exit!\n");
            return -1;
        }
        printf(".");
    }
    printf("\n\n");
    return -1;
}
int get_fwInfo(bl_info_t *bl)
{
    printf("get fwInfo...\n");
    int sendtry = 0;
    while (1)
    {
        tcflush(f_com, TCIOFLUSH);
        bl_pack_send(bl, CMD_R_FW_INFO, SetSize, NULL, 0);
        // printf("\nSending:");
        // for (int i = 0; i < bl->pack.buffer_size; i++)
        // {
        //     if (i % 16 == 0)
        //         printf("\n");
        //     printf("%02X ", bl->pack.buffer[i]);
        // }
        // printf("\n");
        delay_ms(10);
        uint32_t ret = bl_pack_recv(bl);
        if (ret == 0)
        {
            if (bl->pack.cmd1 == CMD_ACK && bl->pack.data_size == sizeof(firmware_info_t))
            {
                firmware_info_t onchipFW;

                printf("get fwInfo success:crc 0x%08X\n", bl->pack.crc);
                memcpy(&onchipFW, bl->pack.data, sizeof(firmware_info_t));
                uint32_t iCrc = crc32_cal((uint8_t *)&onchipFW + 4, sizeof(firmware_info_t) - 4, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
                if (iCrc == onchipFW.infoCrc)
                {
                    int b3, b2, b1, b0;
                    printf("get onchip fwInfo:\n");
                    printf("name: %s\n", onchipFW.name);
                    b3 = onchipFW.version >> 24;
                    b2 = (onchipFW.version >> 16) & 0xFF;
                    b1 = (onchipFW.version >> 8) & 0xFF;
                    b0 = onchipFW.version & 0xFF;
                    printf("version: V%d.%d.%d.%d\n", b3, b2, b1, b0);
                    b3 = onchipFW.pid >> 24;
                    b2 = (onchipFW.pid >> 16) & 0xFF;
                    b1 = (onchipFW.pid >> 8) & 0xFF;
                    b0 = onchipFW.pid & 0xFF;
                    printf("pid: %d,%d,%d,%d\n", b3, b2, b1, b0);
                    int y, m, d;
                    y = onchipFW.date >> 16;
                    m = (onchipFW.date >> 8) & 0xFF;
                    d = onchipFW.date & 0xFF;
                    printf("date: %d-%d-%d\n", y, m, d);
                    printf("bootTimes:%d\n\n", onchipFW.bootTimes);
                }
                else
                {
                    printf("iCrc error! 0x%08X,%08X\n", iCrc, onchipFW.infoCrc);
                    printf("onchip fw empty!\n");
                }
                // for(int i=0;i<bl->pack.data_size;i++)
                // {
                //     if(i%16==0)printf("\n");
                //     printf("%02X ",bl->pack.data[i]);
                // }
                return 0;
            }
        }
        printf("\n reving: %d,0x%08X\n", bl->pack.buffer_size, ret);
        for (int i = 0; i < bl->pack.buffer_size; i++)
        {
            if (i % 16 == 0)
                printf("\n");
            printf("%02X ", bl->pack.buffer[i]);
        }
        printf("\n");
        delay_ms(1);
        if (sendtry++ >= 1)
        {
            printf("get fwInfo timeout! exit!\n");
            return -1;
        }
        printf(".");
    }
    printf("\n");
    return -2;
}
int erase_flash(bl_info_t *bl)
{
    printf("\n\nerase flash...\n");
    int sendtry = 0;
    uint32_t erase_size = firmware.size;
    uint32_t erase_addr = firmware.start_addr;
    uint32_t sectorCount = erase_size / bl->sectorSize;
    sectorCount += ((erase_size % bl->sectorSize) ? 1 : 0);
    printf("erase addr: 0x%08X, erase size: %d, sector count: %d %d\n", erase_addr, erase_size, sectorCount, bl->sectorSize);

    while (1)
    {
        tcflush(f_com, TCIOFLUSH);
        bl_pack_send(bl, CMD_ERASE, erase_addr, (uint8_t *)&erase_size, 4);

        // printf("Sending:");
        // for (int i = 0; i < bl->pack.buffer_size; i++)
        // {
        //     if (i % 16 == 0)
        //         printf("\n");
        //     printf("%02X ", bl->pack.buffer[i]);
        // }
        // printf("\n");

        uint32_t endAddr = 0;
        int waittry = 0;

        printf("erasing...\n");
        for (int i = 0; i <= sectorCount; i++)
        {
            printf("=");
        }
        printf("\n");

        while (1)
        {
            delay_ms(10);
            uint32_t ret = bl_pack_recv(bl);
            if (ret == 0 && bl->pack.cmd1 == CMD_ACK)
            {
                endAddr = bl->pack.data[0] | (bl->pack.data[1] << 8) | (bl->pack.data[2] << 16) | (bl->pack.data[3] << 24);

                printf("#");
                fflush(stdout);
                // delay_ms(1);
                // printf("erase addr: 0x%08X, endAddr: 0x%08X\n", bl->pack.addr, endAddr);
                if (bl->pack.addr >= endAddr)
                {

                    printf("\n");
                    for (int i = 0; i <= sectorCount; i++)
                    {
                        printf("=");
                    }
                    printf("\n");
                    printf("erase success!\n\n");
                    return 0;
                }
                waittry = 0;
            }
            else
            {
                if (waittry++ > 10)
                {
                    printf("eraser error at 0x%08X\n", bl->pack.addr);
                    return -1;
                }
            }
        }
        if (sendtry++ >= 2)
        {
            printf("erase timeout! exit!\n");
            return -2;
        }
    }
    return -3;
}
int write_firmware(bl_info_t *bl)
{

    uint32_t write_already = 0;
    uint32_t write_remain = firmware.size;
    uint32_t write_addr = firmware.start_addr;
    uint32_t write_size = 0;
    uint32_t sendtry = 0;

    uint32_t stepPercent = 0;
    uint32_t stepPercentPre = 0;
    uint32_t stepMax = (firmware.size) / bl->sectorSize;
    stepMax = stepMax > 100 ? 100 : stepMax;
    printf("programming...\n");
    for (int i = 0; i <= stepMax; i++)
    {
        printf("=");
    }
    printf("\n");
    while (1)
    {
        write_size = write_remain > bl->sectorSize ? bl->sectorSize : write_remain;
        bl_pack_send(bl, CMD_WRITE, write_addr, &firmware_data[write_already], write_size);
        int ret = bl_pack_recv(bl);
        int waitry = 0;
        while (ret != 0)
        {
            delay_ms(1);
            ret = bl_pack_recv(bl);
            if (waitry++ > 100)
            {
                printf("write error [%08X] at 0x%08X, crc error\n", ret, write_addr);
                return -1;
            }
        }
        delay_ms(1);
        if (ret == 0 && bl->pack.cmd1 == CMD_ACK)
        {
            write_already += write_size;
            write_remain -= write_size;
            write_addr += write_size;


            stepPercent = (write_already * 100) / firmware.size;
            if (stepPercent != stepPercentPre)
            {
                stepPercentPre = stepPercent;
                printf(">");
                // printf(">%d%%\n",stepPercent);
                fflush(stdout);
            }
            // printf("write addr: 0x%08X, write_size: %d write_remain:%d\n", write_addr, write_size, write_remain);

            if (write_remain == 0)
            {
                printf("\n");
                for (int i = 0; i <= stepMax; i++)
                {
                    printf("=");
                }
                printf("\n");
                printf("Done!\n\n");
                return 0;
            }
        }
        else
        {
            printf("write error [%08X] at 0x%08X\n rev: [%d] \n", ret, write_addr, bl->pack.buffer_size);
            for (int i = 0; i < bl->pack.buffer_size; i++)
            {
                printf("0x%02X ", bl->pack.buffer[i]);
            }
            printf("\n");
            if (sendtry++ >= 10)
            {
                printf("write timeout! exit!\n");
                return -1;
            }
        }
    }
    return -2;
}
int read_firmware(bl_info_t *bl)
{
    uint32_t read_total = 0;
    uint32_t read_size = 0;
    uint32_t sendtry = 0;
    uint32_t read_addr = firmware.start_addr;
    uint32_t read_remain = firmware.size;
    uint8_t *firmwareRead = malloc(firmware.size);
    if(!firmwareRead)
    {
        printf("malloc error\n");
        return -1;
    }
    uint32_t packCount = firmware.size / bl->sectorSize;
    packCount = packCount > 100 ? 100 : packCount;
    uint32_t curPack=0,prePack=0;
    printf("Verifying...\n");
    for (int i = 0; i <= packCount; i++)
    {
        printf("=");
        // fflush(stdout);
    }
    printf("\n");
    while (1)
    {
        read_size = read_remain > bl->sectorSize ? bl->sectorSize : read_remain;
        bl_pack_send(bl, CMD_READ, read_addr, (uint8_t *)&read_size, 4);

        int waitting = 0;
        while (1) // wait ack
        {
            delay_ms(2);
            int ret = bl_pack_recv(bl);
            if (ret == 0 && bl->pack.cmd1 == CMD_ACK)
            {
                memcpy(&firmwareRead[read_total], bl->pack.data, bl->pack.data_size);
                // printf("read addr: 0x%08X, read_size: %d\n", read_addr, read_size);
                if (read_size != bl->pack.data_size)
                {
                    printf("read error at 0x%08X! wanted[%d] get[%d]\n", read_addr, read_size, bl->pack.data_size);
                }
                read_total += read_size;
                read_remain -= read_size;
                read_addr += read_size;
                sendtry = 0;

                curPack = read_total*100/firmware.size;
                if(curPack != prePack)
                {
                    prePack = curPack;
                    printf("<");
                    fflush(stdout);
                }
                break;
            }
            else
            {
                // printf("rev: 0x%08X, [%d]", ret, bl->pack.buffer_size);
                // for (int i = 0; i < bl->pack.buffer_size; i++)
                // {
                //     if (i % 32 == 0)
                //         printf("\n");
                //     printf("%02X ", bl->pack.buffer[i]);
                // }
                // printf("\n");
                if (waitting++ > 10)
                {
                    printf("read timeout! exit! at: 0x%08X\n", read_addr);
                    return -2;
                }
            }
        }
        if (read_remain == 0)
        {
            printf("\n");
            for (int i = 0; i <= packCount; i++)
            {
                printf("=");
                // fflush(stdout);
            }
            printf("\n");
            printf("done!\n");
            break;
        }
        if (sendtry++ >= 3)
        {
            printf("read timeout! exit!\n");
            return -1;
        }
    }
    if(readout!=0)
    {
        FILE *f_read = fopen("./verify.bin", "wb");
        if (!f_read)
        {
            printf("open file error!\n");
        }

        size_t writeSize = fwrite(firmwareRead, 1, firmware.size, f_read);
        if (writeSize != firmware.size)
        {
            printf("write file error! %d,%d\n", writeSize, firmware.size);
        }
        fclose(f_read);
    }
    uint32_t crc = crc32_cal(firmwareRead, firmware.size, CRC32_INIT_VALUE, CRC32_XOR_VALUE, 1, 1);
    printf("read crc: 0x%08X\n", crc);
    if (crc != firmware.FWcrc)
    {
        printf("crc error! read crc: 0x%08X, firmware crc: 0x%08X\n", crc, firmware.FWcrc);
        for (size_t i = 0; i < firmware.size; i++)
        {
            if (firmware_data[i] != firmwareRead[i])
            {
                printf("read error at 0x%08X, [%02X] [%02X]\n", i, firmware_data[i], firmwareRead[i]);
                return -1;
            }
        }
        return -1;
    }
    free(firmwareRead);
    printf("crc ok!\n");
    return 0;
}
int write_fwInfo(bl_info_t *bl)
{
    uint32_t sendtry = 0;
    printf("\nUpdating firmware info...\n");
    while (1)
    {
        bl_pack_send(bl, CMD_W_FW_INFO, 0, (uint8_t *)&firmware, sizeof(firmware));
        int waitting = 0;
        while (1) // wait ack
        {
            // printf("send: [%D]\n", bl->pack.buffer_size);
            // for(int i = 0; i < bl->pack.buffer_size; i++)
            // {
            //     if(i % 32 == 0)
            //         printf("\n");
            //     printf("%02X ", bl->pack.buffer[i]);
            // }
            delay_ms(10);
            int ret = bl_pack_recv(bl);
            if (ret == 0 && bl->pack.cmd1 == CMD_ACK)
            {
                printf("Done!\n\n");
                return 0;
            }
            else if (ret == 0 && bl->pack.cmd1 == CMD_NACK)
            {
                printf("nack! error:%d\n", bl->pack.data_size);
                break;
            }
            else
            {
                // printf("rev: 0x%08X, [%d]", ret, bl->pack.buffer_size);
                // for (int i = 0; i < bl->pack.buffer_size; i++)
                // {
                //     if (i % 32 == 0)
                //         printf("\n");
                //     printf("%02X ", bl->pack.buffer[i]);
                // }
                // printf("\n");
                if (waitting++ > 10)
                {
                    printf("wait ack on write fw info timeout!\n");
                    break;
                }
            }
        }
        if (sendtry++ >= 2)
        {
            printf("write fw info timeout! exit!\n");
            return -1;
        }
    }
    return 0;
}
int reset(bl_info_t *bl)
{
    uint32_t sendtry = 0;
    printf("\nResetting...\n");
    while (1)
    {
        bl_pack_send(bl, CMD_RESET, 0, NULL, 0);
        int waitting = 0;
        while (1) // wait ack
        {
            delay_ms(10);
            int ret = bl_pack_recv(bl);
            if (ret == 0 && bl->pack.cmd1 == CMD_ACK)
            {
                printf("Done!\n\n");
                return 0;
            }
            else if (ret == 0 && bl->pack.cmd1 == CMD_NACK)
            {
                printf("nack! error:%d\n", bl->pack.data_size);
            }
            else
            {
                if (waitting++ > 10)
                {
                    printf("wait ack on reset timeout!\n");
                    break;
                }
            }
        }
    }
    if (sendtry++ >= 2)
    {
        printf("reset timeout! exit!\n");
        return -1;
    }
}
/**
 * @brief
 * make -f ./win_iap_tool/makefile
 * ./win_iap_tool/win_iap_tool.exe COM1 921600 "./ZINO PREMIUM RT DRONE V4.0.0.0.bin"
 * ./win_iap_tool/win_iap_tool.exe COM5 921600 "./ZINO_PREMIUM_ZINO_FC_V4_V1.0.1_20241119.bin"
 * ./win_iap_tool/win_iap_tool.exe COM3 921600 "./ZINO_PREMIUM_ZINO_FC_V4_V1.0.1_20241120.bin"
 * ./win_iap_tool/win_iap_tool.exe COM5 921600 "./ZINO_DPS_ZINO_POWER_V1.0.1_20241224.bin"
 * @param argc 4
 * @param argv [com] [baudrate] [firmware]
 * @return int
 */
int main(int argc, char *argv[])
{
    bootloader.read = bl_read;
    bootloader.write = bl_write;
    bootloader.sectorSize = FLASH_SectorSize;

// ffmpeg -i ./wav/happy_new_year.wav ./wav/happyNewYear.sbc -ar 8000
//"ls /dev/tty*"可扫描口列表
// gcc ./AromatherapyDiffuser/hardware/W25QXX/loadSbc.c -o loadsbc
//  printf("system run \n");
#if 0
    /**NOTE 使用 "-"来区分指令,不过没有必要 */
    struct
    {
        char *cmd;
        char *param[16];
        uint8_t paramCount;
    } cmdlist[16] = {0};
    uint8_t cmdlistCount = 0;
    uint8_t cmdParamCount = 0;
    for (int i = 1; i < argc; i++)
    {
        if (argv[i][0] == '-')
        {
            cmdlist[cmdlistCount].cmd = argv[i];
            cmdlist[cmdlistCount].paramCount = 0;
            cmdParamCount = 0;
            cmdlistCount++;
        }
        else
        {
            if (cmdlistCount != 0)
            {
                cmdlist[cmdlistCount - 1].param[cmdParamCount] = argv[i];
                cmdParamCount++;
                cmdlist[cmdlistCount - 1].paramCount = cmdParamCount;
            }
        }
    }
    printf("cmdlistCount:%d\n", cmdlistCount);
    for (int i = 0; i < cmdlistCount; i++)
    {
        printf("cmdlist[%d]:%s", i, cmdlist[i].cmd);
        if (cmdlist[i].paramCount != 0)
        {
            for (int j = 0; j < cmdlist[i].paramCount; j++)
            {
                printf(" %s", cmdlist[i].param[j]);
            }
        }
        printf("\n");
    }
#endif

    if (argc < 4)
    {
        printf("Usage:%s [com] [baudrate] [firmware]\n", argv[0]);
        printf("Example:\n ./win_iap_tool COM9 921600 \"./ZINO PREMIUM RT DRONE V4.0.0.0.bin\"\n");
        return -1;
    }

    if (argc >= 5)
    {
        SetSize = atoi(argv[4]);
    }
    if(argc >= 6)
    {
        readout = atoi(argv[5]);
    }
    printf("\n\n * * * * * * ZINO Firmware Burning Tool V2.0.0 * * * * * * \n\n");

    /**serial port init */
    int baudrate = atoi(argv[2]);
    char *com = argv[1];
    if (serial_init(&f_com, com, baudrate) != 0)
    {
        printf("serial init error!\n");
        return -1;
    }

    // 判断文件尾缀是否为.bin
    char *fw_file = argv[3];
    int fw_file_len = strlen(fw_file);
    if (strcmp(fw_file + fw_file_len - 4, ".bin") != 0)
    {
        printf("bin file error! Only accept *.bin file!\n");
        return -1;
    }

    printf("\nbin file:%s\n", fw_file);

    FILE *f_fw = fopen(fw_file, "r");
    if (f_fw == NULL)
    {
        printf("bin file open error! %s\n", strerror(errno));
        return -1;
    }
    printf("firmware extract...\n");
    if (firmware_extract(f_fw, &firmware) != 0)
    {
        printf("firmware extract error!\n");
        return -1;
    }

    size_t sector_size = FLASH_SectorSize;
    size_t read_size = 0;
    size_t total_write = 0;
    size_t total_pack = firmware.size / sector_size + (firmware.size % sector_size ? 1 : 0);
    size_t pack_count = 0;
    uint32_t write_addr = firmware.start_addr;
    uint32_t sector_count = 0;
    // printf("total_pack:%d\n", total_pack);

    timer_t startTime = time(NULL);
    timer_t frameTime = time(NULL);
    timer_t frameTimeUsed = time(NULL);
    timer_t totalTime = time(NULL);
    timer_t totalTimeUsed = time(NULL);

    /**NOTE SYNC */
    if (dev_sync(bl) != 0)
    {
        printf("dev_sync error!\n");
        return -1;
    }
    /**NOTE GET VERSION */
    if (getVersion(bl) != 0)
    {
        printf("dev_get_version error!\n");
        return -1;
    }
    /**NOTE GET ID */
    if (getId(bl) != 0)
    {
        printf("dev_get_id error!\n");
        return -1;
    }

    /**NOTE GET fwInfo */
    if (get_fwInfo(bl) != 0)
    {
        printf("dev_get_fwinfo error!\n");
        return -1;
    }
    /**NOTE ERASE FLASH */
    if (erase_flash(bl) != 0)
    {
        printf("erase_flash error!\n");
        return -1;
    }
    /** NOTE WRITE FLASH */
    if (write_firmware(bl) != 0)
    {
        printf("write_firmware error!\n");
        return -1;
    }
    /** NOTE read FLASH */
    if (read_firmware(bl) != 0)
    {
        printf("read_firmware error!\n");
        return -1;
    }
    /**NOTE write fwInfo */
    if (write_fwInfo(bl) != 0)
    {
        printf("write_fwInfo error!\n");
        return -1;
    }
    /**NOTE RESET */
    if (reset(bl) != 0)
    {
        printf("reset error!\n");
        return -1;
    }
    free(firmware_data);
    tcflush(f_com, TCIOFLUSH); // 刷新输入输出缓冲区
    close(f_com);

    return 0;
}

 运行结果:

$ make -j16 dl
./UploadTool/win_iap_tool.exe COM3 921600 ZINO_DPS_ZINO_POWER_V1.0.1_20250110.bin


 * * * * * * ZINO Firmware Burning Tool V2.0.0 * * * * * * 


comIndex = 2
com = /dev/ttyS2
baudrate:921600 
serial open
serial init done

bin file:ZINO_DPS_ZINO_POWER_V1.0.1_20250110.bin
firmware extract...
 *name     : ZINO_DPS_ZINO_POWER_V1.0.1_20250110.bin
 *infoCrc  : 0x40693BE8
 *magic    : 0x5A1234A5
 *startAddr: 0x08004000
 *size     : 0x00015EFC
 *FWcrc    : 0xFF0BC36F
 *version  : 0x00000001
 *pid      : 0x01020304
 *date     : 0x07E80B13
 *Serial   : 0x00000047
 *ID       : 0x00030240
 *Model    : 0x0000000F
 *Flash    : 0x00000002
 *Footprint: 0x00000003
FWcrc: 0xFF0BC36F

Try to sync
rev: 0
Sync success!

get version
get version success
version:0x02000000

get id...
get id success
 *Serial   : 0x00000047
 *ID       : 0x00030240
 *Model    : 0x0000000F
 *Flash    : 0x00000002
 *Footprint: 0x00000003
 *UID      : 488D3504000064710917D904
 - - - MCU match! - - -


get fwInfo...
get fwInfo success:crc 0xF974C5F7
get onchip fwInfo:
name: ZINO_DPS_ZINO_POWER_V1.0.1_20250110.bin
version: V0.0.0.1
pid: 1,2,3,4
date: 2024-11-19
bootTimes:0



erase flash...
erase addr: 0x08004000, erase size: 89852, sector count: 44 2048
erasing...
=============================================
#############################################
=============================================
erase success!

programming...
============================================
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
============================================
Done!

Verifying...
============================================
<<<<<<<<<<<<<<<<rev: 0xFFFFFFFF, [0]
<rev: 0xFFFFFFFF, [0]
<<<<<<<<<<<<<<<<<<<<<<<<<<<
============================================
done!
read crc: 0xFF0BC36F
crc ok!

Updating firmware info...
Done!


Resetting...
Done!

 

;