ARM Cortex

您所在的位置:网站首页 单片机boot区 ARM Cortex

ARM Cortex

2024-07-14 08:45| 来源: 网络整理| 查看: 265

提示:若转载请备注来源,谢谢

文章目录 启动文件1. 什么是启动代码2. 启动代码主要干了什么 启动文件分析一、设置堆栈二、定义中断向量表三、初始化系统时钟总结

启动文件 1. 什么是启动代码

启动代码是系统上电或者复位后运行的第一段代码,是进入C 语言的main 函数之前需要执行的那段汇编代码。或者说用户程序运行之前对系统硬件及软件环境进行必要的初始化并在最后使程序跳转到用户程序。 将启动文件理解为一种描述性的代码,不要拘泥于它的实现机制,我之前一直在想启动代码中汇编代码的执行顺序,后来没明白,估计和keil工具有关系,只需要记住一个道理,程序上电执行的第一条代码(我们可以控制的代码),就是启动文件中Reset Handler函数,Reset Handler函数 的地址存放在flash的0x00000004处。

2. 启动代码主要干了什么

对于 Cortex-M系列的芯片而言,启动代码大同小,明白一个几乎所有的也就都明白了。

设置堆栈定义中断向量表初始化系统时钟(Reset_Handler中做)初始化堆栈,有的启动代码不一定做(Reset_Handler中做)执行_main,准备c语言的运行环境,初始化程序计数器指针PC指向main,进而来到c语言的世界 启动文件分析 一、设置堆栈 ; Stack Configuration ; Stack Size (in Bytes) ; Stack_Size EQU 0x00000800 AREA STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem SPACE Stack_Size __initial_sp ; Heap Configuration ; Heap Size (in Bytes) ; Heap_Size EQU 0x00000100 AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base Heap_Mem SPACE Heap_Size __heap_limit PRESERVE8 THUMB

以上仅仅定义了堆栈的大小,堆栈的起始地址在keil中设置方式有两种, 第一种: 在这里插入图片描述 第二种,分散加载文件(将keil的Use Memory Layout from Target Dialog去掉勾选就会有该文件),在实际项目中,我通常使用该文件,可以理解问链接文件 在这里插入图片描述 在上图(第一幅图片中),启动文件的最后几行,有关于Use MicoLIB的描述,还有关于堆栈的设置,代码为,主要是初始化堆栈的地址范围:

;******************************************************************************* ; 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 二、定义中断向量表

1. 当内核响应了一个发生的异常后,对应的异常服务例程(ESR)就会执行。为了决定 ESR的入口地址, 内核使用了―向量表查表机制‖(是ARM的实现机制)。这里使用一张向量表。向量表其实是一个WORD(32 位整数)数组,每个下标对应一种异常,该下标元素的值则是该 ESR 的入口地址。 2. 向量表在地址空间中的位置默认存放在0x00000000处,但是还可以设置,通过 NVIC 中的一个重定位寄存器来指出向量表的地址。

代码为:

; Vector Table Mapped to Address 0 at Reset AREA RESET, DATA, READONLY EXPORT __Vectors __Vectors DCD __initial_sp ; Top of Stack DCD Reset_Handler ; Reset Handler DCD NMI_IRQHandler ; NMI Handler DCD HardFault_IRQHandler ; Hard Fault Handler DCD 0 ; Reserved DCD 0 ; Reserved ...... ...... AREA |.text|, CODE, READONLY

附加:常常在涉及到升级loader中使用

向量表重定位 + 跳转代码如下: __asm void SoftReset(u32 address) { LDR R1, =0xE000ED08 // 0xE000ED08 重定位地址 STR R0, [R1, #0] // R0为形参address /* 重定位完后,向量表地址为address */ LDR r1, [r0] // address内前4个直接存放的 Top of Stack msr MSP,r1 // 给MSP赋值 ADDS r0, r0, #4 // Reset_Handler 的地址 LDR r1, [r0] NOP BX R1 // 执行 Reset_Handler NOP NOP NOP } 若仅仅想实现向量表重定位,例如将向量表定位到0x00000200,代码如下: // SCB->VTOR就等于 0xE000ED08 //SCB->VTOR = 0x00000000; // 等价于0xE000ED08 = 0x00000000; SCB->VTOR = 0x00000200; 三、初始化系统时钟

还是要在重申一下,复位子程序Reset_Handler是系统上电后第一个执行的程序。这里分析两个Reset handler,一个是STM32 demo上的,一个是我们使用的thk88芯片的 stm32代码为:

; Reset handler Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit LDR R0, =SystemInit // 系统初始化,里面包含了初始化时钟 BLX R0 LDR R0, =__main // 最后跳转到main() BX R0 ENDP

thk88代码为

Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT main ; 开启内部时钟30M LDR R3, =SCUBASEADDR LDR R0, =0x00000000 STR R0, [R3,#SCUSCK] LDR R0, =0x00000000 STR R0, [R3,#SCUINCKD] ; 关闭USB PHY,禁止DP上拉电阻 LDR R0, =0x00000100 STR R0, [R3, #SCUCM3] ;OPEN USB CLK LDR R2, =USBBASEADDR LDR R0, =0x00000001 STR R0, [R2,#USBPCON] ; 关闭USB PHY LDR R0, =0x00000000 LDR R2, =USBPHYCON STR R0, [R2] ;禁止DP上拉 ;STR R0, [R2,#USBPHYCON] ;禁止DP上拉 ; 屏蔽 NMI 中断 LDR R0, =0XFFFFFFFF LDR R2, =SCUNMI STR R0, [R3,R2] ; DMA使用event方式 LDR R2, = 0x40000100 ;SCUEVTSET1 = 0x02;DMA event mode LDR R0, = 0x00000002 STR R0, [R2] ;初始化用到的RAM区,4字节对齐,包括RW和ZI区域 BL CLEAR_ZI IMPORT __main LDR R0,= __main BX R0 ENDP

每款芯片的启动代码大同小异,熟悉一个其他的都可以看懂。

总结

具体启动流程如图,两种方式

启动流程1(使用标准库,不使用Microlib)如下图: 在这里插入图片描述启动流程2(使用Microlib)如下图: 在这里插入图片描述 假设STM32被设置为从内部FLASH启动(这也是最常见的一种情况),中断向量表起始地位为0x00000000(映射到0x8000000,两个是一个东西),则栈顶地址存放于0x8000000处,而复位中断服务入口地址存放于0x8000004处(复位地址在栈顶地址4字节后)。当STM32上电后,遇到复位信号,则从0x80000004处取出复位中断服务入口地址,继而执行复位中断服务程序,然后跳转__main函数,最后进入mian函数,来到C的世界。

重申:

绝大部分ARM-M协议的芯片,复位之后先进入厂商boot,此时所有的用户均无法接入处理器;厂商boot主要负责芯片最初级的初始化,对MCU进行一些差异性设置等,BOOT完成后,会把主动权交给用户,也就是启动代码;启动代码(执行汇编语言不需要此启动代码),在启动文件中,会设置MSP(主堆栈指针)和PC(程序计数器)的值,MSP的地址默认是0x00000000,PC的地址默认是0x00000004,这两个地址可以通过CORTEX-M中的VTOR寄存器来进行重映射,修改。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3