打印
[其他ST产品]

启动代码的基本组成

[复制链接]
105|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
kqh11a|  楼主 | 2024-12-30 18:06 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在 STM32 微控制器开发中,Keil MDK是一个常用的集成开发环境。启动代码是每个 STM32 项目中至关重要的一部分,它负责初始化硬件、设置中断向量表,并为主程序执行做好准备。这个启动代码通常由 Keil 编译器自动生成,但理解其中的内容是非常重要的,特别是在开发嵌入式系统时。
启动代码的基本组成启动代码的主要功能是:
  • 设置堆栈指针
  • 初始化中断向量表
  • 初始化静态变量
  • 调用系统初始化函数
  • 进入主程序

使用特权

评论回复
沙发
kqh11a|  楼主 | 2024-12-30 18:06 | 只看该作者
一般情况下,STM32 的启动代码会包含以下几个部分:

1. 链接器脚本和启动文件的初始化
Keil 使用 链接器脚本 来指定各个代码段、数据段、堆栈和堆的位置。STM32 启动文件通常包括:

启动汇编代码:这部分代码通常是汇编语言编写的,用于完成微控制器的基本硬件初始化工作。
C 语言的系统初始化函数:这部分代码由 C 语言编写,负责进一步初始化系统,如时钟配置、外设初始化等。

使用特权

评论回复
板凳
kqh11a|  楼主 | 2024-12-30 18:07 | 只看该作者
堆栈指针初始化
当 MCU 启动时,首先会加载 中断向量表,它包含了 MCU 启动时的所有中断处理程序。堆栈指针(SP)是指向栈内存位置的指针,在启动时会通过中断向量表的第一个地址设置。

asm
复制代码
    .section .isr_vector, "a", %progbits
    .weak  __initial_sp
    .set __initial_sp, _estack
    .global __initial_sp
_estack 是堆栈的末尾地址,通常是链接器脚本中定义的一个符号。__initial_sp 是栈指针的初始值。

使用特权

评论回复
地板
kqh11a|  楼主 | 2024-12-30 18:07 | 只看该作者
中断向量表(Interrupt Vector Table)
STM32 的中断向量表通常位于程序的开头位置,它是一个数组,其中存储了每个中断类型的处理函数地址。启动时,MCU 会首先加载此表,初始化各个中断类型。

在启动代码中,你会看到类似以下内容:

asm
复制代码
    .section .isr_vector, "a", %progbits
    .long   _estack                 /* 堆栈的初始位置 */
    .long   Reset_Handler           /* 复位中断 */
    .long   NMI_Handler             /* 非屏蔽中断 */
    .long   HardFault_Handler       /* 硬故障中断 */
    /* 其余的中断处理程序 */
这段代码中的 Reset_Handler 就是复位后的中断处理程序。它是 MCU 上电复位后的第一条指令,负责后续初始化。

使用特权

评论回复
5
kqh11a|  楼主 | 2024-12-30 18:07 | 只看该作者
复位中断处理程序(Reset Handler)
复位处理程序是启动代码中的第一个 C 函数,它在 MCU 上电后执行,并负责初始化系统的硬件和软件。

通常,复位处理程序的实现大致如下:

c
复制代码
void Reset_Handler(void) {
    // 1. 初始化数据段
    uint32_t *src, *dst;
   
    // 将初始化数据从 flash 区复制到 SRAM 区
    src = &_sidata;
    dst = &_sdata;
    while (dst < &_edata) {
        *dst++ = *src++;
    }

    // 2. 清零未初始化数据段
    dst = &_bss;
    while (dst < &_ebss) {
        *dst++ = 0;
    }

    // 3. 调用系统初始化函数(如时钟配置)
    SystemInit();

    // 4. 调用主程序
    main();
}
&_sidata 是存放初始化数据的 flash 区的起始地址,&_sdata 是目标数据段的起始地址。
_edata 和 _bss 分别是数据段和未初始化数据段的结束地址。

使用特权

评论回复
6
kqh11a|  楼主 | 2024-12-30 18:07 | 只看该作者
复位中断处理程序(Reset_Handler)是嵌入式系统中启动时的第一个 C 函数,它负责完成系统初始化工作,包括将初始化的数据从 Flash 区域复制到 SRAM,以及清零未初始化的数据区(.bss 段)。

使用特权

评论回复
7
kqh11a|  楼主 | 2024-12-30 18:07 | 只看该作者
它会调用 SystemInit 函数进行系统级的初始化(如时钟配置等),最后会跳转到 main() 函数,开始正常的应用程序运行。

使用特权

评论回复
8
kqh11a|  楼主 | 2024-12-30 18:07 | 只看该作者
初始化 .data 段:将存放在 Flash 区域的初始化数据复制到 SRAM 中。
清零 .bss 段:将未初始化的数据区清零,以保证数据的正确性。
调用 SystemInit:进行系统级初始化,如时钟配置等。
跳转到 main():系统初始化完成后,程序将进入主应用逻辑。

使用特权

评论回复
9
kqh11a|  楼主 | 2024-12-30 18:08 | 只看该作者
时钟初始化(SystemInit)
在启动文件中,SystemInit() 通常是由 system_stm32f0xx.c 等文件提供的系统初始化函数,它负责配置 MCU 的时钟、外设等硬件。

示例:

c
复制代码
void SystemInit(void) {
    // 配置时钟源
    // 可能会设置 PLL、外部晶振等
}

使用特权

评论回复
10
kqh11a|  楼主 | 2024-12-30 22:56 | 只看该作者
启动代码的执行流程总结:
MCU 上电或复位,处理器开始执行从 Reset_Handler 开始的指令。
初始化堆栈指针:堆栈指针指向 _estack。
初始化中断向量表:加载中断处理程序的地址。
调用复位中断处理程序(Reset Handler):
初始化数据段和 BSS 段。
调用 SystemInit() 配置系统硬件(如时钟、外设)。
最后调用 main() 进入应用程序。
主程序 main() 运行,开始执行应用逻辑。

使用特权

评论回复
11
kqh11a|  楼主 | 2024-12-30 22:56 | 只看该作者
启动代码的执行流程总结:
MCU 上电或复位,处理器开始执行从 Reset_Handler 开始的指令。
初始化堆栈指针:堆栈指针指向 _estack。
初始化中断向量表:加载中断处理程序的地址。
调用复位中断处理程序(Reset Handler):
初始化数据段和 BSS 段。
调用 SystemInit() 配置系统硬件(如时钟、外设)。
最后调用 main() 进入应用程序。
主程序 main() 运行,开始执行应用逻辑。

使用特权

评论回复
12
kqh11a|  楼主 | 2024-12-30 22:56 | 只看该作者
在 STM32 项目中,Keil MDK 的启动代码负责了系统的初始化和应用程序的启动。理解启动代码的工作原理对于嵌入式开发至关重要。启动代码通常由 Keil 自动生成,但你仍然需要了解每个步骤的功能和作用,以便在遇到需要修改硬件初始化、调试系统或修改链接器脚本时能够做出正确的调整。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

31

主题

554

帖子

0

粉丝