二话不说,首先贴上startup_LPC17xx.s文件的源码,如下所示:
;定义堆栈空间
Stack_Size EQU 0x00001600 ; 栈的大小为5632个字节
AREA STACK, NOINIT, READWRITE, ALIGN=3 ; 定义一个段名为STACK的数据段,该段只保留内存单元,但是不进行初始化,可读可写,并且以8字节形式进行对齐
Stack_Mem SPACE Stack_Size ;为栈分配5632个字节的内存
__initial_sp ; 定义__initial_sp该标号为该栈的栈顶
Heap_Size EQU 0x00000000
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base ; 定义 __heap_base该标号为该堆的起始地址
Heap_Mem SPACE Heap_Size
__heap_limit ; 定义 __heap_limit该标号为该堆的终止地址
PRESERVE8 ; 指定当前文件 保持堆栈 8字节对齐的形式
THUMB ; 切换为THUMB指令集状态
AREA RESET, DATA, READONLY ; 定义一个段名为RESET的数据段,只可读
;创建中断向量表
EXPORT __Vectors ; 声明函数 __Vectors (该函数在本文件中定义的,这样其余文件就可以调用本函数了)
__Vectors DCD __initial_sp ; Top of Stack
; DCD申请一个字(32bit)的内存空间,并赋一个初值,在这里,初值为一个函数的函数名,即函数的首地址
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts,外部中断
DCD WDT_IRQHandler ; 16: Watchdog Timer
DCD TIMER0_IRQHandler ; 17: Timer0
DCD TIMER1_IRQHandler ; 18: Timer1
DCD TIMER2_IRQHandler ; 19: Timer2
DCD TIMER3_IRQHandler ; 20: Timer3
DCD UART0_IRQHandler ; 21: UART0
DCD UART1_IRQHandler ; 22: UART1
DCD UART2_IRQHandler ; 23: UART2
DCD UART3_IRQHandler ; 24: UART3
DCD PWM1_IRQHandler ; 25: PWM1
DCD I2C0_IRQHandler ; 26: I2C0
DCD I2C1_IRQHandler ; 27: I2C1
DCD I2C2_IRQHandler ; 28: I2C2
DCD SPI_IRQHandler ; 29: SPI
DCD SSP0_IRQHandler ; 30: SSP0
DCD SSP1_IRQHandler ; 31: SSP1
DCD PLL0_IRQHandler ; 32: PLL0 Lock (Main PLL)
DCD RTC_IRQHandler ; 33: Real Time Clock
DCD EINT0_IRQHandler ; 34: External Interrupt 0
DCD EINT1_IRQHandler ; 35: External Interrupt 1
DCD EINT2_IRQHandler ; 36: External Interrupt 2
DCD EINT3_IRQHandler ; 37: External Interrupt 3
DCD ADC_IRQHandler ; 38: A/D Converter
DCD BOD_IRQHandler ; 39: Brown-Out Detect
DCD USB_IRQHandler ; 40: USB
DCD CAN_IRQHandler ; 41: CAN
DCD DMA_IRQHandler ; 42: General Purpose DMA
DCD I2S_IRQHandler ; 43: I2S
DCD ENET_IRQHandler ; 44: Ethernet
DCD RIT_IRQHandler ; 45: Repetitive Interrupt Timer
DCD MCPWM_IRQHandler ; 46: Motor Control PWM
DCD QEI_IRQHandler ; 47: Quadrature Encoder Interface
DCD PLL1_IRQHandler ; 48: PLL1 Lock (USB PLL)
DCD USBActivity_IRQHandler ; 49: USB Activity interrupt to wakeup
DCD CANActivity_IRQHandler ; 50: CAN Activity interrupt to wakeup
; 芯片程序加密
IF :LNOT::DEF:NO_CRP
AREA |.ARM.__at_0x02FC|, CODE, READONLY
CRP_Key DCD 0xFFFFFFFF
ENDIF
AREA |.text|, CODE, READONLY ; 这个是干嘛的我也不知道,尴尬。。。
; 复位中断函数服务
Reset_Handler PROC ; 定义子程序的伪指令,位于子程序的起始处
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit ; 声明函数 SystemInit(该函数在另外文件中定义的,声明后,本文件就可以调用该函数了)
IMPORT __main
LDR R0, =SystemInit ; 将SystemInit函数的首地址加载到寄存器R0中
BLX R0 ; 切换指令集,跳转到该地址运行,运行完该函数再返回到这
LDR R0, =__main
BX R0 ; 切换指令集,跳转到该地址运行,不回来了
ENDP ; 定义子程序的伪指令,位于子程序的结束处
; 其他各种弱声明中断服务函数
NMI_Handler PROC
EXPORT NMI_Handler [WEAK] ; 这是一个弱声明,详解见下
B . ; 跳转到本行继续运行(在这里死循环了),详解见下
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_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 WDT_IRQHandler [WEAK]
EXPORT TIMER0_IRQHandler [WEAK]
EXPORT TIMER1_IRQHandler [WEAK]
EXPORT TIMER2_IRQHandler [WEAK]
EXPORT TIMER3_IRQHandler [WEAK]
EXPORT UART0_IRQHandler [WEAK]
EXPORT UART1_IRQHandler [WEAK]
EXPORT UART2_IRQHandler [WEAK]
EXPORT UART3_IRQHandler [WEAK]
EXPORT PWM1_IRQHandler [WEAK]
EXPORT I2C0_IRQHandler [WEAK]
EXPORT I2C1_IRQHandler [WEAK]
EXPORT I2C2_IRQHandler [WEAK]
EXPORT SPI_IRQHandler [WEAK]
EXPORT SSP0_IRQHandler [WEAK]
EXPORT SSP1_IRQHandler [WEAK]
EXPORT PLL0_IRQHandler [WEAK]
EXPORT RTC_IRQHandler [WEAK]
EXPORT EINT0_IRQHandler [WEAK]
EXPORT EINT1_IRQHandler [WEAK]
EXPORT EINT2_IRQHandler [WEAK]
EXPORT EINT3_IRQHandler [WEAK]
EXPORT ADC_IRQHandler [WEAK]
EXPORT BOD_IRQHandler [WEAK]
EXPORT USB_IRQHandler [WEAK]
EXPORT CAN_IRQHandler [WEAK]
EXPORT DMA_IRQHandler [WEAK]
EXPORT I2S_IRQHandler [WEAK]
EXPORT ENET_IRQHandler [WEAK]
EXPORT RIT_IRQHandler [WEAK]
EXPORT MCPWM_IRQHandler [WEAK]
EXPORT QEI_IRQHandler [WEAK]
EXPORT PLL1_IRQHandler [WEAK]
EXPORT USBActivity_IRQHandler [WEAK]
EXPORT CANActivity_IRQHandler [WEAK]
WDT_IRQHandler
TIMER0_IRQHandler
TIMER1_IRQHandler
TIMER2_IRQHandler
TIMER3_IRQHandler
UART0_IRQHandler
UART1_IRQHandler
UART2_IRQHandler
UART3_IRQHandler
PWM1_IRQHandler
I2C0_IRQHandler
I2C1_IRQHandler
I2C2_IRQHandler
SPI_IRQHandler
SSP0_IRQHandler
SSP1_IRQHandler
PLL0_IRQHandler
RTC_IRQHandler
EINT0_IRQHandler
EINT1_IRQHandler
EINT2_IRQHandler
EINT3_IRQHandler
ADC_IRQHandler
BOD_IRQHandler
USB_IRQHandler
CAN_IRQHandler
DMA_IRQHandler
I2S_IRQHandler
ENET_IRQHandler
RIT_IRQHandler
MCPWM_IRQHandler
QEI_IRQHandler
PLL1_IRQHandler
USBActivity_IRQHandler
CANActivity_IRQHandler
B . ; 哈,这里有是个死循环,开了中断的外设一定要有自定义的中断服务函数没要不程序会搞死你
ENDP
ALIGN ; 代码段和数据段以默认字节数对齐(默认字节数为一个字,即4个字节)
; 将堆栈地址传递给库函数
IF :DEF:__MICROLIB ; 如果定义了微库
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE ; 如果未勾选微库,则默认为KEIL C 库
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 ; 切换指令集,并跳转到LR寄存器所存储的地址
ALIGN
ENDIF
END
1. AREA伪指令:
用于定义一个代码段或数据段;
语法格式: AREA 段名 属性1 ,属性2 ,……
段名若以数字开头,则该段名需用“|”括起来,如:|1_test|。
常用的属性如下:
NOINIT:指定此数据段仅仅保留了内存单元,而没有将各初始值写入内存单元,或者将各个内存单元值初始化为0;
CODE 属性:用于定义代码段,默认为READONLY;
DATA 属性:用于定义数据段,默认为READWRITE ;
READONLY 属性:指定本段为只读,代码段默认为READONLY;
READWRITE 属性:指定本段为可读可写,数据段的默认属性为READWRITE ;
ALIGN 属性:使用方式为ALIGN表达式。在默认时,ELF(可执行连接文件)的代码段和数据段是按字对齐的,表达式的取值范围为0~31,相应的对齐方式为2表达式次方,上述=3,即2^3=8,8字节对齐;
一个汇编语言程序至少要包含一个段,当程序太长时,也可以将程序分为多个代码段和数据段。
2. SPACE和DCD
功能类似,SPACE申请一片内存空间,DCD申请一个字(32bit)的内存空间。
区别在于,SPACE申请空间但不赋初值,DCD申请一个字的空间,并赋初值。
3. WEAK
官网解释:symbol is only imported into other sources if no other source exports an alternative symbol. If [WEAK] is used without symbol, all exported symbols are weak.
symbol is only imported into other sources if no other source exports an alternative symbol.
If [WEAK] is used without symbol, all exported symbols are weak.
如果没有其他来源导出替代符号,则仅将该符号导入其他来源。
如果在没有符号的情况下使用[WEAK],则所有导出的符号都是弱声明。
弱声明,即:如果用户定义了相同的函数,则此处函数失效而使用用户定义的中断服务函数。这样是为了防止用户使能了中断而没有中断服务函数,从而造成程序崩溃。假设使能了中断,而用户又没有定义中断服务函数则会进入默认中断,默认中断为死循环;
网上有人很形象的解释为:
就是告诉 链接器: "我略弱但我很绅士, 如果你在别处看到和我一样的符号实例.你就用它吧. 表管我, 求忽视!
死循环与程序崩溃不是一个概念,最近在调试期间经常进入HardFault_Handler中断里面,然后一直出不来,原来是在这发生死循环了。
为什么会出现死循环呢,因为该中断服务函数里面有句
B .
表示跳转到该行,然后就会一直在该行运行,即死循环了
下次遇到可以试着将该句去掉,看看会出现什么样的情况。
这篇文档是经过网上各种资料查阅以及自己领悟的成果,欢迎大家指正。