【蓝桥杯嵌入式入门与进阶】4.初读启动文件:粗略阅读,经常翻阅,知己知彼,百战百胜

目录

1.二者差异

1. 1适用芯片型号不同

1.2中断向量表差异

1.2.1 中断数量和种类

1.2.2 部分中断处理函数命名差异

1.2.3. 复位处理描述差异

1.2.4代码注释中的功能描述差异

1.2.5 DMA 通道中断处理函数差异

示例代码对比片段 

startup_stm32g431xx.s

startup_stm32f103xe.s

2.F103启动代码

2.1说明部分

2.2栈初始化

2.3堆初始化

2.4中断向量表初始化

1. ; Vector Table Mapped to Address 0 at Reset

2. AREA RESET, DATA, READONLY

3. EXPORT Vectors, Vectors_End, __Vectors_Size

4. Vectors DCD initial_sp

5. DCD Reset_Handler

6. DCD NMI_Handler, HardFault_Handler, ...

7. DCD 0 (Reserved)

8. DCD SVC_Handler, DebugMon_Handler, PendSV_Handler, SysTick_Handler

9. DCD WWDG_IRQHandler, PVD_IRQHandler, ...

10. __Vectors_End

11. Vectors_Size EQU Vectors_End - __Vectors

12. AREA |.text|, CODE, READONLY

总结:

2.5中断向量表如何理解?

1. 邮局与信箱

2. 电话本

3. 机场航班调度

4. 事件管理系统

小结

2.6复位处理程序

1. Reset_Handler PROC

2. EXPORT Reset_Handler [WEAK]

3. IMPORT __main

4. IMPORT SystemInit

5. LDR R0, =SystemInit

6. BLX R0

7. LDR R0, =__main

8. BX R0

9. ENDP

总结:

形象的例子:

2.7其他中断程序定义

1. NMI_Handler PROC

2. EXPORT NMI_Handler [WEAK]

3. B .

4. ENDP

总结:

形象的例子:

2.8EXPORT NMI_Handler [WEAK]

作用:

其他模块如何使用:

示例:

小结:

示例 C 语言定义

如何链接 C 语言和汇编:

其他注意事项:

2.9用户栈和堆的初始化

1. IF :DEF:__MICROLIB

2. EXPORT __initial_sp

3. EXPORT __heap_base

4. EXPORT __heap_limit

5. ELSE

6. IMPORT __use_two_region_memory

7. EXPORT __user_initial_stackheap

8. __user_initial_stackheap

9. LDR R0, =Heap_Mem

10. LDR R1, =(Stack_Mem + Stack_Size)

11. LDR R2, = (Heap_Mem + Heap_Size)

12. LDR R3, =Stack_Mem

13. BX LR

14. ALIGN

15. ENDIF

16. END

总结:

形象的例子:


个人主页:Icomi

专栏地址:蓝桥杯嵌入式组入门与进阶

大家好,我是一颗米,本篇专栏旨在帮助大家从0开始入门蓝桥杯并且进阶,若对本系列文章感兴趣,欢迎订阅我的专栏,我将持续更新,祝你们在四月份的赛场上横着走

我们认识并且熟悉开发板的过程少不了去查看启动文件,所以查看并了解启动文件代码有助于我们进一步学习,但笔者认为,由于很多内容并没有学,初学者并不知道其中的意思,一开始粗略读个大概意思就可以,重要的是以后学到相关内容了回头再进行查看,收货就会更多!

1.二者差异

STM32G431xx 和 STM32F103xE 的启动文件,它们的主要功能都是为芯片启动做准备,包括设置堆栈、向量表、异常处理函数等,但由于芯片型号不同,具体细节存在一些区别,以下是详细分析:

1. 1适用芯片型号不同

  • startup_stm32g431xx.s:适用于 STM32G431xx 系列芯片,该系列基于 Cortex - M4 内核。
  • startup_stm32f103xe.s:适用于 STM32F103xE 系列芯片,该系列基于 Cortex - M3 内核。

1.2中断向量表差异

1.2.1 中断数量和种类
  • startup_stm32g431xx.s:中断数量更多,且有一些特定于 STM32G431xx 系列的中断,例如FDCAN1_IT0_IRQHandlerFDCAN1_IT1_IRQHandlerUCPD1_IRQHandlerCOMP1_2_3_IRQHandlerCOMP4_IRQHandlerCRS_IRQHandlerSAI1_IRQHandlerRNG_IRQHandlerLPUART1_IRQHandlerI2C3_EV_IRQHandlerI2C3_ER_IRQHandlerDMAMUX_OVR_IRQHandlerCORDIC_IRQHandlerFMAC_IRQHandler等。
  • startup_stm32f103xe.s:包含一些特定于 STM32F103xE 系列的中断,如USB_HP_CAN1_TX_IRQHandlerUSB_LP_CAN1_RX0_IRQHandlerCAN1_RX1_IRQHandlerCAN1_SCE_IRQHandlerFSMC_IRQHandlerSDIO_IRQHandlerUART5_IRQHandlerDMA2_Channel4_5_IRQHandler等。
1.2.2 部分中断处理函数命名差异

例如,STM32G431xx 有PVD_PVM_IRQHandler,而 STM32F103xE 是PVD_IRQHandler;STM32G431xx 有RTC_TAMP_LSECSS_IRQHandler,STM32F103xE 则分别有TAMPER_IRQHandlerRTC_IRQHandler

1.2.3. 复位处理描述差异

  • startup_stm32g431xx.s:描述中提到复位后 Cortex - M4 处理器处于线程模式,优先级为特权级,堆栈设置为主堆栈。
  • startup_stm32f103xe.s:描述中提到复位后 Cortex - M3 处理器处于线程模式,优先级为特权级,堆栈设置为主堆栈。

1.2.4代码注释中的功能描述差异

  • startup_stm32g431xx.s:没有提到配置时钟系统的功能。
  • startup_stm32f103xe.s:明确提到该模块会配置时钟系统。

1.2.5 DMA 通道中断处理函数差异

  • startup_stm32g431xx.s:DMA1 和 DMA2 通道中断处理函数分别列出每个通道,如DMA1_Channel1_IRQHandlerDMA2_Channel1_IRQHandler等。
  • startup_stm32f103xe.s:DMA2 有一个DMA2_Channel4_5_IRQHandler,将通道 4 和通道 5 的中断处理合并。

示例代码对比片段 

以下是两个文件中向量表部分的对比:

startup_stm32g431xx.s

收起

asm

; External Interrupts
DCD     WWDG_IRQHandler                   ; Window WatchDog
DCD     PVD_PVM_IRQHandler                ; PVD/PVM1/PVM2/PVM3/PVM4 through EXTI Line detection
DCD     RTC_TAMP_LSECSS_IRQHandler        ; RTC, TAMP and RCC LSE_CSS through the EXTI line
; ... 更多中断向量 ...
startup_stm32f103xe.s

收起

asm

; External Interrupts
DCD     WWDG_IRQHandler            ; Window Watchdog
DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
DCD     TAMPER_IRQHandler          ; Tamper
DCD     RTC_IRQHandler             ; RTC
; ... 更多中断向量 ...

2.F103启动代码

2.1说明部分

;******************** (C) COPYRIGHT 2017 STMicroelectronics ********************
;* File Name          : startup_stm32f103xe.s
;* Author             : MCD Application Team
;* Description        : STM32F103xE Devices vector table for MDK-ARM toolchain. 
;*                      This module performs:
;*                      - Set the initial SP
;*                      - Set the initial PC == Reset_Handler
;*                      - Set the vector table entries with the exceptions ISR address
;*                      - Configure the clock system
;*                      - Branches to __main in the C library (which eventually
;*                        calls main()).
;*                      After Reset the Cortex-M3 processor is in Thread mode,
;*                      priority is Privileged, and the Stack is set to Main.
;******************************************************************************

这段代码注释部分描述了一个 STM32F103xE 设备的启动文件 startup_stm32f103xe.s 的功能,主要用于 MDK-ARM 工具链。以下是对每一部分的解释:

  1. 文件名和作者

    • 指定了文件名称和作者,表明这是 MCD 应用团队开发的。

  2. 描述

    • 该模块的主要功能包括:

      • 设置初始栈指针(SP):在程序开始时,初始化栈指针,以便后续的函数调用和局部变量管理。

      • 设置初始程序计数器(PC):将程序计数器指向重置处理函数 Reset_Handler,这是程序启动后的第一条执行指令。

      • 设置向量表条目:配置异常中断服务程序(ISR)地址,确保在发生中断时 CPU 能正确跳转到相应的处理函数。

      • 配置时钟系统:初始化时钟,确保 MCU 按预期速度运行。

      • 跳转到 C 库中的 __main:这个函数负责调用 main() 函数,实际上是程序的入口点。

      • 处理器状态:在复位后,Cortex-M3 处理器处于线程模式,具有特权优先级,栈设置为主栈。

2.2栈初始化

Stack_Size      EQU     0x00000400
​
                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

这段代码定义了一个栈的大小和栈的内存区域。以下是各部分的详细解释:

  1. Stack_Size EQU 0x00000400

    • 使用 EQU 指令定义一个常量 Stack_Size,其值为 0x00000400(1024 字节)。这表示栈的大小为 1 KB。

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

    • AREA

      指令用于定义一个新的内存区域,命名为

      STACK

      。参数说明:

      • NOINIT:表示这个区域在复位时不需要初始化,通常用于栈等动态数据。

      • READWRITE:表示该区域可以读写。

      • ALIGN=3:表示地址对齐到 8 字节(232^323),这有助于提高访问效率。

  3. Stack_Mem SPACE Stack_Size

    • SPACE 指令在 STACK 区域中分配 Stack_Size 字节的空间,用于存放栈的内容。

  4. __initial_sp

    • __initial_sp 是一个标签,通常用于表示栈指针的初始值。在启动时,CPU 会将此标签的地址加载到栈指针(SP)寄存器,以便栈可以正常使用。

2.3堆初始化

Heap_Size       EQU     0x00000200
​
                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit
​
                PRESERVE8
                THUMB

Heap_Size EQU 0x00000200

  • 使用 EQU 指令定义一个常量 Heap_Size,其值为 0x00000200(512 字节)。这表示堆的大小为 512 字节。

AREA HEAP, NOINIT, READWRITE, ALIGN=3

  • AREA

    指令用于定义一个新的内存区域,命名为

    HEAP

    。参数说明:

    • NOINIT:表示这个区域在复位时不需要初始化,通常适用于动态分配的内存。

    • READWRITE:表示该区域可以进行读写操作。

    • ALIGN=3:表示地址对齐到 8 字节(232^323),有助于提高内存访问的效率。

__heap_base

  • __heap_base 是一个标签,表示堆的起始地址。它通常用作动态内存分配的起始点。

Heap_Mem SPACE Heap_Size

  • SPACE 指令在 HEAP 区域中分配 Heap_Size 字节的空间,用于存放堆的内容。

__heap_limit

  • __heap_limit 是另一个标签,表示堆的限制或结束地址。这有助于在动态内存分配时进行边界检查,确保不超过分配的堆大小。

PRESERVE8THUMB

  • PRESERVE8:这是一个指令,指示编译器在处理过程中保留 8 字节对齐,通常用于保证在不同函数调用时的堆栈对齐。

  • THUMB:指示编译器使用 Thumb 指令集,这是 ARM 体系结构的一种指令集,能够在较小的代码空间内提高执行效率。

2.4中断向量表初始化

 

这段代码定义了 STM32F103xE 启动文件中的 向量表(Vector Table)。向量表用于存放处理器在复位或异常事件(如中断)发生时,跳转到的处理程序地址。下面逐句解释这段代码:

1. ; Vector Table Mapped to Address 0 at Reset

  • 注释说明:向量表在复位时映射到地址 0,这意味着处理器在复位后会从地址 0 处的向量表开始执行。

2. AREA RESET, DATA, READONLY

  • AREA

    指令定义了一个新的内存区域:

    • 名称是 RESET,表示复位相关的数据。

    • DATA 表示这是一个数据段。

    • READONLY 表示该段是只读的。

3. EXPORT Vectors, Vectors_End, __Vectors_Size

  • EXPORT 指令将 __Vectors__Vectors_End__Vectors_Size 这些符号导出,使它们可以被其他模块引用。

  • __Vectors 表示向量表的开始,__Vectors_End 是结束地址,__Vectors_Size 表示向量表的大小。

4. Vectors DCD initial_sp

  • DCD

    (Define Constant Double word)指令定义了一个 32 位常量:

    • __initial_sp 是程序启动时的 初始栈指针,即程序启动时将栈指针(SP)设置为这个地址。

5. DCD Reset_Handler

  • 这行定义了复位处理程序的入口地址,指向 Reset_Handler。当系统复位时,处理器会跳转到这个地址执行复位处理。

6. DCD NMI_Handler, HardFault_Handler, ...

  • 这些行定义了其他异常和中断的处理程序的入口地址:

    • NMI_Handler:不可屏蔽中断的处理程序。

    • HardFault_Handler:硬件故障的处理程序。

    • MemManage_HandlerBusFault_HandlerUsageFault_Handler 等是 ARM Cortex-M 处理器中定义的其他异常处理程序。

7. DCD 0 (Reserved)

  • 保留的中断向量,用作占位符,暂时未使用的中断或异常处理向量都用 0 占位。

8. DCD SVC_Handler, DebugMon_Handler, PendSV_Handler, SysTick_Handler

  • 这些是系统服务相关的中断向量:

    • SVC_Handler:系统服务调用的处理程序。

    • DebugMon_Handler:调试监视中断处理程序。

    • PendSV_Handler:用于操作系统调度的挂起中断处理程序。

    • SysTick_Handler:系统滴答定时器的中断处理程序。

9. DCD WWDG_IRQHandler, PVD_IRQHandler, ...

  • 这是外部中断的向量表部分,列出了 STM32F103xE MCU 所支持的外部中断和它们的处理程序:

    • WWDG_IRQHandler:窗口看门狗的中断处理程序。

    • PVD_IRQHandler:电压检测中断的处理程序。

    • EXTI0_IRQHandlerEXTI1_IRQHandler 等是外部中断的处理程序。

10. __Vectors_End

  • __Vectors_End 标签标识了向量表的结束位置。

11. Vectors_Size EQU Vectors_End - __Vectors

  • __Vectors_Size 使用 EQU(等于)指令定义为向量表的大小,即 __Vectors_End__Vectors 的差值。它用于计算整个向量表的字节大小。

12. AREA |.text|, CODE, READONLY

  • 定义了代码段,|.text| 是段名称,CODE 表示这是可执行代码段,READONLY 表示该段是只读的。


总结:

  • 向量表 是一个关键的表格,用于定义处理器在系统复位或异常(如中断)时,应该跳转到的处理程序地址。

  • 复位后的第一步是从 __Vectors 中读取栈指针和复位处理程序地址。

  • 该表还包含系统异常和外部中断的处理程序入口地址。

2.5中断向量表如何理解?

中断向量表是嵌入式系统中一个关键的结构,它提供了一组地址,这些地址指向特定的中断服务程序(ISR)。当中断发生时,处理器会查找向量表,跳转到相应的地址执行中断处理。可以用以下形象的例子来帮助理解:

1. 邮局与信箱

  • 邮局:想象一个城市的邮局(中断向量表),其中存储了所有居民(中断类型)各自的信箱地址(ISR 地址)。

  • 信箱:每个居民都有自己的信箱,只有他们的信件(中断请求)到达时,才能访问。

  • 当某个事件(中断)发生时,邮递员(处理器)会查找邮局,根据事件的类型找到相应的信箱,递送邮件(调用 ISR)。

2. 电话本

  • 电话本:把中断向量表想象成一本电话本。每个条目(向量)对应一种特定的事件(中断)。

  • 当你接到一个电话(中断请求)时,你会翻开电话本,查找拨打的号码(中断类型),找到对应的联系人(ISR)进行回复(处理事件)。

3. 机场航班调度

  • 航班调度表:想象一个机场的航班调度表(向量表),列出所有即将起飞和降落的航班(中断)及其对应的跑道(ISR)。

  • 当一架飞机(中断请求)准备降落时,控制塔(处理器)会查看调度表,找到对应的跑道,指挥飞机安全着陆(执行 ISR)。

4. 事件管理系统

  • 事件管理器:把中断向量表想象成一个活动组织者(事件管理器),他负责处理不同的活动(中断)。

  • 当活动开始时(中断触发),组织者会查看他的计划表(向量表),找到对应的活动负责人(ISR)来处理这个活动。

小结

中断向量表就是一个“地址索引”,帮助处理器快速找到和执行对应中断的处理程序。通过这些形象的例子,可以更好地理解中断向量表的工作原理和重要性。如果还有其他疑问或需要更深入的理解,请随时问我!

2.6复位处理程序

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

这段代码定义了 复位处理程序(Reset Handler),即当系统复位时,处理器首先会跳转并执行的代码。复位处理程序的任务是初始化系统,然后跳转到主程序(main 函数)。我们逐句来解释:

1. Reset_Handler PROC

  • PROC 表示 Reset_Handler 是一个过程(即函数)的开始。这里定义了复位处理程序 Reset_Handler

2. EXPORT Reset_Handler [WEAK]

  • EXPORTReset_Handler 导出,使其可以被其他模块引用。

  • [WEAK] 表示这是一个弱符号,即如果有另一个相同名字的 Reset_Handler 定义,它将覆盖这个弱定义。通常用来允许用户重写默认的复位处理程序。

3. IMPORT __main

  • IMPORT 声明外部符号 __main,表示 __main 是在其他地方定义的,并将在本模块中使用。它通常是 C 语言的启动函数,最终会调用用户的 main() 函数。

4. IMPORT SystemInit

  • IMPORT SystemInit 声明了一个外部符号 SystemInit,这是一个系统初始化函数,通常用来设置系统时钟、外设等基本硬件配置。

5. LDR R0, =SystemInit

  • LDR 指令用于将立即数或地址加载到寄存器中。

  • 这里 LDR R0, =SystemInit 是将 SystemInit 函数的地址加载到寄存器 R0 中,准备调用该函数。

6. BLX R0

  • BLX(Branch with Link and Exchange)指令会跳转到 R0 中的地址(即 SystemInit 函数),并且保存返回地址(即从 SystemInit 函数执行完后回到复位处理程序继续执行)。BLX 同时切换处理器的指令集模式(ARM 和 Thumb)。

  • 执行此行后,SystemInit 函数会运行。它通常会设置系统时钟、配置电源等必要的初始化工作。

7. LDR R0, =__main

  • 再次使用 LDR 指令,将 __main 的地址加载到寄存器 R0 中。

  • __main 是 C 语言启动代码的入口,它会调用用户编写的 main() 函数。

8. BX R0

  • BX(Branch and Exchange)指令用于跳转到 R0 中存储的地址,即 __main 函数入口。执行这行后,程序就跳转到 __main 开始执行 C 语言的启动代码,最后进入 main() 函数。

  • BX 还会切换处理器的指令集模式(ARM 和 Thumb),类似于 BLX,但它不保存返回地址,因此这里不需要回到复位处理程序。

9. ENDP

  • ENDP 指令表示过程的结束,表示复位处理程序 Reset_Handler 的定义到此结束。


总结:

  • 复位处理程序的主要工作

    1. 调用 SystemInit 函数,完成系统的初始硬件配置(比如时钟、外设等的初始化)。

    2. 跳转到 __main 函数,开始执行 C 语言的启动代码,最后进入 main() 函数执行用户的程序。

形象的例子:

可以将 Reset_Handler 想象成一个启动引导程序,当设备启动(复位)时,它先做好基本的硬件准备工作(比如给汽车加油、启动引擎),然后跳转到用户的应用程序(开始驾驶),即 main() 函数。

2.7其他中断程序定义

NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .
                ENDP

这段代码定义了 不可屏蔽中断处理程序(NMI_Handler),用于处理不可屏蔽中断(NMI)。我们逐句来解释:

1. NMI_Handler PROC

  • PROC 表示 NMI_Handler 是一个过程(即函数)的开始。这是定义不可屏蔽中断的处理程序。

2. EXPORT NMI_Handler [WEAK]

  • EXPORTNMI_Handler 导出,使其可以被其他模块引用。

  • [WEAK] 表示这是一个弱符号,意味着如果在其他地方定义了同名的 NMI_Handler,则该定义会被覆盖。这通常允许用户提供自定义的 NMI 处理程序。

3. B .

  • B 指令表示无条件分支,这里的 . 表示当前地址。

  • 这行代码使得处理程序在被调用时进入一个无限循环。也就是说,如果 NMI 发生并调用这个处理程序,它将不会执行任何有用的代码,而是保持在这里。

4. ENDP

  • ENDP 指令表示过程的结束,标志着不可屏蔽中断处理程序 NMI_Handler 的定义到此结束。


总结:

  • NMI_Handler 的主要作用是作为 NMI 的占位符。当 NMI 发生时,如果没有自定义的处理程序,它会进入无限循环,不会做任何其他操作。

  • 这种设计通常是为了避免未处理的 NMI 导致系统不稳定,开发者可以选择实现自己的 NMI 处理程序来替代这个弱符号。

形象的例子:

可以将 NMI_Handler 想象成一个紧急停车按钮。当触发紧急停车时,这个处理程序会停止所有操作,保持系统在安全状态。开发者可以选择实现更复杂的应急处理(比如,记录故障或进行安全操作),而这个默认的处理程序只会让系统保持在一个“暂停”状态。

2.8EXPORT NMI_Handler [WEAK]

这个代码还是不能理解,他在整个工程中有什么作用?其他模块如何使用??

在其他模块或者程序中重新定义他,若无定义则一直空循环

EXPORT NMI_Handler [WEAK] 这行代码在整个工程中扮演了重要的角色,具体作用和使用方式如下:

作用:

  1. 导出符号EXPORT 声明 NMI_Handler 为外部可访问的符号,允许其他模块(或文件)在链接时引用这个处理程序。这意味着其他模块可以调用或重定义这个处理程序。

  2. 弱符号[WEAK] 表示这是一个弱符号。在链接过程中,如果存在其他模块中有同名的 NMI_Handler 定义,则链接器将使用那个强符号版本。如果没有找到其他强符号,链接器就会使用这个弱符号作为默认的处理程序。

其他模块如何使用:

  • 提供自定义处理程序:如果开发者在其他源文件中定义了一个强符号的 NMI_Handler(例如,定义成 NMI_Handler PROC),在链接时,这个强符号会覆盖这个弱符号,系统将使用开发者定义的处理程序。

  • 使用默认处理程序:如果其他模块没有提供 NMI_Handler 的强符号版本,那么在发生 NMI 时,系统会调用这个弱符号的处理程序,进入无限循环。这可以确保系统在没有自定义处理的情况下保持一定的稳定性。

示例:

假设你在一个项目中有两个源文件:

  1. main.c:默认的 NMI 处理程序由 EXPORT NMI_Handler [WEAK] 提供。

  2. custom_handlers.c:你定义了一个强符号的 NMI 处理程序:

assembly复制代码; custom_handlers.c
NMI_Handler PROC
    ; 你的自定义代码
    ENDP

在这种情况下,编译和链接时,链接器将找到 custom_handlers.c 中的强符号 NMI_Handler,并使用它。发生 NMI 时,系统将调用你定义的处理程序。

小结:

通过使用 EXPORT NMI_Handler [WEAK],代码实现了灵活性,允许开发者选择使用默认处理程序或提供自定义处理程序,以适应不同的应用需求。这种机制在嵌入式开发中非常常见,允许开发者根据具体需求定制中断处理逻辑。

是的,你可以用 C 语言定义中断处理程序,包括 NMI 处理程序。在 STM32 等嵌入式系统中,通常可以通过 C 语言来实现中断服务程序(ISR)。下面是一个如何在 C 语言中定义 NMI 处理程序的示例:

示例 C 语言定义

c复制代码#include "stm32f4xx.h"  // 根据你的 STM32 型号调整
​
// NMI Handler
void NMI_Handler(void) {
    // 在这里添加你的自定义代码
    while (1) {
        // 可以在这里处理 NMI,比如记录错误或其他处理
    }
}

如何链接 C 语言和汇编:

  1. 链接时的注意事项:在 C 语言中定义的处理程序默认是强符号,因此在链接时会覆盖之前的弱符号定义(如果有的话)。在你的启动文件(通常是汇编文件)中,NMI 处理程序的符号会被链接器识别。

  2. 确认函数名:确保 C 语言定义的函数名和启动文件中的弱符号名称一致。例如,如果在启动文件中使用 EXPORT NMI_Handler [WEAK],那么在 C 文件中也需要定义 void NMI_Handler(void)

其他注意事项:

  • 中断优先级:如果使用 RTOS 或多个中断,确保你的 NMI 处理程序的优先级设置正确。

  • 不使用耗时操作:中断处理程序应该尽量短小,不要在其中执行耗时的操作,避免影响系统响应。

2.9用户栈和堆的初始化

;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
                 IF      :DEF:__MICROLIB
                
                 EXPORT  __initial_sp
                 EXPORT  __heap_base
                 EXPORT  __heap_limit
                
                 ELSE
                
                 IMPORT  __use_two_region_memory
                 EXPORT  __user_initial_stackheap
                 
__user_initial_stackheap
​
                 LDR     R0, =  Heap_Mem
                 LDR     R1, =(Stack_Mem + Stack_Size)
                 LDR     R2, = (Heap_Mem +  Heap_Size)
                 LDR     R3, = Stack_Mem
                 BX      LR
​
                 ALIGN
​
                 ENDIF
​
                 END

这段代码是关于 用户栈和堆初始化 的,主要用于配置堆和栈的内存区域。我们逐句解析:

1. IF :DEF:__MICROLIB

  • 这是一个条件编译指令,用于检查是否定义了 __MICROLIB。如果定义了,代码将执行 IF 块中的内容;否则执行 ELSE 块。

2. EXPORT __initial_sp

  • 如果 __MICROLIB 被定义,这行代码将导出 __initial_sp 符号,通常表示初始栈指针的地址。

3. EXPORT __heap_base

  • 导出堆的起始地址符号 __heap_base

4. EXPORT __heap_limit

  • 导出堆的结束地址符号 __heap_limit

5. ELSE

  • 进入 ELSE 块,如果没有定义 __MICROLIB

6. IMPORT __use_two_region_memory

  • 导入一个外部符号 __use_two_region_memory,通常用于指示是否使用两个内存区域(栈和堆)。

7. EXPORT __user_initial_stackheap

  • 导出符号 __user_initial_stackheap,表示用户初始化栈和堆的函数。

8. __user_initial_stackheap

  • 这是一个标签,表示用户初始化栈和堆的函数入口。

9. LDR R0, =Heap_Mem

  • 将堆内存的起始地址加载到寄存器 R0

10. LDR R1, =(Stack_Mem + Stack_Size)

  • 将栈内存的结束地址(栈起始地址加上栈的大小)加载到寄存器 R1

11. LDR R2, = (Heap_Mem + Heap_Size)

  • 将堆内存的结束地址(堆起始地址加上堆的大小)加载到寄存器 R2

12. LDR R3, =Stack_Mem

  • 将栈的起始地址加载到寄存器 R3

13. BX LR

  • 使用 BX 指令跳转到链接寄存器 LR 中的地址,通常用于返回调用者。这意味着函数执行完后,将返回到调用它的地方。

14. ALIGN

  • 这个指令用于确保后续代码的地址对齐,通常用于数据或代码段的对齐。

15. ENDIF

  • 结束条件编译指令的块。

16. END

  • 表示这个汇编文件的结束。


总结:

这段代码的目的是初始化用户的栈和堆,为后续的程序执行做好准备。具体步骤包括:

  1. 根据是否定义了 __MICROLIB,导出相关符号。

  2. 根据条件选择不同的初始化路径。

  3. 加载堆和栈的相关地址信息到寄存器中,为用户使用堆和栈做准备。

形象的例子:

可以把这段代码想象成开设一家新学校

  • 条件判断IF):学校的形式(如微型学校)决定了是否导出初始的栈和堆信息。

  • 导出符号:开设学校时,记录下学校的基础设施(栈和堆)的地址。

  • 初始化:确定学校的教室(栈)和资源(堆)的起止位置,以便学生(程序)可以在这个环境中学习和成长。

  • 返回:一旦学校准备好了,学生可以开始上课。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/963707.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

unity中的动画混合树

为什么需要动画混合树,动画混合树有什么作用? 在Unity中,动画混合树(Animation Blend Tree)是一种用于管理和混合多个动画状态的工具,包括1D和2D两种类型,以下是其作用及使用必要性的介绍&…

C语言 --- 分支

C语言 --- 分支 语句分支语句含义if...else语句单分支if语句语法形式 双分支 if-else 语句语法形式 悬空else含义问题描述 多分支 if-else 语句语法形式 switch...case语句含义语法形式 总结 💻作者简介:曾与你一样迷茫,现以经验助你入门 C 语…

pytorch实现长短期记忆网络 (LSTM)

人工智能例子汇总:AI常见的算法和例子-CSDN博客 LSTM 通过 记忆单元(cell) 和 三个门控机制(遗忘门、输入门、输出门)来控制信息流: 记忆单元(Cell State) 负责存储长期信息&…

C++:抽象类习题

题目内容: 求正方体、球、圆柱的表面积,抽象出一个公共的基类Container为抽象类,在其中定义一个公共的数据成员radius(此数据可以作为正方形的边长、球的半径、圆柱体底面圆半径),以及求表面积的纯虚函数area()。由此抽象类派生出…

GEE | 计算Sentinel-2的改进型土壤调整植被指数MSAVI

同学们好!今天和大家分享的是 “改进型土壤调整植被指数MSAVI”,它能够更准确地反映植被生长状态,且广泛应用于植被覆盖监测、生态环境评估等领域。 1. MSAVI 改进型土壤调整植被指数(MSAVI)是一种针对植被覆盖区域土…

deepseek+vscode自动化测试脚本生成

近几日Deepseek大火,我这里也尝试了一下,确实很强。而目前vscode的AI toolkit插件也已经集成了deepseek R1,这里就介绍下在vscode中利用deepseek帮助我们完成自动化测试脚本的实践分享 安装AI ToolKit并启用Deepseek 微软官方提供了一个针对AI辅助的插件,也就是 AI Toolk…

CodeGPT使用本地部署DeepSeek Coder

目前NV和github都托管了DeepSeek,生成Key后可以很方便的用CodeGPT接入。CodeGPT有三种方式使用AI,分别时Agents,Local LLMs(本地部署AI大模型),LLMs Cloud Model(云端大模型,从你自己…

[c语言日寄]C语言类型转换规则详解

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋:这是一个专注于C语言刷题的专栏,精选题目,搭配详细题解、拓展算法。从基础语法到复杂算法,题目涉及的知识点全面覆盖,助力你系统提升。无论你是初学者,还是…

FPGA 使用 CLOCK_DEDICATED_ROUTE 约束

使用 CLOCK_DEDICATED_ROUTE 约束 CLOCK_DEDICATED_ROUTE 约束通常在从一个时钟区域中的时钟缓存驱动到另一个时钟区域中的 MMCM 或 PLL 时使 用。默认情况下, CLOCK_DEDICATED_ROUTE 约束设置为 TRUE ,并且缓存 /MMCM 或 PLL 对必须布局在相同…

Ollama 介绍,搭建本地 AI 大模型 deepseek,并使用 Web 界面调用

Ollama 是一个基于 Go 语言的本地大语言模型运行框架,类 Docker产品(支持 list,pull,push,run 等命令),事实上它保留了 Docker 的操作习惯,支持上传大语言模型仓库(有 deepseek、llama 2,mistral&#xff0…

OpenEuler学习笔记(十四):在OpenEuler上搭建.NET运行环境

一、在OpenEuler上搭建.NET运行环境 基于包管理器安装 添加Microsoft软件源:运行命令sudo rpm -Uvh https://packages.microsoft.com/config/centos/8/packages-microsoft-prod.rpm,将Microsoft软件源添加到系统中,以便后续能够从该源安装.…

【Linux】从硬件到软件了解进程

个人主页~ 从硬件到软件了解进程 一、冯诺依曼体系结构二、操作系统三、操作系统进程管理1、概念2、PCB和task_struct3、查看进程4、通过系统调用fork创建进程(1)简述(2)系统调用生成子进程的过程〇提出问题①fork函数②父子进程关…

物联网 STM32【源代码形式-使用以太网】连接OneNet IOT从云产品开发到底层MQTT实现,APP控制 【保姆级零基础搭建】

物联网(IoT)‌是指通过各种信息传感器、射频识别技术、全球定位系统、红外感应器等装置与技术,实时采集并连接任何需要监控、连接、互动的物体或过程,实现对物品和过程的智能化感知、识别和管理。物联网的核心功能包括数据采集与监…

【背包问题】二维费用的背包问题

目录 二维费用的背包问题详解 总结: 空间优化: 1. 状态定义 2. 状态转移方程 3. 初始化 4. 遍历顺序 5. 时间复杂度 例题 1,一和零 2,盈利计划 二维费用的背包问题详解 前面讲到的01背包中,对物品的限定条件…

眼见着折叠手机面临崩溃,三星计划增强抗摔能力挽救它

据悉折叠手机开创者三星披露了一份专利,通过在折叠手机屏幕上增加一个抗冲击和遮光层的方式来增强折叠手机的抗摔能力,希望通过这种方式进一步增强折叠手机的可靠性和耐用性,来促进折叠手机的发展。 据悉三星和研发可折叠玻璃的企业的做法是在…

首发!ZStack 智塔支持 DeepSeek V3/R1/ Janus Pro,多种国产 CPU/GPU 可私有化部署

2025年2月2日,针对日益强劲的AI推理需求和企业级AI应用私有化部署场景(Private AI),云轴科技 ZStack 宣布 AI Infra 平台 ZStack 智塔全面支持企业私有化部署 DeepSeek V3/R1/ Janus Pro三种模型,并可基于海光、昇腾、…

25寒假算法刷题 | Day1 | LeetCode 240. 搜索二维矩阵 II,148. 排序链表

目录 240. 搜索二维矩阵 II题目描述题解 148. 排序链表题目描述题解 240. 搜索二维矩阵 II 点此跳转题目链接 题目描述 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性: 每行的元素从左到右升序排列。每列的元素从上到…

it基础使用--5---git远程仓库

文章目录 it基础使用--5---git远程仓库1. 按顺序看2. 什么是远程仓库3. Gitee操作3.1 新建远程仓库3.2 远程操作基础命令3.3 查看当前所有远程地址别名 git remote -v3.4 创建远程仓库别名 git remote add 别名 远程地址3.4 推送本地分支到远程仓库 git push 别名 分支3.5 拉取…

SpringBoot 整合 Mybatis:注解版

第一章&#xff1a;注解版 导入配置&#xff1a; <groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.1</version> </dependency> 步骤&#xff1a; 配置数据源见 Druid…

海思ISP开发说明

1、概述 ISP&#xff08;Image Signal Processor&#xff09;图像信号处理器是专门用于处理图像信号的硬件或处理单元&#xff0c;广泛应用于图像传感器&#xff08;如 CMOS 或 CCD 传感器&#xff09;与显示设备之间的信号转换过程中。ISP通过一系列数字图像处理算法完成对数字…