【STM32】解读启动文件startup_stm32f10x_md.s

栈空间 

  • 栈(Stack):栈是一种后进先出(LIFO)的数据结构,用于存储函数调用时的局部变量、返回地址和寄存器的值。启动文件会定义栈的大小,并将栈指针初始化为栈顶地址。在函数调用时,栈指针会自动调整,以分配和释放内存空间。例如,当一个函数被调用时,返回地址和局部变量会被压入栈中;函数返回时,这些数据会从栈中弹出。

Stack_Size EQU 0x00000400

  • EQU 属于汇编伪指令,其功能是定义一个常量。在这里,Stack_Size 被定义为0x00000400,换算成十进制就是 1024。这意味着栈的大小为 1024 字节。

AREA STACK, NOINIT, READWRITE, ALIGN=3

  • AREA 也是汇编伪指令,用于定义一个存储区域。
  • STACK 是该存储区域的名称,表明这个区域是用来作为栈空间的。
  • NOINIT 表示此区域无需进行初始化操作,也就是在程序启动时,不会对这个区域的内存赋初值。
  • READWRITE 说明这个区域是可读写的,程序能够对其进行读写操作。
  • ALIGN=3意味着该存储区域的起始地址要按照 2的3次方=8 字节对齐。地址对齐有助于提高内存访问的效率。

Stack_Mem SPACE Stack_Size

  • Stack_Mem 是这个栈空间的起始标签。
  • SPACE 同样是汇编伪指令,其作用是分配一定大小的内存空间。这里分配的空间大小由 Stack_Size 决定,也就是 1024 字节。

 __initial_sp

  • __initial_sp 代表栈顶指针的初始值。因为栈是从高地址向低地址增长的,所以 __initial_sp 指向的是栈空间的末尾地址,也就是栈顶。

代码功能

这段代码的主要功能是定义一个大小为 1024 字节、起始地址按 8 字节对齐、可读写且无需初始化的栈空间,并且确定了栈顶指针的初始位置。在程序启动时,栈指针会被设置为 __initial_sp,后续函数调用、局部变量存储等操作都会使用这个栈空间。

 堆空间

堆(Heap):堆用于动态内存分配,例如使用malloc()free()函数。启动文件会定义堆的大小和起始地址,为动态内存分配提供空间。堆的管理通常由操作系统或运行时库负责。

; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

                PRESERVE8
                THUMB

1. Heap_Size EQU 0x00000200

  • EQU 是汇编伪指令,用于定义常量。这里将 Heap_Size 定义为 0x00000200,换算成十进制是 512。这表明堆的大小为 512 字节。

2. AREA HEAP, NOINIT, READWRITE, ALIGN=3

  • AREA 也是汇编伪指令,用于定义一个存储区域。
  • HEAP 是该存储区域的名称,意味着这个区域是用来作为堆空间的。
  • NOINIT 表示此区域无需进行初始化操作,即程序启动时,不会对该区域的内存赋初值。
  • READWRITE 说明这个区域是可读写的,程序能够对其进行读写操作。
  • ALIGN=3 表示该存储区域的起始地址要按照 2的3次方=8 字节对齐,地址对齐有助于提高内存访问效率。

3. __heap_base

  • __heap_base 是一个标签,代表堆空间的起始地址。

4. Heap_Mem SPACE Heap_Size

  • Heap_Mem 同样是一个标签,SPACE 是汇编伪指令,用于分配一定大小的内存空间。这里分配的空间大小由 Heap_Size 决定,即 512 字节。

5. __heap_limit

  • __heap_limit 是一个标签,代表堆空间的结束地址。

6. PRESERVE8

  • 这是一个汇编指令,其作用是指示编译器保证代码中的数据和栈的对齐方式为 8 字节对齐。在某些架构中,保持 8 字节对齐能够提高内存访问的性能。

7. THUMB

  • 此指令表明后续的代码会使用 Thumb 指令集进行编译。Thumb 指令集是 ARM 指令集的一种精简版本,它具有更高的代码密度,能够节省存储空间。

代码整体功能

这段代码的主要功能是定义一个大小为 512 字节、起始地址按 8 字节对齐、可读写且无需初始化的堆空间,同时标记出堆空间的起始地址和结束地址。在程序运行过程中,动态内存分配函数(如 malloccalloc 等)会使用这个堆空间来分配内存。

 

初始化向量表

  • 向量表的作用:向量表是一个固定地址的数组,存储着各个异常和中断处理函数的入口地址。当异常或中断发生时,处理器会根据异常或中断号从向量表中获取相应的处理函数地址,并跳转到该地址执行。
  • 异常和中断处理:启动文件会将向量表初始化为默认的异常和中断处理函数地址。这些处理函数包括复位处理函数、非屏蔽中断(NMI)处理函数、硬故障处理函数等。用户可以根据需要修改这些处理函数的地址,以实现自定义的异常和中断处理逻辑。
; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                ; 其他中断向量...

  • 复位处理函数Reset_Handler是复位后执行的第一个函数,它会完成一些必要的初始化工作,然后跳转到主程序。

中断向量表结束标记 

__Vectors_End

__Vectors_Size  EQU  __Vectors_End - __Vectors
  • 向量表结束标记__Vectors_End表示向量表的结束地址。
  • 向量表大小__Vectors_Size计算向量表的大小,用于后续的一些初始化操作。

复位处理函数 

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
     IMPORT  __main
     IMPORT  SystemInit
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

系统初始化

  • 硬件初始化:启动文件通常会调用一个系统初始化函数,如SystemInit(),该函数会对芯片的硬件资源进行初始化,包括时钟系统、总线配置、外设等。例如,配置系统时钟源、设置时钟频率、使能外设时钟等。这些初始化操作确保芯片以正确的时钟频率和工作模式运行。
  • 全局变量初始化:启动文件会将全局变量和静态变量初始化为其初始值。对于已初始化的全局变量,启动文件会将其从只读存储器(ROM)复制到随机存取存储器(RAM)中;对于未初始化的全局变量,启动文件会将其所在的 RAM 区域清零。
  • 跳转到主程序:调用__main函数,__main是 C/C++ 程序的入口点,它会完成全局变量的初始化、调用main()函数等操作。程序的主要逻辑通常从这里开始执行。

 中断处理函数

; Dummy Exception Handlers (infinite loops which can be modified)

NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .
                ENDP
HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler          [WEAK]
                B       .
                ENDP
; 其他中断处理函数...

默认处理函数:这些是默认的中断处理函数,当相应的异常或中断发生时,如果用户没有提供自定义的处理函数,就会执行这些默认函数。默认函数通常是一个无限循环B .,表示程序会一直停在这里,等待用户进一步处理。

<think>嗯,用户问的是如何修改启动文件名为startup_stm32f10x_md.s。首先,我需要确定用户的具体需求。启动文件STM32开发中非常重要,它处理初始化堆栈、中断向量表等。不同的STM32型号对应不同的启动文件,比如小容量、中容量、大容量设备。文件名中的“md”代表中容量,所以用户可能是在更换芯片型号后需要调整启动文件。 接下来,我需要考虑用户可能的操作环境,比如使用Keil、IAR还是其他IDE。不同工具修改启动文件的方法可能不同。例如,在Keil中需要替换项目中的文件并修改包含路径,而在IAR中可能需要更改项目配置中的链接脚本。此外,用户可能还需要注意启动文件是否与编译器兼容,比如是否支持ARMCC或GCC。 另外,用户可能遇到的问题是为什么需要修改启动文件。比如,更换了不同Flash或RAM大小的芯片,或者启动文件不兼容当前的编译工具链。这时候需要确认芯片的具体型号,确保选用了正确的启动文件。例如,hd代表高密度,适用于大容量设备,而cl适用于互联型设备。 还需要提醒用户备份原文件,避免替换后出现问题无法恢复。同时,修改后需要重新编译整个项目,检查是否有错误或警告,确保中断向量表和初始化代码正确无误。如果用户使用的是库函数或HAL库,可能需要同步更新其他相关文件,比如链接脚本或系统初始化代码。 最后,用户可能忽略了一些细节,比如启动文件中的设备定义是否与目标芯片匹配,或者是否在项目设置中正确指定了启动文件的位置。这时候需要逐步引导用户检查这些配置,确保每一步都正确无误。</think>关于STM32启动文件startup_stm32f10x_md.s的修改,以下是分步说明: 1. **理解文件命名规则**: - `stm32f10x`:表示STM32F1系列芯片 - `md`:表示中密度(Medium-density)设备(Flash容量64-128KB) - 其他常见后缀: $$ startup\_stm32f10x\_ld.s \quad (低密度,16-32KB) $$ $$ startup\_stm32f10x\_hd.s \quad (高密度,256-512KB) $$ 2. **修改前提**: - 确认芯片具体型号(如STM32F103C8T6属于中密度) - 核对Flash容量是否匹配后缀定义 3. **修改步骤**(以Keil MDK为例): ```markdown a. 在工程目录中删除原启动文件 b. 将新启动文件复制到项目文件夹 c. 右键点击工程目录中的启动文件组 -> "Manage Project Items" d. 移除旧启动文件,添加新文件 e. 检查Options for Target -> C/C++中的预定义宏: 正确对应关系: - ld: STM32F10X_LD - md: STM32F10X_MD - hd: STM32F10X_HD ``` 4. **注意事项**: - 必须同步修改`system_stm32f10x.c`中的时钟配置 - 检查链接脚本(.sct)是否匹配芯片容量 - 不同编译器(IAR/Keil/GCC)的启动文件不可混用 - 修改前务必做好备份 5. **验证方法**: - 编译后检查生成的.map文件 - 确认中断向量表地址是否正确 - 通过调试器验证程序能否正常进入main函数 6. **特殊场景处理**: - 如果使用标准外设库,需对应`stm32f10x.h`中的`#define STM32F10X_MD` - 使用HAL库时,建议通过STM32CubeMX重新生成启动文件 建议通过STM32CubeIDE的图形化界面进行自动配置,可降低手动修改风险。如果仅需适配不同容量芯片,正确选择对应后缀的启动文件即可,不建议直接修改汇编代码内容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值