ARM(笔记)

ARM接口技术

ARM接口技术 : 芯片内部 + 向外扩展

​ ARM: 架构 芯片 公司

​ 汇编 - C语言

系统移植 Linux

驱动开发

一、ARM系统硬件组成和运行原理

1.硬件组成(手机为例)

​ (1)flash储存器:存储程序

​ 特点:永久的保存数据,且掉电不消失,运行速度快,价格便宜

​ (2)内存:程序运行在内存

​ 特点:运行速度快,但掉电即消失

​ (3)CPU:

​ ①寄存器:储存数据的场所

​ 特点:运行速度快,价格昂贵

​ ②控制器:取指,译码

​ ③ALU运算器:运算

2.运行原理

​ 上电之后,控制器就会从flash/内存中取指并译码,通过存储器存储运算量及结果,通过ALU运算器做运算

二、ARM接口技术 : 芯片内部 + 向外扩展

ARM: 架构 芯片 公司

​ (1)ARM是一家做RISC处理器内核的公司

​ (2)ARM不生产芯片

汇编 - C语言

预备知识 学习

汇编 操作寄存器 + C语言

接口技术: 点灯 通信

芯片 :

辅助芯片: 选择器 译码器

设备芯片: 加速度传感器 温度传感器 …传感器 电机驱动芯片 舵机芯片 通信模块

主控制: STM32 C51/C52 单片机 ucos

主运算: FS4412 系统级别的芯片 Linux

控制+运算 二合一的芯片 FS_MP157

SOC: system on chip 片上系统, stm32 cortex-A 电脑/手机芯片

1.ARM Cortex-A处理器的工作模式: 7 + 1(8种)

User:普通用户模式

Svc:超级用户模式,Reset或软中断发生时CPU切换到此模式

IRQ:普通中断模式

FIQ:高优先级中断模式

Abort:数据存取异常对应的模式

Undef:未定义指令对应的模式

System:与user相同,权限稍高于user

Monitor(Cortex-A特有模式):特权模式,监控安全代码

2.ARM Cortex-A处理器的寄存器: 37 + 3

(1)40个寄存器(非Cortex-A 37个寄存器)
(2)特殊寄存器(r0-r7为普通寄存器)

r13(sp):栈指针寄存器,异常时需要进栈(现场保护)、出栈(恢复现场),不同模式有自己的栈顶寄存器

​ r14(lr):链接寄存器,异常返回地址或函数返回地址

​ r15(pc):程序计数器,保存即将要执行的指令的地址

​ cpsr:当前程序状态寄存器,保存当前处理器状态的寄存器

​ spsr:保存当前程序状态寄存器(备份cpsr)

(3)cpsr寄存器

​ 4~0 -----模式位

​ 10000 User mode 应用程序常处于该模式

​ 10001 FIQ mode

​ 10011 SVC mode

​ 10111 Abort mode

​ 11011 Udefined mode

​ 11111 System mode

​ 10110 Monitor mode

​ 10010 IRQ mode

​ 5 -----T位:处理器的状态 0:ARM状态

​ 6 -----F位:FIQ中断禁止位 1:禁止 0:使能

​ 7 -----I位:IRQ中断禁止位 1:禁止 0:使能

条件位:

​ 31 -----N = Negative result from ALU

​ 30 -----Z = Zero result from ALU 1:结果为0 0:结果不为0

​ 29 -----C = ALU operation carried out of borrow

​ 28 -----V = ALU operation overflowed

注:当处理器执行在ARM状态,所有指令32bits宽,所有指令必须word对齐

(4)ARM体系架构及处理器
(4)ARM体系架构及处理器
ARM体系:ARMv4ARMv5ARMv6ARMv7
ARM CPU:arm7arm9 arm10arm11arm-cortex-a8
流水线:35 6813
频率(MHZ):80150 260335667
MMU:无/有有 有
结构:冯诺依曼哈佛 哈佛哈佛哈佛

注:

​ 不同ARM体系采用不同指令集

​ 哈佛结构是数据和指令分开存储并行

​ 冯诺依曼(普林斯顿)结构是混合储存的

学习汇编:

了解机器的思维, 方便理解程序

3.ARM指令集

ARM是32位的cpu

大部分ARM core提供:

ARM指令集(32-bit):每条指令4字节

Thumb指令集(16-bit):每条指令2字节

指令机器码(如图):

在这里插入图片描述

MOV r0, #4

ADD r0, r1, r2 //r0 = r1 + r2

ADD r0, r1, #0x1234 //r0 = r1 + 5

条件码:

在这里插入图片描述

伪指令: 会被编译器转换为指令后再翻译

ldr r0, =0x12345678

立即数:

8位存放数字 4位存放偏移量

将一个数循环右移偶数位后, 得到一个8位以内的数, 这就是立即数

将一个数转换为二进制, 看1的个数, 将连续的偶数个0去掉

汇编中的一些符号:

#    后面跟数字, 表明是立即数
#    放在语句最前面, 充当注释
@    注释, 相当于//

三、寄存器数据操作指令:

指令 <目标寄存器> <第一个操作数> <第二个操作数>

目标寄存器和第一个操作数 必须是寄存器 (r0-r15)

第二个操作数可以是寄存器, 也可以是立即数(#数字)

数据搬移: MOV MVN
算术指令: ADD ADC SUB SBC RSB RSC
逻辑指令: AND ORR EOR BIC
比较指令: CMP CMN TST TEQ

乘法指令: MUL

算数指令:

MOV   r0, r1    @r0 = r1
mvn   r0, r1    @r0 = ~r1

ADD   r0, r1, r2  @r0 = r1+r2 不会改变cpsr寄存器
ADD   r0, r1, #4  @r0 = r1 + 4
ADDS  r0, r1, r2  @r0 = r1+r2 如果有溢出或进位cpsr寄存器的c位被改变为1,没有则为0
ADD   r0, r1   	  @r0 = r0 + r1  r0 += r1
eg:
	Ox00000001 ffffffff
	0x00000001 00000001
	ldr r0,=#0xffffffff @低位
	ldr r1,=#0x00000001 @低位
	ldr r2,=#0x00000001 @高位
	ldr r3,=#0x00000001 @高位
	ADDS r4,r0,r1 @允许cpsr改变,如果有进位cpsr的c位就会变为1,没有就会变为0
	ADC r5, r2,r3   @r5 = r2+r3+cpsr的c
	结果为0x0000000300000000


SUB	  r0, r1, r2  @r0 = r1 - r2
SUB	  r0, r1, #4  @r0 = r1 - 4
SUBS  r0, r1, r2  @当运算时没有借位CPSR_C位置1,产生了借位CPSR_C=0

RSB   r0, r1, r2  @r0 = r2 - r1
RSB	  r0, r1, #4  @r0 =  4 - r1

ADC   r0, r1, r2  @r0 = r1+r2 + cpsr_c  

cmp   r0, r1   @ r0 - r1 结果 通过 CPSR 前4位体现
subgt   r2, r0,r1  @if(r0>r1) r2=r0-r1
addlt   r2, r0,r1  @if(r0<r1) r2=r0+r1
moveq   r2, r0     @if(r0==r1) r2=r0

CMN  r0, r1   @ r0 - (-r1)  r0 + r1 结果 改变CPSR前4位

tst  r0, #0x02  @ (r0 & 0x02) == 0    CPSR_Z 置一
tst  r0, #0x0f  @ (r0 & 0x0f) == 0    CPSR_Z 置一

TEQ  r0, r1   @r0==r1  r0^r1 == 0(相等)   CPSR_Z 置一

AND	 r0, r1, r2  @r0 = r1 & r2
ORR  r0, r1, r2  @r0 = r1 | r2
EOR  r0, r1, r2  @r0 = r1 ^ r2
BIC  r0, r1, r2  @r0 = r1 & (~r2) 按位清零

MUL  r0, r1, r2  @r0 = r1 * r2

移位指令:

LSL:逻辑左移(Logical Shift Left),寄存器中字的低端空出的位补0。
LSR:逻辑右移(Logical Shift Right),寄存器中字的高端空出的位补0。
ASL:算术左移(Arithmetic Shift Left),和逻辑左移LSL相同。
ASR:算术右移(Arithmetic Shift Right),移位过程中符号位不变,即如果源操作数是正数,则字的高端空出的位补0,否则补1。

ROR:循环右移(Rotate Right),由字的低端移出的位填入字的高端空出的位。
RRX:带扩展的循环右移(Rotate Right eXtended),操作数右移一位, 高端空出的位用进位标志C的值来填充,低端移出的位填入进位标志位。

mov   r0, r1, lsl  #4      @r0 = r1 << 4
add   r0, r1, r2, lsl  #4  @ r0 = r1 + (r2 << 4)
add   r0, r1, lsl  #4      @ r0 = r0 + (r1 << 4)

跳转指令:

b 直接跳转, 类似于goto

bl 在跳转的同时, lr寄存器会记录返回地址 lr = pc-4

只能在前后32M的空间内跳转

label:
	b  label
	
	b  flag
flag:

1:
	b  1b
	
	b  2f
2:
_start:
	mov r0, #1
	mov r1, #2
	bl  sum
	nop
	nop
	
sum:
	add r3, r0, r1
	mov pc, lr

操作CPSR SPSR的指令

msr mrs

msr   cpsr,  r0     @cpsr = r0
mrs   r1,  cpsr     @r1 = cpsr
bic  r1, r1, #0xf   @r1后4位清零
msr  cpsr, r1       @cpsr = r1


msr   spsr,  r0     @spsr = r0
mrs   r1,  spsr     @r1 = spsr

练习1: 比较三个数的大小, 找出最大的数

在这里插入图片描述

练习2: 1+2+3+…+100

r0 = 1;
r1 = 0;

while(1)
{
	r1 = r1 + r0;
	r0 = r0 + 1;
	if(r0 > 100)
		break;
}

在这里插入图片描述

操作内存的指令

ldr/str架构

规定:存储器之间不能直接拷贝数据,需要借助CPU的寄存器做中转,即存储器的内容需要先load到寄存器,然后再store到存储器

store--存储,写;  load--加载, 读
		
	在ARM架构下,  数据从内存到CPU之间的移动只能通过LDR/STR指令来完成.  
	而MOV只能在寄存器之间移动数据,或者把立即数移动到寄存器中,并且数据的长度不能超过8位
	
	str r2,[r0]   //把r2的数据 存储到r0地址     *((int *)r0) = r2
	ldr r1,[r0]   //把r0地址中的数据加载到r1中  r1 = *((int *)r0)

三种索引方式:
	LDR    R0 , [R1 , #4]   //r0 = *((int *)(r1+4))
    LDR    R0 , [R1 , #4]!  //r0 = *((int *)(r1+4))   r1 += 4
    LDR    R0 , [R1], #4    //r0 = *((int *)r1)    r1 += 4
	
	后面的 ! 表示要更新寄存器的值

stmxx/ldmxx

stm--store much,多数据存储,将寄存器的值存到地址上
ldm--load much,多数据加载,将地址上的值加载到寄存器上

栈的类型:
空栈:栈顶指针在元素的后一个位置(指针指向即将插入数据的位置)
满栈:栈顶指针在元素当前位置(指针指向数据所在的位置)
递增栈:入栈时地址向高地址移动
递减栈:入栈时地址向低地址移动

xx有下面8种类型:
    (1)IA:(Increase After) 每次传送后地址加4,其中的寄存器从左到右执行
	 	eg: stmia r0,{r1,r4}  //先存R1,将r0里面的地址加上4再存r4 
	 	eg: ldmia r0,{r0,r1,r2}   //将r0地址中的值逐个写入到寄存器r0 r1 r2中
    (2)IB:(Increase Before)每次传送前地址加4,其中的寄存器从左到右执行
    (3)DA:(Decrease After)每次传送后地址减4,其中的寄存器从右到左执行
	 	eg:STMDA R0,{R1,LR}  //先存LR,再存R1
    (4)DB:(Decrease Before)每次传送前地址减4,其中的寄存器从右到左执行
    
	(5)FD:  满递减堆栈(每次传送前地址减4)(LDMFD--LDMIA; STMFD--STMDB)
    (6)FA:  满递增堆栈(每次传送后地址减4)(LDMFA--LDMDA; STMFA--STMIB)
    (7)ED:  空递减堆栈(每次传送前地址加4)(LDMED--LDMIB; STMED--STMDA)
    (8)EA:  空递增堆栈(每次传送后地址加4)(LDMEA--LDMDB; STMEA--STMIA)

非栈地址
D decrease	入栈时地址向低地址移动
I increase	入栈时地址向高地址移动
A after		指针先赋值再移动
B before	指针先移动再赋值    

stmfd sp!,{r0-r12,lr}  --- 入栈 保护现场 (sp!加!栈顶指针sp的位置自动变化)
ldmfd sp!,{r0-r12,pc}^ --- 出栈 恢复现场
	^  在出栈的同时 恢复cpsr 

软中断指令:

SWI{条件码} <软中断号> @通过代码产生中断, 类似于Qt的发射信号

在这里插入图片描述

eg: swi 0x2

伪指令:

被编译器翻译成汇编指令

gnu工具链

ldr  r0, =0x1234
    .word   0x1234
    ldr  r0, [pc, #-8]
    
nop    @空指令

数据定义伪指令:

数据定义伪操作一般用于为特定的数据分配存储单元,同时可完成已分配存储单元的初始化。
常用的数据定义伪操作有如下几种:
.byte     单字节定义,8bits            	 .byte     0x12,’a’,23,0x13
.short    定义双字节数据      		.short    0x1234,65535
.long /.word  定义4字节数据,32bits(4byte)      	 .word    0x12345678
.halfword     16bits(2byte)
.doubleword    64bits(8byte)   (Cortex—A处理器)

num:  .word   0x100    @int  num = 0x100;
ldr  r0, num           @r0  = *num  =  0x100

arr:  .word   0x100,0x200    @int  arr[] = {0x100,0x200};
ldr  r0, num                 @r0  = *num  =  0x100

开辟空间:

.space    开辟空间, 相当于 malloc       

buf:  .space  64    @ buf = malloc(64)

ldr r0, =buf         @将buf地址保存到寄存器r0中   r0 = buf
ldr r0, buf         @将buf地址中的内容保存到寄存器r0中   r0 = *buf
str  r0, buf        @ *buf = r0

杂项伪指令:

arm         .arm          	      定义一下代码使用ARM指令集编译
.thumb      .thumb                定义一下代码使用Thumb指令集编译
.section    .section     expr	  定义一个段。expr可以使.text   .data.   .bss
.text        .text {subsection}   将定义符开始的代码编译到代码段
.data        .data {subsection}   将定义符开始的代码编译到数据段,初始化数据段
.bss         .bss {subsection}    将变量存放到.bss段,未初始化数据段

_start   汇编程序的缺省入口是_ start标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点.

.global/ .globl :用来声明一个全局的符号

.end       文件结束

协处理器相关指令:

ARM 协处理器指令包括以下 5 条:
    — CDP 协处理器数操作指令
    — LDC 协处理器数据加载指令
    — STC 协处理器数据存储指令
    — MCR ARM处理器寄存器到协处理器寄存器的数据传送指令
    — MRC 协处理器寄存器到ARM处理器寄存器的数据传送指令

寻址方式:

寻址: cpu 找到/拿到 数据

8种:

立即数寻址		 mov  r0, #4
寄存器寻址		 mov  r0, r1
寄存器移位寻址	   mov  r0, r1, lsl  #4
寄存器间接寻址	   ldr  r0, [r1]

基址变址寻址		ldr  r0, [r1, #4] @r0 = *(r1+4)
				 ldr  r0, [r1, #4]! @r0 = *(r1+4) r1+=4
				 ldr  r0, [r1], #4 @r0 = *(r1) r1+=4
多寄存器寻址		ldmxx r0!,{r1-r3}
				 ldmia  r0!, [r1-r12]
堆栈寻址		  ldmxx sp!,{r1-r3}
				 stmfd  sp!, [r0-r12, lr]

相对寻址		b  label

四、异常:

(1)概念

​ 不是出问题了,而是芯片内部的调度。可以看作qt的信号。即CPU正在执行某个应用程序时突然来了一个异常(中断、复位、undef、abort…)打断当前应用程序的执行跳转到异常处理函数中处理异常,处理完之后回来接着执行app。

(2)种类

​ 中断:IRQ、FIQ由外部硬件触发

​ 软中断:软件模拟中断

​ 复位异常:reset 例:手机关机,按power键

​ 未定义异常:undef 当指令不识别时产生的异常

​ 数据异常:Data Abort 例:越界

(3)异常向量表

​ 规定了每一种异常的入口地址

地址异常类型CPU模式
0x00reset异常SVC
0x04udef未定义异常Undef
0x08软中断异常SVC
0x0cPrefetch Abort预取指令异常Abort
0x10Data Abort数据异常Abort
0x14保留
0x18IRQIRQ
0x1cFIQFIQ
(4)异常发生,CPU如何跳转到异常处理函数:

​ CPU收到异常信号之后就会自动打断当前应用程序的执行,并跳转到异常向量表中规定好的异常入口地址,入口地址放的是一条跳转指令,例:bl swi_handler


异常源有7种:
1.复位异常  reset    从头开始执行,所有数据都刷新了
2.未定义指令异常  undefined instruction    执行未定义的指令,执行时产生异常
	 如:user模式下执行msr指令
3.软中断异常  swi    指令本身产生的异常,执行时产生
4.预取指异常  prefetch abort   取值发生了异常,不会影响正在执行的指令,执行完后处理异常
5.数据异常  data abort   执行指令时用到的数据有问题,会更新PC寄存器的值,处理完异常后,cpu认为需要重新执行这条指令      
	如: ldr r0,[r1]  可能r1这个地址不存在
6.IRQ异常  irq   中断,在执行指令时来了异常,会在语句执行完后再去处理异常
7.FIQ异常  fiq   中断响应尽可能的快,在执行指令时来了异常,会在语句执行完后再去处理异常

在这里插入图片描述

FIQ 快速中断:
    1.有自己独立的r8-r12寄存器, 处理速度更快
    2.异常优先级更高
    3.位于异常向量表的末尾, 可以直接将异常处理函数跟在后面, 顺序执行, 效率更高

异常优先级:

异常在当前指令执行完成之后才被响应, 多个异常可以在同一时间产生
异常指定了优先级和固定的服务顺序:
Reset
Data Abort
FIQ
IRQ
Prefetch Abort
SWI
Undefined instruction

异常恢复时的返回地址:

fiq/irq  :  pc = lr - 4
reset:      从头开始执行, 不用管返回地址
undefined:  pc = lr
swi:	    pc = lr
prefetch:   pc = lr - 4
data:       pc = lr - 8

在这里插入图片描述

异常处理流程:

(1)当异常产生时,ARM core:

​ ①.拷贝cpsr到spsr_

​ ②.设置适当的cpsr位

​ 改变处理器状态进入ARM态

​ 改变处理器模式进入相应的异常模式

​ 设置中断禁止位禁止相应中断(如果需要)

​ ③.保存返回地址到LR _

​ ④.设置PC为相应的异常向量

(2)返回时,异常处理需要:

​ ①.cpsr = spsr

​ ②.pc = lr

注:这些操作只能在ARM态执行

在这里插入图片描述

.globl _start
_start: 
    b	reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq
_undefined_instruction: .word _undefined_instruction
_software_interrupt:	.word swi_interrupt
_prefetch_abort:	.word _prefetch_abort
_data_abort:		.word _data_abort
_not_used:		.word _not_used
_irq:			.word _irq
_fiq:			.word _fiq


reset:
    /* set the cpu to SVC32 mode */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f
	orr	r0, r0, #0xd3
	msr	cpsr,r0

    /* Set vector address in CP15 VBAR register */
	ldr	r0, =_start
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR

    ldr  sp, =stacktop  @用sp保存栈顶地址


    /* set the cpu to user mode */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f
	orr	r0, r0, #0xd0  @改变模式,并禁止了irq fiq
	msr	cpsr,r0

    ldr  sp, =stacktop  @用sp保存栈顶地址
    sub  sp, sp, #64

    bl	_main
    
        
     
_main:
    mov r0, #3
    mov r1, #9

    bl  user_add

    nop
    nop
    nop
    nop


user_add:
    stmfd sp!, {lr}

    ldr r2, =sharedata    @r2 保存共享空间地址
    str r0, [r2]
    str r1, [r2, #4]

    swi 0

    ldr r3, [r2, #8]

    ldmfd sp!, {pc}


swi_interrupt:
    stmfd  sp!, {r0-r12, lr}

    @判断中断号
    ldr  r0, [lr, #-4]         @取出swi 0 指令放到r0
    bic  r0, r0, #0xff000000   @将前8位清零(条件码+指令码)
    cmp  r0, #0

    bleq sys_add
 
    ldmfd  sp!, {r0-r12, pc}^
     @ r0-r12 -恢复-> r0-r12   pc = lr   cpsr = spsr_svc(^起的作用)


sys_add:
    stmfd sp!, {lr}

    ldr r2, =sharedata    @r2 保存共享空间地址
    ldr r0, [r2]
    ldr r1, [r2, #4]

    add  r10, r0, r1

    str r10, [r2, #8]

    ldmfd sp!, {pc}


@开辟栈空间, 因为是满递减栈, 所以记录栈顶地址
stack:  .space  64*7
stacktop:  .word stack+64*7

@开辟共享空间
sharedata: .space 64

汇编:

理解机器执行过程(异常跳转等)

汇编效率会高一些

向上理解软件, 向下感知硬件, 对理解系统也有帮助

汇编是最接近机器的语言, 简单理解

汇编启动机器后, 就可以转到C语言编程/应用层编程

五、接口技术:

led操作-GPIO输出:

需求: 点亮led灯 - led3

看原理图: GPX1_0 输出 高电平

看芯片手册/用户手册:

Disable Pull-up/Pull-down when you use port as output function.

GPX1CON   0x11000c20    GPX1CON[0]   [3:0]   0x1 = Output
 Base Address: 0x1100_0000
 Address = Base Address + 0x0C20


GPX1DAT  0x11000c24   [0]    1
 Base Address: 0x1100_0000
 Address = Base Address + 0x0C24


GPX1PUD  0x11000c28    [1:0]     0x0 = Disables Pull-up/Pull-down
 Base Address: 0x1100_0000
 Address = Base Address + 0x0C28


GPX1DRV   0x11000c2C   [1:0]    0x0 = 1x
 Base Address: 0x1100_0000
 Address = Base Address + 0x0C2C

写代码

#define  GPX1CON   *(volatile unsigned int *)0x11000c20
#define  GPX1DAT   *(volatile unsigned int *)0x11000c24
#define  GPX1PUD   *(volatile unsigned int *)0x11000c28
#define  GPX1DRV   *(volatile unsigned int *)0x11000c2C

int main(int argc, char *argv[])
{ 
    /*
    unsigned int *p = (unsigned int *)0x11000c20 ;
    *p  = *p & (~0xf);
    *p  = *p | 0x1;
    */

    GPX1CON  &=  ~0xf; 
    GPX1CON  |=  0x1; 

    GPX1DAT  |= 1;

    GPX1PUD  &=  ~0x3;

    while(1);

    return 0;
} 

需求: 点亮led灯 - led4 led5

看原理图: GPF3_4 GPF3_5 输出 高电平

看芯片手册/用户手册:

GPF3CON     0x114001E0   GPF3CON[5] [23:20]    0x1 = Output
						 GPF3CON[4] [19:16]   0x1 = Output
						 
GPF3DAT    0x114001E4    修改 4  5 两位

GPF3PUD    0x114001E8   [11:10]  [9:8]    0x0 = Disables Pull-up/Pull-down
			[2n+1 : 2n]    n=5    n=4 

key操作-GPIO输入:

需求: 通过按键key2点亮led灯

看原理图: key2 UART_RING GPX1_1 未按下-高电平 按下后-低电平

看芯片手册/用户手册:

GPX1CON  0x11000c20    GPX1CON[1] [7:4]    0x0 = Input

GPX1DAT  0x11000c24    [1]   读取对应位的状态判断输入电平

GPX1PUD  0x11000c28    [3:2]   0x3 = Enables Pull-up 上拉,默认电平是高电平

uart-通用异步收发器:

全双工 异步

考虑: 波特率 数据位数 停止位 校验码

需求: 通过 uart 发送一个 ‘A’ 给电脑

看原理图:

BUF_XuTXD2/UART_AUDIO_TXD 发送引脚 XuTXD2/UART_AUDIO_TXD/GPA1_1

BUF_XuRXD2/UART_AUDIO_RXD 接收引脚 XuRXD2/UART_AUDIO_RXD/GPA1_0

看芯片手册/用户手册:

ULCONn  开始  数据位  停止位  奇偶校验位  
UBRDIVn UFRACVALn    波特率

发送:
GPA1CON   0x11400020    [7:4]    0x2 = UART_2_TXD

接收:
GPA1CON   0x11400020    [3:0]   0x2 = UART_2_RXD

uart模块控制
ULCON2     0x13820000     =0x3   (0 000 0 11)普通模式 无奇偶校验 1个停止位 8个数据位
UCON2      0x13820004    [3:0]    0101 =  polling mode(轮询)
UTRSTAT2   0x13820010    [1]     1 - 发送完成, 发送缓冲区为空
UTXH2      0x13820020    [7:0]   要发送的数据 'A'   UTXH2 = 'A'
URXH2      0x13820024    [7:0]   接收到的数据   
UBRDIV2    0x13820028    53     100000000/(115200*16)-1 取整数部分
UFRACVAL2  0x1382002C    4      ((100000000/(115200*16)-1)-53)*16 取整数部分

在这里插入图片描述

#define  GPA1CON    (*(volatile unsigned int *)0x11400020) 
#define  ULCON2     (*(volatile unsigned int *)0x13820000)
#define  UCON2      (*(volatile unsigned int *)0x13820004)
#define  UTRSTAT2   (*(volatile unsigned int *)0x13820010)
#define  UTXH2      (*(volatile unsigned int *)0x13820020)
#define  URXH2      (*(volatile unsigned int *)0x13820024)
#define  UBRDIV2    (*(volatile unsigned int *)0x13820028)
#define  UFRACVAL2  (*(volatile unsigned int *)0x1382002C)

void  uart2_init(void)
{
	GPA1CON = GPA1CON & ~(0xff << 0) | (0x22 << 0);

	ULCON2 = 0x3;
	UCON2 = UCON2 & ~(0xf << 0)  |  (0x5 << 0);

	UBRDIV2 = 53;
	UFRACVAL2 = 4;
}

void uart2_send(char data)
{
	UTXH2 = data;
    while( (UTRSTAT2 & (0x1<<1))  ==  0); //发送未完成
}

void send_str(char *str)
{
    while( *str != '\0')
    {
        uart2_send(*str);
        str++;
    }
}

char uart2_recv(void)
{
    if((UTRSTAT2 & (0x1 <<0)) == 1)
    {
        return (URXH2 & 0xff);
    }
    else
    {
        return 0;
    }
}

通信相关知识:

单工通信: 一方发送, 一方接收, 只能单方向传输信息 如:广播

半双工通信: 双方都能发送接收, 但是同一时间只能发送或接收, 如: 对讲机

全双工: 双方都能发送接收, 并且能同时进行, 如: 电话, qq

异步: 双方规定好通信频率, 大部分通过波特率来确定双方通信频率, 双方通信特率一定要一致

同步: 多增加一个时钟线(clk - clock)

波特率: 1s内发送的 数据 位数(二进制的位)

数据位:传输数据的长度,一般是8位

奇偶校验: 多增加一个校验位

奇校验: 保证 传输数据(校验位+数据位) 中 1 的个数是奇数个

偶校验: 保证 传输数据(校验位+数据位) 中 1 的个数是偶数个

优点: 简单

缺点: 容错率不高; 每次传输都要多增加一位, 降低效率

RTC-内部设备

实时时钟 - 片内设备, 无外接引脚, 不用看原理图

直接看芯片手册/用户手册:

RTCCON     0x10070040  [0]   1-设置初始时间   0-rtc自己计时, 后续读取时间
BCDSEC     0x10070070 
BCDMIN     0x10070074 
BCDHOUR    0x10070078 
BCDDAYWEEK 0x10070080 
BCDDAY     0x1007007C 
BCDMON     0x10070084 
BCDYEAR    0x10070088 

代码:

#define RTCCON     (*(volatile unsigned int *)0x10070040) 
#define BCDSEC     (*(volatile unsigned int *)0x10070070) 
#define BCDMIN     (*(volatile unsigned int *)0x10070074) 
#define BCDHOUR    (*(volatile unsigned int *)0x10070078) 
#define BCDDAYWEEK (*(volatile unsigned int *)0x10070080) 
#define BCDDAY     (*(volatile unsigned int *)0x1007007C) 
#define BCDMON     (*(volatile unsigned int *)0x10070084) 
#define BCDYEAR    (*(volatile unsigned int *)0x10070088) 

#include "uart.h"

void rtc_init(void)
{
    RTCCON |=  0x1;
    
    BCDYEAR = 0x022;
    BCDMON  = 0x10;
    BCDDAY  = 0x18;
    BCDDAYWEEK = 0x02;
    BCDHOUR = 0x16;
    BCDMIN  = 0x59;
    BCDSEC  = 0x55;

    RTCCON &= ~0x1;
}

void rtc_show(void)
{
    uart2_send( (BCDHOUR>>4) + '0' );
    uart2_send( (BCDHOUR&0xf) + '0' );
    uart2_send(':');
    uart2_send( (BCDMIN>>4) + '0' );
    uart2_send( (BCDMIN&0xf) + '0' );
    uart2_send(':');
    uart2_send( (BCDSEC>>4) + '0' );
    uart2_send( (BCDSEC&0xf) + '0' );
    send_str("\r\n");
}

Watchdog Timer-看门狗

本质:计数器(功能:定时、计数,时间到则复位)

内部设备, 不用看原理图

直接看芯片手册/用户手册:

t_watchdog = 1/(PCLK/(Prescaler value + 1)/Division_factor)   
PCLK = 100 MHz  (7 Clock Management Unit)

WTCON[15:8]     8-bit Prescaler
WTCON[4:3]      Division_factor

WTCON[2] 		Interrupt
WTCON[0]		Reset Signal Generator

WTCNT		Down Counter
WTDAT       WTCNT = WTDAT   (喂狗)

To start WDT, set WTCON[0] and WTCON[5] as 1.



t_watchdog = 1/(100000000/(255 + 1)/128)   
f_watchdog = 100M/(255+1)/128 = 3051 Hz(次/S)

WTCON   0x10060000   = 0xff39 (11111111 00 1 11 0 0 1)
WTDAT   0x10060004   初始时不起作用, 不用设置, 后面喂狗用
WTCNT   0x10060008	 启动前给一个初始值(时间 - 3S)

代码:

#define  WTCON   (*(volatile unsigned int *)0x10060000)   
#define  WTDAT   (*(volatile unsigned int *)0x10060004)   
#define  WTCNT   (*(volatile unsigned int *)0x10060008)

void wdt_init(int n)
{
    WTCON = 0xff39;
    WTCNT = n*3051;
}

PWM-无源蜂鸣器

本质: 计数 timer(定时器) pwm(脉冲宽度调制)-控制高低电平变化

需求: 让无源蜂鸣器响

看原理图: MOTOR_PWM GPD0_0

看手册: PCLK 100MHz

GPD0CON  0x114000A0   [3:0]     0x2 = TOUT_0   (timer out - pwm)

Timer Input Clock Frequency = PCLK/({prescaler value + 1})/{divider value}
TCFG0  0x139D0000    [7:0]    分频值 1-255  一级分频prescaler value
TCFG1  0x139D0004    [3:0]    0100 = 1/16  二级分频 divider value
TCON   0x139D0008	 [3]      1 = Interval mode (auto-reload)
					 [2]      0 = Inverter Off (默认为0, 可以不用设置)
					 [1]      初始化先手动更新一次(1), 后续可以关闭手动更新(0)
					 [0]      1 = Starts Timer 0
TCNTB0 0x139D000C			  Count 计数值, 周期值 (count*fre == 周期时间)
TCMPB0 0x139D0010			  Compare 比较值, 电平翻转的位置, 脉宽值(高电平持续的时间)


PWM 周期频率 : 20-20000Hz (受材质影响, 范围再小一点)

代码:

#define  GPD0CON  (*(volatile unsigned int *)0x114000A0) 
#define  TCFG0    (*(volatile unsigned int *)0x139D0000)
#define  TCFG1    (*(volatile unsigned int *)0x139D0004)
#define  TCON     (*(volatile unsigned int *)0x139D0008)
#define  TCNTB0   (*(volatile unsigned int *)0x139D000C)
#define  TCMPB0   (*(volatile unsigned int *)0x139D0010)

void pwm_init(void)
{
	GPD0CON = GPD0CON & ~(0xf << 0) | (0x2 << 0);
    
    TCFG0 = TCFG0 | (0xff < 0);
    TCFG1 = TCFG1 & ~(0xf << 0) | (0x4 << 0);
    
    TCNTB0  = 500;
    TCMPB0  = 200;
    
    TCON  = TCON | (0x1 << 3) | (0x1 << 1) & ~(0x1 << 0); //手动更新, 先关pwm
    TCON  = TCON & ~(0x1 << 1); //关闭手动更新
}
void pwm_on(void)
{
	TCON  = TCON | (0x1 << 0);
}
void pwm_off(void)
{
	TCON  = TCON & ~(0x1 << 0);
}

ADC-模数转换

采集电位器后的电压值

看原理图: XadcAIN3 adc专用引脚, 不受GPIO控制 0-1.8v

看手册: Analog Input Range: 0 ~ 1.8V

ADC_CFG   0x10010118   [16]    0 : General ADC
ADCCON 	控制转换模数
ADCDAT  存放转换后的数据(0~2^12-1) 电压值=data*1.8v/(2^12 - 1)
ADCCONn   [15] end of conversion flag (结束转换的标志)

ADC_CFG 0x10010118   [16]    0 : General ADC
ADCCON  0x126C0000 =  0x17fc2 (1 0 1 11111111 000 0 1 0)
ADCDLY  0x126C0008  使用默认值, 不用配置
ADCDAT  0x126C000C  [11:0]  0x0~0xfff  转换后的数据
ADCMUX  0x126C001C  [3:0]   0011 = AIN 3  
#define ADC_CFG (*(volatile unsigned int *)0x10010118)
#define ADCCON  (*(volatile unsigned int *)0x126C0000)
#define ADCDAT  (*(volatile unsigned int *)0x126C000C)
#define ADCMUX  (*(volatile unsigned int *)0x126C001C)

#include "uart.h"

void adc_init(void)
{
    ADC_CFG = ADC_CFG & ~(0x1 << 16);
    
    ADCMUX  = ADCMUX & ~(0xf<<0) | (0x3<<0);
    ADCCON  = 0x17fc2;
}

void adc_show(void)
{
    int mv = 0;

    mv = (ADCDAT&0xfff) * 1800 / 4095;

    uart2_send(  mv/1000 + '0' );
    uart2_send('.');
    uart2_send(  mv%1000/100 + '0' );
    uart2_send(  mv%100/10 + '0' );
    send_str("v\r\n");
}

中断-key

硬件中断触发方式:①电平触发:高、低;②边沿触发:上升、下降

硬件中断处理流程图

在这里插入图片描述

在这里插入图片描述

异常: irq fiq swi

start.S 要修改, 异常向量表设置异常处理函数(irq), 设置对应模数下的栈空间

看原理图: key2 UART_RING GPX1_1 XEINT9

看手册: 6章节 - GPIO 9章节 - Interrupt Controller

GPX1CON  0x11000c20    GPX1CON[1] [7:4]    0xF = EXT_INT41[1]  (extern interrupt)

EXT_INT41CON 0x11000E04  [6:4]  0x2 = Triggers Falling edge(由原理图得到-下降沿)
EXT_INT41_FLTCON0  0x11000E88  [15]-1 enable  [14]-0 delay  就是默认值,可以不用设置
EXT_INT41_MASK   0x11000F04  [1]  0x0 = Enables Interrupt
EXT_INT41_PEND   0x11000F44  [1]  0x1 = Interrupt Occurs(代表中断处理完成-在中断处理函数内部置一)

25(SPI Port No)    57(ID)  – EINT[9]
The CPU interface always uses the IRQ exception request for Non-secure interrupts.
(CPU接口对于非安全中断总是使用IRQ异常请求。)
 
ICCICR_CPU0    0x10480000  1 = Enables signaling of interrupts(使能cpu interface)
ICCPMR_CPU0    0x10480004  [7:0]  priority mask (cpu interface 的屏蔽码)
ICCIAR_CPU0	   0x1048000C  [9:0] ACKINTID - The interrupt ID(只读,通过这个寄存器知道具体的中断)
ICCEOIR_CPU0   0x10480010  [9:0] ACKINTID (只写, 写入id, 表明中断处理完成)
ICDDCR		   0x10490000  [0]   1 (使能分发器)
ICDISER1_CPU0  0x10490104  [25]  1 (使能id为57的中断)
ICDIPR14_CPU0  0x10490438  [15:8]  设置id为57的优先级
ICDIPTR14_CPU0 0x10490838  [15:8]  0b00000001 - CPU 0 (设置id为57的中断对应的cpu interface)

Distributor (分发器)

接收多种中断, 找出最高优先级的发给cpu interfaces

数字越小, 优先级越高 0的优先级最高

 Enabling the forwarding of interrupts to the CPU interfaces globally.
	ICDDCR  允许全局地将中断转发到CPU接口。 - 使能分发器
	
 Enabling or disabling each interrupt.
	ICDISER1_CPU0  正在启用或禁用每个中断。 - 使能单独的某一个中断源
	
 Setting the priority level of each interrupt.
	ICDIPR14_CPU0  设置每个中断的优先级。
	
 Setting the target processor list of each interrupt.
	ICDIPTR14_CPU0 设置每个中断的目标处理器列表。 - 设置中断要发送的cpu
	
 Setting each peripheral interrupt to be level-sensitive or edge-triggered.
	EXT_INT41CON  将每个外围中断设置为电级敏感或边缘触发。
	
 Setting each interrupt as either secure or Non-secure if the GIC implements the Security Extensions.
	如果GIC实现了安全扩展,则将每个中断设置为安全中断或不安全中断。 - 未设置,不用管
	
 Sending an SGI to one or more target processors
	向一个或多个目标处理器发送SGI。 - 分发器自己要做的事情

CPU interface(CPU接口)

设置屏蔽码, 中断优先级高于屏蔽码才会发送给具体的CPU进行处理

 Enabling the signaling of interrupt requests by the CPU interface.
	ICCICR_CPU0  通过CPU接口启用中断请求的信令。 - 使能 CPU接口工作  
    
 Acknowledging an interrupt.
	ICCIAR_CPU0  承认了一个中断。 - 确认中断正在处理
    
 Indicating completion of the processing of an interrupt.
	ICCEOIR_CPU0  EXT_INT41_PEND 表示中断处理的完成。 - 回应处理完成 
	
 Setting an interrupt priority mask for the processor.
	ICCPMR_CPU0  为处理器设置中断优先级掩码。
	
 Defining the preemption policy for the processor.
	定义处理器的抢占策略。 - 分组后的组优先级 - 不用分组, 就不用设置
	
 Determining the highest priority pending interrupt for the processor.
	确定处理器的未决中断的最高优先级。 - cpu interface 自己决定, 不用设置

代码:

#define GPX1CON        (*(volatile unsigned int *)0x11000c20)
#define EXT_INT41CON   (*(volatile unsigned int *)0x11000E04)
#define EXT_INT41_MASK (*(volatile unsigned int *)0x11000F04)
#define EXT_INT41_PEND (*(volatile unsigned int *)0x11000F44)
#define ICCICR_CPU0    (*(volatile unsigned int *)0x10480000)
#define ICCPMR_CPU0    (*(volatile unsigned int *)0x10480004)
#define ICCIAR_CPU0	   (*(volatile unsigned int *)0x1048000C)
#define ICCEOIR_CPU0   (*(volatile unsigned int *)0x10480010)
#define ICDDCR		   (*(volatile unsigned int *)0x10490000)
#define ICDISER1_CPU0  (*(volatile unsigned int *)0x10490104)
#define ICDIPR14_CPU0  (*(volatile unsigned int *)0x10490438)
#define ICDIPTR14_CPU0 (*(volatile unsigned int *)0x10490838)

void key_init(void)
{
	GPX1CON |= (0xf << 4); //[7:4] 0xF = EXT_INT41[1] (extern interrupt)
    EXT_INT41CON &= ~(0x7 << 4); //[6:4]  0x2 = Triggers Falling edge(由原理图得到-下降沿)
    EXT_INT41CON |= (0x2 << 4);
    
    ICCPMR_CPU0 |= (0xff << 0); //[7:0]  priority mask (cpu interface 的屏蔽码 255 )
    ICDIPR14_CPU0 &= ~(0xff << 8); //[15:8]  设置id为57的优先级 0
    
    ICDIPTR14_CPU0 &= ~(0xff << 8); //(设置id为57的中断对应的cpu interface)
    ICDIPTR14_CPU0 |= (0x1 << 8); //[15:8]  0b00000001 - CPU 0 
    
    EXT_INT41_MASK &= ~(0x1 << 1); //[1]  0x0 = Enables Interrupt
    ICDISER1_CPU0 |= (0x1 << 25); //[25]  1 (使能id为57的中断)
    ICDDCR |= (0x1 << 0); //[0]   1 (使能分发器)
    ICCICR_CPU0 |= (0x1 << 0); //[0]  1 = 使能cpu interface
}

void do_irq(void)
{
    int id = ICCIAR_CPU0; //[9:0] ACKINTID - The interrupt ID(只读,通过这个寄存器知道具体的中断)
    if((id & 0x3ff) == 57)
    {
    	//led3_on();  led3_off();
        uart_send('A');
        //pwm_on();
        
        //代表中断处理完成-在中断处理函数内部置一
    	EXT_INT41_PEND |= (0x1 << 1); //[1]  0x1 = Interrupt Occurs    
    }
    
	ICCEOIR_CPU0 = id;  //[9:0] ACKINTID (只写, 写入id, 表明中断处理完成)
}

六、通信相关知识:

1.基础知识:

单工通信: 一方发送, 一方接收, 只能单方向传输信息 如:广播

半双工通信: 双方都能发送接收, 但是同一时间只能发送或接收, 如: 对讲机

全双工: 双方都能发送接收, 并且能同时进行, 如: 电话, qq

异步: 双方规定好通信频率, 大部分通过波特率来确定双方通信频率, 双方通信特率一定要一致

同步: 多增加一个时钟线(clk - clock)

波特率: 1s内发送的 数据 位数(二进制的位)

奇偶校验: 多增加一个校验位

奇校验: 保证 传输数据(校验位+数据位) 中 1 的个数是奇数个

偶校验: 保证 传输数据(校验位+数据位) 中 1 的个数是偶数个

优点: 简单

缺点: 容错率不高; 每次传输都要多增加一位, 降低效率

通信协议: 通信双方必须遵循一定的规则进行数据的传输

串行通信: 只能一位一位传送, 效率较低, 通常用于设备间的传输

并行通信: 占用引脚数量多, 易受干扰, 不易同步, 通常用于短距离传输, 如内存数据传输

无线通信: WIFI, 蓝牙, ZIGBEE…

2.串行通信主要分为: uart spi IIC 单总线

(1)uart: 全双工(Rx Tx) 异步(波特率) 一对一通信
(2)SPI: 全双工(MISO MOSI) 同步(clk) 支持一对多通信(CS)
在这里插入图片描述
①特点:

​ 其通讯双方有主从之分,通讯由主设备引导,从设备被动响应,
​ 且只有一台设备可作为主设备,其他设备均为从设备,
​ 每次通讯主设备通过片选线来确定从设备。

②SPI接口具有如下优点:

​ 1) 全双工的协议,既能发送数据也能接受数据;
​ 2) 操作简单,输入输出的bit数也没什么限制,不局限于一个byte;
​ 3) 相对于I2C协议,时钟速度快,没有最大限制;
​ 4) 三态输出的驱动能力强,相对I2C的开漏输出,抗干扰能力强,传输稳定;
​ 5) 协议简单利于硬件设计与实现,比如不需要像I2C协议中每个从器件都需要一个地址。

③同时,它也具有如下缺点:

​ 1) 需要占用主机较多的口线(每个从机都需要一根片选线);
​ 2) 只支持单个主机;
​ 3) 传输的过程没有确认信号,只负责传,不管从器件收不收到;
​ 4) 没有校验机制。

④应用

​ ads1292 心电传感器 心率 呼吸波

(3)IIC(I2C): 半双工(data) 同步(CLK) 多对多(同一时间只能有一个主设备, clk是双向的)

在这里插入图片描述

I2C启动时序图

在这里插入图片描述

①特点

​ 双线多主机同步、半双工、串行低速率。
​ 广泛应用于传输速率要求不高、传输距离短的场合,
​ 最大优势是可以在总线上扩展多个外围设备的支持。
​ 每一个接入i2c总线的设备都有唯一地址标识符,由7位二进制数表示。
​ 因为I2C通信速率不高,而且通信双方距离很近,所以常见各种物联网传感器芯片
​ (如gsensor、温度、湿度、光强度、酸碱度、烟雾浓度、压力等)
​ I2C总线只需要一根数据线和一根时钟线两根线,总线接口已经集成在芯片内部,优化主板空间和成本。

②I2C总线最主要的优点:

​ · 无论总线上有多少设备,都只使用两条线,保持低引脚/信号数。
​ · 真正的支持多主机设备,但是同一时刻只允许一台主机。
​ · I2C总线具有低功耗、抗干扰强的优点,传输距离短的特点(引脚数量少, 低功耗, 抗干扰能力强)。
​ · 连接到相同总线的I2C 数量只受到总线的最大电容400pF 限制。
​ · 串行的8 位双向数据传输位速率在标准模式下可达100kbit/s,
​ 快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s。

③缺点:

​ 相对效率不高 (总线速度分为标准速度100kbps,快速模式400kbps,高速模式3.4Mbps)

④应用

​ 通常用于一些物联网传感器设备 如: 陀螺仪/加速度传感器(mpu6050)

(4)单总线: one-wire 半双工(data) 异步(高低电平时间控制 - 看具体应用)

​ 一根线传输数据

ds18b20 温度传感器

dht11 温湿度传感器

(5)总结:SPI协议的优缺点
优点

协议简单利于硬件设计与实现,比如不需要像I2C协议中每个从器件都需要一个地址;只用到4根线,封装也很容易做
全双工的协议,既能发送数据也能接受数据
三态输出的驱动能力强,相对I2C的开漏输出,抗干扰能力强,传输稳定;
相对于I2C协议,时钟速度快,没有最大限制
输入输出的bit数也没什么限制,不局限于一个byte

缺点

信号线4根,比I2C多,芯片选择线会随着从器件的个数的增加而增加
传输的过程没有确认信号,只负责,不管从器件收不收到;在SPI Flash中会有read status 这个命令确认从器件的状态,是否处于busy状态
没有校验机制(I2C也没有)

③同时,它也具有如下缺点:

​ 1) 需要占用主机较多的口线(每个从机都需要一根片选线);
​ 2) 只支持单个主机;
​ 3) 传输的过程没有确认信号,只负责传,不管从器件收不收到;
​ 4) 没有校验机制。

④应用

​ ads1292 心电传感器 心率 呼吸波

(3)IIC(I2C): 半双工(data) 同步(CLK) 多对多(同一时间只能有一个主设备, clk是双向的)

​ [外链图片转存中…(img-4NB5A539-1677236670339)]

I2C启动时序图

[外链图片转存中…(img-FTm8YJoY-1677236670340)]

①特点

​ 双线多主机同步、半双工、串行低速率。
​ 广泛应用于传输速率要求不高、传输距离短的场合,
​ 最大优势是可以在总线上扩展多个外围设备的支持。
​ 每一个接入i2c总线的设备都有唯一地址标识符,由7位二进制数表示。
​ 因为I2C通信速率不高,而且通信双方距离很近,所以常见各种物联网传感器芯片
​ (如gsensor、温度、湿度、光强度、酸碱度、烟雾浓度、压力等)
​ I2C总线只需要一根数据线和一根时钟线两根线,总线接口已经集成在芯片内部,优化主板空间和成本。

②I2C总线最主要的优点:

​ · 无论总线上有多少设备,都只使用两条线,保持低引脚/信号数。
​ · 真正的支持多主机设备,但是同一时刻只允许一台主机。
​ · I2C总线具有低功耗、抗干扰强的优点,传输距离短的特点(引脚数量少, 低功耗, 抗干扰能力强)。
​ · 连接到相同总线的I2C 数量只受到总线的最大电容400pF 限制。
​ · 串行的8 位双向数据传输位速率在标准模式下可达100kbit/s,
​ 快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s。

③缺点:

​ 相对效率不高 (总线速度分为标准速度100kbps,快速模式400kbps,高速模式3.4Mbps)

④应用

​ 通常用于一些物联网传感器设备 如: 陀螺仪/加速度传感器(mpu6050)

(4)单总线: one-wire 半双工(data) 异步(高低电平时间控制 - 看具体应用)

​ 一根线传输数据

ds18b20 温度传感器

dht11 温湿度传感器

(5)总结:SPI协议的优缺点
优点

协议简单利于硬件设计与实现,比如不需要像I2C协议中每个从器件都需要一个地址;只用到4根线,封装也很容易做
全双工的协议,既能发送数据也能接受数据
三态输出的驱动能力强,相对I2C的开漏输出,抗干扰能力强,传输稳定;
相对于I2C协议,时钟速度快,没有最大限制
输入输出的bit数也没什么限制,不局限于一个byte

缺点

信号线4根,比I2C多,芯片选择线会随着从器件的个数的增加而增加
传输的过程没有确认信号,只负责,不管从器件收不收到;在SPI Flash中会有read status 这个命令确认从器件的状态,是否处于busy状态
没有校验机制(I2C也没有)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值