DAY 1
很重要
【0】:硬件是躯体,软件是灵魂。
【1】逻辑1和0 :数字电路中用高、低电平来表示逻辑1和0
【2】二极管具有单向导电性 :正向导通,反向截止
【3】三极管具有开关特性
NPN型:电压方向C->E 基极是高电平时CE导通
PNP型:电压方向E->C 基极是低电平时CE导通
【4】电路图
1)如何让查找一个元器件:
1)搜索丝印
2)通过目录
2)电路图中网络标号相同的节点在电气上是连接在一块的
【5】位操作
unsigned int a; a为32位无符号整型 将a的第三位置1 其他位不变
******** ******** ******** ********
******** ******** ******** ****1***
00000000 00000000 00000000 00001000
a = a | 0x00000008;
a = a | (1 << 3);
unsigned int a; a为32位无符号整型 将a的第三位置0 其他位不变
******** ******** ******** ********
******** ******** ******** ****0***
11111111 11111111 11111111 11110111
a = a & 0xFFFFFFF7;
a = a & (~(1 << 3));
【6】位域操作
unsigned int a; a为32位无符号整型 将a的第[7:4]改为0101 其他位不变
******** ******** ******** ********
******** ******** ******** 0101****
00000000 00000000 00000000 01010000
1.先清零
a = a & ~(0xF << 4);
2.再置位
a = a | (0x5 << 4);
=> a = a & ~(0xF << 4) | (0x5 << 4);
练习:
unsigned int a; a为32位无符号整型 将a的第[28:23]改为101010 其他位不变
***10101 0******* ******** ********
a = a & (~(0x3F << 23)) | (0x2A << 23);
【7】计算机的组成:
计算机由输入设备、输出设备、存储器、运算器、控制器五部分组成,
其中运算器、控制器共同组成了CPU
1)输入设备:
输入设备的任务是把人们编好的程序和原始数据送到计算机中去
并且将它们转换成计算机内部所能识别和接受的信息方式
常见的有键盘、鼠标、扫描仪等
2)输出设备:
输出设备的任务是将计算机的处理结果以人或其他设备所能接受的形式送出计算机
常用的有显示器、打印机、绘图仪等。
3)存储器:
存储器是用来存放程序和数据的部件,也是计算机能够实现“存储程序控制”的基础
三级存储结构:
a.高速缓存:
速度最快、价格最贵、容量最小、断电数据丢失、cpu可直接访问
存放当前正在执行的程序中的活跃部分,以便快速地向CPU提供指令和数据
b.主存储器:
速度、价格、容量介于高速缓存与辅助存储之间,数据断电丢失
cpu可以直接访问,用来存放当前正在执行的程序和数据
c.辅助存储器:
速度最慢、价格最低、容量最大、断电数据不丢失、cpu不可直接访问
用来存放暂时不参与运行的程序和数据,需要时再传送到主存
地址空间:一个处理器能够访问的内存空间的大小
N位地址总线的处理器能访问的空间是2的N次方
4)运算器:
CPU对信息处理和运算的部件,常进行算术运算和逻辑运算,其核心是算术逻辑单元ALU
5)控制器:是整个计算机的指挥中心
一条指令(机器码)的执行通常分为三个阶段:
取址:根据PC中的值读取对应内存地址中的指令到CPU
译码:指令译码器对指令进行识别,将指令(机器码)翻译成具体的运算操作
执行:执行指令并将结果写回寄存器
每次完成取址后处理器内部的PC值自增使其指向下一条指令的地址
【8】总线:
能为多个部件服务的公共信息传送线路,能分时地发送与接收各部件的信息
总线按照其传递的信息的类型可以分为数据总线、地址总线、控制总线
DMA:直接内存操作(Direct Memory Access)
使用DMA总线可以不通过CPU直接在内存和内存之间进行数据交换
【9】RISC(Reduced Instruction Set Computer)
RISC:精简指令集处理器,
RISC结构简单,选取了使用频率高的简单指令,指令长度固定,多为单周期指令
在功耗、体积、价格等方面有很大优势,多用于嵌入式领域
CISC:复杂指令集处理器
侧重于硬件执行指令的功能性,CISC指令及处理器的硬件结构复杂
CISC指令复杂,指令长度与周期不固定,在处理能力上有优势
编译原理:
处理器能够识别的语言是机器码,一个处理器能够识别哪些机器码是由处理器的硬件决定的
所以不同的机器码代表不同的运算,同样不同的处理器机器码是不兼容的即不可以移植
汇编语言是机器码的符号化,即汇编就是使用符号代替机器码,所以不同的处理器汇编也不一样,汇编语言不可以移植
C语言编译的时候我们可以使用不同的编译器将C编译成不同的汇编和机器码,所以C可以不依赖硬件
【10】SoC:(System on Chip)片上系统
将一个系统中所需要的全部部件集成在一个芯片上,在体积、功耗、价格上有很大优势
【11】ARM11以后的产品改用Cortex命名,并分成A、R、M三个分支,旨在为各种不同的市场提供服务
Cortex-A: 面向尖端的基于虚拟内存的操作系统和用户应用
Cortex-R: 实时处理器为要求可靠性、容错功能和实时响应的嵌入式系统提供高性能解决方案
Cortex-M: 针对成本和功耗敏感的MCU和终端应用,一般不跑操作系统
【12】ARM Cortex-A9处理器为32位架构,能够单次对32位数据进行处理
【13】ARM 约定:
Byte:8bits(1byte)
Halfword:16bits(2byte)
Word:32bits(4byte)
【14】大部分ARM支持ARM指令集与Thumb指令集
ARM指令集32bit,即每条指令占用32为的存储空间
Thumb指令集16bit
【15】当处理器运行ARM指令时
所有指令必须word对齐
pc值由其[31:2]决定,[1:0]位未定义,因为指令存储的起始地址必须为4的整数倍
【16】ARM中指令本身是多少位在内存存储时就应该多少位对其
【17】多字节数据的存储
小端对齐:低地址放低有效位,高地址放高有效位
大端对齐:低地址放高有效位,高地址放低有效位
ARM默认是小端对其
【18】ARM Cortex-A处理器有8个基本工作模式:
User: 非特权模式,一般在执行上层的应用程序时处理器处于该模式
FIQ: 当一个高优先级的中断产生时处理器将自动进入这种模式
IRQ: 当一个低优先级(normal) 中断产生时将会进入这种模式
SVC: 当复位或软中断指令执行时将会进入这种模式
Abort: 当存取异常时将会进入这种模式
Undef: 当执行未定义指令时会进入这种模式
System: 使用和User模式相同寄存器集的特权模式
Cortex-A处理器特有模式:
Monitor:为了安全而扩展出的用于执行安全监控代码的模式
模式的分类:
1)除User模式外其他七种模式都是特权模式 - 权限比较高
2)FIQ、IRQ、SVC、Abort、Undef异常模式 - 产生异常时进入这几种模式
特定的模式下执行特定的代码,完成特定的功能,拥有特定的权限
【19】寄存器:处理器内部的存储器
处理器留给用户的接口
寄存器处于CPU内部,没有地址
每个模式只能使用本模式下可以使用的寄存器,一个模式下特有的寄存器其他模式下不可访问
1)r13 (stack pointer, sp)
栈指针,指向当前模式下的栈顶,保存的是栈顶的地址
2)r14 (link register, lr)
连接寄存器,当程序跳转时处理器自动将当前指令下一条指令的地址保存到r14,
程序返回时将LR寄存器的值赋给PC就实现了程序的返回
3)r15 (program counter, pc)
程序计数器,存储了当前取址指令的地址
DAY 2
【19】寄存器:处理器内部的存储器
处理器留给用户的接口
寄存器处于CPU内部,没有地址
每个模式只能使用本模式下可以使用的寄存器,一个模式下特有的寄存器其他模式下不可访问
1)r13 (stack pointer, sp)
栈指针,指向当前模式下的栈顶,保存的是栈顶的地址
2)r14 (link register, lr)
连接寄存器,当程序跳转时处理器自动将当前指令下一条指令的地址保存到r14,
程序返回时将LR寄存器的值赋给PC就实现了程序的返回
3)r15 (program counter, pc)
程序计数器,存储了当前取址指令的地址
4)CPSR(current program status register)当前程序状态寄存器
bit[4:0]处理器模式位:
决定当前处理器所处的模式
中断禁止位:
I=1:禁止IRQ中断
F=1:禁止FIQ中断
状态位:
T=0:处理器处于ARM状态
T=1:处理器处于Thumb状态
条件位:
N:ALU中产生了负数的结果
Z:ALU中运算结果产生了0
C:加法时产生了进位C自动置1否则为0,减法时产生了借位C位自动置0否则为1
V:对于有符号数符号位发生变化时自动置1
[31:24]条件域F表示[23:16]状态域S表示[15:8]预留域X表示[8:0]控制域C表示
【20】ARM Cortex-A处理器的异常处理
当处理器遇到异常后会暂停当前的程序转而去执行异常处理程序对异常进行处理
处理完成后返回到被异常打断的代码的代码处继续执行
1)异常源:导致产生异常的事件
FIQ、IRQ、Reset、软中断、DataAbort、PrefetchAbort、Undef
2)异常产生时ARM处理器的动作(自动完成):
1.拷贝CPSR到SPSR_
2.修改CPSR:
a.改变处理器状态进入ARM状态
b.改变处理器模式进入相应的异常模式
c.设置中断禁止位禁止相应中断(如果需要)
3.保存返回地址到LR_LR=PC-4;
4.设置PC为相应的异常向量(跳转到异常向量表中对应的位置)
3)异常处理完成后,返回时需要(用户自己完成)
1.从SPSR_恢复CPSR,使处理器恢复到异常前的状态
2.从LR_恢复PC,程序返回到被异常打断的位置继续执行
注:CPSR中保存的永远是当前程序运行状态,SPSR只是异常时对CPSR进行备份
4)7种异常源,异常发生时进入对应的异常模式
异常源: FIQ IRQ Reset/软中断 DataAbort/PrefetchAbort Undef
异常模式:FIQ IRQ SVC Abort Undef
5)异常向量表
异常发生时,ARM完成保护现场后自动将PC指向异常向量中对应的位置
异常向量表是处于内存中的一段空间
在异常向量表中为每个异常源分配了四个字节的存储空间
一般情况下我们在异常向量表对应的位置写一条跳转指令使其跳转到异常处理程序入口
6)异常响应优先级
Reset、Data Abort、FIQ、IRQ、Prefetch Abort、SWI、Undefined instruction
7)FIQ的响应速度比IRQ快的原因:
a.FIQ位于异常向量表的最末
可以直接把异常处理程序写在异常向量表之后,省去了程序跳转的时间
b.FIQ模式有5个私有寄存器 (r8-r12)
执行中断处理程序前无需压栈保存现场,可直接处理中断
c.FIQ的优先级高于IRQ
当两个异常同时发生时先响应FIQ
FIQ可以中断RIQ,但RIQ不能中断FIQ
【21】指令流水线
指令执行过程中取址、译码、执行三个阶段使用的硬件电路相互独立
当对N地址中的指令进行取址时译码器可以对N-4地址的指令进行译码
运算器可以对N-8地址中的指令进行执行,从而形成了三级流水线结构
这种结构降低了指令执行的平均周期。
ARM处理器中当前取指的指令由PC决定,所以当前执行的指令的地址是PC-8
不管多少级流水线PC指向的永远是当前取址的指令,当前执行的指令是PC-8地址处的指令
area reset, code
;定义一个代码段叫reset
;/*汇编文件中的符号*/
;1.指令:编译完成后作为一条指令存储在内存单元当中,CPU执行时能完成一定的操作
;2.伪操作:不会生成代码也不会占用内存,告诉编译器怎样编译
;3.伪指令:本身不是指令,编译器在编译的时候将其替换成CPU能识别的指令
;/*指令*/
;1.数据操作指令:对数据进行逻辑、数学等运算与处理
;2.跳转指令:实现程序的跳转,实质是修改PC
;3.Load/Store:对内存的读写操作
;4.状态寄存器传送指令:对CPSR进行读写操作
;5.异常中断产生指令:触发软中断
;6.协处理器指令:操作协处理器的指令
entry
;汇编的入口
;数据搬移指令
;mov r1, #1
;r1 = 1
;mov r2, r1
;r2 = r1
;mvn r1, #0xFF
;r1 = ~0xFF
;立即数
;mov r1, #0xFF000000
;立即数0xFF000000由0xFF循环右移(0x4*2)次得到 所以机器码中【7:0】为0xFF 【11:8】为0x4
;立即数的本质是包含在指令当中的数
;在mov指令中给立即数留了12位的存储空间,所以立即数只包含了2的12次方个
;一个立即数是由一个八位的数循环右移偶数次得到的
;mov r1, #0xFFFFFFFF
;编译器将其进行了指令的替换
;加法指令
;mov r1, #1
;mov r2, #2
;add r3, r1, r2
;r3 = r1 + r2
;add r3, r1, #2
;r3 = r1 + 2
;数据运算指令的基本格式
;《操作码》《目标寄存器》《第一操作寄存器》《第二操作数》
;默认情况下,数据处理指令不影响标志位,可以选择通过添加后缀“S”来影响标志位。
;mov r1, #-1
;adds r2, r1, #0
;加法产生了进位 即运算结果在目标寄存器存储不下
;mov r1, #0xFFFFFFFE
;adds r2, r1, #5
;带进位的加法指令
;两个64位数相加,第一个数的低32位放在r0高32位放在r1,第二个数的低32位放在r2高32位放在r3
;编写代码实现两个64位数的和,结果的低32位放在r4高32位放在r5
;mov r0, #0xFFFFFFFE
;mov r1, #1
;mov r2, #0x5
;mov r3, #1
;adds r4, r0, r2
;adc r5, r1, r3
;实质:r5 = r1 + r3 + 'C'
;减法指令
;mov r1, #5
;mov r2, #3
;subs r3, r1, r2
;r3 = r1 - r2
;带借位的减法指令
;mov r0, #5
;mov r1, #3
;mov r2, #6
;mov r3, #1
;subs r4, r0, r2
;sbc r5, r1, r3
;本质:r5 = r1 - r3 - !c
;逆向减法
;mov r1, #5
;rsb r2, r1, #8
;r2 = 8 - r1
;乘法指令
;mov r1, #3
;mov r2, #5
;mul r3, r1, r2
;乘法指令不能使用立即数
;逻辑与
;mov r0, #0xf0
;mov r1, #0x0f
;and r2, r0, r1
;r2 = r0 & r1
;逻辑或运算
;mov r0, #0xf0
;mov r1, #0x0f
;orr r2, r0, r1
;r2 = r0 | r1
;逻辑异或运算
;mov r0, #0xff
;mov r1, #0xff
;eor r2, r1, r0
;r2 = r1 eor r0
;位清零指令
;mov r0, #0xff
;bic r1, r0, #0xf
;比较指令
;mov r1, #1
;mov r2, #1
;cmp r1, r2
;没有目标寄存器
;比较的结果在Z位显示
;指令的实质是减法指令
;移位器
;mov r1, #0xFF
;lsl r1, r1, #4
;逻辑左移 高位移出低位补零
;lsr r1, r1, #4
;逻辑右移 低位移出高位补零
;mov r1, #0xF0000000
;asr r1, r1, #16
;算数右移 低位移出高位补符号位
;mov r1, 0xff
;ror r2, r1, #4
;循环右移 低位移出高位补低位
;mov r1, 0xFF
;mov r2, r1 ,lsl #2
;r2 = (r1 << 2)
end
;汇编的结束
DAY 3
area reset, code ;定义一个代码段叫reset ;/*汇编文件中的符号*/ ;1.指令:编译完成后作为一条指令存储在内存单元当中,CPU执行时能完成一定的操作 ;2.伪操作:不会生成代码也不会占用内存,告诉编译器怎样编译 ;3.伪指令:本身不是指令,编译器在编译的时候将其替换成CPU能识别的指令 ;/*指令*/ ;1.数据操作指令:对数据进行逻辑、数学等运算与处理 ;2.跳转指令:实现程序的跳转,实质是修改PC ;3.Load/Store:对内存的读写操作 ;4.状态寄存器传送指令:对CPSR进行读写操作 ;5.异常中断产生指令:触发软中断 ;6.协处理器指令:操作协处理器的指令 entry ;汇编的入口 ;-----数据搬移指令----- ;mov r1, #1 ;r1 = 1 ;mov r2, r1 ;r2 = r1 ;mvn r1, #0xFF ;r1 = ~0xFF ;立即数 ;mov r1, #0xFF000000 ;立即数0xFF000000由0xFF循环右移(0x4*2)次得到 所以机器码中【7:0】为0xFF 【11:8】为0x4 ;立即数的本质是包含在指令当中的数 ;在mov指令中给立即数留了12位的存储空间,所以立即数只包含了2的12次方个 ;一个立即数是由一个八位的数循环右移偶数次得到的 ;mov r1, #0xFFFFFFFF ;编译器将其进行了指令的替换 ;加法指令 ;mov r1, #1 ;mov r2, #2 ;add r3, r1, r2 ;r3 = r1 + r2 ;add r3, r1, #2 ;r3 = r1 + 2 ;数据运算指令的基本格式 ;《操作码》《目标寄存器》《第一操作寄存器》《第二操作数》 ;默认情况下,数据处理指令不影响标志位,可以选择通过添加后缀“S”来影响标志位。 ;mov r1, #-1 ;adds r2, r1, #0 ;加法产生了进位 即运算结果在目标寄存器存储不下 ;mov r1, #0xFFFFFFFE ;adds r2, r1, #5 ;带进位的加法指令 ;两个64位数相加,第一个数的低32位放在r0高32位放在r1,第二个数的低32位放在r2高32位放在r3 ;编写代码实现两个64位数的和,结果的低32位放在r4高32位放在r5 ;mov r0, #0xFFFFFFFE ;mov r1, #1 ;mov r2, #0x5 ;mov r3, #1 ;adds r4, r0, r2 ;adc r5, r1, r3 ;实质:r5 = r1 + r3 + 'C' ;减法指令 ;mov r1, #5 ;mov r2, #3 ;subs r3, r1, r2 ;r3 = r1 - r2 ;带借位的减法指令 ;mov r0, #5 ;mov r1, #3 ;mov r2, #6 ;mov r3, #1 ;subs r4, r0, r2 ;sbc r5, r1, r3 ;本质:r5 = r1 - r3 - !c ;逆向减法 ;mov r1, #5 ;rsb r2, r1, #8 ;r2 = 8 - r1 ;乘法指令 ;mov r1, #3 ;mov r2, #5 ;mul r3, r1, r2 ;乘法指令不能使用立即数 ;逻辑与 ;mov r0, #0xf0 ;mov r1, #0x0f ;and r2, r0, r1 ;r2 = r0 & r1 ;逻辑或运算 ;mov r0, #0xf0 ;mov r1, #0x0f ;orr r2, r0, r1 ;r2 = r0 | r1 ;逻辑异或运算 ;mov r0, #0xff ;mov r1, #0xff ;eor r2, r1, r0 ;r2 = r1 eor r0 ;位清零指令 ;mov r0, #0xff ;bic r1, r0, #0xf ;比较指令 ;mov r1, #1 ;mov r2, #1 ;cmp r1, r2 ;没有目标寄存器 ;比较的结果在Z位显示 ;指令的实质是减法指令 ;移位器 ;mov r1, #0xFF ;lsl r1, r1, #4 ;逻辑左移 高位移出低位补零 ;lsr r1, r1, #4 ;逻辑右移 低位移出高位补零 ;mov r1, #0xF0000000 ;asr r1, r1, #16 ;算数右移 低位移出高位补符号位 ;mov r1, 0xff ;ror r2, r1, #4 ;循环右移 低位移出高位补低位 ;mov r1, 0xFF ;mov r2, r1 ,lsl #2 ;r2 = (r1 << 2) ;-----跳转指令----- ;mov r1, #1 ;mov r2, #2 ;mov r3, #3 ;b loop ;mov r4, #4 ;mov r5, #5 ;loop ;mov r6, #6 ;mov r7, #7 ;跳转指令的实质就是将PC修改为跳转标签下的第一条指令的地址 ;带返回的跳转指令 ;MAIN ;mov r1, #1 ;mov r2, #2 ;mov r3, #3 ;bl FUNC ;跳转的同时将下一条指令的地址保存到LR ;mov r4, #4 ;mov r5, #5 ;FUNC ;mov r6, #6 ;mov r7, #7 ;mov pc, lr ;将返回地址赋值给PC实现程序的返回 ;ARM指令的条件执行 ;mov r1, #2 ;cmp r1, #1 ;beq loop ;if(eq){b loop} =>实质:if(z == 1){b loop} ;bne loop ;if(z != 1){b loop} ;mov r2, #2 ;mov r3, #3 ;loop ;mov r4, #4 ;mov r5, #5 ;练习 ;start ; mov r0, #9 ; mov r1, #15 ;loop ; cmp r0, r1 ; beq stop ; subgt r0, r0, r1 ; sublt r1, r1, r0 ; b loop ;stop ; b stop ;-----load/store指令----- ;单寄存器操作 ;mov r1, #0xFF000000 ;mov r2, #0x40000000 ;str r1, [r2] ;将寄存器r1中的数据存储到以r2为起始地址的连续的四个字节的内存空间中 ;ldr r3, [r2] ;将r2为起始地址的连续的四字节当中的数据读取到r2寄存器 ;单寄存器的索引方式 ;前索引 ;mov r1, #0x40000000 ;mov r2, #4 ;mov r3, #0xFF ;str r3, [r1,r2] ;将r3中的数据存储到r1+r2地址 ;str r3, [r1,#8] ;将r3中的数据存储到r1+8地址 ;str r3, [r1,r2, lsl #1] ;将r3中的数据存储到r1 + (r2 << 1)地址 ;后索引 ;mov r1, #0x40000000 ;mov r2, #4 ;mov r3, #0xFF ;str r3, [r1], r2 ;将r3中的数据存储到r1地址,然后r1=r1+r2 ;自动索引 ;mov r1, #0x40000000 ;mov r2, #4 ;mov r3, #0xFF ;str r3, [r1,r2]! ;将r3中的数据存储到r1+r2地址,然后r1=r1+r2 ;三种索引方式同样适用于ldr ;ldr r3, [r1,r2] ;ldr r3, [r1], r2 ;ldr r3, [r1,r2]! ;批量寄存器操作 ;mov r1, #1 ;mov r2, #2 ;mov r3, #3 ;mov r4, #4 ;mov r5, #5 ;mov r11, #0x40000010 ;stm r11, {r1-r5} ;将r1-r5寄存器中的数据存储到以r11为起始地址的连续的20个字节的空间 ;stm r11, {r1,r3,r5} ;如果寄存器不连续 可以使用逗号分隔 ;stm r11, {r3,r5,r2,r4,r1} ;不管寄存器顺序如何写,存到内存中永远是小编号的寄存器对应低地址 ;ldm r11, {r6-r10} ;将以r11为起始地址的连续的20个字节中的数据读取到r6-r10 ;自动索引同样适用于批量寄存器操作 ;stm r11!, {r1-r5} ;批量寄存器操作中地址的增长方式 ;mov r1, #1 ;mov r2, #2 ;mov r3, #3 ;mov r4, #4 ;mov r5, #5 ;mov r11, #0x40000020 ;stmia r11!, {r1-r5} ;先存储数据 后增长地址 ;stmib r11!, {r1-r5} ;先增长地址 后存储数据 ;stmda r11!, {r1-r5} ;先存储数据 后递减地址 ;stmdb r11!, {r1-r5} ;先递减地址 后存储数据 ;栈的种类与使用 ;增栈:每次压栈后栈指针向高地址方向增长 ;减栈:每次压栈后栈指针向低地址方向增长 ;满栈:栈指针指向的是最后一次压入到栈中的数据 所以再压栈的时候需要先移动栈指针后压栈 ;空栈:栈指针指向的是最后一次压入到栈中的数据相邻的内存空间 所以再压栈的时候可以直接先压栈再移动栈指针 ;所以栈可以分为满增(FA) 满减(FD) 空增(EA) 空减(ED) 四类 ;习惯上我们使用满减栈 ;stmfd r11!, {r1-r5} ;ldmfd r11!, {r6-r10} ;练习-模拟子程序调用 ;初始化栈 ;MOV SP, #0x40000020 ;MAIN ;MOV R1, #3 ;MOV R2, #5 ;BL FUNC ;ADD R3, R1, R2 ;B STOP ;FUNC ;压栈保护现场 ;STMFD SP!, {R1,R2} ;MOV R1, #10 ;MOV R2, #20 ;SUB R3, R2, R1 ;出栈恢复现场 ;LDMFD SP!, {R1,R2} ;MOV PC, LR ;STOP ;B STOP ;优化 ;初始化栈 ;MOV SP, #0x40000020 ;MAIN ;MOV R1, #3 ;MOV R2, #5 ;BL FUNC ;ADD R3, R1, R2 ;B STOP ;FUNC ;压栈保护现场 ;STMFD SP!, {R1,R2,LR} ;MOV R1, #10 ;MOV R2, #20 ;SUB R3, R2, R1 ;出栈恢复现场 ;LDMFD SP!, {R1,R2,PC} ;STOP ;B STOP ;信号量的操作 ;mov r2, #0x40000000 ;mov r4,#0x12 ;str r4, [r2] ;mov r1, #0x34 ;swp r0, r1, [r2] ;将r2指向的内存空间的数据读取到r0寄存器 然后将r1寄存器的值存回到r2指向的内存空间 ;数据交换过程不会被打断,常用于操作信号量 ;-----状态寄存器传送指令----- ;读 ;mrs r0, cpsr ;r0 = cpsr ;改 ;bic r0, r0, #0x3 ;写 ;msr cpsr_c, r0 ;写CPSR的控制域【7:0】 ;非特权模式不能通过修改CPSR切换到特权模式 ;-----异常中断产生指令----- ;mrs r0, cpsr ;bic r0, r0, #0xc3 ;USER模式 IRQ和FIQ打开 ;msr cpsr_c, r0 ;mov r1, #1 ;mov r2, #2 ;mov r3, #3 ;swi #1 ;mov r4, #4 ;mov r5, #5 ;mov r6, #6 ;-----协处理器指令----- ;协处理器指令的种类 ;1.协处理器的数据处理指令 ;2.协处理器操作存储器的指令 ;3.ARM处理器对协处理器内部寄存器的读写 ;MRC-从协处理器寄存器移到ARM寄存器 ;MCR-从ARM寄存器移到协处理器寄存器 ;-----伪指令----- ;nop ;空操作 ldr r1, =0x12345678 ;r1 = 0x12345678 将任意32位数据放到寄存器 end ;汇编的结束
DAY 4
.global _start
@.global将标号定义成全局的 在其他文件可以引用_start标号
_start:
@汇编入口
@/******************************伪操作********************************/
@.equ 相当于C中的宏定义
@.equ DATA, 0xFF
@mov r1, #DATA
@mov r1, #1
@.word 0x12345678
@在当前地址申请一个字的空间并将其初始化
@mov r2, #2
@.byte 1
@在当前地址申请一个字节的空间并将其初始化
@.byte 2
@.byte 3
@.byte 4
@mov r1, r2
@.space 12, 1
@mov r3, r4
@在当前的地址申请多个字节的空间 并将其初始化
@条件编译
@.if 0
@
mov r1, #1
@.endif
@/******************************混合编程********************************/
@ 原则:在哪种语言下符合哪种语言的语法
@ 1.汇编跳转到C
@ 汇编文件.s
@
mov r1 #1
@
b main
@ C语言文件.c
@
int main()
@
{
@
int a;
@
a = 10;
@
}
@ 2.C跳转到汇编
@ C语言文件.c
@ extern void Func(void);
@int main()
@{
@
int a;
@
a = 10;
@
Func();
@
a = 20;
@}
@ 汇编文件.s
@Func:
@
mov r1, #1
@
mov r2, #2
@
mov pc, lr
@ 3.C内联汇编
@只有一个.c文件
@int main()
@{
@
int a;
@
a = 10;
@
asm
@
(
@
"mov r1, #1\n"
@
"mov r2, #2\n"
@
);
@
a = 100;
@}
@/******************************ATPCS协议********************************/
@ 1.栈的使用:使用满减栈
@ 2.寄存器的使用
@ a.如果函数有参数的时候 使用r0-r3传参 多于4个参数的时候使用数据栈
@ b.如果有返回值 使用r0传递 如果返回值是64位 使用r0、r1共同传递
@ c.栈指针使用r13
@/******************************uboot的下载步骤********************************/
@ 将开发板薄码开关拨成0110
@ 安装好CH340驱动后在 “我的电脑”-“管理”-“设备管理器”-“端口”-“COMx”
@ 打开超级终端 波特率:115200 流控制:无
@ 打开开发板进行设置:
@
1、set bootcmd
@
2、save
@ 程序下载
@
loadb 40008000
将二进制程序下载到40008000地址(kermit协议)
@
go 40008000程序跳转到40008000执行
@/******************************LED实验********************************/
@/*一、实验步骤*/
@ 1.分析电路图,分析LED的控制方式
高低电平控制亮灭
@ 2.分析电路图,查找LED与主控的连接关系
GPX2_7
@ 3.将对应的引脚设置成输出功能
@ 4.控制对应引脚输出高低电平
MAIN:
bl CONFIG
LOOP:
bl LEDON
bl DELAY
bl LEDOFF
bl DELAY
b LOOP
CONFIG:
ldr r1, =0x10000000
ldr r2, =0x11000c40
str r1, [r2]
mov pc, lr
LEDOFF:
ldr r1, =0x0
ldr r2, =0x11000c44
str r1, [r2]
mov pc, lr
LEDON:
ldr r1, =0x80
ldr r2, =0x11000c44
str r1, [r2]
mov pc, lr
DELAY:
ldr r1, =100000000
T:
sub r1, r1, #1
cmp r1, #0
bne
T
moveq pc, lr
stop:
b stop
@死循环,防止跑飞 while(1)
.end
@汇编程序结束
DAY 5(实例演示)
ADC
#include"exynos_4412.h"
/*
* ADC转换频率 = ADC时钟频率 / 5
* ADC时钟频率 = PLCK/(分频值+1)
*/
int main()
{
/*1.设置转换精度为12位精度*/
ADCCON = ADCCON | (1 << 16);
/*2.使能预分频器*/
ADCCON = ADCCON | (1 << 14);
/*3.设置预分频 ADC的时钟频率 = 100M/(19+1)=5M ADC的转换频率 = 5M/5=1M*/
ADCCON = ADCCON & (~(0xFF << 6)) | (19 << 6);
/*4.设置为正常模式*/
ADCCON = ADCCON & (~(1 << 2));
/*6.设置转换通道为通道3*/
ADCMUX = 0x3;
unsigned int AdcValue;
while(1)
{
/*触发开始一次转换*/
ADCCON = ADCCON | 1;
/*等待转换完成*/
while(!(ADCCON & (1 << 15)));
/*读取转换结果*/
AdcValue = ADCDAT & 0xFFF;
/*转换成实际电压mv*/
AdcValue = AdcValue * 0.44;
/*打印转换结果*/
printf("AdcValue = %dMV\n",AdcValue);
}
return 0;
}
iic
#include "exynos_4412.h"
#include "uart.h"
/****************MPU6050内部寄存器地址****************/
#define
SMPLRT_DIV
0x19
//陀螺仪采样率,典型值:0x07(125Hz)
#define
CONFIG
0x1A
//低通滤波频率,典型值:0x06(5Hz)
#define
GYRO_CONFIG
0x1B
//陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define
ACCEL_CONFIG
0x1C
//加速计自检、测量范围及高通滤波频率,典型值:0x18(不自检,2G,5Hz)
#define
ACCEL_XOUT_H
0x3B
#define
ACCEL_XOUT_L
0x3C
#define
ACCEL_YOUT_H
0x3D
#define
ACCEL_YOUT_L
0x3E
#define
ACCEL_ZOUT_H
0x3F
#define
ACCEL_ZOUT_L
0x40
#define
TEMP_OUT_H
0x41
#define
TEMP_OUT_L
0x42
#define
GYRO_XOUT_H
0x43
#define
GYRO_XOUT_L
0x44
#define
GYRO_YOUT_H
0x45
#define
GYRO_YOUT_L
0x46
#define
GYRO_ZOUT_H
0x47
#define
GYRO_ZOUT_L
0x48
#define
PWR_MGMT_1
0x6B
//电源管理,典型值:0x00(正常启用)
#define
WHO_AM_I
0x75
//IIC地址寄存器(默认数值0x68,只读)
#define
SlaveAddress
0x68
//MPU6050-I2C地址
/************************延时函数************************/
void mydelay_ms(int time)
{
int i,j;
while(time--)
{
for(i=0;i<5;i++)
for(j=0;j<514;j++);
}
}
/**********************************************************************
* 函数功能:I2C向特定地址写一个字节
* 输入参数:
*
slave_addr: I2C从机地址
*
addr: 芯片内部特定地址
*
data:写入的数据
**********************************************************************/
void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data)
{
/*对时钟源进行512倍预分频 打开IIC中断(每次完成一个字节的收发后中断标志位会自动置位)*/
I2C5.I2CCON = I2C5.I2CCON | (1<<6) | (1<<5);
/*设置IIC模式为主机发送模式 使能IIC发送和接收*/
I2C5.I2CSTAT = 0xd0;
/*将第一个字节的数据写入发送寄存器 即从机地址和读写位(MPU6050-I2C地址+写位0)*/
I2C5.I2CDS = slave_addr<<1;
/*设置IIC模式为主机发送模式 发送起始信号启用总线 使能IIC发送和接收*/
I2C5.I2CSTAT = 0xf0;
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*将要发送的第二个字节数据(即MPU6050内部寄存器的地址)写入发送寄存器*/
I2C5.I2CDS = addr;
/*清除中断挂起标志位 开始下一个字节的发送*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*将要发送的第三个字节数据(即要写入到MPU6050内部指定的寄存器中的数据)写入发送寄存器*/
I2C5.I2CDS = data;
/*清除中断挂起标志位 开始下一个字节的发送*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*发送停止信号 结束本次通信*/
I2C5.I2CSTAT = 0xD0;
/*清除中断挂起标志位*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*延时*/
mydelay_ms(10);
}
/**********************************************************************
* 函数功能:I2C从特定地址读取1个字节的数据
* 输入参数: slave_addr: I2C从机地址
*
addr: 芯片内部特定地址
* 返回参数: unsigned char: 读取的数值
**********************************************************************/
unsigned char iic_read(unsigned char slave_addr, unsigned char addr)
{
unsigned char data = 0;
/*对时钟源进行512倍预分频 打开IIC中断(每次完成一个字节的收发后中断标志位会自动置位)*/
I2C5.I2CCON = I2C5.I2CCON | (1<<6) | (1<<5);
/*设置IIC模式为主机发送模式 使能IIC发送和接收*/
I2C5.I2CSTAT = 0xd0;
/*将第一个字节的数据写入发送寄存器 即从机地址和读写位(MPU6050-I2C地址+写位0)*/
I2C5.I2CDS = slave_addr<<1;
/*设置IIC模式为主机发送模式 发送起始信号启用总线 使能IIC发送和接收*/
I2C5.I2CSTAT = 0xf0;
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*将要发送的第二个字节数据(即要读取的MPU6050内部寄存器的地址)写入发送寄存器*/
I2C5.I2CDS = addr;
/*清除中断挂起标志位 开始下一个字节的发送*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
while(!(I2C5.I2CCON & (1<<4)));
/*清除中断挂起标志位 重新开始一次通信 改变数据传送方向*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*将第一个字节的数据写入发送寄存器 即从机地址和读写位(MPU6050-I2C地址+读位1)*/
I2C5.I2CDS = slave_addr << 1 | 0x01;
/*设置IIC为主机接收模式 发送起始信号 使能IIC收发*/
I2C5.I2CSTAT = 0xb0;
/*等待从机接收到数据后应答*/
while(!(I2C5.I2CCON & (1<<4)));
/*禁止主机应答信号(即开启非应答 因为只接收一个字节) 清除中断标志位*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<7))&(~(1<<4));
/*等待接收从机发来的数据*/
while(!(I2C5.I2CCON & (1<<4)));
/*将从机发来的数据读取*/
data = I2C5.I2CDS;
/*直接发起停止信号结束本次通信*/
I2C5.I2CSTAT = 0x90;
/*清除中断挂起标志位*/
I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
/*延时等待停止信号稳定*/
mydelay_ms(10);
return data;
}
/**********************************************************************
* 函数功能:MPU6050初始化
**********************************************************************/
void MPU6050_Init ()
{
iic_write(SlaveAddress, PWR_MGMT_1, 0x00);
//设置使用内部时钟8M
iic_write(SlaveAddress, SMPLRT_DIV, 0x07);
//设置陀螺仪采样率
iic_write(SlaveAddress, CONFIG, 0x06);
//设置数字低通滤波器
iic_write(SlaveAddress, GYRO_CONFIG, 0x18);
//设置陀螺仪量程+-2000度/s
iic_write(SlaveAddress, ACCEL_CONFIG, 0x0);
//设置加速度量程+-2g
}
/**********************************************************************
* 函数功能:主函数
**********************************************************************/
int main(void)
{
unsigned char zvalue_h,zvalue_l;
//存储读取结果
short int zvalue;
/*设置GPB_2引脚和GPB_3引脚功能为I2C传输引脚*/
GPB.CON = (GPB.CON & ~(0xF<<12)) | 0x3<<12;
//设置GPB_3引脚功能为I2C_5_SCL
GPB.CON = (GPB.CON & ~(0xF<<8)) | 0x3<<8;
//设置GPB_2引脚功能为I2C_5_SDA
uart_init();
//初始化串口
MPU6050_Init();
//初始化MPU6050
printf("\n********** I2C test!! ***********\n");
while(1)
{
zvalue_h = iic_read(SlaveAddress, GYRO_ZOUT_H);
//获取MPU6050-Z轴角速度高字节
zvalue_l = iic_read(SlaveAddress, GYRO_ZOUT_L);
//获取MPU6050-Z轴角速度低字节
zvalue = (zvalue_h<<8)|zvalue_l;
//获取MPU6050-Z轴角速度
printf(" GYRO--Z :Hex: %d
\n", zvalue);
//打印MPU6050-Z轴角速度
mydelay_ms(100);
}
return 0;
}
IRQ
#include"exynos_4412.h"
/*
* 中断管理器GIC的作用
* 1.当多个中断同时产生的时候 中断管理器将对这些中断进行排队 将优先级最高的转发给CPU处理 其他再进行排队等待
* 2.中断管理器可以为每一个中断分配一个优先级
* 3.当一个中断正在处理的时候 外设又产生另一个中断 中断管理器会对这个中断进行挂起记忆 等上一个响应完成后再去响应下一个
* 3.为每一个中断分配一个CPU去处理
* 4.为每个中断选择一个中断类型(FIQ或IRQ)
* 5.CPU不能区分是哪个外设产生的中断 但CPU可以通过查询中断管理器来获取当前中断的中断号
* 6.在中断管理器中我们可以任意打开或关闭一个中断
*/
void Delay_Ms(unsigned int Time)
{
unsigned int i,j;
for(i=0;i
for(j=0;j<3000;j++);
}
void do_irq(void)
{
unsigned int IrqNum;
/*从中断管理器中获取当前中断的中断号*/
IrqNum = CPU0.ICCIAR & 0x3FF;
switch(IrqNum)
{
case 0:
break;
case 1:
break;
/*
* ... ...
* */
case 56:
break;
case 57:
GPX2.DAT = GPX2.DAT | (1 << 7);
Delay_Ms(500);
GPX2.DAT = GPX2.DAT & (~(1 << 7));
Delay_Ms(500);
/*清除外设层次的中断挂起位 使其不再向中断管理器发送中断信号*/
EXT_INT41_PEND = (1 << 1);
break;
/*
* ... ...
* */
case 158:
break;
case 159:
break;
default:
break;
}
/*将当前中断的中断号写回到中断管理器 以这种方式来通知中断管理器上一个中断响应完成可以送入新的中断信号*/
CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3FF)) | IrqNum;
}
int main()
{
/*外设层次-让外部设备能够产生一个中断信号并且顺利的送给中断管理器*/
/*1.将GPX1_1设置成中断功能GPX1CON[7:4]*/
GPX1.CON = GPX1.CON | (0xF << 4);
/*2.设置GPX1_1的中断触发方式EXT_INT41_CON[6:4]*/
EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 4)) | (0x2 << 4);
/*3.在外设层次将GPX1_1的中断使能使信号能送达中断管理器EXT_INT41_MASK[1]*/
EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 1));
/*中断管理器层次-让中断管理器将中断信号进行统一的管理并将信号转发给CPU 比如中断优先级、选择一个CPU、选择一个中断类型*/
/*4.全局使能中断管器监控外设信号并将信号转发到CPU接口ICDDCR[0]*/
ICDDCR = ICDDCR | 1;
/*5.在中断管理器中打开57号中断ICDISER1[25]*/
ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 25);
/*6.给57号中断选择cpu0处理ICDIPTR14[15:8]*/
ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 8)) | (0x1 << 8);
/*CPU层次-让CPU能接受中断管理器发来的信号*/
/*7.全局使能CPU接收来自中断管理器的信号ICCICR[0]*/
CPU0.ICCICR = CPU0.ICCICR | 1;
GPX2.DAT = GPX2.DAT & (~(1 << 7));
while(1)
{
}
return 0;
}
PWM
#include"exynos_4412.h"
/*
* 1.static关键字
*
1) 修饰局部变量:变量存储在静态存储区而不是存储在栈,该变量只会被初始化一次,每次使用都保持上一次的值
*
2) 修饰全局变量:被static修饰的全局变量只在本文件中被引用
*
3) 修饰函数 :被static修饰的函数只在本文件中被引用
* 2.volatile关键字
*
告诉编译器不要优化这个变量,每次在使用到这个变量的时候都要去内存地址中取该变量,而不是使用之前的备份
* */
/*
* 占空比:高电平时间/周期
*/
void DelayMs(unsigned int Time)
{
unsigned int i,j;
for(i=0;i
for(j=0;j<2500;j++);
}
int main()
{
/*1.将对应的引脚设置成PWM功能
GPD0CON[3:0]*/
GPD0.CON = GPD0.CON & (~(0xF)) | (0x2);
/*2.设置一级分频的值
TCFG0[7:0] 一级分频后的频率=PLCK/(99+1)=1M*/
PWM.TCFG0 = PWM.TCFG0 & (~(0xFF)) | (99);
/*3.设置二级分频的值
TCFG1[3:0]
递减计数器递减频率率=PLCK/(99+1)/1=1M*/
PWM.TCFG1 = PWM.TCFG1 & (~(0xF));
/*4.设置自动重装载
TCON[3]*/
PWM.TCON = PWM.TCON | (1 << 3);
/*5.设置周期的值
TCNTB0*/
PWM.TCNTB0 = 2000;
/*6.设置高电平的值
TCMPB0*/
PWM.TCMPB0 = 1000;
/*7.设置手动装载 将TCNTB0的值手动装载到递减计数器TCNTO0
TCON[1]*/
PWM.TCON = PWM.TCON | (1 << 1);
/*8.关闭手动装载 后续的周期使用自动装载
TCON[1]*/
PWM.TCON = PWM.TCON & (~(1 << 1));
/*9.打开递减计数器开始递减
TCON[0]*/
PWM.TCON = PWM.TCON | (1);
while(1)
{
PWM.TCON = PWM.TCON | (1);
DelayMs(500);
PWM.TCON = PWM.TCON & (~1);
DelayMs(500);
}
return 0;
}
RTC
#include"exynos_4412.h"
{
/*使能RTC控制*/
RTCCON = RTCCON | 1;
/*校准时间信息*/
RTC.BCDSEC = 0x50;
RTC.BCDMIN = 0x59;
RTC.BCDHOUR = 0x23;
RTC.BCDWEEK = 0x31;
RTC.BCDDAY = 0x1;
RTC.BCDMON = 0x12;
RTC.BCDYEAR = 0x017;
/*关闭RTC控制*/
RTCCON = RTCCON & (~1);
unsigned int Sec,OldSec;
while(1)
{
Sec = RTC.BCDSEC;
if(Sec != OldSec)
{
printf("%x-%x-%x %x %x:%x:%x\n",RTC.BCDYEAR,RTC.BCDMON,RTC.BCDWEEK,RTC.BCDDAY,RTC.BCDHOUR,RTC.BCDMIN,RTC.BCDSEC);
OldSec = Sec;
}
}
return 0;
}
UART
#include"exynos_4412.h"
void Delay_Ms(unsigned int Time)
{
unsigned int i,j;
for(i=0;i
for(j=0;j<3000;j++);
}
void UartSendByte(char Dat)
{
/*等待上一次的数据发送完成*/
while(!(UART2.UTRSTAT2 & 0x2));
/*将想要发送的数据写入发送寄存器*/
UART2.UTXH2 = Dat;
}
void UartSendString(char * Pstr)
{
while(*Pstr != '\0')
{
UartSendByte(*Pstr);
Pstr ++;
}
}
char UartRecByte(void)
{
char Dat;
if(UART2.UTRSTAT2 & 0x1)
{
Dat = UART2.URXH2;
return Dat;
}
else
{
return 0;
}
}
int main()
{
/*1.将GPA1_0和GPA1_1分别设置成串口2的接收和发送
GPA1CON*/
GPA1.CON = GPA1.CON & (~(0xFF)) | 0x22;
/*2.正常模式 无校验 一位停止位 8位数据位
ULCON2*/
UART2.ULCON2 = 0x3;
/*3.将收发模式设置成轮询模式
UCON2*/
UART2.UCON2 = UART2.UCON2 & (~(0xF)) | 0x5;
/*4.设置波特率为115200
UBRDIV2=53
UFRACVAL2=4*/
UART2.UBRDIV2 = 53;
UART2.UFRACVAL2 = 4;
char RecDat = 0;
while(1)
{
//RecDat = UartRecByte();
//if(RecDat)
//
UartSendByte(RecDat+1);
//UartSendString("Hellow World !\n");
printf("Hellow World !\n");
}
return 0;
}
WDT
#include"exynos_4412.h"
void DelayMs(unsigned int Time)
{
unsigned int i,j;
for(i=0;i
for(j=0;j<2500;j++);
}
int main()
{
/*1.设置一级分频*/
WDT.WTCON = WDT.WTCON | (0xFF<< 8);
/*2.设置二级分频 递减频率=PLCK/(255+1)/128=3052hz*/
WDT.WTCON = WDT.WTCON | (0x3<< 3);
/*3.关闭中断功能*/
WDT.WTCON = WDT.WTCON & (~(1 << 2));
/*4.使递减计数器递减到零后产生复位信号*/
WDT.WTCON = WDT.WTCON | 1;
/*5.给看门狗计数器赋值*/
WDT.WTCNT = 30520;
/*6.打开看门狗定时器开始倒计时*/
WDT.WTCON = WDT.WTCON | (1 << 5);
while(1)
{
GPX2.DAT = GPX2.DAT | (1 << 7);
DelayMs(500);
GPX2.DAT = GPX2.DAT & (~(1 << 7));
DelayMs(500);
/*喂狗*/
WDT.WTCNT = 30520;
}
return 0;
}