一文简单了解单片机bootloader程序

前言:记得刚开始学习单片机的时候只会在开发版上面按照现成的原理图进行设计并烧录程序,知道程序是从main函数开始执行的,后面随着从脱离象牙塔的学校学习转而进入到实际运用的工作当中去,发现设计一款单片机的boot程序也算是一个必备的技能,从开始对它的朦胧概念到慢慢不断的深入了解,当然其中也参考了一些技术博主的文章(相关项都总结的人不错,我都一并将其放到文章的下面),也是本着输出就是最好的学习,所以就先浅浅的记录一下吧。


一,单片机程序的运行空间位置

欲了解bootload(下面我们都简称BL程序),则需要对单片机程序运行起来的原理有一个大致的认识,当然这其中涉及的东西还是比较多的就做一下简略的介绍,大家有一个认知就可以了

下面还是以最经典的一款单片机STM32F412来做一下举例介绍。顾名思义,这是一款32bit的单片机,这个32bit代表的也是一种地址空间(PC指针的寻址范围,因此也叫寻址空间的大小一般用一个16进制的数值来表示0xFFFFFFFF,

上面是这款单片机的参考手册里面的查到的寻址空间的示意图,我们平常所写的单片机的程序在大多数情况下都是运行在这个空间范围里面的(即为0x0800 0000),

记得刚开始学习单片机开发的时候,都会有一个现成的开发板供学习,估计各位小伙伴刚开始学习的时候也是这样大同小异方式吧,创建一个简单的点灯程序之后就使用JLink烧录进去,小灯就可以点亮了,好像当时也没有烧录什么BL程序?要知道为什么没有BL程序,那就得先知道BL程序的作用是什么,也就是为什么要有BL程序。

二,BL程序的作用

比较官方的回答就是下面这些,简单的概括其主要功能就是引导APP程序更新APP程序。

  • 硬件初始化:设置时钟、初始化引脚和外设,以便后续固件运行。
  • 应用加载:从特定位置(如 FLASH、EEPROM、SD 卡、UART、CAN、USB 等接口)加载应用程序到程序存储区。
  • 固件更新:通过串口、USB、CAN 或 OTA(Over-The-Air)等方式更新固件。
  • 启动管理:检查和验证应用程序的完整性(如校验和或签名验证)后,将控制权转移给应用程序。
  • 再回到上面的一个章节的疑问,刚开始学习的时候好像也没有写什么BL程序呀,其实,不是说没有写什么BL程序就代表着没有这个BL程序不存在了。

    每一个MCU在出厂的时候都会芯片出厂前就在系统存储器中固化了一段代码(bootloader,用于存放串口下载的引导程序)。因而使用系统存储器启动方式时, 内核会执行该代码,该代码运行时,会为ISP提供支持(In System Program),如检测USART1/2、CAN2及USB通讯接口传输过来的信息, 并根据这些信息更新到自己内部FLASH的内容,达到升级产品应用程序的目的,因此这种启动方式也称为ISP启动方式。

    说到启动方式了,不得不介绍一下单片机的三种启动方式

    如上是参考手册里面介绍的,其中提到的自举模式,刚一听到的时候还是有些懵逼的,其实大白话就是单片机自己加载自己并运行存储在Flash空间中的APP程序,前面提到的出厂自带程序就是第二种,而我们介绍的要自己写的BL程序就是第一种选择主Flash进行加载APP程序的空间(这个在实际的工程中应用的相对是最多的),至于第三种一般作为调试使用。

    当然,知道上面的这些,还不够,BL程序到底是如何加载的呢?

    三,BL程序加载APP程序的过程

    如下图所示,BL程序一般在APP地址的前面,占用的空间不会特别大,一般也就是16K左右,

     我们常用的keil程序BOOT工程中的设置的这个IROM参数就是程序起始的运行地址,IRAM是我们设置的存储变量、开辟堆栈的地方。

    下面是一个ST工程的BOOT程序,这个大致的逻辑大家可以产生参考一下,如果想看一个比较完整的BOOTR工程的,这边有一个开源地址,大家也可以自行去查看https://gitee.com/tianlair/zboot

    int main(void)
    {
        bsp_init();
        app_log("boot init ok\r\n");
        EI();
    
        while(1) {
          WD_KICK();
        
        //判断是否进入到了串口下载  
          while (!g_download_flag) {
            
            if (/*BOOT_I == 1 || */boot_it ) {
            // delayms(500);
            if (boot_it) {
              
              app_log("boot press/%d,%d\r\n", BOOT_I, boot_it);
              boot_it = false;
              
              uint8_t ret = Dfu_setValidFlag(USER_BASE_ADDR, APP_FLAG_DOWNLOAD);
              if (ret == OP_SUCCESS) {
                dfu_settings_t *pInfo = (dfu_settings_t *)(APP_FLAG_ADDR);
                memcpy((void *)&info, pInfo, sizeof(dfu_settings_t));
    
                app_log("flag/0x%x\r\n", info.appValidFlag);
    
                DatMgr_Init();
                g_download_flag = true;
                goto dl;
              }
              else {
                WHILE(1);
              }
            }
          }
    
          //2500ms 判断串口下载是否超时
          if (HAL_sysTickGetCurMs() > 2500) {
              g_download_flag = true;
              app_log("quit\r\n");
              break;
            }
    
          }
          
          //程序重新开是运行
          if (info.appValidFlag == APP_FLAG_STARTUP) {
              g_download_flag = false;
              app_log("boot start\r\n");
              APP_boot();
          }
          else if (info.appValidFlag == APP_FLAG_BSDIFF_OTA) { //判断程序是否是差分串口升级
    				int status = 0;
    				app_log("boot ota\r\n");
            #ifdef BSDIFF_UPGRADE_ENABLE
                      status = restore_new_bin_fw();
            #endif        
            if(status == OSA_SOK) {
              APP_boot();
            }
            else {
              WHILE(1);
            }
          }
          else if (info.appValidFlag == APP_FLAG_DOWNLOAD) {   //判断是否全量OTA升级
    dl:
                // app_log("Boot download\r\n");
                DatMgr_Run();
          }
          else if (info.appValidFlag == APP_FLAG_COPY_OTW) {   //判断是否差分OTA升级
              app_log("Boot copy\r\n");
              uint32_t size = info.appSize;
            
              int status = APP_bakCopy(size);
            
              if(status == OSA_SOK) {
                  APP_boot();
              }
              else {
                   WHILE(1);
              }
          }
          else {
            app_log("Boot err\r\n");
            WHILE(1);
          }
    
        }
    }
    

    参考文章:

    一文理解单片机BootLoader的前世今生 (uml.org.cn)

    单片机程序是如何运行起来_单片机程序运行流程-CSDN博客

    作者:仰望星空的凡人

    物联沃分享整理
    物联沃-IOTWORD物联网 » 一文简单了解单片机bootloader程序

    发表回复