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

审核编辑:符乾江

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

    关注

    5209

    文章

    20637

    浏览量

    336884
  • Boot
    +关注

    关注

    0

    文章

    154

    浏览量

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

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    【CANopen实战】如何实现PDO数据变化即刻发送(事件触发模式详解

    【CANopen实战】如何实现PDO数据变化即刻发送(事件触发模式详解
    的头像 发表于 04-09 14:39 121次阅读
    【CANopen实战】如何<b class='flag-5'>实现</b>PDO数据变化即刻发送(事件触发模式<b class='flag-5'>详解</b>)

    瑞萨RX MCU启动文件详解

    本文详细介绍了RX MCU从复位到进入main函数的过程,有助于读者对RX MCU体系结构的理解,RAM和ROM的初始化,以及bootloader程序的开发。
    的头像 发表于 02-11 08:43 1.6w次阅读
    瑞萨RX MCU启动文件<b class='flag-5'>详解</b>

    USBISP/USBasp编程器给Atmega32U4下载Arduino bootloader引导程序

    对于新出厂的ATmega32U4芯片内部是没有arduino引导程序的,需要用户预先下载bootloader后才能用串口下载自己的应用程序.在某些罕见情况下旧的bootloader会导致一些bug
    的头像 发表于 01-31 14:38 918次阅读
    USBISP/USBasp编程器给Atmega32U4下载Arduino <b class='flag-5'>bootloader</b>引导程序

    深入解析rk平台Android Bootloader核心代码:从启动流程到AVB验证

    U-Boot中Android Bootloader的核心实现,核心作用是 衔接硬件初始化与内核启动 ,主要负责: •读写Bootloader控制块(BCB),判断设备启动
    的头像 发表于 01-22 07:06 481次阅读
    深入解析rk平台Android <b class='flag-5'>Bootloader</b>核心代码:从启动流程到AVB验证

    讲解C语言代码的实现过程

    重点讲解C语言代码的实现过程,算法的C语言实现过程具有一般性,通过PID算法的C语言实现,可以以此类推,设计其它算法的C语言
    发表于 01-21 07:58

    LAT1171+STM32F745 USART1 Bootloader 失败原因分析与解决

    STM32 的 Bootloader 可以支持多种协议的,比如 USART,I2C,DFU 等等,USARTBootloader 是客户使用 STM32 的时候常常会用到的协议。客户在
    发表于 01-11 17:33 0次下载

    深入解析RK平台Android/Linux Bootloader核心文件:android_bootloader.c

    Bootloader是Android设备启动的第一道“关卡”,负责初始化硬件、加载系统镜像并完成内核启动的前置准备。在基于U-Boot的Android设备中,android_bootloader
    的头像 发表于 01-09 10:58 1412次阅读
    深入解析RK平台Android/Linux <b class='flag-5'>Bootloader</b>核心文件:android_<b class='flag-5'>bootloader</b>.c

    ESP32 编译过程bootloader 配置阶段的 CMake 缓存冲突错误,记录

    你遇到的是 ESP32 编译过程bootloader 配置阶段的 CMake 缓存冲突错误,核心原因是系统中混合了 ESP-IDF v5.5.1 和 v5.4.3 两个版本的路径,导致
    发表于 12-23 07:07

    如何在CW32F030上实现IAP功能实现远程升级?

    区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。 IAP 功能的实现,一般将程序分为两个部分,即:Bootloader 和 APP。Bootloader
    发表于 12-11 06:15

    Bootloader固件升级的步骤

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

    bootloader无法更新的原因?

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

    是否可以仅使用 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 2978次阅读
    瑞萨MCU方案:瑞萨RZ/G2L <b class='flag-5'>Bootloader</b>单独编译方法<b class='flag-5'>详解</b>

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

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

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

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