最近呢,写了一个远程升级。当然呢得了解一下程序启动流程这些。

首先呢说一下这个启动的过程:从上电到 main()函数

  • 初始化堆栈指针 SP=initial_sp,初始化 PC 指针 = Reset_Handler
  • 初始化中断向量表
  • 配置系统时钟
  • 调用 C 库函数_main 初始化用户堆栈,然后进入 main 函数。

# 存放程序的 FLASH

image-20230613204921271

我用的呢是 GD32F303CGT6,它一共有 256K 用来存放程序。

# 启动模式

image-20230613205353142

  • 主 FLASH 存储器:我们下载的程序的位置
  • 引导装载程序:烧录程序时用到
  • 片上 SRAM:调试时用到

启动后从 0x0000 0000 地址获取栈顶值

所以,这个操作在哪呢?

# 启动文件分析.s

image-20230613205918082

这个文件就是启动文件啦

# 1、Stack 栈

image-20230613210202475

第 44 行:开辟一段可读可写数据空间,ARER 伪指令表示下面将开始定义一个代码段或者数据段。此处是定义数据段。ARER 后面的关键字表示这个段的属性。段名为 STACK,可以任意命名;NOINIT 表示不初始化;READWRITE 表示可读可写,ALIGN=3,表示按照 8 字节对齐。

第 45 行:SPACE 用于分配大小等于 Stack_Size 连续内存空间,单位为字节。

第 46 行: __initial_sp 表示栈顶地址。栈是由高向低生长的。

# 2、Heap 堆

堆主要用来动态内存的分配,像 malloc () 函数申请的内存就在堆中。

开辟堆的大小为 0X00000400(1024 字节),名字为 HEAP,NOINIT 即不初始化,可读可写,8 字节对齐。__heap_base 表示对的起始地址,__heap_limit 表示堆的结束地址。

# 向量表

向量表是一个 WORD( 32 位整数)数组,每个下标对应一种异常,该下标元素的值则是该 ESR 的入口地址。向量表在地址空间中的位置是可以设置的,通过 NVIC 中的一个重定位寄存器来指出向量表的地址。在复位后,该寄存器的值为 0。因此,在地址 0 (即 FLASH 地址 0)处必须包含一张向量表,用于初始时的异常分配。

值得注意的是这里有个另类: 0 号类型并不是什么入口地址,而是给出了复位后 MSP 的初值。

image-20230613211112657

# 复位程序

image-20230613211451390

第 155 行:定义了一个服务程序,PROC 表示程序的开始。

第 146 行:使用 EXPORT 将 Reset_Handler 申明为可被外部引用,后面 WEAK 表示弱定义,如果外部文件定义了该标号则首先引用该标号,如果外部文件没有声明也不会出错。这里表示复位程序可以由用户在其他文件重新实现,这种写法在 HAL 库中是很常见的。

第 157-158 行:表示该标号来自外部文件,SystemInit () 是一个库函数,在 system_stm32f1xx.c 中定义的,__main 是一个标准的 C 库函数,主要作用是初始化用户堆栈,这个是由编译器完成的,该函数最终会调用我们自己写的 main 函数,从而进入 C 世界中。

第 159 行:这是一条汇编指令,表示从存储器中加载 SystemInit 到一个寄存器 R0 的地址中。

第 160 行:汇编指令,表示跳转到寄存器 R0 的地址,并根据寄存器的 LSE 确定处理器的状态,还要把跳转前的下条指令地址保存到 LR。

第 161 行:和 149 行是一个意思,表示从存储器中加载__main 到一个寄存器 R0 的地址中。

第 162 行:和 150 稍微不同,这里跳转到至指定寄存器的地址后,不会返回。

第 163 行:和 PROC 是对应的,表示程序的结束。

# 伪异常处理程序程序

image-20230613211827553

# 外部中断处理程序

​ 我们平时要使用哪个中断,就需要编写相应的中断服务程序,只是启动文件把这些函数留出来了,但是内容都是空的,真正的中断复服务程序需要我们在外部的 C 文件里面重新实现,这里只是提前占了一个位置罢了。

image-20230613211930912

# 堆栈初始化

堆栈初始化是由一个 IF 条件来实现的,MICROLIB 的定义与否决定了堆栈的初始化方式。

这个定义是在 Options->Target 中设置的。

image-20230613212114207

如果没有定义__MICROLIB , 则会使用双段存储器模式,且声明了__user_initial_stackheap 具有全局属性,这需要开发者自己来初始化堆栈。

image-20230613212203696

ALIGN 表示对指令或者数据存放的地址进行对齐,缺省表示 4 字节对齐。

# 字节对齐

image-20230613212308267

第 60 行:PRESERVE8 用于指定当前文件的堆栈按照 8 字节对齐。

第 61 行:THUMB 表示后面指令兼容 THUMB 指令。现在 Cortex-M 系列的都使用 THUMB-2 指令集,THUMB-2 是 32 位的,兼容 16 位和 32 位的指令,是 THUMB 的超集。

# 初始化 SP、PC、向量表

当系统复位后,处理器首先读取向量表中的前两个字(8 个字节),第一个字存入 MSP,第二个字为复位向量,也就是程序执行的起始地址。

image-20230613212452632

# SystemInit

大部分是配置时钟的,具体参考手册吧。

# main

执行指令 LDR R0, =__main,然后就跳转到__main 程序段运行,当然这里指标准库的__main 函数。

最后就进入 C 文件的 main 函数中。

# 流程

  • MCU 上电后从 0x0800 0000 处读取栈顶地址并保存
  • 然后从 0x0800 0004 读取中断向量表的起始地址,这就是复位程序的入口地址
  • 接着跳转到复位程序入口处,初始向量表,然后设置时钟,设置堆栈
  • 最后跳转到 C 空间的 main 函数,即进入用户程序。
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

flechazo 微信支付

微信支付

flechazo 支付宝

支付宝

flechazo 贝宝

贝宝