本篇文章使用的芯片为 LPC1768, 对于内核为 Cortex-M3 的其他芯片, 也可参考, 不同之处在于外围器件的中断, 使用GCC编译器 GNU Arm Embedded Toolchain
基础语法
符号(Symbol)
形如下面以冒号结尾的符号被称为标签(label), 表示符号出现位置的地址
__StackLimit:
.space Stack_Size
.size __StackLimit, . - __StackLimit
. 是一个特殊的符号, 表示当前位置的地址
符号的值通常为 32 bits, 全局符号可用于链接器和调试器
汇编指令(Directive)
所有的汇编指令均以 . 开头, 其余部分通常是小写字母
启动代码
建立栈
本节所用到的汇编指令
指令格式 | 描述 |
---|---|
.equ symbol, expression | 设置 symbol 的值为 expression |
.global symbol | 使得 symbol 对链接器(ld) 可见 |
.space size , fill | 使用 fill 填充 size 字节的空间, 可以省略 fill(默认为 0) |
栈用于函数调用过程中保存局部变量
.section ".stack", "w"
.align 3
#ifdef __STACK_SIZE
.equ Stack_Size, __STACK_SIZE
#else
.equ Stack_Size, 0x00000400
#endif
.global __StackTop
.global __StackLimit
__StackLimit:
.space Stack_Size
.size __StackLimit, . - __StackLimit
__StackTop:
.size __StackTop, . - __StackTop
建立堆
堆用于 malloc 函数动态分配内存
.section ".heap", "w"
.align 3
#ifdef __HEAP_SIZE
.equ Heap_Size, __HEAP_SIZE
#else
.equ Heap_Size, 0x00000C00
#endif
.global __HeapBase
.global __HeapLimit
__HeapBase:
.if Heap_Size
.space Heap_Size
.endif
.size __HeapBase, . - __HeapBase
__HeapLimit:
.size __HeapLimit, . - __HeapLimit
中断向量表
在链接时, 中断向量表需要放在代码区的最前面
.long 指令用于分配 4 字节的内存, 并填充值
.section ".interrupt_vector"
.align 2
.global __interrupt_vector
.type __interrupt_vector, %object
__interrupt_vector:
.long __StackTop /* Top of Stack */
.long Reset_Handler /* Reset Handler */
.long NMI_Handler /* NMI Handler */
.long HardFault_Handler /* Hard Fault Handler */
.long MemManage_Handler /* MPU Fault Handler */
.long BusFault_Handler /* Bus Fault Handler */
.long UsageFault_Handler /* Usage Fault Handler */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long SVC_Handler /* SVCall Handler */
.long DebugMon_Handler /* Debug Monitor Handler */
.long 0 /* Reserved */
.long PendSV_Handler /* PendSV Handler */
.long SysTick_Handler /* SysTick Handler */
/* External Interrupts */
.long WDT_IRQHandler /* 16: Watchdog Timer */
.long TIMER0_IRQHandler /* 17: Timer0 */
.long TIMER1_IRQHandler /* 18: Timer1 */
.long TIMER2_IRQHandler /* 19: Timer2 */
.long TIMER3_IRQHandler /* 20: Timer3 */
.long UART0_IRQHandler /* 21: UART0 */
.long UART1_IRQHandler /* 22: UART1 */
.long UART2_IRQHandler /* 23: UART2 */
.long UART3_IRQHandler /* 24: UART3 */
.long PWM1_IRQHandler /* 25: PWM1 */
.long I2C0_IRQHandler /* 26: I2C0 */
.long I2C1_IRQHandler /* 27: I2C1 */
.long I2C2_IRQHandler /* 28: I2C2 */
.long SPI_IRQHandler /* 29: SPI */
.long SSP0_IRQHandler /* 30: SSP0 */
.long SSP1_IRQHandler /* 31: SSP1 */
.long PLL0_IRQHandler /* 32: PLL0 Lock (Main PLL) */
.long RTC_IRQHandler /* 33: Real Time Clock */
.long EINT0_IRQHandler /* 34: External Interrupt 0 */
.long EINT1_IRQHandler /* 35: External Interrupt 1 */
.long EINT2_IRQHandler /* 36: External Interrupt 2 */
.long EINT3_IRQHandler /* 37: External Interrupt 3 */
.long ADC_IRQHandler /* 38: A/D Converter */
.long BOD_IRQHandler /* 39: Brown-Out Detect */
.long USB_IRQHandler /* 40: USB */
.long CAN_IRQHandler /* 41: CAN */
.long DMA_IRQHandler /* 42: General Purpose DMA */
.long I2S_IRQHandler /* 43: I2S */
.long ENET_IRQHandler /* 44: Ethernet */
.long RIT_IRQHandler /* 45: Repetitive Interrupt Timer */
.long MCPWM_IRQHandler /* 46: Motor Control PWM */
.long QEI_IRQHandler /* 47: Quadrature Encoder Interface */
.long PLL1_IRQHandler /* 48: PLL1 Lock (USB PLL) */
.size __interrupt_vector, . - __interrupt_vector
所有以 _IRQHandler
结尾的符号在下面的代码中被定义, 表示中断函数的起始地址
.macro 是汇编指令中的宏定义
.macro IRQ handler
.align 1
.thumb_func
.weak \handler
.type \handler, %function
\handler:
b .
.size \handler, . - \handler
.endm
IRQ NMI_Handler
IRQ HardFault_Handler
IRQ MemManage_Handler
IRQ BusFault_Handler
IRQ UsageFault_Handler
IRQ SVC_Handler
IRQ DebugMon_Handler
IRQ PendSV_Handler
IRQ SysTick_Handler
IRQ WDT_IRQHandler
IRQ TIMER0_IRQHandler
IRQ TIMER1_IRQHandler
IRQ TIMER2_IRQHandler
IRQ TIMER3_IRQHandler
IRQ UART0_IRQHandler
IRQ UART1_IRQHandler
IRQ UART2_IRQHandler
IRQ UART3_IRQHandler
IRQ PWM1_IRQHandler
IRQ I2C0_IRQHandler
IRQ I2C1_IRQHandler
IRQ I2C2_IRQHandler
IRQ SPI_IRQHandler
IRQ SSP0_IRQHandler
IRQ SSP1_IRQHandler
IRQ PLL0_IRQHandler
IRQ RTC_IRQHandler
IRQ EINT0_IRQHandler
IRQ EINT1_IRQHandler
IRQ EINT2_IRQHandler
IRQ EINT3_IRQHandler
IRQ ADC_IRQHandler
IRQ BOD_IRQHandler
IRQ USB_IRQHandler
IRQ CAN_IRQHandler
IRQ DMA_IRQHandler
IRQ I2S_IRQHandler
IRQ ENET_IRQHandler
IRQ RIT_IRQHandler
IRQ MCPWM_IRQHandler
IRQ QEI_IRQHandler
IRQ PLL1_IRQHandler
这里仅仅是定义了一系列默认的中断函数, 由于使用了 .weak 声明, 可以在 C 代码中定义相同的函数进行覆盖
默认的中断函数被实现为 b .
, 即死循环
系统复位
芯片上电后首先执行系统复位(Reset_Handler)
系统复位需要处理全局变量的值, 对于有初始值的全局变量, 需要从 Flash 拷贝初始值到 RAM, 对于没有初始值的全局变量, 需要把值清零
.text
.thumb
.thumb_func
.align 2
.global Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
ldr r1, = __etext
ldr r2, = __data_start__
ldr r3, = __data_end__
FLASH2RAM:
cmp r2, r3
ittt lt
ldrlt r0, [r1], #4
strlt r0, [r2], #4
blt FLASH2RAM
ldr r1, = __bss_start__
ldr r2, = __bss_end__
movs r0, 0
CLEAR_BSS:
cmp r1, r2
itt lt
strlt r0, [r1], #4
blt CLEAR_BSS
bl SystemInit
bl _start
.pool
.size Reset_Handler, . - Reset_Handler
__etext, __data_start__, __data_end__ 等符号是在链接脚本中定义的, 因为代码段和数据段的地址是在链接阶段才确定下来
_start
是标准库中的符号, 它会调用 main 函数, 当然也可以像下面这样, 跳过 _start 直接调用 main 函数
bl SystemInit
bl main
.pool
.size Reset_Handler, . - Reset_Handler