当前位置: 首页 > article >正文

嵌入式之详解:startup.S文件

文章目录

  • 前言
  • 一、MCU上电
    • 1、上电复位
    • 2、复位向量表
    • 3、跳转到复位处理程序
    • 4、执行startup.s文件
  • 二、startup.s代码解析
    • 1、汇编指令和架构声明
    • 2、复位处理函数定义
    • 3、中断屏蔽
    • 4、寄存器初始化
    • 5、第一阶段内存初始化(RamInit0)
    • 6、堆栈指针初始化
    • 7、第二阶段内存初始化(RamInit1)
    • 8、向量表复制(可选)
    • 9、系统初始化(可选)
    • 10、第三阶段内存初始化(RamInit2)
    • 11、中断使能
    • 12、调用主函数
    • 13、无限循环
  • 总结


前言

我们在实际嵌入式软件开发过程中,大多数都是直接使用,关于其内部底层以及汇编了解甚少。刚好把笔记记录在博客上,方便复习以及分享给需要帮助的人。
本章将从两部分:1 MCU上电->执行startup.s; 2 startup.s代码解析。

不必担心,走你~
在这里插入图片描述

一、MCU上电

1、上电复位

  • 当 32 位 MCU 上电时,电源电压会从 0V 开始上升,直到达到稳定的工作电压。在这个过程中,复位电路会监测电源电压的变化。一旦电源电压达到复位电路的触发阈值,复位电路会产生一个复位信号。
  • 复位信号会将 MCU 的内部状态进行重置,包括将所有的寄存器恢复到初始状态,程序计数器(PC)也会被设置为一个特定的地址,这个地址通常是 MCU 的复位向量地址,对于很多 32 位 ARM Cortex - M 系列的 MCU,这个地址是0x00000000。

2、复位向量表

  • 在 MCU 的 Flash 或 ROM 的起始地址(即复位向量地址)处,存放着一个向量表。向量表是一个包含多个异常向量的表格,每个异常向量都是一个 32 位的地址,指向相应异常处理程序的入口地址。
  • 向量表的第一个条目是初始栈指针(MSP)的值,第二个条目是复位处理程序的入口地址。当 MCU 复位后,程序计数器(PC)会被设置为复位向量地址,然后 MCU 会从这个地址开始读取数据。首先,MCU 会将向量表中第一个地址处存储的值加载到主堆栈指针(MSP)寄存器中,完成堆栈指针的初始化,确保后续的函数调用和中断处理有正确的堆栈空间可用。接着,MCU 会从向量表的第二个地址处获取复位处理程序的入口地址,并将这个地址加载到程序计数器(PC)中,从而跳转到复位处理程序的入口。

3、跳转到复位处理程序

  • 复位处理程序的入口地址通常指向startup.s文件中的Reset_Handler标签处。因此,MCU 会跳转到startup.s文件中的Reset_Handler开始执行代码。

4、执行startup.s文件

  • 中断屏蔽:在Reset_Handler中,首先会执行cpsid i指令,用于屏蔽中断。这是为了防止在初始化过程中,外部中断干扰 MCU 的正常初始化操作。
  • 寄存器初始化:将一些通用寄存器(如r1 - r12)初始化为 0,为后续的操作提供一个已知的初始状态。
  • 内存初始化:
  • RamInit0 阶段:调用RamInit0函数,该函数主要进行一些与内存相关的初始化操作,例如 ECC(错误纠正码)的初始化。
  • 堆栈指针初始化:将STACK_end所代表的地址加载到堆栈指针(SP,即r13)中,完成堆栈的初始化。
  • RamInit1 阶段:调用RamInit1函数,该函数通常用于将数据段从 Flash 复制到 SRAM,并将 BSS 段清零。数据段包含已初始化的全局变量,BSS 段包含未初始化的全局变量。
  • 向量表复制(可选):如果没有定义__NO_VECTOR_TABLE_COPY宏,会调用VectorTableCopy函数,将中断向量表从 Flash 复制到 RAM 中,以提高中断响应速度。
  • 系统初始化(可选):如果没有定义__NO_SYSTEM_INIT宏,会调用SystemInit函数,用户可以在该函数中初始化 PLL(锁相环)等,以调整系统时钟,提高 MCU 的运行速度。
  • RamInit2 阶段:调用RamInit2函数,进行其他内存相关的初始化操作。
  • 中断使能:执行cpsie i指令,重新使能中断,允许 MCU 响应外部中断。
  • 调用主函数:使用bl main指令调用main函数,进入用户编写的主程序,开始执行具体的应用任务。
    通过以上步骤,32 位 MCU 从上电开始,经过复位、初始化等一系列操作,最终执行到startup.s文件中的代码,并完成系统的初始化,进入用户程序的执行阶段。

二、startup.s代码解析

这段代码是来自于云途YTM32B1LE0x系列的,其它也类似。

/*
 *  Copyright 2020-2024 Yuntu Microelectronics co.,ltd
 *  All rights reserved.
 * 
 *  YUNTU Confidential. This software is owned or controlled by YUNTU and may only be
 *  used strictly in accordance with the applicable license terms. By expressly
 *  accepting such terms or by downloading, installing, activating and/or otherwise
 *  using the software, you are agreeing that you have read, and that you agree to
 *  comply with and are bound by, such license terms. If you do not agree to be
 *  bound by the applicable license terms, then you may not retain, install,
 *  activate or otherwise use the software. The production use license in
 *  Section 2.3 is expressly granted for this software.
 * 
 * @file startup.S
 * @brief 
 * 
 */

    .syntax unified
    .arch armv6-m

    .thumb
/* Reset Handler */
    .thumb_func
    .align 2
    .globl   Reset_Handler
    .type    Reset_Handler, %function
_start:
Reset_Handler:
    cpsid   i               /* Mask interrupts */

    /* Init the rest of the registers */
    ldr     r1,=0
    ldr     r2,=0
    ldr     r3,=0
    ldr     r4,=0
    ldr     r5,=0
    ldr     r6,=0
    ldr     r7,=0
    mov     r8,r7
    mov     r9,r7
    mov     r10,r7
    mov     r11,r7
    mov     r12,r7

 
    /* RamInit 0 Stage, focus on ecc init, asm code*/
    bl     RamInit0

    /* Initialize the stack pointer */
    ldr     r0,=STACK_end
    mov     r13,r0

    /* RamInit 1 Stage, focus on copy data,clear bss, c code*/
    ldr     r0,=RamInit1
    blx     r0

    /* Copy Vector Table for interrupt, c code */
#ifndef __NO_VECTOR_TABLE_COPY
    /* Call the to copy vector table from flash to ram */
    ldr     r0,=VectorTableCopy
    blx     r0
#endif
    /* SystemInit, user can init PLL to speed up left startup code, c code*/
#ifndef __NO_SYSTEM_INIT
    /* Call the system init routine */
    ldr     r0,=SystemInit
    blx     r0
#endif
    /* RamInit 2 Stage, focus on others ram init, c code */
    ldr     r0,=RamInit2
    blx     r0

    /* Unmask interrupts */
    cpsie   i               

    /* Call the main routine */
    bl      main
JumpToSelf:
    b       JumpToSelf

1、汇编指令和架构声明

```asm
.syntax unified
.arch armv6-m
.thumb
  • .syntax unified:指定使用统一的汇编语法,这样可以在ARM和Thumb指令集之间更方便地切换。
  • .arch armv6-m:指定目标架构为ARMv6-M,这是Cortex - M系列微控制器常用的架构。
  • .thumb:表示后续的代码使用Thumb指令集,Thumb指令集具有更高的代码密度。

2、复位处理函数定义

```asm
.thumb_func
.align 2
.globl   Reset_Handler
.type    Reset_Handler, %function
_start:
Reset_Handler:
  • .thumb_func:声明这是一个Thumb指令集的函数。
  • .align 2:将函数的起始地址对齐到2的倍数,即按字对齐。
  • .globl Reset_Handler:声明 Reset_Handler 为全局符号,这样其他文件可以引用它。
  • .type Reset_Handler, %function:指定 Reset_Handler 是一个函数。
  • _startReset_Handler 是同一个入口点,当单片机复位时,程序会从这里开始执行。

3、中断屏蔽

```asm
cpsid   i               /* Mask interrupts */
  • cpsid i:这是一条指令,用于屏蔽中断(i 表示IRQ中断)。在初始化过程中,通常会先屏蔽中断,以防止中断干扰初始化操作。

4、寄存器初始化

```asm
/* Init the rest of the registers */
ldr     r1,=0
ldr     r2,=0
ldr     r3,=0
ldr     r4,=0
ldr     r5,=0
ldr     r6,=0
ldr     r7,=0
mov     r8,r7
mov     r9,r7
mov     r10,r7
mov     r11,r7
mov     r12,r7
  • 这部分代码将寄存器 r1r12 初始化为0。通过 ldr 指令将立即数0加载到 r1r7 中,然后使用 mov 指令将 r7 的值复制到 r8r12 中。

5、第一阶段内存初始化(RamInit0)

```asm
/* RamInit 0 Stage, focus on ecc init, asm code*/
bl     RamInit0
  • bl RamInit0:这是一条带链接的跳转指令,调用 RamInit0 函数。该函数可能是用于内存的第一阶段初始化,重点是ECC(错误纠正码)的初始化,并且是用汇编语言编写的。

6、堆栈指针初始化

```asm
/* Initialize the stack pointer */
ldr     r0,=STACK_end
mov     r13,r0
  • ldr r0,=STACK_end:将符号 STACK_end 所代表的地址加载到寄存器 r0 中。STACK_end 通常是栈的结束地址。
  • mov r13,r0:将 r0 的值(即栈的结束地址)赋值给 r13r13 是堆栈指针(SP),这样就完成了堆栈指针的初始化。

7、第二阶段内存初始化(RamInit1)

```asm
/* RamInit 1 Stage, focus on copy data,clear bss, c code*/
ldr     r0,=RamInit1
blx     r0
  • ldr r0,=RamInit1:将 RamInit1 函数的地址加载到寄存器 r0 中。
  • blx r0:带链接的跳转并根据目标地址的最低位决定使用ARM还是Thumb指令集,调用 RamInit1 函数。该函数主要用于数据段的复制和BSS段的清零,是用C语言编写的。

8、向量表复制(可选)

```asm
/* Copy Vector Table for interrupt, c code */
#ifndef __NO_VECTOR_TABLE_COPY
/* Call the to copy vector table from flash to ram */
ldr     r0,=VectorTableCopy
blx     r0
#endif
  • #ifndef __NO_VECTOR_TABLE_COPY:如果没有定义 __NO_VECTOR_TABLE_COPY 宏,则执行下面的代码。
  • ldr r0,=VectorTableCopy:将 VectorTableCopy 函数的地址加载到寄存器 r0 中。
  • blx r0:调用 VectorTableCopy 函数,该函数用于将中断向量表从Flash复制到RAM中,是用C语言编写的。

9、系统初始化(可选)

```asm
/* SystemInit, user can init PLL to speed up left startup code, c code*/
#ifndef __NO_SYSTEM_INIT
/* Call the system init routine */
ldr     r0,=SystemInit
blx     r0
#endif
  • #ifndef __NO_SYSTEM_INIT:如果没有定义 __NO_SYSTEM_INIT 宏,则执行下面的代码。
  • ldr r0,=SystemInit:将 SystemInit 函数的地址加载到寄存器 r0 中。
  • blx r0:调用 SystemInit 函数,用户可以在该函数中初始化PLL(锁相环)等,以加速后续的启动代码,该函数是用C语言编写的。

10、第三阶段内存初始化(RamInit2)

```asm
/* RamInit 2 Stage, focus on others ram init, c code */
ldr     r0,=RamInit2
blx     r0
  • ldr r0,=RamInit2:将 RamInit2 函数的地址加载到寄存器 r0 中。
  • blx r0:调用 RamInit2 函数,该函数用于内存的第三阶段初始化,重点是其他内存相关的初始化,是用C语言编写的。

11、中断使能

```asm
/* Unmask interrupts */
cpsie   i               
  • cpsie i:这是一条指令,用于使能中断(i 表示IRQ中断)。在完成初始化后,重新使能中断,允许系统响应外部中断。

12、调用主函数

```asm
/* Call the main routine */
bl      main
  • bl main:带链接的跳转指令,调用 main 函数,进入用户编写的主程序。

13、无限循环

```asm
JumpToSelf:
b       JumpToSelf
  • 如果 main 函数执行完毕返回,程序会跳转到 JumpToSelf 标签处,然后不断地跳转到自身,形成一个无限循环,防止程序跑飞。

总结

综上所述,这段启动代码的主要功能是在单片机复位后,依次完成中断屏蔽、寄存器初始化、内存初始化、系统初始化等操作,最后调用 main 函数进入用户程序,并在必要时使能中断。


http://www.kler.cn/a/543054.html

相关文章:

  • IEEE期刊Word导出PDF注意事项
  • C# ASP.NET的发展历程
  • http 与 https 的区别?
  • Flutter_学习记录_基本组件的使用记录_2
  • bug-ant下拉框解决下拉框跟随表单容器(指定下拉框挂载容器):getPopupContainer=“p=>p.parentNode“
  • 访问Elasticsearch服务 curl ip 端口可以 浏览器不可以
  • Cherry Studio 连接私域deepseek-r1模型搭建私域知识库和智能体(也可使用第三方模型)
  • 图像处理之图像亮度/对比度调整
  • 【AI知识点】Adversarial Validation(对抗验证)
  • Redis核心技术知识点全集
  • 从工匠故事读懂开源软件的特点与价值
  • 物理引擎Box2D
  • 《图解设计模式》笔记(八)管理状态
  • 异位妊娠唯一相关的是年龄(U型曲线)
  • SWIFT (Scalable lightWeight Infrastructure for Fine-Tuning)
  • 【多模态大模型】系列2:Transformer Encoder-Decoder——BLIP、CoCa、BEITv3
  • 【MATLAB源码-第261期】基于matlab的帝企鹅优化算法(EPO)机器人栅格路径规划,输出做短路径图和适应度曲线
  • 浏览器渲染方式及性能优化
  • 使用 meshgrid函数绘制网格点坐标的原理与代码实现
  • SAP-ABAP:dialog界面中的数据块Event Block详解举例
  • Kafka 集群原来是使用ZK管理,现在新版本是怎么管理的?
  • IPC 共享通俗讲解及其安全风险
  • 【devops】 Git仓库如何fork一个私有仓库到自己的私有仓库 | git fork 私有仓库
  • 【电路笔记】-同步计数器
  • 学习星开源在线考试教育系统
  • FFmpeg Audio options