spi通信协议_arduino的SPI通信

SPI通信简介


SPI全称Serial Peripheral Interface,即串行外设接口。

由Motorola公司提出的一种同步串行数据传输标准。

所谓同步,即数据收发双方共用一个时钟;

所谓串行,即待传输的数据排成一行,一位一位地传送出去。

主要用于微控制器与其他外围设备,

如EEPROM、Flash、AD转换器等之间的短距离传输,

当然也可实现微控制器与微控制器间的数据传输。

相比于其它通信协议,

SPI采用四线制的硬件连接方式,

8914d5fb5f3d99824dc29a27dc2c58a8.png

Arduino Uno就是通过上图中D10~D13对应的SS、MOSI、MISO、SCK四个接口实现SPI通信。

四个接口的定义如下:

8ffa77a0eb3eece326416615079b4809.png

SPI协议决定了可以有多个从机,但只能存在一个主机,主机通过从机选择线来确定当前要通信的从机。

SPI通信的硬件连接


一主一从

这是最简单的SPI通信方式,由于主机和从机的角色是固定不变的,可以将主机的SS端接高电平,将从机的SS端固定接地。其它信号一一对应连接即可。

9e201cd0a85311e0939b7aa881bcb5f6.png

一主多从

主机选用独立的IO分别连接到从机的SS引脚,当需要与某个从机通信时,拉低相应的IO口即可。

c9c2743bb85af80e8f25f3c6d94f1d5b.png

主机和从机的信号传输方向见下表所示

e931050d961b237738f4d75f2dc9223a.png

SPI通信方式解读

不管是一主一从还是一主多从的SPI通信系统,

某一时刻通信双方只能是一个主机和一个从机,

内部主要由主从双方的两个移位寄存器(8 BIT SHIFT REGISTER)

和主机SPI时钟发生器(SPI CLOCK GENERATOR)组成。

8c95bad53c42c165c35a7646d42037fb.png

通信过程简述如下:

1)条件准备。包括四线引脚的输入输出配置,主机SCK、MOSI必须配置为输出模式,MISO配置为输入模式,从机正好相关,说见上面的信号传输方向表。除之这外,还要开启SPI的工作使能,即置SPI控制寄存器的SPE位。

2)拉低从机的SS电平,从机做好数据传输准备,时刻注意主机发出的SCK信号。

3)数据传输。每来一个时钟脉冲信号,主从机间完成一位数据交换,8个时钟脉冲完成一个字节的数据交换。该字节传输完成,等待写入下一个传输字节。主从机间的交换逻辑见下图所示。主机和从机的移位寄存器连接成环,随着时钟脉冲,数据按照从高位到低位的方式依次移出主机寄存器和从机寄存器,并且依次移入从机寄存器和主机寄存器。当寄存器中的内容全部移出时,相当于完成了两个寄存器内容的交换。

3615e411088919f5f8f4e9aa5ee58a61.png

4)传输结束。此时,硬件自动置位传输完成标识SPIF(位于SPI状态寄存器SPSR中),通过轮询状态寄存器SPIF位或中断的方式,读取传入的字节。最后置位SS(设为1),重置SPI内部逻辑为初始状态。

Arduino对SPI通信的实现


Arduino以SPIClass类的形式对SPI进行了封装,

并对用户开放了SPI对象用于操作SPI。

SPI的常用操作方法如下:

SPI.begin()-完成主机的初始化工作,包括:四线的输入输出配置、开启SPI的工作使能。从机的四线输入输出、工作使能需要手工配置,可以参见下面的例程。

SPI.transfer()-主机传送字节,并返回从从机接收的字节。注意:主机是通过轮询的方式等待发送完成(也即接收完成)。

SPI.attachInterrupt()-从机开启传输完成中断。注意:主机不要使用,因为固定为了轮询方式。

SPCR |=_BV(SPE)-从机开启SPI工作使能。这条语句为直接寄存器操作,并不是SPI类中的一部分。

Arduino的SPI通信实例


两块Arduino之间通过SPI通信,并用串口打印传输数据,方便用户查看。

使用两块Arduino UNO,一主一从。

Arduino UNO A: SPI 主机

Arduino UNO B: SPI 从机

连线方式:

A——————-B

(10) SS———->(10) SS

(11) MOSI——->(11) MOSI

(12) MISO

(13) SCLK——->(13) SCLK

主机代码

#include

void setup (void)

{

// 开始串口通讯

//注意:此串口与SPI通信没有任何关系,只是为了程序演示输出SPI接收到的字节。

Serial.begin(115200);

digitalWrite(SS, HIGH); //SPI内部逻辑复位

SPI.begin (); // SPI通讯初始化配置

}

void loop (void)

{

char c;

// 使能从机

digitalWrite(SS, LOW); // SS – pin 10

// 循环发送字节,实现字符串的发送

for (const char * p = "Hello,world!" ; c = *p; p++) {

SPI.transfer (c);//主机SPI发送

Serial.print(c);//串口显示发送的字节

}

// 复位从机

digitalWrite(SS, HIGH);

delay (1000);

}

从机代码(轮询方式)

#include

char buf [100];

volatile byte pos;

volatile booleanprocess_it;

void setup (void)

{

Serial.begin (115200);

//从机的MISO要配置为输出模式

pinMode(MISO, OUTPUT);

//使能SPI,SPI可以正常工作了

SPCR |= _BV(SPE);

pos = 0;

}

charSPI_SlaveReceive(void){

while(!(SPSR & (1<

return SPDR;

}

void loop(void){

buf[pos++] = SPI_SlaveReceive();

if(buf[pos-1]==''){

buf[pos] = 0;

pos = 0;

Serial.print(buf);

}

}

例程运行结果

fd799f41d2dbd9679689c99b20c8828a.png

Published by

风君子

独自遨游何稽首 揭天掀地慰生平