bootloader的实现

前面我们已经将APP下载到了QSPI, 终于进入主题了,描述一下跳转的过程,为了保证bootloader的纯净,我们应当尽量让bootloader简化,使用到了QSPI,我们只需要保证QSPI是正常运行的,这里需要设置为内存映射模式,便可直接访问QSPI的地址,像操作内存那样,其他的外设都需要解初始化,以及使用到的中断需要清除关闭,并且为了保证正常的跳转我们还需要屏蔽掉可能出现的中断(异常)

这里是跳转的主要逻辑

image-20231014172158826

首先会检查qspi是否初始化正常,并且会开启内存映射模式,这一步之后应该就可以通过仿真来看到qspi地址的内容了,即app的内容

image-20231014172631693

这里的前八个字节对应着跳转过去的 sp与pc ,

F8 08 00 24       B1 02 00 90

因为是小端字节序,

跳转过去的首地址为为0x9000 0000,对应的指令为 0x2400 08F8

将加载0x2400 08F8的值到堆栈指针寄存器(SP)中,从而设置堆栈的首地址为0x2400 08F8

下一个四字节即 地址为 0x9000 0000+4 对应的指令为 9000 02B1

将加载初始程序计数器(PC)值,即程序的入口点 9000 02B1

我们再回头看一下APP的map文件理解一下 堆栈的首地址前一部分是什么,以及sp的前一部分是什么

image-20231014174729780

从以上的文件中不难看出,内存中存在data段,bss段以及 STACK栈,

  • data:

    • data 段通常用于 存储初始化的全局变量和静态变量
    • 这些变量在程序开始运行时被赋予一个非零的初始值。
    • data 段的内容通常在程序加载时从非易失性存储(如Flash内存)复制到RAM中。
  • bss:

    • bss 段用于 存储未初始化的全局变量和静态变量
    • 在程序开始运行时,bss 段的所有内容通常被初始化为零。
    • bss 段不占用程序的存储空间,它仅在链接时保留空间,以便在运行时可以初始化为零。
  • stack:

    • stack 段用于存储局部变量、函数参数、返回地址和保存的寄存器值。
    • 它是动态的,其大小会随着函数的调用和返回而改变。
    • stack通常是由程序的运行时系统自动管理的,例如,当函数被调用时,它的参数和返回地址被推送到栈上,而当函数返回时,这些值被弹出。

在APP中我们为栈分配了,2KB (堆也给了2k)

image-20231014183821882

image-20231014184027272

由于栈是向下生长的,所以我们的栈顶地址会被设置为0x240000F8+0X800,最终即得到了我们的栈顶地址 0x2400 08F8

这也可以解释栈溢出是个什么东西了,总结看来就是当栈用完以后如果没有安全保护将 会改写bss段的数据,这将导致我们程序中的某个 未初始化的全局变量和静态变量被改写,这将导致致命错误,

以上看来似乎是这样的,但是这里忽略了堆区的存在(以上说法是错误的)

堆区位于 栈与bss段之间,由于我们在程序中没有分配内存,编译器自动优化掉了,看下面小实验

在原来的工程中添加以下示例

#include <stdlib.h>

 int *arr;
 int n = 5; // 例如,我们想要分配5个整数的空间
 arr = (int *)malloc(n * sizeof(int)); // 分配内存

image-20231017161603318

现在堆出现了,现在可以正确解释堆栈溢出了

以上解释完了栈顶地址,接下来该看 base address +4的值是个什么东西,以及它的前一段内容

9000 02B1

image-20231014185717974

一点一点来看,首先基地址对应的为中断向量表的地址 计算可以得出 中断向量表占664个字节(这包括异常)

__Vectors                                0x90000000   Data           4 
__Vectors_End                            0x90000298   Data           0 
_main_scatterload 设置内存区域,将读写(RW)和只读(RO)输出段从加载地址复制到执行地址,并初始化零初始化(ZI)区域
这一阶段可能包括调用_main_cpp_init和_main_init等函数,以初始化C++运行时环境和执行其他必要的系统初始化。

中间的阶段似乎是时钟初始化与 引入 c c++ 并初始化, 这里具体的过程不太了解

  • Reseat_Handle 配置中断向量表 从复位向量所指向的复位中断ISR开始运行的,pc指针指向它,接着加载指令执行

制作logo

系统上电的时候有一个启动logo很是炫酷,咱也来做一个

logo制作网站, 实际上是打印出来,简单明了

http://patorjk.com/software/taag/

image-20231014194443594

最重要跳转

image-20231014194743800

image-20231014194723760

其中我们需要清理所用到的外设

DeInit_all_bsp_and_IRQ函数

image-20231014194824203

    if(qspi_init_and_enable_MemoryMapMode() == 0)
     {
      display_logo(); 
        
      jump_app();
    
     }

这里QSPI初始化完成并开启内存映射之后 会打印一个logo,接着跳转到qspi的基地址

image-20231014195300131

再次提醒

需要在我们的的bootloader中失能全局中断 并在app的开始打开全局中断

踩坑:在配置时钟的时候避免与 QSPI 时钟重叠 在解初始化外设的时候在 再次检验一下QSPI是否正常,如果不正常则是解初始化的外设时钟与QSPI重叠

如下

image-20231015002140692

另一种情况,,把其他外设的时钟关闭了QSPI就不能正常运行,很玄学,总之 解初始化其他外设时钟之后 再判断QSPI是否正常 在调试阶段有必要的

如果觉得我的文章对你有用,请随意赞赏