0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

bootloader的原理及实现过程详解

嵌入式应用开发 来源:嵌入式应用开发 作者:嵌入式应用开发 2022-06-18 17:57 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

一、背景

嵌入式操作系统中,BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。在嵌入式系统中,通常并没有像BIOS那样的固件程序

二、实现思路

bootloader其实就是一段启动程序,它在芯片启动的时候首先被执行,它可以用来做一些硬件的初始化,当初始化完成之后跳转到对应的应用程序中去。

我们可以将内存分为两个区,一个是启动程序区(0x0800 0000 - 0x0800 2000 )大小为8K Bytes,剩下的为应用程序区(0x0800 2000 - 0x0801 0000)。

芯片上电时先运行启动程序,然后跳转到应用程序区执行应用程序。

三、程序跳转

bootloader一个主要的功能就是首先程序的跳转。在STM32中只要将要跳转的地址直接写入PC寄存器,就可以跳转到对应的地址中去。

怎么实现呢?

当我们实现一个函数的时候,这个函数最终会占用一段内存,而它的函数名代表的就是这段内存的起始地址。当我们调用这个函数的时候,单片机会将这段

内存的首地址(函数名对应的地址)加载到PC寄存器中,从而跳转到这段代码来执行。那么我们也可以利用这个原理,定义一个函数指针,将这个指针指向我们

想要跳转的地址,然后调用这个函数,就可以实现程序的跳转了。

代码如下:

#define  APP_ADDR  0x08002000   //应用程序首地址定义 
typedef void (*APP_FUNC)(); //函数指针类型定义

APP_FUNC jump2app; //定义一个函数指针

jump2app = ( APP_FUNC )(APP_ADDR + 4); //给函数指针赋值
jump2app(); //调用函数指针,实现程序跳转

上面的代码实现了我们要的跳转功能,但是为什么要跳转到(APP_ADDR + 4) 这个地址,而不是APP_ADDR.

首先我们要了解主控芯片的启动过程。以STM32为例,在芯片上电的时候,首先会从内存地址位0x0800 0000(由启动模式决定)的地方加载栈顶地址(4字节),从0x0800 0004的地方加载程序复位地址(4字节),然后跳转到对应的复位地址去执行。

所以上面的程序会中,jump2app这个函数指针的地址为(APP_ADDR + 4),调用这个函数指针的时候,芯片内核会自动跳转到这个指针指向的内存地址,也即是应用程序的复位地址。

四、加载栈地址

实际运行会发现,上面的程序可能会出现问题。因为我们还缺少了一个栈地址的加载过程,也就是芯片上电的第一个动作。这里要用到一点汇编的知识:

__asm void MSR_MSP(uint32_t addr)
{
    MSR MSP, r0
    BX r14;
}
__asm void MSR_MSP(uint32_t addr) 是MDK嵌入式汇编形式。

MSR MSP, r0 意思是将r0寄存器中的值加载到MSP(主栈寄存器,复位时默认使用)寄存器中,r0中保存的是参数值,即addr的值

BX r14 跳转到连接寄存器保存的地址中,即退出函数,跳转到函数调用地址

完整的程序如下:

#define APP_ADDR 0x08002000 //应用程序首地址定义 
typedef void (*APP_FUNC)(); //函数指针类型定义

/**
  * @brief
  * @param
  * @retval
  */
__asm void MSR_MSP(uint32_t addr)
{
    MSR MSP, r0
    BX r14;
}


/**
  * @brief
  * @param
  * @retval
  */
void run_app(uint32_t app_addr)
{
    uint32_t reset_addr = 0;
    APP_FUNC jump2app;
    
    /* 跳转之前关闭相应的中断 */
    NVIC_DisableIRQ(SysTick_IRQn);
    NVIC_DisableIRQ(LPUART_IRQ);
    
    /* 栈顶地址是否合法(这里sram大小为8k) */
    if(((*(uint32_t *)app_addr)&0x2FFFE000) == 0x20000000)
    {
        /* 设置栈指针 */
        MSR_MSP(app_addr);
        /* 获取复位地址 */
        reset_addr = *(uint32_t *)(app_addr+4);
        jump2app = ( APP_FUNC )reset_addr;
        jump2app();
    }
    else
    {
        printf("APP Not Found!n");
    }
}

五、编译设置

我们需要在设置界面将默认(0x8000000)改为我们的应用程序地址(0x8002000)

poYBAGKtoIGAOtpZAAGKkMPFU-E907.png

六、中断向量表重映射

完成了上面的工作,实际测试发现程序还是无法正确运行。原因是我们没有进行中断向量表的重映射。向量表映射?什么时候有做过这个工作,我们来看一下:

.s文件里有如下代码:

; Reset handler routine
Reset_Handler    PROC
                 EXPORT  Reset_Handler                 [WEAK]
        IMPORT  __main
        IMPORT  SystemInit  
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

这代码表示,程序在执行main函数之前,会先执行SystemInit这个函数。下面看看这个函数:

/**
  * @brief  Setup the microcontroller system.
  * @param  None
  * @retval None
  */
void SystemInit (void)
{
/*!< Set MSION bit */
  RCC->CR |= (uint32_t)0x00000100U;

  /*!< Reset SW[1:0], HPRE[3:0], PPRE1[2:0], PPRE2[2:0], MCOSEL[2:0] and MCOPRE[2:0] bits */
  RCC->CFGR &= (uint32_t) 0x88FF400CU;

  /*!< Reset HSION, HSIDIVEN, HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFF6U;

  /*!< Reset HSI48ON  bit */
  RCC->CRRCR &= (uint32_t)0xFFFFFFFEU;

  /*!< Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFFU;

  /*!< Reset PLLSRC, PLLMUL[3:0] and PLLDIV[1:0] bits */
  RCC->CFGR &= (uint32_t)0xFF02FFFFU;

  /*!< Disable all interrupts */
  RCC->CIER = 0x00000000U;

  /* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}

从上面的代码可以看到,这个函数主要是做了时钟的初始化和中断初始化,还有就是中断向量表的映射,就是最后那一段代码

poYBAGKtoMKAO1bzAAD8OXA5lO8440.png

再看看FLASH_BASE 和 VECT_TAB_OFFSET的定义:

poYBAGKtoOCAIUjEAAEZB3-GFsQ012.png

这里默认映射地址就是FLASH的初始地址,所以只要将其改成我们程序的起始地址就行了: SCB->VTOR = 0x08002000

编译,运行,下载.

七、总结

程序跳转完成,对于bootloader来说也就完成了一大半。剩下的就是根据自己的需求去完善相应功能了,比如我的在线升级功能,就要在bootloader里做固件接收和校验的功能。这里有一点需要特别注意的是,跳转程序之前最好把你用到的中断都关了,不然跳转之后的程序没有对应的中断处理函数,那就又可能使得程序进入死循环中。

审核编辑:符乾江

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    5186

    文章

    20143

    浏览量

    328669
  • Boot
    +关注

    关注

    0

    文章

    154

    浏览量

    37474
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    Bootloader固件升级的步骤

    文件则下载到 ROM 或 Flash 中BootLoader 后面的地址中。也就是说,存在 ROM/Flash 中的内容是分为两部分的。 4、要实现在同一个 ROM/Flash 中保存两段程序
    发表于 11-19 07:41

    bootloader无法更新的原因?

    bootloader下载新的固件后,重启在download区搬新的固件时,就出现地址错误是怎么回事
    发表于 10-11 06:07

    Art-Pi2的BootLoader用rtthread有什么特殊的作用吗?

    我的基础不太好,在创建artpi2的bootloader的例程的时候发现bootloader编译完好像不小,然后发现bootloader里面也用上了rtthread的系统,不太明白这里使用rtthread有没有什么特殊的作用。
    发表于 09-22 06:28

    通用bootloader无法使用45db161flash怎么解决?

    通用bootloader不选“支持spi flash”,download放于片内flash,正常启动,ota正常,app驱动at45db161读写正常。通用bootloader选“支持spi
    发表于 09-18 08:23

    BootLoader app程序反复重启,IAP升级失败怎么处理?

    keil开发的BootLoader程序,用RT-Thread Studio开发的app程序,板子每次上电后,从BootLoader程序跳转到app程序,就打印几行RT-Thread的日志,然后就反复重启:先执行BootLoader
    发表于 09-10 07:36

    是否可以仅使用 Bootloader Host 来实现可引导加载项目的相同编程结果?

    你好 我想使用 Bootloader Host 在我的 CY8CKIT-059 上对 CY8C5888LTI-LP097 芯片进行编程,并将项目类型设置为可引导加载。我的目标是实现与通过使用 SWD
    发表于 07-18 07:39

    瑞萨MCU方案:瑞萨RZ/G2L Bootloader单独编译方法详解

    会提出需要单独编译Bootloader的需求。为了满足用户需求,本文将介绍一种RZ/G2L上脱离Yocto的单独编译Bootloader的方法,从而能够快
    的头像 发表于 07-08 14:47 2267次阅读
    瑞萨MCU方案:瑞萨RZ/G2L <b class='flag-5'>Bootloader</b>单独编译方法<b class='flag-5'>详解</b>

    【HarmonyOS 5】桌面快捷方式功能实现详解

    【HarmonyOS 5】桌面快捷方式功能实现详解 ##鸿蒙开发能力 ##HarmonyOS SDK应用服务##鸿蒙金融类应用 (金融理财# 一、前言 在移动应用开发中,如何让用户快速触达核心
    的头像 发表于 06-21 16:42 1775次阅读
    【HarmonyOS 5】桌面快捷方式功能<b class='flag-5'>实现</b><b class='flag-5'>详解</b>

    Art-Pi2的BootLoader用rtthread有什么特殊的作用吗?

    我的基础不太好,在创建artpi2的bootloader的例程的时候发现bootloader编译完好像不小,然后发现bootloader里面也用上了rtthread的系统,不太明白这里使用rtthread有没有什么特殊的作用。
    发表于 05-27 06:08

    nRF Connect SDK(NCS)/Zephyr固件升级详解 – 重点讲述MCUboot和蓝牙空中升级

    如何在nRF Connect SDK(NCS)中实现蓝牙空中升级?MCUboot和B0两个Bootloader有什么区别?MCUboot升级使用的image格式是怎么样的?什么是SMP协议?CBOR
    的头像 发表于 05-09 14:14 2992次阅读
    nRF Connect SDK(NCS)/Zephyr固件升级<b class='flag-5'>详解</b> – 重点讲述MCUboot和蓝牙空中升级

    SMA接头制造工艺详解:精密加工技术与实现策略

    SMA接头制造工艺详解:精密加工技术与实现策略
    的头像 发表于 04-26 09:22 518次阅读
    SMA接头制造工艺<b class='flag-5'>详解</b>:精密加工技术与<b class='flag-5'>实现</b>策略

    倒装芯片键合技术的特点和实现过程

    本文介绍了倒装芯片键合技术的特点和实现过程以及详细工艺等。
    的头像 发表于 04-22 09:38 2168次阅读
    倒装芯片键合技术的特点和<b class='flag-5'>实现</b><b class='flag-5'>过程</b>

    求助,关于S32G3 Multi-Boot Bootloader创建的问题求解

    使用Bootloader_S32G3XX_ASR_4.4_M7 示例,我根据以下内容修改了引导源信息: A53: Reset Handler Address: 0x34302000 Load Image Address: 0x342F8000 Image
    发表于 04-09 08:30

    U-Boot 和 Bootloader,99% 的工程师都分不清?

    嵌入式软件工程师听说过 u-boot 和 bootloader,但很多工程师依然不知道他们到底是啥。   今天就来简单讲讲 u-boot 和 bootloader 的内容以及区别
    的头像 发表于 03-25 20:47 1410次阅读

    自定义RISC V的bootloader-v3

    在生成SoC时,会生成一个预定义bootloader .bin文件,用于指定soc的工程运行的地址,这包括在flash的存储地址 ,加载到外存中的运行地址及在外存中分配的存储空间的大小 。下面我们
    的头像 发表于 03-10 09:05 1521次阅读
    自定义RISC V的<b class='flag-5'>bootloader</b>-v3