串行外设接口

SPI全称Serial Peripheral Interface,即串行外设接口。由Motorola公司提出的一种同步串行数据传输标准。

  • 同步:数据收发双方共用一个时钟;
  • 串行:待传输的数据排成一行,一位一位地传送出去。
    主要用于微控制器与其他外围设备,如EEPROM、Flash、AD转换器等之间的短距离传输,当然也可实现微控制器与微控制器间的数据传输。

特点:相比于其它通信协议,SPI采用四线制的硬件连接方式,结合四种信号间的时序关系,共同构成了SPI通信的语法!

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

SPI通信的连接

Stduino UNO/Nano借助SS、MOSI、MISO、SCK四个接口实现SPI通信,四个接口的定义如下:

信号线 全称 中文名称 功能说明
SCK Serial CLock 串行时钟 同步数据传输
MOSI Master Out Slave In 主出从进 主机输出从机输入数据线
MISO Master In Slave Out 主进从出 主机输入从机输出数据线
SS Slave Select 从机选择 从机此引脚设置为低电平

一主一从

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

一主多从

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

Stduino对SPI通信的实现

Stduino对SPI进行了封装,并对用户开放了SPI对象用于操作SPI。
SPI的常用操作方法如下:

  • SPI.begin()-完成主机的初始化工作,包括:四线的输入输出配置、开启SPI的工作使能。从机的四线输入输出、工作使能需要手工配置,可以参见下面的例程。
  • SPI.setClockDivider(分频器) -相对于系统时钟设置SPI时钟分频器。在基于AVR的面板,可用的分频器为2,4,8,16,32,64或128。 SPI_CLOCK_DIV4,则为SPI时钟设置为系统时钟的四分之一(对于20 MHz的电路板为5 Mhz)。
  • SPI.transfer()-主机传送字节,并返回从从机接收的字节。注意:主机是通过轮询的方式等待发送完成(也即接收完成)。
  • SPI.attachInterrupt()-从机开启传输完成中断。注意:主机不要使用,因为固定为了轮询方式。

示例

两块Stduino之间通过SPI通信,并用串口打印传输数据,方便用户查看。
使用两块Stduino UNO,一主一从。
Stduino UNO A: SPI 主设备
Stduino UNO B: SPI 仆从设备

连线方式

Stduino UNO A Stduino UNO B
MOSI MOSI
MISO MISO
SCLK SLK
SS SS

主设备代码

#include <Arduino.h>
#include <SPI.h>
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!\n" ; c = *p; p++) {
    SPI.transfer (c);//主机SPI发送
    Serial.print(c);//串口显示发送的字节
  }
  // 复位从机
  digitalWrite(SS, HIGH);
  delay (1000);
}

仆从设备代码(轮询方式)

#include <Arduino.h>
#include <SPI.h>
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<<SPIF)));
      return SPDR;
}
void loop(void){
      buf[pos++] = SPI_SlaveReceive();
      if(buf[pos-1]=='\n'){
            buf[pos] = 0;
            pos = 0;
            Serial.print(buf);
      }
}
文档更新时间: 2021-02-14 14:27   作者:admin