SPI通信简介
SPI全称Serial Peripheral Interface,即串行外设接口。
由Motorola公司提出的一种同步串行数据传输标准。
所谓同步,即数据收发双方共用一个时钟;
所谓串行,即待传输的数据排成一行,一位一位地传送出去。
主要用于微控制器与其他外围设备,
如EEPROM、Flash、AD转换器等之间的短距离传输,
当然也可实现微控制器与微控制器间的数据传输。
相比于其它通信协议,
SPI采用四线制的硬件连接方式,
Arduino Uno就是通过上图中D10~D13对应的SS、MOSI、MISO、SCK四个接口实现SPI通信。
四个接口的定义如下:
SPI协议决定了可以有多个从机,但只能存在一个主机,主机通过从机选择线来确定当前要通信的从机。
SPI通信的硬件连接
一主一从
这是最简单的SPI通信方式,由于主机和从机的角色是固定不变的,可以将主机的SS端接高电平,将从机的SS端固定接地。其它信号一一对应连接即可。
一主多从
主机选用独立的IO分别连接到从机的SS引脚,当需要与某个从机通信时,拉低相应的IO口即可。
主机和从机的信号传输方向见下表所示
SPI通信方式解读
不管是一主一从还是一主多从的SPI通信系统,
某一时刻通信双方只能是一个主机和一个从机,
内部主要由主从双方的两个移位寄存器(8 BIT SHIFT REGISTER)
和主机SPI时钟发生器(SPI CLOCK GENERATOR)组成。
通信过程简述如下:
1)条件准备。包括四线引脚的输入输出配置,主机SCK、MOSI必须配置为输出模式,MISO配置为输入模式,从机正好相关,说见上面的信号传输方向表。除之这外,还要开启SPI的工作使能,即置SPI控制寄存器的SPE位。
2)拉低从机的SS电平,从机做好数据传输准备,时刻注意主机发出的SCK信号。
3)数据传输。每来一个时钟脉冲信号,主从机间完成一位数据交换,8个时钟脉冲完成一个字节的数据交换。该字节传输完成,等待写入下一个传输字节。主从机间的交换逻辑见下图所示。主机和从机的移位寄存器连接成环,随着时钟脉冲,数据按照从高位到低位的方式依次移出主机寄存器和从机寄存器,并且依次移入从机寄存器和主机寄存器。当寄存器中的内容全部移出时,相当于完成了两个寄存器内容的交换。
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);
}
}
例程运行结果