19.1 详解startup_M051.s
在Keil新建的所有工程中,毫无例外地都包含startup_M051.s,如图19.1-1。
图19.1-1 startup_M051.s
该文件主要作用于上电时初始化单片机的硬件堆栈、初始化RAM、分配内存空间和跳转到主函数即main函数。硬件堆栈是用来存放函数调用地址、变量和寄存器值的;分配内存空间为异常提供更加快速的访问,减少中断延迟。如果不加载该startup_M051.s文件,编译的代码可能会使单片机不能正常工作。
那么什么是堆栈呢?在计算机领域,堆栈是一个不容忽视的概念,但是很多人甚至是计算机专业的人也没有明确堆栈这两种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。
堆,一般是在堆的头部用一个字节存放堆的大小,堆中的具体内容由程序员安排。
栈,在函数调用时,第一个进栈的是主函数中函数调用后的下一条指令的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,接着是函数中的局部变量,注意静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址(后进先出),也就是主函数中的下一条指令,程序由该点继续运行。
虽然堆栈的说法是连起来叫,但是他们还是有很大区别的,连着叫只是由于历史的原因。
startup_M051.s文件并不复杂,只要用户有基本的汇编基础,就可以看懂,以下就给出该上电初始化文件的详细注解,可以作为参考,只作为选学内容。
程序清单19.1-1 startup_M051.s核心内容详解
Stack_Size EQU 0x00000400 ;//栈大小定义为0x00000400字节
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;声明数据段STACK
;该数据段内存单元无初始化,可读写,并重新字对齐
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size ;//为栈分配内存空间,并初始化为0
__initial_sp
Heap_Size EQU 0x00000000 ;//堆大小定义为0x00000000字节
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size ;//为堆分配内存空间,并初始化为0
__heap_limit
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;声明数据段RESET
;该数据段内存单元只读
;功能:为所有Handler分配内存单元
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PRESERVE8 ;//当前堆栈保持8字节对齐
THUMB ;//THUMB模式
;//向量表映射到复位地址0
AREA RESET, DATA, READONLY
EXPORT __Vectors
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
DCD BOD_IRQHandler
DCD WDT_IRQHandler
DCD EINT0_IRQHandler
DCD EINT1_IRQHandler
DCD GPAB_IRQHandler
DCD GPCDE_IRQHandler
DCD PWMA_IRQHandler
DCD PWMB_IRQHandler
DCD TMR0_IRQHandler
DCD TMR1_IRQHandler
DCD TMR2_IRQHandler
DCD TMR3_IRQHandler
DCD UART0_IRQHandler
DCD UART1_IRQHandler
DCD SPI0_IRQHandler
DCD SPI1_IRQHandler
DCD SPI2_IRQHandler
DCD SPI3_IRQHandler
DCD I2C0_IRQHandler
DCD I2C1_IRQHandler
DCD CAN0_IRQHandler
DCD CAN1_IRQHandler
DCD Default_Handler
DCD USBD_IRQHandler
DCD PS2_IRQHandler
DCD ACMP_IRQHandler
DCD PDMA_IRQHandler
DCD Default_Handler
DCD PWRWU_IRQHandler
DCD ADC_IRQHandler
DCD Default_Handler
DCD RTC_IRQHandler
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;声明代码段|.text|,只读
;功能:复位时,代码从该代码段首先执行
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
AREA |.text|, CODE, READONLY
ENTRY ;//进入代码段
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main ;//引入C文件中的main函数
LDR R0, =__main ;//获取C文件中的main函数地址
BX R0 ;//跳转到main函数
ENDP
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B . ;//停止
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B . ;//停止
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B . ;//停止
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B . ;//停止
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B . ;//停止
ENDP
Default_Handler PROC
EXPORT BOD_IRQHandler [WEAK]
EXPORT WDT_IRQHandler [WEAK]
EXPORT EINT0_IRQHandler [WEAK]
EXPORT EINT1_IRQHandler [WEAK]
EXPORT GPAB_IRQHandler [WEAK]
EXPORT GPCDE_IRQHandler [WEAK]
EXPORT PWMA_IRQHandler [WEAK]
EXPORT PWMB_IRQHandler [WEAK]
EXPORT TMR0_IRQHandler [WEAK]
EXPORT TMR1_IRQHandler [WEAK]
EXPORT TMR2_IRQHandler [WEAK]
EXPORT TMR3_IRQHandler [WEAK]
EXPORT UART0_IRQHandler [WEAK]
EXPORT UART1_IRQHandler [WEAK]
EXPORT SPI0_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT SPI3_IRQHandler [WEAK]
EXPORT I2C0_IRQHandler [WEAK]
EXPORT I2C1_IRQHandler [WEAK]
EXPORT CAN0_IRQHandler [WEAK]
EXPORT CAN1_IRQHandler [WEAK]
EXPORT USBD_IRQHandler [WEAK]
EXPORT PS2_IRQHandler [WEAK]
EXPORT ACMP_IRQHandler [WEAK]
EXPORT PDMA_IRQHandler [WEAK]
EXPORT PWRWU_IRQHandler [WEAK]
EXPORT ADC_IRQHandler [WEAK]
EXPORT RTC_IRQHandler [WEAK]
BOD_IRQHandler
WDT_IRQHandler
EINT0_IRQHandler
EINT1_IRQHandler
GPAB_IRQHandler
GPCDE_IRQHandler
PWMA_IRQHandler
PWMB_IRQHandler
TMR0_IRQHandler
TMR1_IRQHandler
TMR2_IRQHandler
TMR3_IRQHandler
UART0_IRQHandler
UART1_IRQHandler
SPI0_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
SPI3_IRQHandler
I2C0_IRQHandler
I2C1_IRQHandler
CAN0_IRQHandler
CAN1_IRQHandler
USBD_IRQHandler
PS2_IRQHandler
ACMP_IRQHandler
PDMA_IRQHandler
PWRWU_IRQHandler
ADC_IRQHandler
RTC_IRQHandler
B . ;//停止
ENDP
ALIGN ;//添加补丁字节满足一定的对齐方式
; //用户初始化的堆栈
IF :DEF:__MICROLIB ;//检查是否定义了__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
深入重点:
ü 什么是堆栈?
ü startup_M051.s上电时初始化单片机的硬件堆栈、初始化RAM、分配内存空间和跳转到主函数即main函数。
ü 使用IMPORT或EXPORT声明外部符号时,若链接器在连接微控制器时不能解释该符号,而伪指令中没有[WEAK]选项时,则链接器会报告错误,若伪指令中有[WEAK]选项时,则链接器不会报告错误,而是进行下面的操作:
1:如果该符号被B或者BL指令引用,则该符号被设置成下一条指令的地址,该B或者 BL指令相当于一条NOP指令。
2:其他情况下该符号被设置为0
…………………………………………
书籍下载地址(复制到下载工具进行下载):
https://files.cnblogs.com/wenziqi/ARMCortex-M0原理与应用实践.part1.rar
https://files.cnblogs.com/wenziqi/ARMCortex-M0原理与应用实践.part2.rar
注:书籍内容会不定期进行更新!