无论是最简单的51
单片机,还是复杂的
ARM、DSP等,最简单的操作莫过于IO口的高、低电平控制了,本章以前面讲述的库函数为基础,通过一个经典的跑马灯程序,带大家开始SWM320的学习之旅,通过本章的学习,你将掌握SWM320的GPIO口作为输出使用的方法。在本章实现跑马灯时,我们加入了一个名为“system_SWM320.c”的汇编文件,这个文件起什么作用呢?这章将给出简单的讲述。
6.1跑马灯的实现过程前面几章,讲述库函数的过程时,我们简单提到了点亮一个LED小灯的过程,但是没有说明为何是控制GPIOP端口的几个位,这里我们先上原理图,再来解释其原理。
6.1.1LED小灯的硬件威廉希尔官方网站
设计对于LED来说,总共两个引脚,在威廉希尔官方网站
设计上没有难度,这里以FSSW32
开发板上的原理图来做讲解。通常情况下,LED内部需要通过一定的电流且存在一定的压差(也即压降)才能使得其发光。通常使用的LED的工作电流为3~20mA左右,但二极管本身的内阻又比较小,所以不能直接将两端接
电源和GND,而需要加一个限流电阻(阻值读者可通过欧姆定律计算得到),限制通过LED的电流不要太大,LED原理图如图6-1所示。D2在核心板上,LED标号接SWM320的GPIOP.22;D8在底板上,LED_R、LED_G、LED_B分别接GPIOM.5、GPIOM.0、GPIOP.12,由于FSSW32板载资源比较丰富,这些端口有复用,读者可以不用理会,以后项目中用到SDRAM、NorFlash等外设时,LED可能会出现不规则的闪烁,读者知道是怎么回事就好。
注意:威廉希尔官方网站
设计中,标号相同,表示物理连接。
该方式的LED驱动威廉希尔官方网站
是将正极接在3.3V(高电平)上,负极再串联一个47O欧姆的限流电阻,再接到单片机的I/O口上,只需给LED小灯所对应的I/O口上低电平,就可以点亮LED;还有一种接法是将LED的正极接单片机的I/O口,再通过一限流电阻,将负极接地,单片机输出高电平,就可以点亮LED,但是需要将I/O设置为强推挽输出模式,具体情况依设计者的爱好和需求而定。
经上述分析,LED两端只要有一合适的压差和电流,就会点亮LED。那么单片机又是如何控制的呢。举个例子,要有水流,必须有水压差,同样,要有电流,就得有电压差,结合上图,LED的正极已经接了高电平,如果LED所对应的单片机端口也为高电平(LED小灯熄灭),则LED的左右两端电平都是高电平(3.3V),就没有压差,LED就不会亮,相反,若单片机端口为低电平,从而LED两端则会有压差,这样LED灯就会被点亮(LED小灯点亮)。接下来就是通过软件控制LED小灯对应的端口实现周期性的高、低电平变化,就可以实现LED小灯的跑马灯效果。
6.1.2跑马灯的软件设计在设计之前,读者需要建立完整的跑马灯工程,这里我们建立了三个Group,分别为“CMSIS”、“USER”和“SWMLib”,CMSIS下包含两个文件,分别为“startup_SWM320.s”、“system_SWM320.c”,这两个文件是华芯微特官方提供的,至于来源前面有述;USER中就一个读者很熟悉main.c文件,里面的内容是我们实现跑马灯的源码,源码的内容稍后讲述;SWMLib中包括了“SWM320_gpio.c”、“SWM320_port.c”两个文件,同样是官方提供的库函数。工程的建立过程当然还包括文件路径的设置、下载算法RAM大小的修改等,详细的建立过程请读者参考3.2节的内容,建立好的工程如图6-2所示。
工程建立好之后,5个文件我们只需编写main.c文件,其源码如下,剩余的4个只需添加即可。
图6-2 跑马灯工程框架图
1. #include "SWM320.h"
2.
3. void Delay(void)
4. {
5. for(uint16_
ti = 0; i < 5000; i++)
6. {
7. for(uint16_tj = 0; j < 1000; j++);
8. }
9. }
10.
11. intmain(void)
12. {
13. SystemInit();
14.
15. GPIO_Init(GPIOM, PIN0, 1, 0, 0); // 输出, 接LED
16. GPIO_Init(GPIOM, PIN5, 1, 0, 0); // 输出, 接LED
17. GPIO_Init(GPIOP, PIN12, 1, 0, 0); // 输出, 接LED
18. GPIO_Init(GPIOP, PIN22, 1, 0, 0); // 输出, 接LED
19.
20. while(1)
21. {
22. GPIO_ClrBit(GPIOM, PIN0); // 点亮LED小灯
23. Delay();
24. GPIO_SetBit(GPIOM, PIN0); // 熄灭LED小灯
25. GPIO_ClrBit(GPIOM, PIN5); // 点亮LED小灯
26. Delay();
27. GPIO_SetBit(GPIOM, PIN5); // 熄灭LED小灯
28. GPIO_ClrBit(GPIOP, PIN12); // 点亮LED小灯
29. Delay();
30. GPIO_SetBit(GPIOP, PIN12); // 熄灭LED小灯
31. GPIO_ClrBit(GPIOP, PIN22); // 点亮LED小灯
32. Delay();
33. GPIO_SetBit(GPIOP, PIN22); // 熄灭LED小灯
34. }
35. }
如果前面的内容读者都已经很好的掌握了,这几十行程序应该不难理解,如果读者没有掌握,那接下来笔者为读者简单分析一下这35行语句。
第1行,包含SWM320的头文件,只有包含了这个头文件,才能正确运用库函数,前面讲解库函数的时候和读者有讲述,其各个寄存器的映射地址、结构体指针等都包含在这个头文件中。还有“SWM320_gpio.c”、“SWM320_port.c”两个文件的头文件包含,都是在第1行这个头文件中被包含进来的,如果读者想深入研究,可以选中头文件,右键单击,再选中第二个选项打开该头文件,仔细研读这几千行代码,笔者不建议这么做哈。
第3~9行,是一个简单的延时函数,这个我们在学习51的时候经常用,原理这里不赘。
第13行,这是系统的时钟初始化函数,函数的原型在“system_SWM320.c”中,这个函数我们在后面为读者讲述,这里读者可理解为“空气”。
第15~18行,这个函数我们在5.4节做了详细的讲述,目的是将接LED小灯的4个端口设置为输出端口,而且不使能上、下拉电阻。
第22行,使GPIOM.0端口输出低电平,这样在LED小灯两端形成压差,即可点亮LED小灯。函数原型在“SWM320_gpio.c”中,具体内容后面再述。
第24行,使GPIOM.0端口输出高电平,这样LED小灯两端电平一样,没有压差,LED小灯熄灭。函数原型在“SWM320_gpio.c”中,细节后面再述,剩余行同理,读者自行理解。
6.1.3下载验证待上面的程序编写好之后,编译、下载到FSSW32开发板上,这时可观察到开发板上的4颗LED小灯每隔一定的时间会轮流点亮、熄灭,看起来有点跑马的意思。
6.2启动代码在上面的跑马灯实验中,我们加入了一个“startup_SWM320.s”文件,这个文件俗称启动代码,也即BootLoader。这个代码也是由IC官方编写的,我们只需应用即可,但是这里还是有必要为读者讲述一下,如果读者暂时觉得学起来有点吃力,可以暂时搁置,待学习一段时间的SWM320之后,再结合ARM公司的《M4权威指南》来理解这个启动代码。
6.2.1启动代码的作用概述启动代码由汇编语言编写,是系统上电复位之后第一个执行的程序,主要的功能有以下几点。
(1)堆和栈的初始化
包括堆栈的大小,MSP(main stack pointer)值等,MSP的初始值在复位阶段取自存储区的第一个字(即0地址处的值)。
栈Stack:由编译器自动分配和释放,存放函数的参数值、局部变量的值等,其操作方式类似于数据结构中的栈,向低地址扩展。
堆Heap:一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。分配方式类似于数据结构中的链表,向高地址扩展。
(2)向量表定义
定义MSP初值以及各个中断服务程序(ISR)的入口地址。
(3)中断服务程序
CPU根据中断号从向量表中获取入口地址后,跳转至对应的ISR中。
(4)设置系统时钟频率
SWM320的启动代码没有时钟设置,时钟设置在“system_SWM320.c”中。如果读者想把SystemInit()函数添加到服务程序中,可采用如下的方式来实现。
1. Reset_Handler PROC
2. EXPORT Reset_Handler [WEAK]
3. IMPORT SystemInit
4. LDR R0, =SystemInit
5. BLX R0
6. ENDP
(5)中断寄存器的初始化
(6)跳转到C应用程序
在复位中断服务程序即Reset_Handler中实现进入C程序。
6.2.2启动代码的详解双击跑马灯工程中的“startup_SWM320.s”文件,浏览发现,启动代码总共有500行左右,此时读者可能无从下手,这里按上面概述的几点将其分类,一点一滴为读者介绍,同时便于讲解,将其堆和栈分开讲述。
1. 栈Stack1. Stack_Size EQU 0x00000400
2.
3. AREA STACK, NOINIT, READWRITE, ALIGN=3
4. Stack_Mem SPACE Stack_Size
5. __initial_sp
分配0x400(也即1K)个连续字节的栈,名称为STACK,NOINT即不初始化,可读可写,8(23)字节对齐,几个指令解释如下。
EQU:宏定义的伪指令,可理解为等于,和C语言中的define类似;
AREA:告诉汇编器汇编一个新的代码段或者数据段。STACK表示段名,这个可以任意命名;NOINIT表示不初始化;READWRITE表示可读可写,ALIGN=3,表示按照8(23)字节对齐。
SPACE:用于分配一定大小的内存空间,单位为字节。这里指定大小等于Stack_Size。标号__initial_sp紧挨着SPACE语句放置,表示栈的结束地址,即栈顶地址,栈是由高向低生长的。
栈的作用是用于局部变量,函数调用,函数形参等的开销,栈的大小不能超过内部SRAM 的大小。如果编写的程序比较大,定义的局部变量很多,那么就需要修改栈的大小。如果某一天,读者写的程序出现了莫名奇妙的问题,并进入了硬件fault的时候,就需要考虑是不是栈不够大,已经溢出了。
2. 堆Heap1. Heap_Size EQU 0x00000200
2.
3. AREA HEAP, NOINIT, READWRITE, ALIGN=3
4. __heap_base
5. Heap_Mem SPACE Heap_Size
6. __heap_limit
7.
8. PRESERVE8
9. THUMB
开辟0x200(512B)个字节的堆,名字为HEAP,NOINIT即不初始化,可读可写,8(23)字节对齐。__heap_base表示对的起始地址,__heap_limit表示堆的结束地址。堆是由低向高生长的,跟栈的生长方向相反。堆主要用来动态内存的分配,类似于malloc()函数申请的内存的过程。
PRESERVE8:指定当前文件的堆栈按照8字节对齐。
THUMB:表示后面指令兼容THUMB指令。THUBM是ARM以前的指令集,为16bit,现在 Cortex-M系列的都使用THUMB-2(这里的2可理解为2倍于THUBM指令集)指令集,THUMB-2是32位的,兼容16位和32位的指令,是THUMB的超级。
3. 中断向量表定义1. ; Vector Table Mapped toAddress 0 at Reset
2. AREA RESET, DATA, READONLY
3. EXPORT __Vectors
4. EXPORT __Vectors_End
5. EXPORT __Vectors_Size
6.
7. __Vectors DCD __initial_sp ; Top of Stack
8. DCD Reset_Handler ; Reset Handler
9. DCD NMI_Handler ; NMI Handler
10. DCD HardFault_Handler ; Hard Fault Handler
11. DCD MemManage_Handler ; MPU Fault Handler
12. DCD BusFault_Handler ; Bus Fault Handler
13. DCD UsageFault_Handler ; Usage Fault Handler
14. DCD 0 ; Reserved
15. DCD 0 ; Reserved
16. DCD 0 ; Reserved
17. DCD 0 ; Reserved
18. DCD SVC_Handler ; SVCall Handler
19. DCD DebugMon_Handler ; Debug Monitor Handler
20. DCD 0 ; Reserved
21. DCD PendSV_Handler ; PendSV Handler
22. DCD SysTick_Handler ; SysTick Handler
23.
24. ; External Interrupts
25. DCD GPIOA0_Handler
26. DCD GPIOA1_Handler
27. DCD GPIOA2_Handler
28. DCD GPIOA3_Handler
29. DCD GPIOA4_Handler
30. DCD GPIOA5_Handler
31. DCD GPIOA6_Handler
32. DCD GPIOA7_Handler
33. DCD GPIOB0_Handler
34. DCD GPIOB1_Handler
35. DCD GPIOB2_Handler
36. DCD GPIOB3_Handler
37. DCD GPIOB4_Handler
38. DCD GPIOB5_Handler
39. DCD GPIOB6_Handler
40. DCD GPIOB7_Handler
41. DCD GPIOC0_Handler
42. DCD GPIOC1_Handler
43. DCD GPIOC2_Handler
44. DCD GPIOC3_Handler
45. DCD GPIOC4_Handler
46. DCD GPIOC5_Handler
47. DCD GPIOC6_Handler
48. DCD GPIOC7_Handler
49. DCD GPIOM0_Handler
50. DCD GPIOM1_Handler
51. DCD GPIOM2_Handler
52. DCD GPIOM3_Handler
53. DCD GPIOM4_Handler
54. DCD GPIOM5_Handler
55. DCD GPIOM6_Handler
56. DCD GPIOM7_Handler
57. DCD DMA_Handler
58. DCD LCD_Handler
59. DCD NORFLC_Handler
60. DCD CAN_Handler
61. DCD PULSE_Handler
62. DCD WDT_Handler
63. DCD PWM_Handler
64. DCD UART0_Handler
65. DCD UART1_Handler
66. DCD UART2_Handler
67. DCD UART3_Handler
68. DCD 0
69. DCD I2C0_Handler
70. DCD I2C1_Handler
71. DCD SPI0_Handler
72. DCD ADC0_Handler
73. DCD RTC_Handler
74. DCD ANAC_Handler
75. DCD SDIO_Handler
76. DCD GPIOA_Handler
77. DCD GPIOB_Handler
78. DCD GPIOC_Handler
79. DCD GPIOM_Handler
80. DCD GPION_Handler
81. DCD GPIOP_Handler
82. DCD ADC1_Handler
83. DCD FPU_Handler
84. DCD SPI1_Handler
85. DCD TIMR0_Handler
86. DCD TIMR1_Handler
87. DCD TIMR2_Handler
88. DCD TIMR3_Handler
89. DCD TIMR4_Handler
90. DCD TIMR5_Handler
91.
92. __Vectors_End
93.
94. __Vectors_Size EQU __Vectors_End - __Vectors
第1行为注释,汇编的注释有别于C语言。
第2行定义一个复位向量段,名字为RESET,只读。
第3~5并声明__Vectors_Size、__Vectors_End和__Vectors三个标号具有全局属性,可供外部的文件调用。EXPORT:声明一个标号可被外部的文件使用,使标号具有全局属性。
当内核响应了一个发生的异常后,对应的异常服务例程(ESR)就会执行。为了决定ESR的入口地址,内核使用了―向量表查表机制。这里使用一张向量表。向量表其实是一个WORD(32位整数)数组,每个下标对应一种异常,该下标元素的值则是该ESR的入口地址。向量表在地址空间中的位置是可以设置的,通过NVIC中的一个重定位寄存器来指出向量表的地址。在复位后,该寄存器的值为0。因此,在地址0(即FLASH地址0)处必须包含一张向量表,用于初始时的异常分配。要注意的是这里有个另类:0号类型并不是什么入口地址,而是给出了复位后MSP的初值。
第7~90行,都定义了中断向量的地址。DCD:分配一个或者多个以字为单位的内存,以四字节对齐,并要求初始化这些内存。在向量表中,DCD分配了一堆内存,并且以ESR的入口地址初始化它们。向量表从FLASH的0地址开始放置,以4个字节为一个单位,地址0存放的是栈顶地址,0X04存放的是复位程序的地址,以此类推。从代码上看,向量表中存放的都是中断服务函数的函数名,可我们知道C语言中的函数名就是一个地址。
第94行,__Vectors为向量表起始地址,__Vectors_End为向量表结束地址,两个相减即可算出向量表大小。
4. 复位程序1. AREA |.text|, CODE, READONLY
2.
3. Reset_Handler PROC
4. EXPORT Reset_Handler [WEAK]
5. IMPORT __main
6. LDR R0, =__main
7. BX R0
8. ENDP
第1行,定义一个名称为.text的代码段,可读。
第3~8,复位子程序是系统上电后第一个执行的程序,调用C库函数_mian,最终调用main函数去到C的世界里。除了代码外,笔者再为读者解释一下这些陌生的命令。
WEAK:表示弱定义,如果外部文件优先定义了该标号则首先引用该标号,如果外部文件没有声明也不会出错。这里表示复位子程序可以由用户在其他文件重新实现,这里并不是唯一的。
IMPORT:表示该标号来自外部文件,其作用和C语言中的“extern”关键字类似。这里表示__main函数来自外部的文件。
__main是一个标准的C库函数,主要作用是初始化用户堆栈,最终调用main函数去到C的世界里。这就是为什么我们写的程序都有一个main函数的原因。读者此时可能会问到,能不能不调用__main,用SWM_main呢?答案当然是肯定的。我们只需修改主函数的名称,然后在IMPORT里写自己的主函数名称即可,实例代码如下。
1. AREA |.text|, CODE, READONLY
2.
3. Reset_Handler PROC
4. EXPORT Reset_Handler [WEAK]
5. IMPORT SWM_main
6. LDR R0, = SWM_main
7. BX R0
8. ENDP
那么C世界里的代码就如下:
1. int SWM_main(void)
2. {
3. SystemInit();
4.
5. while(1)
6. {
7. }
8. }
其中LDR、BLX、BX等内核的指令,请读者参阅《M4权威指南》。
5. 中断服务程序1. NMI_Handler PROC
2. EXPORT NMI_Handler [WEAK]
3. B .
4. ENDP
5.
6. HardFault_Handler PROC
7. EXPORT HardFault_Handler [WEAK]
8. B .
9. ENDP
10.
11. MemManage_Handler PROC
12. EXPORT MemManage_Handler [WEAK]
13. B .
14. ENDP
15.
16. BusFault_Handler PROC
17. EXPORT BusFault_Handler [WEAK]
18. B .
19. ENDP
20.
21. UsageFault_Handler PROC
22. EXPORT UsageFault_Handler [WEAK]
23. B .
24. ENDP
25.
26. SVC_Handler PROC
27. EXPORT SVC_Handler [WEAK]
28. B .
29. ENDP
30.
31. DebugMon_Handler PROC
32. EXPORT DebugMon_Handler [WEAK]
33. B .
34. ENDP
35.
36. PendSV_Handler PROC
37. EXPORT PendSV_Handler [WEAK]
38. B .
39. ENDP
40.
41. SysTick_Handler PROC
42. EXPORT SysTick_Handler [WEAK]
43. B .
44. ENDP
45.
46. GPIOA0_IRQHandler PROC
47. EXPORT GPIOA0_IRQHandler [WEAK]
48. B .
49. ENDP
50.
51. GPIOA1_IRQHandler PROC
52. EXPORT GPIOA1_IRQHandler [WEAK]
53. B .
54. ENDP
55.
56. GPIOA2_IRQHandler PROC
57. EXPORT GPIOA2_IRQHandler [WEAK]
58. B .
59. ENDP
60.
61. GPIOA3_IRQHandler PROC
62. EXPORT GPIOA3_IRQHandler [WEAK]
63. B .
64. ENDP
65.
66. GPIOA4_IRQHandler PROC
67. EXPORT GPIOA4_IRQHandler [WEAK]
68. B .
69. ENDP
70.
71. GPIOA5_IRQHandler PROC
72. EXPORT GPIOA5_IRQHandler [WEAK]
73. B .
74. ENDP
75.
76. GPIOA6_IRQHandler PROC
77. EXPORT GPIOA6_IRQHandler [WEAK]
78. B .
79. ENDP
80.
81. GPIOA7_IRQHandler PROC
82. EXPORT GPIOA7_IRQHandler [WEAK]
83. B .
84. ENDP
85.
86. GPIOB0_IRQHandler PROC
87. EXPORT GPIOB0_IRQHandler [WEAK]
88. B .
89. ENDP
90.
91. GPIOB1_IRQHandler PROC
92. EXPORT GPIOB1_IRQHandler [WEAK]
93. B .
94. ENDP
95.
96. GPIOB2_IRQHandler PROC
97. EXPORT GPIOB2_IRQHandler [WEAK]
98. B .
99. ENDP
100.
101. GPIOB3_IRQHandler PROC
102. EXPORT GPIOB3_IRQHandler [WEAK]
103. B .
104. ENDP
105.
106. GPIOB4_IRQHandler PROC
107. EXPORT GPIOB4_IRQHandler [WEAK]
108. B .
109. ENDP
110.
111. GPIOB5_IRQHandler PROC
112. EXPORT GPIOB5_IRQHandler [WEAK]
113. B .
114. ENDP
115.
116. GPIOB6_IRQHandler PROC
117. EXPORT GPIOB6_IRQHandler [WEAK]
118. B .
119. ENDP
120.
121. GPIOB7_IRQHandler PROC
122. EXPORT GPIOB7_IRQHandler [WEAK]
123. B .
124. ENDP
125.
126. GPIOC0_IRQHandler PROC
127. EXPORT GPIOC0_IRQHandler [WEAK]
128. B .
129. ENDP
130.
131. GPIOC1_IRQHandler PROC
132. EXPORT GPIOC1_IRQHandler [WEAK]
133. B .
134. ENDP
135.
136. GPIOC2_IRQHandler PROC
137. EXPORT GPIOC2_IRQHandler [WEAK]
138. B .
139. ENDP
140.
141. GPIOC3_IRQHandler PROC
142. EXPORT GPIOC3_IRQHandler [WEAK]
143. B .
144. ENDP
145.
146. GPIOC4_IRQHandler PROC
147. EXPORT GPIOC4_IRQHandler [WEAK]
148. B .
149. ENDP
150.
151. GPIOC5_IRQHandler PROC
152. EXPORT GPIOC5_IRQHandler [WEAK]
153. B .
154. ENDP
155.
156. GPIOC6_IRQHandler PROC
157. EXPORT GPIOC6_IRQHandler [WEAK]
158. B .
159. ENDP
160.
161. GPIOC7_IRQHandler PROC
162. EXPORT GPIOC7_IRQHandler [WEAK]
163. B .
164. ENDP
165.
166. GPIOM0_IRQHandler PROC
167. EXPORT GPIOM0_IRQHandler [WEAK]
168. B .
169. ENDP
170.
171. GPIOM1_IRQHandler PROC
172. EXPORT GPIOM1_IRQHandler [WEAK]
173. B .
174. ENDP
175.
176. GPIOM2_IRQHandler PROC
177. EXPORT GPIOM2_IRQHandler [WEAK]
178. B .
179. ENDP
180.
181. GPIOM3_IRQHandler PROC
182. EXPORT GPIOM3_IRQHandler [WEAK]
183. B .
184. ENDP
185.
186. GPIOM4_IRQHandler PROC
187. EXPORT GPIOM4_IRQHandler [WEAK]
188. B .
189. ENDP
190.
191. GPIOM5_IRQHandler PROC
192. EXPORT GPIOM5_IRQHandler [WEAK]
193. B .
194. ENDP
195.
196. GPIOM6_IRQHandler PROC
197. EXPORT GPIOM6_IRQHandler [WEAK]
198. B .
199. ENDP
200.
201. GPIOM7_IRQHandler PROC
202. EXPORT GPIOM7_IRQHandler [WEAK]
203. B .
204. ENDP
205.
206. DMA_IRQHandler PROC
207. EXPORT DMA_IRQHandler [WEAK]
208. B .
209. ENDP
210.
211. LCD_IRQHandler PROC
212. EXPORT LCD_IRQHandler [WEAK]
213. B .
214. ENDP
215.
216. NORFLC_IRQHandler PROC
217. EXPORT NORFLC_IRQHandler [WEAK]
218. B .
219. ENDP
220.
221. CAN_IRQHandler PROC
222. EXPORT CAN_IRQHandler [WEAK]
223. B .
224. ENDP
225.
226. TIMR_IRQHandler PROC
227. EXPORT TIMR_IRQHandler [WEAK]
228. B .
229. ENDP
230.
231. WDT_IRQHandler PROC
232. EXPORT WDT_IRQHandler [WEAK]
233. B .
234. ENDP
235.
236. PWM_IRQHandler PROC
237. EXPORT PWM_IRQHandler [WEAK]
238. B .
239. ENDP
240.
241. UART0_IRQHandler PROC
242. EXPORT UART0_IRQHandler [WEAK]
243. B .
244. ENDP
245.
246. UART1_IRQHandler PROC
247. EXPORT UART1_IRQHandler [WEAK]
248. B .
249. ENDP
250.
251. UART2_IRQHandler PROC
252. EXPORT UART2_IRQHandler [WEAK]
253. B .
254. ENDP
255.
256. UART3_IRQHandler PROC
257. EXPORT UART3_IRQHandler [WEAK]
258. B .
259. ENDP
260.
261. I2C0_IRQHandler PROC
262. EXPORT I2C0_IRQHandler [WEAK]
263. B .
264. ENDP
265.
266. I2C1_IRQHandler PROC
267. EXPORT I2C1_IRQHandler [WEAK]
268. B .
269. ENDP
270.
271. SPI0_IRQHandler PROC
272. EXPORT SPI0_IRQHandler [WEAK]
273. B .
274. ENDP
275.
276. ADC0_IRQHandler PROC
277. EXPORT ADC0_IRQHandler [WEAK]
278. B .
279. ENDP
280.
281. RTC_IRQHandler PROC
282. EXPORT RTC_IRQHandler [WEAK]
283. B .
284. ENDP
285.
286. ANAC_IRQHandler PROC
287. EXPORT ANAC_IRQHandler [WEAK]
288. B .
289. ENDP
290.
291. SDIO_IRQHandler PROC
292. EXPORT SDIO_IRQHandler [WEAK]
293. B .
294. ENDP
295.
296. GPIOA_IRQHandler PROC
297. EXPORT GPIOA_IRQHandler [WEAK]
298. B .
299. ENDP
300.
301. GPIOB_IRQHandler PROC
302. EXPORT GPIOB_IRQHandler [WEAK]
303. B .
304. ENDP
305.
306. GPIOC_IRQHandler PROC
307. EXPORT GPIOC_IRQHandler [WEAK]
308. B .
309. ENDP
310.
311. GPIOM_IRQHandler PROC
312. EXPORT GPIOM_IRQHandler [WEAK]
313. B .
314. ENDP
315.
316. GPION_IRQHandler PROC
317. EXPORT GPION_IRQHandler [WEAK]
318. B .
319. ENDP
320.
321. GPIOP_IRQHandler PROC
322. EXPORT GPIOP_IRQHandler [WEAK]
323. B .
324. ENDP
325.
326. ADC1_IRQHandler PROC
327. EXPORT ADC1_IRQHandler [WEAK]
328. B .
329. ENDP
330.
331. FPU_IRQHandler PROC
332. EXPORT FPU_IRQHandler [WEAK]
333. B .
334. ENDP
335.
336. SPI1_IRQHandler PROC
337. EXPORT SPI1_IRQHandler [WEAK]
338. B .
339. ENDP
在启动文件里面已经帮我们写好了所有中断的中断服务函数,跟我们平时写的中断服务函数不一样的就是这些函数都是空的,真正的中断复服务程序需要我们在外部的C文件里面重新实现,这里只是提前抢占了一个位置而已。如果我们在使用某个外设的时候,开启了某个中断,但是又忘记编写配套的中断服务程序或者函数名写错,那当中断来临的时,程序就会跳转到启动代码中预先写好空的中断服务程序里,并且在这个空函数中无线循环,这样程序就会死在这里。
B:跳转到一个标号。这里跳转到一个‘.’,即表示无线循环,有点类似于C语言中的while(1)。
6. 用户堆栈初始化1. ALIGN
2.
3. ;**************************************************************************
4. ; User Stack and Heapinitialization
5. ;**************************************************************************
6. IF :DEF:__MICROLIB
7.
8. EXPORT __initial_sp
9. EXPORT __heap_base
10. EXPORT __heap_limit
11.
12. ELSE
13.
14. IMPORT __use_two_region_memory
15. EXPORT __user_initial_stackheap
16.
17. __user_initial_stackheap
18.
19. LDR R0, = Heap_Mem
20. LDR R1, =(Stack_Mem + Stack_Size)
21. LDR R2, = (Heap_Mem + Heap_Size)
22. LDR R3, = Stack_Mem
23. BX LR
24.
25. ALIGN
26.
27. ENDIF
28.
29. END
第1行,ALIGN:对指令或者数据存放的地址进行对齐,后面会跟一个立即数。缺省表示4字节对齐。
第6~27行,判断是否定义了__MICROLIB,如果定义了则赋予标号__initial_sp(栈顶地址)、__heap_base(堆起始地址)、__heap_limit(堆结束地址)全局属性,可供外部文件调用。如果没有定义(实际的情况就是我们没定义__MICROLIB)则使用默认的C库,然后初始化用户堆栈大小,这部分由C库函数__main来完成,当初始化完堆栈之后,就调用main函数去到C的世界。
DEF:XXX,表示XXX定了则为真,否则为假。类似于C语言中的“#define”。