为什么使用Cortex-A汇编
Alpah开发板原理硬件分析
LED0为低电平,DS0就会亮,再看一下LED0接到哪里
由图可知,LED0接到GPIO 3,可以查找参考手册了
stm32初始化流程
I.MX6ULL初始化流程
这七个寄存器控制6ULL所有时钟外设使能
可以看到11就是任何模式下都使能,除了STOP
可以把这七个时钟都使能一遍(0xFFFFFFFF)(32位)
看一下前四位,是复用为什么管脚
由图中可以看到,需要设为GPIO的是0101
都是32位,每一个位代表一个GPIO的输入输出
综上,对于点灯,就是GDIR的bit3设置为输出模式,DR控制高低电平。
作用:
我们使用的GCC交叉编译器,汇编需要符合GNU语法,GNU汇编语法适用于所有的架构,每条语句有三个可选地方:
label: instruction @ comment
label即标号,表示地址位置,有些指令前面可能会有标号,标号也可以用来表示数据地址,后面以:结尾。任何以:结尾的标志符都是一个标号
instruction是指令,也就是汇编指令或者伪指令
@标号,表示后面的是注释,comment是注释内容。
比如:
add:
MOVS R0,#0X12 @设置R0 = 0X12
add就是标号,下面就是指令和注释
会变系统预定义了一些段名:
我们也可以自己使用.section来定义一个段,每个段以段名开始,以下一段名或者文件结尾结束
.section .testsection @定义一个testsection段
汇编程序的默认入口标号是_start,也可以在链接脚本中ENTRY来指明入口点,比如
.global _start
_start:
ldr r0,=0x12 @r0 = 0x12
.global是伪操作,表示_start是一个全局标号,类似于C语言里面的全局变量一样,常见的伪操作有:
GNU也支持函数:
函数名称:
函数体
返回语句
例如:
/*未定义中断*/
Undefined_Handler:
ldr r0,=Undefined_Handler
bx r0 @返回指令
/*SVC*/
SVC_Handler:
ldr r0,=SVC_Handler
bx r0
常见的操作有:
MOV指令
将R1里面的数据复制到R0中,或者一个立即数(常数)复制到R0中
MOV R0 R1
MOV R0 #0x12 @立即数前面加一个#
MRS指令
用于将特殊寄存器(如CPSR和SPSR)中的数据传递到通用寄存器,即要读取通用寄存器里面的数据必须用MRS
MRS R0,CPSR @将CPSR里面的数据读取到R0中
MSR指令
用于将通用寄存器的数据传递给特殊寄存器(如CPSR和SPSR)
MSR CPSR,R0 @将CPSR里面的数据读取到R0中
ARM中不能直接访问存储器,比如RAM中的数据,我们用汇编来配置寄存器的时候需要借助存储器访问指令,一般先写到通用寄存器中,然后借助存储器访问指令将通用寄存器中的数据写入到寄存器中,读取寄存器也是一样。
LDR
LDR主要用于存储加载数据到寄存器Rx中,LDR也可以将一个立即数加载到寄存器Rx中, LDR加载立即数的时候要用等于不是#
LDR R0, =0x0209C004 @将存储器地址加载到R0中,即R0 = 0x0209C004
LDR R1, [R0] @读取地址的数据到R1中
STR
LDR是从存储器读取数据,STR是将数据写入存储器中。
LDR R0, =0x0209C004
LDR R1, =0x20000002 @R1 = 0x20000002
STR R1, [R0] @将R1中的值写入到R0保存的地址中
如果按照字节半字就在后面加上B或者H
例子 a = b, a的地址为0x20, b为0x30
LDR R0, =0x30
LDR R1, [R0]
LDR R0, =0x20
STR R1, [R0]
通常在A函数中调用B函数,当B函数执行完之后继续A,所以需要保存现场和恢复现场,现场就是寄存器的值,用到POP和PUSH,可以一次性操作多个存储器,他们利用当前的栈指针来生成地址
如果当前SP指针指向0x80000000
PUSH {R0~R3, R12}
此时SP就是原来-4*5 0x7FFFFFEC。
出栈也是从栈顶出栈,原理类似。
也可以使用STMFD SP!和LDMFD SP!代替上面的指令。
STM和LDM对应STR和LDR,就是多寄存器的,多个连续数据。
FD是满递减的意思。 地址是向下增长的,编号小对应低地址,编号大对应高地址。
B
最简单的跳转指令,B指令会将PC寄存器的值设置为跳转目标地址,一旦执行B指令,ARM就会立即跳转到指定的目标地址。如果调用的函数不会再返回到原来执行地方,可以用B指令
_start:
ldr sp,=0x80200000
b main
常用的初始化C语言环境
BL
BL在跳转之前会在寄存器LR中保存当前PC的值,所以可以通过将LR的值重新加载到PC寄存器中来恢复现场,即保护现场,跳转到C中处理中断,再恢复现场。
push {r0, r1}
cps #0x13 @进入SVC模式,允许其他中断再次进去
bl system_irqhandler @加载函数到r2中
cps #0x12 @进入IRQ模式
pop {r0, r1}
str r0,[r1, #0x10] @写EDIR
算数运算
逻辑运算
除了R0到R14,还有R15(PC)程序计数器,CPSR当前状态寄存器和SPSR备用状态寄存器。R13是sp,R14是LR寄存器。
LR寄存器
恢复现场:
MOV PC, LR @将LR保存的值传递给PC
或者:
PUSH {LR} @压栈
POP {PC} @弹栈,并将值赋给PC
当异常发生以后,该异常模式对应的 R14 寄存器被设置成该异常模式将要返回的地址,R14 也可以当作普通寄存器使用。
程序计数器PC
pc = 当前执行位置+8个字节
解释一下:
.global _start
_start:
@ 使能所有时钟
@ CCM_CCGR0的内存地址为20C_4068
ldr r0,=0x020c4068 @取得时钟0的地址
ldr r1,=0xffffffff @要赋的值
str r1, [r0] @赋值
ldr r0,=0x020c406c @取得时钟1的地址
@或者 add r0, r0, #0x04
str r1, [r0] @赋值
ldr r0,=0x020c4070 @取得时钟2的地址
str r1, [r0] @赋值
ldr r0,=0x020c4074 @取得时钟3的地址
str r1, [r0] @赋值
ldr r0,=0x020c4078 @取得时钟4的地址
str r1, [r0] @赋值
ldr r0,=0x020c407c @取得时钟5的地址
str r1, [r0] @赋值
ldr r0,=0x020c4080 @取得时钟6的地址
str r1, [r0] @赋值
/* 配置IO复用 */ @ ctrl+shift+A
ldr r0,=0x020e0068 @MUX复用地址
ldr r1,=0x5 @要赋的值,16进制,0101是二进制
str r1, [r0] @赋值
/* 电气属性
* bit0 : 0 低速率
* bit5-3 : 110 R0/6驱动能力
* bit7-6 : 10 100MHz速度
* bit11 : 0 关闭开路输出
* bit12 : 1 使能上拉保持
* bit13 : 0 保持
* bit15-14 : 00 默认100K下拉
* bit16 : 0 关闭hys
* 综上:
* 0000_0000_0000_0000_0001_0000_1011_0000
* 0x000010C0
*/
ldr r0,=0x020e02f4 @电气属性地址
ldr r1,=0x10b0
str r1, [r0]
/* 设置GPIO */
@ GPIO1_GDIR的bit3
ldr r0,=0x0209c004 @GDIR
ldr r1,=0x8 @8是1000, 输入输出设置
str r1, [r0]
/* 打开LED GPIO1_IO03为0 */
ldr r0,=0x0209c000 @DR
ldr r1,=0xfffffff7 @默认全是1,要配成0
str r1, [r0]
/* 程序执行到这的时候,不知道会有什么事情,所以要有个死循环 */
loop:
b loop
使用arm-linux-gnueabihf-gcc把所有的.s文件编译成点o
arm-linux-gnueabihf-gcc -g -c led.s -o led.o
-g产生调试信息,
-c是选择编译源文件,并且不链接
使用arm-linux-gnueabihf-ld将所有的.o文件链接为elf格式的可执行文件
把所有的o链接到一起,要有起始位置。本实验链接的时候要链接起始地址,链接起始地址就是代码运行的起始地址,或者保存的起始地址(一定是运行的,一般运行的和保存的是一个地址)
对于6ULL,链接起始地址应该指向RAM地址。
RAM分为内部RAM和外部RAM,DDR。内部为0x90000-0x91FFFFF,外部DDR中, 对于开发板0x80000000-0xA0000000(512MB)。
起始地址为0x8780000,后面UBOOT也是这个,所以要统一起来。要使用DDR必须要初始化DDR,bin文件不能直接烧写到mmc等外置存储中启动运行,需要添加一个头部,头部包含了DDR初始化参数。bootrom会从SD卡,EMMC等外置存储中读取头部信息,初始化DDr,并且将bin拷贝到链接起始地址,bin的运行地址一定要和链接起始地址一致。位置无关代码除外。
arm-linux-gnueabihf-ld -Ttext 0x87800000 led.o -o led.elf
使用arm-linux-gnueabihf-objcopy将elf文件转化为bin文件
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
使用arm-linux-gnueabihf-objdump elf转为汇编,反汇编
arm-linux-gnueabihf-objdump -D led.elf > led.dis
将bin文件烧写到mmc中。
在Ubuntu下烧写SD卡:
插入U盘链接
将bin文件烧写到SD卡绝对地址上,对于I.MX而言必须在bin文件添加头部,使用imxdownload
两次插拔可知
烧在sdb1上
先给imx可执行权限
烧写成功
添加了头部的.bin文件就是load.imx
led.bin:led.s
arm-linux-gnueabihf-gcc -g -c led.s -o led.o
arm-linux-gnueabihf-ld -Ttext 0x87800000 led.o -o led.elf
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
arm-linux-gnueabihf-objdump -D led.elf > led.dis
clean:
rm -rf *.o *.bin *.elf *.dis
chmod 777 imxdownload
./imxdownload led.bin /dev/sdb
手机扫一扫
移动阅读更方便
你可能感兴趣的文章