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里做固件接收和校验的功能。这里有一点需要特别注意的是,跳转程序之前最好把你用到的中断都关了,不然跳转之后的程序没有对应的中断处理函数,那就又可能使得程序进入死循环中。

审核编辑:符乾江

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

    关注

    4986

    文章

    18319

    浏览量

    289160
  • Boot
    +关注

    关注

    0

    文章

    142

    浏览量

    35313
收藏 人收藏

    评论

    相关推荐

    详解开关电源RCD钳位电路工作过程,为什么它能够吸收能量?

    详解开关电源RCD钳位电路工作过程,为什么它能够吸收能量?
    的头像 发表于 12-06 16:14 593次阅读
    <b class='flag-5'>详解</b>开关电源RCD钳位电路工作<b class='flag-5'>过程</b>,为什么它能够吸收能量?

    单片机BootLoader与APP如何合并?

    单片机BootLoader与APP如何合并?
    的头像 发表于 10-25 17:17 647次阅读
    单片机<b class='flag-5'>BootLoader</b>与APP如何合并?

    RL78启动过程详解

    RL78启动过程详解
    的头像 发表于 09-28 16:39 878次阅读
    RL78启动<b class='flag-5'>过程</b><b class='flag-5'>详解</b>

    STM32F09x不使用BOOT脚实现System Bootloader升级代码

    电子发烧友网站提供《STM32F09x不使用BOOT脚实现System Bootloader升级代码.pdf》资料免费下载
    发表于 09-25 15:08 1次下载
    STM32F09x不使用BOOT脚<b class='flag-5'>实现</b>System <b class='flag-5'>Bootloader</b>升级代码

    STM32 Bootloader异常复位案例

    电子发烧友网站提供《STM32 Bootloader异常复位案例.pdf》资料免费下载
    发表于 09-25 09:17 3次下载
    STM32 <b class='flag-5'>Bootloader</b>异常复位案例

    u-boot与bootloader的区别

    Bootloader从字面上来看就是启动加载的意思。用过电脑的都知道,windows开机时会首先加载bios,然后是系统内核,最后启动完毕。那么bootloader就相当于手机的bios,它在手机启动的时候根据基带初始化硬件,然后引导系统内核,直到系统启动。
    发表于 08-29 10:17 403次阅读

    图解基于UDS的Flash BootLoader

    这张图和恒润教程中的BootLoader流程大体是一致的。
    的头像 发表于 08-14 10:49 751次阅读
    图解基于UDS的Flash <b class='flag-5'>BootLoader</b>

    ARM处理器如何编写Bootloader

    前面给大家分享过Bootloader从应用角度执行的相关文章,今天从底层原理来给大家描述ARM处理器如何编写Bootloader,以及底层流程。
    的头像 发表于 07-26 09:10 572次阅读
    ARM处理器如何编写<b class='flag-5'>Bootloader</b>

    单片机BootLoader与APP如何合并

    嵌入式固件一般分为BootLoader和App,BootLoader用于启动校验、App升级、App版本回滚等功能,BootLoader在cpu上电第一阶段中运行,之后跳转至App地址执行应用程序。
    的头像 发表于 07-25 11:29 1002次阅读
    单片机<b class='flag-5'>BootLoader</b>与APP如何合并

    介绍一下CAN Bootloader的整个实现过程

    对于大多数汽车软件开发者来说,从客户需求的角度,他们更多关心Bootloader的下载模式。
    发表于 06-29 11:13 948次阅读
    介绍一下CAN <b class='flag-5'>Bootloader</b>的整个<b class='flag-5'>实现</b><b class='flag-5'>过程</b>

    想要自己实现一个bootloader,APROM中程序如何实现跳转,中断向量表如何设置?

    我要自己实现一个bootloaderbootloader在M487的APROM里面,APP也在APROM里面,如何实现bootloader
    发表于 06-27 08:39

    一文了解bootloader

    大多数Bootloader 包含两种操作模式。
    的头像 发表于 06-25 10:05 1629次阅读
    一文了解<b class='flag-5'>bootloader</b>

    mm32-2nd-bootloader技术进阶设计:实现Ymodem更新代码

    前文中实现了一款简单的 2nd Bootloader,能够跳转执行存储在 QSPI Flash 中的应用程序,但 2nd Bootloader 如果仅仅只是用于跳转执行程序的话,岂不是有些太简单
    的头像 发表于 06-09 09:28 898次阅读
    mm32-2nd-<b class='flag-5'>bootloader</b>技术进阶设计:<b class='flag-5'>实现</b>Ymodem更新代码

    STM32H7实现BootLoader内SDRAM的初始化注意事项

    Function Implementation:在 ArtPi 的环境下,实现一个简单的 BootLoader,该 BootLoader 实现了 QSPI 和 SDRAM 功能的初始化
    的头像 发表于 06-07 15:51 2281次阅读
    STM32H7<b class='flag-5'>实现</b><b class='flag-5'>BootLoader</b>内SDRAM的初始化注意事项

    适合单片机的几款开源Bootloader

    如果日常工作中经常使用linux,那么对U-Boot,LILO,GRUB等常用Bootloader一定不陌生。但是这些Bootloader主要用于引导linux启动,不太适合单片机产品。
    发表于 05-25 15:42 4746次阅读
    适合单片机的几款开源<b class='flag-5'>Bootloader</b>