根据提供的《GD32F4xx用户手册》及相关文档,GD32F470系列微控制器(基于ARM Cortex-M4内核)的上电启动过程是硬件和软件协同工作的结果。以下是详细的启动步骤,直到执行用户程序的main函数。
核心启动流程概览
整个过程可以分为三个阶段:
- 硬件复位与内核启动:芯片上电,硬件逻辑将CPU从复位状态释放,内核从固定的存储地址开始执行预置代码。
- 启动程序(Bootloader / 启动文件)执行:这通常由固化在芯片内部或用户编写的汇编启动代码完成,负责初始化最基本的系统环境。
- C运行时环境建立与跳转 :初始化C语言的运行环境(如堆栈、全局变量),最后跳转到用户编写的
main函数。
详细步骤分解
第一阶段:硬件复位与内核启动
-
上电复位:
- 当给芯片提供稳定的电源(
VDD/VDDA)后,内部的上电复位(POR) 电路会产生一个复位信号,将芯片(除备份域外)的所有逻辑复位到初始状态。 - 如果使能了欠压复位(BOR),电源电压需高于设定的阈值,才会释放复位信号。
- 当给芯片提供稳定的电源(
-
引导配置(Boot Configuration):
- 在复位信号释放后的第四个系统时钟(
CK_SYS)上升沿,芯片会锁存BOOT0和BOOT1引脚的电平状态。这两个引脚决定了CPU从哪个存储空间开始取指(引导源)。根据手册 表1-3. 引导模式 :BOOT0=0:从主FLASH存储器 启动(地址0x0800 0000)。这是最常见的用户程序运行模式。BOOT0=1, BOOT1=0:从系统存储器 (Bootloader)启动(地址0x1FFF 0000)。BOOT0=1, BOOT1=1:从片上SRAM 启动(地址0x2000 0000)。
- 在复位信号释放后的第四个系统时钟(
-
映射与取向量:
- 根据选择的引导模式,相应的存储空间会被映射到引导存储空间 (即从
0x0000 0000开始的地址空间)。例如,当选择从主FLASH启动时,FLASH的起始地址0x0800 0000被映射到0x0000 0000。 - Cortex-M4内核 从
0x0000 0000地址读取数据,并将其作为栈顶指针(MSP) 的初始值。 - 紧接着,内核从
0x0000 0004地址读取数据,并将其作为复位向量,即复位后要执行的第一条指令的地址。
- 根据选择的引导模式,相应的存储空间会被映射到引导存储空间 (即从
第二阶段:启动程序执行
-
跳转到复位处理函数:
- CPU跳转到复位向量指向的代码地址,这个地址通常是启动文件(
startup_gd32f470.s)中的Reset_Handler函数入口。
- CPU跳转到复位向量指向的代码地址,这个地址通常是启动文件(
-
Reset_Handler执行 :这个函数是启动程序的核心,主要执行以下操作:
- 系统时钟初始化 :调用
SystemInit函数。该函数配置时钟控制单元(RCU),将系统时钟从默认的内部16MRC振荡器(IRC16M) 切换到用户配置的时钟源(如外部高速晶体振荡器(HXTAL) 和锁相环(PLL)),最终设置到最高频率(例如200MHz)。这一步在手册第4章"复位和时钟单元(RCU)"中有详细说明。 - 中断向量表重定位 :如果程序在SRAM中运行或需要重定位中断向量表,
SystemInit或后续代码会配置中断向量表偏移量寄存器(VTOR),将其指向正确的位置。 - 初始化静态数据 :
- 将已初始化的全局变量和静态变量 (
.data段)从FLASH复制到SRAM中。 - 将未初始化的全局变量和静态变量 (
.bss段)所在的SRAM区域清零。
- 将已初始化的全局变量和静态变量 (
- 初始化堆和栈 :为C语言的运行库设置堆栈指针(SP),并可能初始化堆内存(用于
malloc等动态内存分配)。
- 系统时钟初始化 :调用
第三阶段:进入用户程序
-
跳转到
__main:Reset_Handler函数的最后一步通常是跳转到C库的入口函数__main(注意,不是用户写的main)。
-
C库初始化:
__main函数负责完成C运行时环境的最后设置,例如初始化C库函数所需的资源。
-
执行用户
main函数:- C库初始化完成后,最终会调用用户程序中的
main函数。至此,用户程序开始执行。
- C库初始化完成后,最终会调用用户程序中的
总结图示

关键点总结
- 启动文件 :整个过程的核心是启动文件(通常由芯片厂商提供),它包含了
Reset_Handler和SystemInit函数的汇编/C实现。 - 向量表 :存储在FLASH起始位置(
0x0800_0000)的向量表是程序正确运行的基石。第一个字是栈顶地址,第二个字是Reset_Handler的地址。 SystemInit:在进入用户main函数之前,一个稳定且高效的时钟配置是必需的。SystemInit函数正是为此而设。你可以在system_gd32f4xx.c文件中找到它并进行配置。- 用户入口点 :尽管启动过程复杂,但最终目标就是可靠地跳转到你编写的
main函数,开始应用程序的逻辑。