储备知识(部分)
GNU:
GNU编译器套装,指一套编程语言编译器,以GPL及LGPL许可证所发行的自由软件,也是GNU计划的关键部分,也是GNU工具链的主要组成部分之一。GCC(特别是其中的C语言编译器)也常被认为是跨平台编译器的事实标准。1985年由理查德·马修·斯托曼开始发展,现在由自由软件基金会负责维护工作。
原名为GNU C语言编译器(GNU CCompiler),因为它原本只能处理C语言。GCC在发布后很快地得到扩展,变得可处理C++。之后也变得可处理Fortran、Pascal、Objective-C、Java、Ada,Go与其他语言。许多操作系统,包括许多类Unix系统,如Linux及BSD家族都采用GCC作为标准编译器。
GCC原本用C开发,后来因为LLVM、Clang的崛起,它更快地将开发语言转换为 C++ 。许多C的爱好者在对 C++ 一知半解的情况下主观认定 C++ 的性能一定会输给C,但是Ian Lance Taylor给出了不同的意见,并表明C++不但性能不输给C,而且能设计出更好,更容易维护的程序。
基本语句:
label: instuction @ comment
- label 标号,表示地址位置,有些指令前面可能会有标号,这样就可以通过这个标号得到指令的地址,标号也可以用来表示数据地址。注意 label 后面的“:”,任何以“:”结尾的标识符都会被识别为一个标号。
- instruction 指令,汇编指令或伪指令
- @ 符号,表示后边的是注释
- comment 是注释内容
指令、伪指令、伪操作等不能大小写混用
eg:
add:
MOVS R0,#0X12 @设置 R0 = 0X12
伪操作:
系统预定段名:
- .text 代码段
- .data 初始化的数据段
- .bss 未初始化的数据段
- .rodata 只读数据段
也可以使用.section伪操作来定义一个段
.section .testsection @定义一个 testsetcion 段
函数
函数名:
函数体
返回语句(非必须)
Cortex-A7 常用汇编指令
常用的指令有三个: MOV、MRS和MSR
指令 | 目的 | 源 | 描述 |
---|---|---|---|
MOV | R0 | R1 | 将R1里面的数据复制到R0种 |
MRS | R0 | CPSR | 将特殊寄存器CPSR里面的数据复制到R0里 |
MSR | CPSR | R1 | 将R1里面的数据复制到特殊寄存器CPSR里 |
储存器访问指令
ARM不能直接访问储存器 一般先将要配置的值写入到 Rx(x=0~12)寄存器中,然后借助存储器访问指令将 Rx 中的数据写入到ARM寄存器种。取出过程则相反。
指令 | 描述 |
---|---|
LDR Rd,[Rn,#offset] | 从存储器Rn+offset的位置读取数据存放到Rd中 |
STR Rd,[Rn,#offset] | 将Rd中的数据写入到存储器中的Rn+offset位置 |
原理分析
原理图如下:
只要把GPIO 3的输出电平拉低就可以亮。
流程
- 使能GPIO1时钟
将CCM_CCGR1的bit27、bit26两个控制位都置11。 - 设置GPIO1_IO03的服用功能。
复用寄存器“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03”的地址为0X020E0068,设置此寄存器,将GPIO1_IO03这个IO复用为GPIO功能 - 配置GPIO1_IO03
配置寄存器“IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03” - 设置GPIO
配置GPIO的工作模式 - 控制GPIO的输出电平
(寄存器具体配置参考数据手册)
编写代码
在Ubuntu系统下进行编写(不用再从Windows传到Linux上)
/*
* 裸机汇编电灯
*/
.global _start
// 从此函数开始执行此函数完成时钟使能、初始化等
_start:
// 1.使能所有时钟
ldr r0, =0x020c4068
ldr r1, =0xFFFFFFFF
str r1, [r0]
ldr r0, =0x020c406c
str r1, [r0]
ldr r0, =0x020c4070
str r1, [r0]
ldr r0, =0x020c4074
str r1, [r0]
ldr r0, =0x020c4078
str r1, [r0]
ldr r0, =0x020c407c
str r1, [r0]
ldr r0, =0x020c4080
str r1, [r0]
// 2.设置复用为GPIO1_IO03
ldr r0, =0x02E0068
ldr r1, =0x5
str r1,[r0]
// 3. 配置IO的属性
ldr r0, =0x020E02F4
ldr r1, =0x10B0
str r1, [r0]
// 4. 设置为输出
ldr r0, =0x0209C004
ldr r1, =0x0000008
str r1, [r0]
// 5. 打开LED0
ldr r0, =0x0209C000
ldr r1, =0
str r1, [r0]
// 死循环
loop:
b loop
编译代码
- 编译文件
使用交叉编译器将 .s文件编译为 .o文件。命令如下:
arm-linux-gnueabihf-gcc -g -c led.s -o led.o
- 链接文件
将工程所需的全部.o文件与地址链接起来生产 .elf文件。命令如下:
arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
- 格式转换
因为最终要烧录的文件格式为.bin文件(二进制文件),所以需要进行转换。 命令如下:
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
- 反汇编(检验)
检查上述过程是否正确。命令如下:
arm-linux-gnueabihf-objdump -D led.elf > led.dis
当文件多时可以通过make进行完成上述过程。
在工程目录下创建“Makefile”文件,并写入代码:
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:
代码烧写(SD卡烧录启动)
采用imxdownload进行下载SD卡。
其中遇到这样一个问题:
当正常读卡器插入电脑时, /dev/ sd* 文件中应该含有 /dev/sdd , /dev/sdd1。
然而,我的读卡器插上去却多了 /dev/sdb /dev/sdb1
sda是系统的第一块磁盘空间,sdb是第二块,sdb1是第二块磁盘的第一分区,也就是Ubuntu将读卡器当作一个磁盘或者说是U盘。
但这个并不影响往SD卡里写入数据,大概率是读卡器的问题。