单片机启动流程分析

一 单片机启动流程概述

单片机上电后一直到准备好C语言运行环境并跳转到main函数执行总共经历了5个步骤:
1.内核初始化;
2.强制PC指针指向中断向量表的复位中断向量执行复位中断函数;
3.在复位中断函数中调用 SystemInit 函数,初始化时钟,配置中断向量表等
4.调用 __main 函数完成全局/静态变量的初始化和重定位工作,初始化堆栈和库函数
5.跳转到main函数中执行

二 内核初始化

在单片机上电后首先会进行一系列内核的初始化,关于这部分工作我们只需要了解即可,在内核初始化的过程中主要做了以下几件事情:

1.内核复位和NVIC寄存器部分清零;
2.内核设置堆栈:内核从向量表0地址读出堆栈地址,并设置主堆栈指针(SP_main);
3.设置PC和LR寄存器
a. LR设置未初始复位值0xFFFF FFFF
b. 单片机的内部硬件机制自动将PC指针定位到中断向量表的复位中断向量出,把复位中断函数Reset_Handler的地址赋值给PC指针,然后跳转执行Reset_Handler。

三 复位中断函数 Reset_Handler

在内核复位的最后一步,将PC指针指向了复位中断向量,而复位中断服务函数中的内容才是我们真正需要关心的内容。查看IAR环境中的* .s 单片机汇编启动文件有以下一段内容:

Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
     IMPORT  __main
     IMPORT  SystemInit
                 LDR     R0, =SystemInit  ;加载SystemInit的地址到寄存器R0中
                 BLX     R0               ;跳转R0中的地址执行(执行SystemInit函数)
                 LDR     R0, =__main      ;加载SystemInit的地址到寄存器R0中
                 BX      R0               ;跳转R0中的地址执行(执行__main函数)
                 ENDP

上面的代码就是Reset_Handler的中断服务函数,可以看到在服务中断函数中先使用 IMPORT声明了两个函数 __mainSystemInit,然后顺序跳转执行SystemInit和**__main**函数。下面我们再来了解一下这两个函数具体干了些什么事情:
SystemInit 函数:

在system_stm32f4xx.c文件中我们可以看到该函数的定义,该函数主要干了以下两件事情:
1.初始化时钟:SYSCLK,HCLK,PCLK2 and PCLK1 prescalers
2.配置中断向量表:中断向量表的定位是在 Flash 还是 SRAM,是否需要偏移)
注意:可以通过system_stm32f4xx文件中的宏定义修改系统时钟频率(通过设置锁相环的相关系数),中断向量表的地址(位于SRAM还是Flsah,是否偏移,偏移地址多少等参数)

__main()函数(在IAR中是 __iar_program_start ):

该函数被封装进了编译器的库中,所以不同的IDE该函数的名称有所区别,但所实现的功能大致类似:

1.完成全局变量/静态变量/常量的初始化和重定位工作:
a. 跳转进入__scatterload_rt2函数:通过设置四个寄存器来配置待copy内容(静态变量、全局变量、常量)的的加载域和运行域,设置待copy内容的大小,为后续__scatterload_cpy()函数服务。
b. 跳转进入__scatterload_cpy函数,完成静态变量、全局变量、常量的从flash到SRAM的重定位。
c. 跳转进入__scatterload_zeroinit函数,完成未初始化的全局变量的初始化。

2.初始化堆栈(这里指程序栈)和库函数:
跳转进入__user_steup_stackheap函数:调用**__user_libspac__user_libspace**为C库保持了静态数据。这是一个96字节,0初始化的数据块,该块由C库创建。在C库初始化期间可以用来当做临时栈。再调用 __user_initial_stackheap 用户的初始化堆栈函数,实现用户的堆栈的配置,调用 _fp_init 和 __rt_fp_status_addr (C库函数) 两个函数调用实现浮点运算的支持。

3.程序的跳转,进入main()函数:
跳转进入用户的main函数

注意:

  1. 未初始化和初始值为零的全局变量,静态变量一般在RAM中, 初始值不为零的全局变量/静态变量 一般在FLASH中。
  2. 因为Flash不能随机写(只能写0,不能写1),所以一般会在程序运行之前将初始值不为零的全局变量重定位到RAM中。
  3. 全局变量和常量的地址在编译时都已经被分配好了(所以能够在 .map 文件中看到), 而局部变量则是程序运行时在栈中创建的,栈空间大小可以在IDE中设置。
  4. 单片机启动时,不需要用将代码从ROM搬移到RAM ,而 ARM 则需要。单片机程序执行的过程分三个步骤:取执行->分析指令->执行指令。取指令的任务是:根据 PC的值从程序存储器读出指令,送到指令寄存器。然后分析执行执行。这样单片机就从内部程序存储器去代码指令,从 RAM 存取相关数据。要知道RAM取数的速度是远高于ROM的,但是单片机因为本身运行频率不高,所以从ROM取指令慢并不影响。而ARM不同,CPU运行的频率高,远大于从 ROM读写的速度,所以一般有大型的操作系统时,都需要将代码部分拷贝到RAM中再执行。
<think>好的,用户想了解单片机启动流程,特别是希望有一个图示解析。首先,我需要回忆单片机启动的基本步骤,并结合用户提供的参考资料来组织答案。用户提到的引用[2]提到了STM32F10x.s启动文件的三个主要工作,这应该是关键点。 首先,单片机后,硬件会初始化复位向量,让程序计数器指向复位处理函数。接着启动文件负责设置堆栈,分配内存空间,这是系统运行的基础。然后初始化.data段和.bss段,前者存放已初始化的全局变量,后者处理未初始化的静态变量。之后调用SystemInit函数配置时钟,确保系统时钟正确。最后跳转到main函数,开始执行用户代码。 用户可能需要更详细的步骤解释,以及每个阶段的作用。比如,堆栈初始化为什么重要,中断向量表的作用是什么。同时,结合引用[2]中的内容,可以强调启动文件的具体任务,如分配堆栈、定义复位向量和中断向量表处理。 另外,用户提到需要图示解析,虽然无法直接提供图片,但可以用文字描述各个步骤的流程图,并建议参考相关文档或教程中的图示,比如引用[1]中的Proteus和Keil联调例程可能有相关示意图。 还需要检查是否有遗漏的步骤,比如某些单片机可能有的看门狗初始化或硬件自检过程,但根据提供的引用,可能不需要深入这些细节。确保语言简洁明了,符合中文表达,避免使用Markdown格式,用自然的分段和编号来组织内容。 最后,生成相关问题时要围绕启动流程的各个部分,比如启动文件的作用、堆栈初始化、中断向量表等,帮助用户进一步理解可能遇到的疑问点。</think>### 单片机启动流程解析 单片机启动流程是系统从加复位到执行用户程序的初始化过程,其核心步骤可概括如下: 1. **硬件复位** 上后,硬件复位路触发,程序计数器(PC)指向复位向量地址(通常为`0x00000000`或特定地址)。此处存放复位处理函数的入口地址[^2]。 2. **启动文件初始化(以STM32为例)** - **堆栈分配**:分配并初始化栈(Stack)和堆(Heap)空间,栈用于函数调用和局部变量存储,堆用于动态内存分配[^2]。 - **中断向量表**:定义中断向量表,每个中断对应一个异常处理程序的入口地址。例如: ```c __Vectors DCD __initial_sp ; 栈顶地址 DCD Reset_Handler ; 复位处理函数 DCD NMI_Handler ; NMI中断处理 ... ; 其他中断向量 ``` - **初始化.data和.bss段**: - `.data`段(已初始化的全局变量)从Flash复制到RAM。 - `.bss`段(未初始化的静态变量)清零。 3. **系统时钟配置** 调用`SystemInit()`函数初始化时钟源(如HSI、HSE),配置PLL生成系统主频(如72MHz)。 4. **跳转至用户程序** 最终通过`__main`或直接调用`main()`函数进入用户代码。 ### 启动流程图示说明(文字描述) ``` 加复位 → 复位向量跳转 → 初始化堆栈 → 复制.data段 → 清零.bss段 → 配置系统时钟 → 执行main() ``` ### 相关问题 1. 启动文件中中断向量表的作用是什么?如何自定义中断服务函数? 2. 堆(Heap)和栈(Stack)在单片机运行时有什么区别? 3. 如何验证.data段和.bss段的初始化是否成功? 4. 若系统时钟配置失败会导致哪些问题? [^1]: 参考启动文件与硬件初始化关系的示例代码和仿真流程。 : 基于STM32启动文件的核心功能描述。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值