首页 嵌入式学习笔记2
文章
取消

嵌入式学习笔记2

2022.8.17

objdump反汇编工具

作用:由编译好的elf格式的可执行程序发过来的到汇编源代码,arm-linux-objdump -D led.elf > led.elf.dis

-D表示反汇编,>左边为可执行程序,右边的是返回拜年生成的反汇编程序。

可能需要反汇编的原因

  1. 逆向破解
  2. 调试程序时,反汇编来帮助理解程序,尤其在理解链接脚本、链接地址等
  3. 有助于理解C语言与汇编语言之间的关系

2022.8.15

点亮LED–1

GPIO

GPIO:通用输入输出,即芯片的部分引脚,可以编程器控制它的工作模式或电压高低等。

通过设计时将LED接到GPIO上,得以让我们用编程控制GPIO的模式和输入输出值来控制LED的亮和灭。

GPJ0_3(端口号_引脚号)

控制LED->控制GPIO->设置GPIO的寄存器

GPIO相关的寄存器

通过查数据手册,得知有6个寄存器,其中GPJ0CON、GPJ0DAT两个较为重要。

在驱动LED点亮时,应将GPIO配置为output模式

  1. GPJ0CON:用来配置各引脚的工作模式;
  2. GPJ0DAT:当引脚配置为input/output时,寄存器的相应位和引脚的电平高低相对应。
  3. GPJ0PUD:控制引脚内部弱上拉、下拉
  4. GPJ0DRV:配置GPIO引脚的驱动能力
  5. GPJ0CONPDN
  6. GPJ0PUDPDN

点亮LED的步骤

  1. 控制GPJ0COn控制器,选中output模式
  2. 控制GPJ0DAT寄存器,相应的位设置为0

点亮LED–2

寄存器分析

GPJ0共有8个引脚,为GPJ0_0~GPJ0_7,所以GPJ0CON寄存器中共有8个引脚的工作模式,比如GPJ0_0对应的位为bit0~3。比如给bit0~4写入0b0001,即将GPJ0_0调为输出模式。

先决条件

  1. 硬件接法和引脚:GPJ0_3,GPJ0_4,GPJ0_5
  2. 内存地址:GPJ0CON(0xE0200240),GPJ0DAT(0xE0200244)
  3. 工程管理(Makefile)

总结

  1. 软件通过控制相应设备的寄存器来控制硬件;
  2. 要使用操作手册来查找硬件对应的寄存器及其内存地址,从而使用代码来更改其相对应的状态。

点亮LED–3

一些细节

  1. 使用宏定义来定义内存地址,后面在进行调用。
  2. 用b . 来实现死循环
  3. 用.global把_start链接属性改为外部

只点亮一颗LED

不用改GPJ0CON的属性,修改GPJ0DAT相应的LED所在引脚为1即可。但是存在缺陷:需要人为计算的设定值。

GPJ0DAT修改为0x28(00101000)

解决方案:在程序中使用位运算,让代码自动计算特征值。

常用的位运算

位与&、位或|、位非(取反~)、移位(左移« 右移»)

0x28还可以表达为((1«3)(1«5))

1«3等同于0b1000,1«5等同于0b100000,两者位或等同于0b101000

点亮LED–4

闪烁原理分析

亮、延时、灭、延时、亮 …

延时函数:使用没有意义的代码消耗时间。

注意点

  1. 函数要写在死循环后,类似于将函数写在main函数外
  2. 调用函数用bl指令,子函数最后用mov pc, lr来返回
  3. 延时函数原理是通过两个数的相比较,将其中一个数-1来回比较直至相等,相等时跳出函数

流水灯效果

流水灯实现的原理

LED1亮,延时;LED2亮,延时;LED3亮,延时。

恰当使用位取反操作符来轻松实现LED点亮流水灯效果

2022.8.2

ARM汇编伪指令

伪指令的意义

伪指令不是指令,编译后不会生成机器码,意义在于指导编译过程,与具体的编译器相关。

伪指令中的一些符号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@  #  //  /* */ 注释
: 标号,用来标记这行指令的地址
. 当前指令的地址
# $ 加上立即数,表示这是个立即数

//常用伪指令
.global _start 将_start声明为外部链接属性,可以在别的文件访问
.section .text 指定当前段为代码段
.ascii .byte .short .long .word 定义变量类型
.quad .float .string 定义数据
.align 4 以16(2^4)字节对齐
.balignl 16 0xabcdefgh 以16字节对齐填充
@ b表示位填充,align表示位对齐,l表示long,以四字节为单位填充,16表示16字节对齐
.equ 类似于宏定义

//偶尔使用的伪指令
.end 表示文件结束
.include 头文件包含
.arm /.code32 以下程序为ARM指令
.thumb /.code16 以下程序为thumb指令

//最重要的伪指令
ldr  大范围的地址加载指令,一般都是优先用此伪指令
adr  小范围的地址加载指令
adrl 中等范围的地址加载指令
nop  空操作

ldr r0, #0xff 真实指令,要考虑是否为合法立即数
ldr r0, =0xff 伪指令,不需要考虑数字是否为合法立即数

adr总是以PC为基准来表示地址,指令本身和运行地址有关,可以用来检测程序运行位置

ldr加载的地址和链接时给的地址有关,由链接脚本决定,可以结合adr一起判断当前程序是否在链接时指定的地址运行。

ARM汇编指令4

多寄存器访问指令

ldm、stm:为了提高批量写入、读取的效率

1
2
3
stmia sp, {r0 - r12}
@将r0存入sp指向的内存处,然后地址+4,存入r1,再+4,存入r2,直至存至r12
@一个访存周期同时完成13个寄存器的读写

不同的后缀

  1. ia:increase after,先传输,再地址+4

  2. ib: increase before,先地址+4,再传输

  3. da、db:同上,但是地址为-4

  4. fd:full decrease:满递减堆栈

  5. ed:empty decrease:空递减堆栈

  6. fa、ea:同上,但是为递增堆栈

以理解为主,不用强行记忆,stmia、stmfd(满减栈)常用。操作时用相同的后缀就不会出错。

堆就是堆,栈就是栈。

  1. 空栈:栈指针指向,空位,每次存入时直接存入,然后栈指针移动一格,取出时需先移动一格后才能取出。

  2. 满栈:栈指针指向栈中的最后一格数据,每次存入时需要先移动栈指针一格后再存入,取出时直接取出,再移动栈指针。

  3. 增栈:栈指着移动时向地址增加的方向移动的栈。

  4. 减栈:栈指着移动时向地址减少的方向移动的栈。

!的作用

r0!:表示r0的值在ldm过程中发生的增加或减少,最后写回r0中,即该命令会改变r0的值

^的作用

{r0 - r6, pc}^:表示在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式返回

2022.7.28

ARM汇编指令集3

数据处理指令

  • 数据传输指令:mov,mxn
1
2
3
4
5
6
7
8
mov(move) 
mov r1, r2 @两个寄存器之间数据传递
mov r1, #0xff @将立即数赋值给寄存器
@mvn和mov用法一样,区别是mov是原封不动的传递,但mvn是按位取反后传递
@按位取反的含义:
@假如r1 = 0xff(0x000000ff)
@mov r0, r1后,r0 = 0xff
@mxn r0, r1后,r0 = 0xffffff00
  • 算术指令:add,sub,rsb,adc,sbc,rsc

  • 逻辑指令:and,orr,eor,bic

1
2
3
4
5
6
and @逻辑与
orr @逻辑或
eor @逻辑异或
bic @位清除指令
bic r0,r1,#0x1f  @将r1中的数的bit0到bit4清零后赋值给r0
@ 0xlf=0x0000001f=0x0000····11111(即bit0~bit1为1,将该几位数清零后赋值)
  • 比较指令:cmp,cmn,tst,teq
1
2
3
4
5
cmp r0, r1 @等价于 sub r2,r0,r1(r2 = r0 - r1),比较是否相等
cmn r0, r1 @等价于add r0,r1,比较是否互为补数
tst @测试某些位是否等于1
tst r0, #0xf @测试bit0~bit3是否全为0
teq @测试等价

用来比较两个寄存器中的数,比较指令不用后加s后缀就可以影响cpsr中的标志位。

  • 乘法指令:mvl,mla,umull,umlal,smull,smlal

  • 前导零计数:clz(算数的前面有所少个0)

cpsr访问指令

1
2
3
4
5
6
mrs @用来读cpsr/spsr
msr @用来写cpsr/spsr
mrs r0,cpsr @将cpsr写入r0
msr cpsr,r0 @将r0再写入cpsr
cpsr_c @cpsr的bit0~bit7,控制位
@cpsr是程序状态寄存器,只有1个,spsr用来保存普通模式下的cpsr,有5个,在5种异常模式下。

跳转指令

1
2
3
b @直接跳转,后续不打算再返回
bl @branch and link,跳转前把返回地址放入lr中,以便后续返回
bx @跳转同时切换到arm模式,用于异常处理的切换

访存指令

1
2
3
4
ldr/str @单字b/半字h/字节读写
ldm/stm @多字批量访问
swp r1,r2,[r0] @内存与寄存器互换,把r0的内存地址读到r1,将r2写到r0的内存地址
swp r1,r1,[r0] @r0的内存地址读到r1,将r1原来的内容写入r0的内存地址

立即数

数字的标志是#,有合法立即数和非法立即数之分。

合法立即数:经过任意位数的位移之后,非0部分可以用八位表示,例如0x000001ff为非法。

软中断指令

swi:用来实现操作系统中系统调用

ARM汇编指令集4

协处理器cp15

协处理器:SoC内部的另一个处理核心,理论上支持16个,但一般只实现其中的cp15.

协处理器和MMU(虚拟地址映射)、cache(缓存)、TLB有关。

1
2
3
4
5
6
7
mrc p15, 0 ,r0 ,c1, c0, 0
@r0:cpu中的寄存器
@c1,c0:协处理器中的寄存器
@对于cp15,永远为0(第二位)
@第四位:cp15的寄存器,合法值为c0~c15
@第五位:cp15的寄存器,一般为c0
@第六位:

mcr&mrc:读取&写入cp15中的寄存器

协处理器的学习要点

  1. 不用深究,能搞懂代码在干什么就可以。

  2. 只看一般用法,不用细分参数细节。

  3. 关键在于理解,不在于记忆。

本文由作者按照 CC BY 4.0 进行授权

嵌入式学习笔记1

嵌入式学习笔记3