三.ARM处理器
ARM处理器值得是使用ARM架构的处理器 1.ARM版本
ARM11之后,ARM的产品线开始细分,改用cortex命令,分为三个系列 -------- A R M,分别对应不同的场景。
cortex-A(Application):面向尖端应用,使用虚拟内存的操作系统用户使用
A8 ---> A9 ---> A15 A7 ---> A57 A53(64位) ---> A72 A78 A55 A34 X1
cortex-R(Realtime):针对可靠性,实时性要求高的场合
R4 ---> R5 ---> R8 ---> R52 R82
cortex-M(microchip):针对成本,功耗敏感的MCU领域,一般不运行操作系统
M3 ---> M0 M4 M7 ---> M55 M33 M23
GEC6818开发板使用三星s5p6818芯片,64位8核cortex-A53内核,指令集版本ARMv8-A
GEC-M4开发板使用sytm32f407zet6芯片,32位cortex-M4内核,指令集版本ARMv7-M
2.ARM指令集分类(状态)
大部分ARM处理器支持ARM指令集和THUMB指令集
ARM指令集:32bits(64bits),每条指令占用32位存储空间
THUMB指令集:16bits,每条指令占用16位存储空间
ARM处理器有两种状态:ARM状态执行ARM指令,THUMB状态执行THUMB指令。
3.ARM处理器的工作模式
经典ARM处理器有7种模式,cortex-A系列有8种
User:用户模式,一般情况下执行应用程序处于该模式
System:系统模式,有特权的用户模式
IRQ:中断模式,用于处理中断
FIQ:快中断模式,用于处理快中断
SVC:管理模式,系统复位/软中断进入该模式
Undef:未定义模式,通过未定义指令进入该模式
Abort:中止模式,指令/数据 存取异常时进入该模式
Monitor(A系列独有):安全监控模式,为安全扩展执行安全代码的模式
模式的分类:
除了User以外的模式都是特权模式
IRQ,FIQ,Undef,Abort属于异常模式
4.ARM的寄存器组织
ARM的寄存器(系统寄存器)是处理器留给用户的接口
处于处理器内部,没有地址,只有名字(编号)
寄存器用于存放正在使用的数据,寄存器中的数据可以在指令中直接使用 经典ARM处理器提供37个32位处理器,cortex-A系列有40个32位寄存器
寄存器分类:
R0~R7:未分组的通用寄存器,所有的模式下共用同一组
R8~R12:分组的通用寄存器,快中断模式下有单独的一组
R13:通用寄存器,通常用作堆栈指针(SP)
R14(LR):链接寄存器,用来保存程序返回的地址 当程序跳转(函数调用)时,可以将返回地址写入LR 以便程序返回时可以回到跳转指令的下一条指令
R15(PC):程序计数器,指向当前取指的指令 PC指向哪,程序就去哪里执行(顺序执行时PC自动后移)
函数调用时: 下一条语句的地址 ===> LR 调用函数的地址 ===> PC 函数返回时: LR ===> PC
CPSR:当前程序状态寄存器,表示当前处理器的状态信息
SPSR:备份程序状态寄存器,模式切换时备份CPSR CPSR的组成:
0~4位:模式位,表示处理器处于哪种模式 5位:状态位,表示处理器处于哪种状态
0表示ARM状态
1表示THUMB状态
6位:快中断禁止位,决定是否处理快中断,1禁止,0允许
7位:中断禁止位,决定是否处理中断,1禁止,0允许
28~31位:条件代码标志位,用来控制指令是否执行(也可以通过指令执行来修改)
if(a>b) a++; else b++;
N=1:运算结果小于0
Z=1:运算结果等于0
V=1:运算结果发生了溢出
C=1:运算结果发生了进位/借位
5.指令流水线
流水线技术可以大大提高指令的执行效率,实现流水线的前提是将一条指令的执行过程分为多个步骤。ARM7是3级流水,ARM9是5级流水。
3级流水:
将一条指令的执行分为三个步骤(三段独立的电路):取指 译码 执行 取指:根据PC值取对应内存地址上的指令到CPU 译码:指令译码器对指令进行识别,将指令翻译成具体的硬件动作 执行:执行指令的操作并将指令执行的结果写回寄存器
流水线的优势: 在使用流水线技术的情况下,执行一条指令平均需要1个时钟周期
n条指令需要 n+2/n ===> 1
四.ARM指令集
1.ARM汇编的分类
(1)指令
编译完成作为一条指令保存在存储器中,CPU执行指令完成某个操作
(2)伪指令
本身不是一条指令,编译器在编译时会使用指令来替换它
(3)伪操作
编译时不产生指令,也不占用存储空间,作用是辅助编译
2.指令的格式
{}{S} ,{,operand2}
//<>中的内容是必须的,{}中的内容是可选的
opcode - 操作码/指令助记符 cond - 执行条件(默认无条件执行) S - 执行结果是否影响条件代码标志位 Rd - 目标寄存器(指令执行的结果) Rn - 第一操作数寄存器 operand2 - 第二操作数
3.基础指令
(1)数据处理指令
MOV ------------------ 数据传送
LSL ------------------- 逻辑左移(<<)
mov r0,r1 //r0 = r1 mov r0, #1 //r0 = 1 mov r0,r1,lsl #2 //r0 = r1<<2 ADD ------------------ 加法
SUB ------------------ 减法
MUL ----------------- 乘法
add r0,r1,r2 //r0 = r1+r2 add r0,r1,#1 //r0 = r1+1 //add r0,#2,#3 错误
AND ---------------- 按位与
ORR ---------------- 按位或
BIC ------------------ 位清除
and r0,r1,r2 //r0 = r1 & r2 bic r0,r1,r2 //r0 = r1 & ~r2
CMP -------------- 比较
cmp r1,r2 //r1-r2,不保存结果,只影响条件代码标志位
(2)跳转指令
B -------------- 绝对跳转
BL ------------ 带返回的跳转
b 地址/标签 bl 地址/标签 //跳转时将返回地址写入LR寄存器
(3)内存访问指令
LDR -------------- 读内存数据到寄存器
STR ------------- 写寄存器数据到内存
ldr r0,[r1] //r0 = r1 str r0,[r1] // r1 = r0
4.伪指令
ldr伪指令 -------------- 长数据传送伪指令
mov r0,#0xffff //错误 ldr r0,=0xffff //正确
5.伪操作
.text -------------- 代码段
.data ------------- 数据段
.code 32/16 -------- arm指令/thumb指令
.global ----------- 声明为全局
.entry ------------ 汇编的入口
.end ------------- 汇编结束
将以下C语言用汇编实现
//定义寄存器 GPIOC14
volatile unsigned int* gpioc_alt0 = (unsigned int*)0xc001c020;
volatile unsigned int* gpioc_outenb = (unsigned int*)0xc001c004;
volatile unsigned int* gpioc_out = (unsigned int*)0xc001c000;
//1.初始化
//将gpioc_alt0的28~29位清0
*gpioc_alt0 &= ~(0x3 << 28);
*gpioc_alt0 |= (0x1 << 28);
//将gpioc_outenb的13位置1
*gpioc_outenb |= (0x1 << 14);
ARM汇编
ldr r1, =0xc001c020 @r1 = 0xc001c020
ldr r0, [r1] @r0 = *r1
mov r2, #3 @r2 = 3
bic r0, r0, r2, lsl #28 @r0 = r0 & ~(r2 << 28)
mov r2, #1 @r2 = 1
orr r0, r0, r2, lsl #28 @r0 = r0 | (r2 << 28)
str r0, [r1] @ * r1 = r0
//练习,将最后一条C语言代码翻译成汇编
ldr r1, =0xc001c004 @r1 = 0xc001c004
ldr r0, [r1] @r0 = *r1
mov r2, #1 @r2 = 1
orr r0, r0, r2, lsl #14 @r0 = r0 | (r2 << 14)
str r0, [r1] @ * r1 = r0
五.ARM汇编的使用
1.编写
汇编代码写在 .s/.S 为后缀的文件中
ARM汇编使用 @ 作为单行注释
2.编译
汇编源码的编译只需要汇编器和链接器
由于C语言编译器包含了汇编器,可以直接使用编译C语言的命令编译汇编
led的汇编实现:
.text @代码段
.code 32 @arm指令
.global led @入口声明为全局
led : @标签(下一条指令的地址), 初始化E13
ldr r1, =0xc001e020 @r1 = 0xc001e020
ldr r0, [r1] @r0 = *r1
mov r2, #3 @r2 = 3
bic r0, r0, r2, lsl #26 @r0 = r0 & ~(r2 << 26)
str r0, [r1] @ * r1 = r0
ldr r1, =0xc001e004 @r1 = 0xc001e004
ldr r0, [r1] @r0 = *r1
mov r2, #1 @r2 = 1
orr r0, r0, r2, lsl #13 @r0 = r0 | (r2 << 13)
str r0, [r1] @ * r1 = r0
led_loop :
@亮
ldr r1, =0xc001e000 @r1 = 0xc001e000
ldr r0, [r1] @r0 = *r1
mov r2, #1 @r2 = 1
bic r0, r0, r2, lsl #13 @r0 = r0 & ~(r2 << 13)
str r0, [r1] @ * r1 = r0
@延时
ldr r0, =0x5000000
bl delay @子程序调用
@灭
ldr r1, =0xc001e000 @r1 = 0xc001e000
ldr r0, [r1] @r0 = *r1
mov r2, #1 @r2 = 1
orr r0, r0, r2, lsl #13 @r0 = r0 | (r2 << 13)
str r0, [r1] @ * r1 = r0
@延时
ldr r0, =0x5000000
bl delay @子程序调用
b led_loop
@延时子程序,将一个数从初始值(r0), 每次循环减1, 直到结果为0, 子程序返回
delay :
subs r0, r0, #1 @r0 -= 1, 结果影响条件代码标志位
bne delay @r0为0, 不执行, r0不为0, 执行
mov pc, lr @pc = lr 子程序返回
练习:
使用ARM汇编实现另外三盏灯的闪烁
七.volatile
用于修饰易变的变量,防止编译器优化。
编译器在编译C语言源码时,遇到要使用的数据已经存在于寄存器中,并且没有被修改,编译器就会直接使用数据在寄存器中的备份,而不去原始地址再取一次,这种操作就叫编译器的优化。被volatile修饰的变量在使用时不允许使用在寄存器的备份,而必须去原始地址读取。
使用场景:
1.用于修饰硬件寄存器的地址 2.用于修饰多线程中的全局变量 3.用于修饰中断中的自动变量
以下代码:
/*volatile*/ int a = 1;
int b = 1;
int c = 1;
int d = 1;
void test()
{
b = a;
c = a;
d = a;
}
不加volatile: 加上volatile:
八.安装source insight
九.Linux内核 1.Linux内核子系统
1.任务调度 2.内存管理 3.设备驱动 4.网络协议 5.虚拟文件系统
2.Linux源码
Linux内核属于完全开源免费的操作系统内核,属于GNU计划,遵循GPL开源协议。内核源码在www.kernel.org下载。(Linus)
(1)内核版本
主版本号.次版本号.维护版本号
GEC6818开发板的内核版本 -------- 3.4.39 mainline ------------ 主线(最新测试版)
stable --------------- 稳定版
longterm ------------ 长期维护的版本
十.source insight的使用
1.安装
2.使用source insight创建内核工程
(1)在windows下解压内核源码
(2)在解压源代码目录新建一个工程文件夹 (3)新建工程
(4)指定工程名和工程文件保存路径
(5)指定工程源代码目录(内核源码解压目录)
(6)关闭项目文件选择框,打开文档选项菜单
修改C源代码文件的过滤器,添加 ---------- ; .S; .s
(7)为工程添加源代码
选中要添加的源代码目录,点击添加所有,勾上所有选项,点击确定
(8)同步工程文件,等待同步完成
练习:
完成内核工程的创建
作业:
使用ARM汇编实现蜂鸣器的驱动