使用Cortex-M3已经有一段时间了,大大小小也做过几个项目,可以说对这个系列的片子是有一定的了解。相对于以前的8位单片机来说,其存储空间,外设种类都有较大的提升,这对于产品的应用显得非常便利。最近,因为项目上的原因做了一些汇编工作,个人觉得如果想把Cortex-M3系列的芯片用好,汇编方面的理论知识还是需要学习和补充的,因此就有了写这一系列文档的动力,权当总结一下汇编的基础知识和实用样例。
本文重点不在于学习每一条汇编指令,而是通过使用汇编语言编写一个个简单实例使我们能对GCC汇编有一个基本的了解。
主机环境: Ubuntu 16.04.1
目标板名称: TI DB-LM3S9B96
主芯片型号: TI LM3S9B96
交叉编译链: gcc-arm-none-eabi-5_4-2016q3
在另一篇文章 http://blog.csdn.net/pengfei240/article/details/52912833 中,已经详细描述了如何下载和制作交叉编译链,大家如果感兴趣可以参考下这篇文章。
工程的结构按下面的目录形式进行组织:
$ tree -L 2 asm
asm
├── source
│ └── example1
└── toolchain
├── CrossCompile -> gcc-arm-none-eabi-5_4-2016q3
└── gcc-arm-none-eabi-5_4-2016q3
由原理图可知,点灯方式为PF3端口设置为高电平。
/*
* example1
*
* Description: It controls the port to turn on the LED.
* Author: Peter Peng
* Ver: 0.1
*/
.equ STACK_BASE, 0x20000000
.equ STACK_TOP, 0x400
.equ SYSCTL_RCGC1_R, 0x400FE104
.equ SYSCTL_RCGC2_R, 0x400FE108
.equ GPIO_PORTF_AFSEL_R, 0x40025420
.equ GPIO_PORTF_DIR_R, 0x40025400
.equ GPIO_PORTF_DEN_R, 0x4002551C
.equ GPIO_PORTF_DATA_R, 0x400253FC
.equ SYSCTL_RCGC2_GPIOF, 0x00000020
.text
.thumb
.syntax unified
.global _start
_start:
.word STACK_BASE + STACK_TOP /* Top of Stack */
.word start /* Reset Handler */
.word NmiSR /* NMI Handler */
.word FaultISR /* Hard Fault Handler */
.word IntDefaultHandler /* MPU Fault Handler */
.word IntDefaultHandler /* Bus Fault Handler */
.word IntDefaultHandler /* Usage Fault Handler */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word IntDefaultHandler /* SVCall Handler */
.word IntDefaultHandler /* Debug Monitor Handler */
.word 0 /* Reserved */
.word IntDefaultHandler /* PendSV Handler */
.word IntDefaultHandler /* SysTick Handler */
.word IntDefaultHandler /* GPIO Port A */
.word IntDefaultHandler /* GPIO Port B */
.word IntDefaultHandler /* GPIO Port C */
.word IntDefaultHandler /* GPIO Port D */
.word IntDefaultHandler /* GPIO Port E */
.word IntDefaultHandler /* UART0 */
.word IntDefaultHandler /* UART1 */
.word IntDefaultHandler /* SSI */
.word IntDefaultHandler /* I2C */
.word IntDefaultHandler /* PWM Fault */
.word IntDefaultHandler /* PWM Generator 0 */
.word IntDefaultHandler /* PWM Generator 1 */
.word IntDefaultHandler /* PWM Generator 2 */
.word IntDefaultHandler /* Quadrature Encoder */
.word IntDefaultHandler /* ADC Sequence 0 */
.word IntDefaultHandler /* ADC Sequence 1 */
.word IntDefaultHandler /* ADC Sequence 2 */
.word IntDefaultHandler /* ADC Sequence 3 */
.word IntDefaultHandler /* Watchdog */
.word IntDefaultHandler /* Timer 0A */
.word IntDefaultHandler /* Timer 0B */
.word IntDefaultHandler /* Timer 1A */
.word IntDefaultHandler /* Timer 1B */
.word IntDefaultHandler /* Timer 2A */
.word IntDefaultHandler /* Timer 2B */
.word IntDefaultHandler /* Comp 0 */
.word IntDefaultHandler /* Comp 1 */
.word IntDefaultHandler /* Comp 2 */
.word IntDefaultHandler /* System Control */
.word IntDefaultHandler /* Flash Control */
.word IntDefaultHandler /* GPIO Port F */
.word IntDefaultHandler /* GPIO Port G */
.word IntDefaultHandler /* GPIO Port H */
.word IntDefaultHandler /* UART2 Rx and Tx */
.word IntDefaultHandler /* SSI1 Rx and Tx */
.word IntDefaultHandler /* Timer 3 subtimer A */
.word IntDefaultHandler /* Timer 3 subtimer B */
.word IntDefaultHandler /* I2C1 Master and Slave */
.word IntDefaultHandler /* Quadrature Encoder 1 */
.word IntDefaultHandler /* CAN0 */
.word IntDefaultHandler /* CAN1 */
.word IntDefaultHandler /* CAN2 */
.word IntDefaultHandler /* Ethernet */
.word IntDefaultHandler /* Hibernate */
.word IntDefaultHandler /* USB0 */
.word IntDefaultHandler /* PWM Generator 3 */
.word IntDefaultHandler /* uDMA Software Transfer */
.word IntDefaultHandler /* uDMA Error */
.word IntDefaultHandler /* ADC1 Sequence 0 */
.word IntDefaultHandler /* ADC1 Sequence 1 */
.word IntDefaultHandler /* ADC1 Sequence 2 */
.word IntDefaultHandler /* ADC1 Sequence 3 */
.word IntDefaultHandler /* I2S0 */
.word IntDefaultHandler /* External Bus Interface 0 */
.word IntDefaultHandler /* GPIO Port J */
.thumb_func
start:
/* SYSCTL_RCGC2_R |= SYSCTL_RCGC2_GPIOF */
LDR R0, =SYSCTL_RCGC2_R
LDR R1, [R0]
ORR R1, #SYSCTL_RCGC2_GPIOF
STR R1, [R0]
NOP
NOP
/* GPIO_PORTF_AFSEL_R = 0x00000000 */
MOV R0, #0x00
LDR R1, =GPIO_PORTF_AFSEL_R
STR R0, [R1]
/* GPIO_PORTF_DIR_R = 0x00000008 */
MOV R0, #0x08
LDR R1, =GPIO_PORTF_DIR_R
STR R0, [R1]
/* GPIO_PORTF_DEN_R = 0x00000008 */
LDR R1, =GPIO_PORTF_DEN_R
STR R0, [R1]
/* GPIO_PORTF_DATA_R = 0x00000008 */
MOV R0, #0x08
LDR R1, =GPIO_PORTF_DATA_R
STR R0, [R1]
b .
.thumb_func
NmiSR:
b .
.thumb_func
FaultISR:
b .
.thumb_func
IntDefaultHandler:
b .
.end
start函数中实际就是几个寄存器(SysCtl和PortF)的赋值操作,代码中都有详细的注释,在这就不解释每一句汇编语法的含义了。
可以使用下面的四条命令来编译和调试程序,Makefile和Linker文件以后再实现:
# 编译
../../toolchain/CrossCompile/bin/arm-none-eabi-gcc -mcpu=cortex-m3 -c -mthumb -mthumb-interwork start.s
# 链接
../../toolchain/CrossCompile/bin/arm-none-eabi-ld -Ttext 0x0 -o start.out start.o
# objdump,如果需要调试可以使用该命令来反编译二进制文件
../../toolchain/CrossCompile/bin/arm-none-eabi-objdump -S start.out > start.dump
# ELF转换为bin文件,该bin文件就是我们下载需要的文件
../../toolchain/CrossCompile/bin/arm-none-eabi-objcopy -O binary start.out start.bin
最后在Windows上通过LM Flash Programmer软件将bin文件下载到目标板(Linux下的烧写工具以后再尝试),可以看见开发板上的LED灯被点亮。