1.​​Bootloader是什么?

Bootloader​​(引导加载程序)就像你手机、电脑或其他智能设备的“启动向导”。当你按下开机键时,这个“向导”是第一个被唤醒的程序,它的任务是把设备从“关机状态”一步步引导到“正常工作状态”。

举个栗子🌰:

想象你刚买了一台新电脑,第一次开机时,屏幕会先显示品牌Logo,接着检查硬件(比如内存、硬盘),最后才进入Windows系统——这个过程就是Bootloader在幕后操控的。

在STM32中,bootloader的主要作用就是用来进行OTA升级。让嵌入式设备维护更加便捷。

2.硬件平台

本次实验主要使用

  1. STM32F407VET6主控(512KB Flash/192KB RAM)
  2. ESP8266模块
  3. 外置Flash W25Q128(128Mbit(16MB),采用 ​​SPI 接口​)

3.实验内容

​bootloader:
上电后 2000 ms内,输入小写字母 w,进入BOOTLOADER命令行
[1]擦除A区                            
[2]串口IAP下载A区程序
[3]设置OTA版本号
[4]查询OTA版本号
[5]向外部Flash下载程序
[6]使用外部Flash中的程序
[7]重启
[8]ESP32wifi连接
[9]设置服务器连接信息    

采用串口交互。可存储0-10(11个)程序,每个程序最大64K。其中0号程序为OTA更新专用区域。

4.实验规划

1.逻辑规划

1.​​命令行交互系统​

  • 串口中断接收与超时检测(2000ms内检测字符'w'触发)
  • 菜单界面动态生成与用户输入解析

2.​​固件管理功能实现​

  • ​功能[1] Flash擦除​​:STM32内部Flash扇区擦除(STM32 HAL库函数调用)
  • ​功能[2] 串口IAP下载​​:XModem协议实现固件传输与校验
  • ​功能[5]/[6] 外部Flash操作​​:SPI驱动W25Q128实现固件存储与加载

​3.OTA版本控制​

  • ​功能[3]/[4] 版本号管理​​:存放于W25Q128(结构体序列化)
  • 固件完整性校验(CRC32算法实现)

4.​​网络通信模块​

  • ​功能[8] ESP8266连接WiFi​​:AT指令集封装(STA模式配置)
  • ​功能[9] 服务器设置​​:TCP/IP参数存储与心跳包机制(JSON格式解析)

5.​​系统控制类功能​

  • ​功能[7] 重启控制​​:软复位实现(NVIC_SystemReset)

2.内存规划

1.内部FLASH :

//32K 扇区0+扇区1   bootloader区域
    FLASH_B_SADDR       0X08000000        //B区FLASH起始位置
    FLASH_B_ENDDR       0X08007FFF         //B区FLASH结束位置
//96K 扇区2+扇区3+扇区4  app区域
     FLASH_A_SADDR       0X08008000        //A区FLASH起始位置
     FLASH_A_ENDDR       0X0801FFFF         //A区FLASH结束位置

2.外部W25Q128:   

 第0-11块 存储程序,一块64KB。   第159块存放OTA相关结构体。

/*---------------------------------------------------*/
/*--------------------- 结构体  -----------------------*/
/*---------------------------------------------------*/
typedef struct
{
    uint32_t            OTA_flag;               //标志性的变量,若等于OTA_flag_cont定义的值时,表明需要0TA更新A区
    uint32_t            Fier_size[11];          //w25Q128中不同块中程序固件的长度,0号成员固定对应w25Q64中编码0的块,用于0TA
    uint8_t             OTA_ver[32];            //存放版本号    VER-1.0.1-2025/04/02-11:45
} OTA_Info;


typedef struct
{
    uint8_t             Updatabuff[FLASH_PAGE_SIZE];    // 更新A区时,用于保存从w25Q64中读取的数据
    uint32_t            w25q128_blockNum;               //更新第几个程序,Fier_size[w25q128_blockNum] 要更新程序的长度
    uint32_t            Xmdoem_time;                    //协议规定的500ms
    uint32_t            Xmdoem_Rx_NUM;                  //协议接收包的个数
    uint32_t            Xmdoem_CRC16;                   //协议CRC
} UpdataA_cb;

5.部分代码

1.跳转APP程序

/******************************************************************************
 * 函  数: LOAD_A
 * 功  能: 程序跳转A分区
 * 备  注: 1. 如果跳转直接运行A区程序
 *          2. 失败则打印 ERROR
 ******************************************************************************/
void LOAD_A(void)
{
    // boot_clear();
    // 获取应用程序的起始地址(确保这个地址正确)
    uint32_t app_address = FLASH_A_SADDR; // 例如 0x08004000

    if ((*(uint32_t *)app_address >= 0x20000000) && (*(uint32_t *)app_address <= 0x2002FFFF))
    {
        typedef void (*pFunction)(void);
        pFunction JumpToApp;

        // 禁用中断
        HAL_RCC_DeInit();
        __disable_irq();

        boot_clear();

        // 设置主堆栈指针 (MSP)
        __set_MSP(*(uint32_t*)app_address);

        // 获取向量表地址(通常是 MSP + 4)
        SCB->VTOR = app_address;

        // 跳转到应用程序的入口(Reset_Handler)
        JumpToApp = (pFunction)(*(uint32_t*)(app_address + 4));
        JumpToApp();
    }
    else
        printf("ERROR:跳转地址越界\r\n");
}

2.软重启

            HAL_Delay(200);
            HAL_NVIC_SystemReset();   // 复位
            printf("MCU重启\r\n");

3.菜单功能实现

1.状态机

/*---------------------------------------------------*/
/*-----------------    宏定义     -------------------*/
/*---------------------------------------------------*/

//ota状态机
uint32_t Bootstaflag = 0;

#define Updata_A_FLAG           0x00000001 //事件1      需要更新  清除A区,从外部flash(w25q128)中拿取数据,存放到A区
#define menu_2_C_FLAG           0x00000002 //XMode  心跳C  //菜单2事件
#define menu_2_DATA_FLAG        0x00000004 //DATA
#define menu_3_FLAG             0x00000008 //设置OTA版本号
#define menu_5_FLAG             0x00000010 //向外部flash下载程序
#define menu_5_xmodem           0x00000020 //向外部flash下载程序
#define menu_6_FLAG             0x00000040 //从外部 向  内部flash下载程序
#define menu_8_FLAG             0x00000080 //wifi连接
#define menu_9_FLAG             0x00000100  //服务器
#define menu_11_FLAG            0x00000200  //服务器
#define menu_8_1_FLAG           0x00000400 // 扫描wifi
#define menu_12_FLAG            0x00000800  //服务器


例如:
Bootstaflag |= menu_6_FLAG; //置位标志位
if (Bootstaflag & menu_6_FLAG) //置位标志位
{
//执行相关函数
}

2.设置OTA版本

if (Bootstaflag & menu_3_FLAG)
    {
        if (uartHandle->RxBuf_size == 26)
        {
            if (sscanf((char *)uartHandle->usart1_RxBuff1, "VER-%d.%d.%d-%d/%d/%d-%d:%d", &temp, &temp, &temp, &temp, &temp, &temp, &temp, &temp)  == 8)
            {
                memset(OTA_Handle.OTA_ver, 0, 32);
                memcpy(OTA_Handle.OTA_ver, uartHandle->usart1_RxBuff1, 26);
                boot_w25q128_write();
                printf("版本号正确 设置成功");
                Bootstaflag &= ~menu_3_FLAG; //清除事件
                Bootloader_InFo();
            }
            else
            {
                printf("版本格式化错误");
            }
        }
        else
        {
            printf("版本长度错误");
        }
    }

6.实验现象

1.擦除A区

可以看到APP存储区域的值已被重置。擦除A区成功。

2.串口IAP下载A区程序

可以看到APP存储区域的值已经下载成功。

3.版本管理

 4.外部Flash操作

1.向外部Flash下载程序

2.使用外部Flash中的程序

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐