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体系:

ARMv4

ARMv5

ARMv6

ARMv7

ARM CPU:

arm7

arm9 arm10

arm11

arm-cortex-a8

流水线:

3

5 6

8

13

频率(MHZ):

80

150 260

335

667

MMU:

无/有

有 有



结构:

冯诺依曼

哈佛 哈佛

哈佛

哈佛

注:

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

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

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

学习汇编:

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

3.ARM指令集

ARM是32位的cpu

大部分ARM core提供:

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

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

指令机器码(如图):

ARM架构连接示意图 arm架构原理_单片机

MOV r0, #4

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

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

条件码:

ARM架构连接示意图 arm架构原理_ARM架构连接示意图_02

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

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: 比较三个数的大小, 找出最大的数

ARM架构连接示意图 arm架构原理_驱动开发_03

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

r0 = 1;
r1 = 0;

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

ARM架构连接示意图 arm架构原理_arm_04

操作内存的指令

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的发射信号

ARM架构连接示意图 arm架构原理_arm_05

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模式

0x00

reset异常

SVC

0x04

udef未定义异常

Undef

0x08

软中断异常

SVC

0x0c

Prefetch Abort预取指令异常

Abort

0x10

Data Abort数据异常

Abort

0x14

保留

0x18

IRQ

IRQ

0x1c

FIQ

FIQ

(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   中断响应尽可能的快,在执行指令时来了异常,会在语句执行完后再去处理异常

ARM架构连接示意图 arm架构原理_寄存器_06

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

ARM架构连接示意图 arm架构原理_arm_07

异常处理流程:

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

①.拷贝cpsr到spsr_

②.设置适当的cpsr位

改变处理器状态进入ARM态

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

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

③.保存返回地址到LR _

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

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

①.cpsr = spsr

②.pc = lr

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

ARM架构连接示意图 arm架构原理_单片机_08

.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 取整数部分

ARM架构连接示意图 arm架构原理_驱动开发_09

#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

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

硬件中断处理流程图

ARM架构连接示意图 arm架构原理_驱动开发_10

ARM架构连接示意图 arm架构原理_驱动开发_11

异常: 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是双向的)


ARM架构连接示意图 arm架构原理_ARM架构连接示意图_12

I2C启动时序图

ARM架构连接示意图 arm架构原理_arm_13

①特点

双线多主机同步、半双工、串行低速率。
广泛应用于传输速率要求不高、传输距离短的场合,
最大优势是可以在总线上扩展多个外围设备的支持。
每一个接入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也没有)