两个 arduino 间的 SPI 通信#

  • 两个 arduino 间的 SPI 单向通信

  • 使用中断实现 SPI Slave

  • 连接方法是(不交叉)平行相连

Question#

SPI 库不支持做为从设备。或者说 SPI 库并没有为 arduino 作为从设备而提供简单的库函数。

How to Use SPI Communication on the Arduino - Circuit Basics

The SPI library only supports the Arduino as a master device. Because of this, pin 10 needs to be configured as an OUTPUT, to avoid conflicts in the operation of the library.

那么怎么实现两个 Arduino 用 SPI 相互通信呢?

解决方法:使用 SPI 硬件及中断#

https://www.makerguides.com/master-slave-spi-communication-arduino/

这个例子的连线方案最简单,两个 Arduino 的通信内容分边由两边的串口内容提供,因此更容易把注意力集中在 SPI 通信上。

两个 Arduino 使用 SPI 通信,必定也是一主一从。主设备只需像原来那样使用 SPI 库,而在作为从设备的 Arduino 上,用中断来处理来自Master 的访问(包含读和写)。

通常的 SPI 外设一般只能读,或者只能写。但两个 Arduino 的 SPI 通信易于做成全双工的。

硬件连接#

在两机通信中,总是发送与接收相连,如两主机间的 UART 通信,以太网通信,都是采用交叉线接法。那么两个 SPI 是否要用交叉接线法呢?

答案是否定的,即不用交叉接线法,而是采用平行连法。要将相同编号(或名字)的引脚相连,Uno-1 的 MOSI 连接 Uno-2 的相同引脚 MOSI,Uno-1 的 MISO 连接 Uno-2 的相同引脚 MISO。

解释原因

Arduino 硬件设备有主从两种不同的工作模式,这是由内部的硬件寄存器设置实现的。在初始化阶段,Uno-2 会被设置为 Slave 模式,那么它的 MOSI 的后两个字母 SI 就是 Slave input 的意思。相对应的, Uno-1 需要设置成 Master 模式,同样用的也是 MOSI,但因为模式为 MO,所以为Master Output。这样就满足了一方是 Output,另一方是 Input 这一基本要求。

总结

在 SPI 通信中, Master 是谁很重要。在本例中, Master 是 Uno-1, Slave 是 Uno-2。所以即使用了相同的引脚,它们的功能也是不同(相反但却匹配)的。

Interfacing of two Arduino Uno Boards for SPI#

Interfacing of two Arduino Uno Boards for SPI#

SPI Pin

Arduino Uno-1

Arduino Uno-2

CS/SS

10

10

MOSI

11

11

MISO

12

12

SCK

13

13

Interfacing of two Arduino Uno Boards for SPI

Interfacing of Arduino Uno Board and Arduino Mega Board for SPI#

SPI Pin

Arduino UNO

Arduino Mega

CS/SS

10

53

MOSI

11

51

MISO

12

50

SCK

13

52

Code#

SPI Master code#

Master 的功能是通过 SPI.transfer() 每隔2秒发送一次固定的字符串消息到 Slave。

# include <SPI.h>
char str[ ]="Hello Slave, I'm Arduino Family\n";

void setup() 
{
Serial.begin(115200); // set baud rate to 115200 for usart
SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV8); //divide the clock by 8
Serial.println("Hello I'm SPI Mega_Master");
} 

void loop (void)
 {
 digitalWrite(SS, LOW); // enable Slave Select
 // send test string
 for(int i=0; i< sizeof(str); i++) 
 SPI.transfer( str [i] );
 digitalWrite(SS, HIGH); // disable Slave Select
 delay(2000);
}

SPI Slave code#

Slave 的功能是:

  1. 将 MISO 设置为

  2. 设置模式为 slave

  3. 绑定 SPI 中断

  4. 在 SPI 中断服务程序中定义字符串的接收

#include <SPI.h>
char str[50];
volatile byte i;
volatile bool pin;

void setup() {
  Serial.begin(115200);  // set baud rate to 115200 for usart
  Serial.println("Hello I'm SPI UNO_SLAVE");
  pinMode(MISO, OUTPUT);  // have to send on Master in so it set as output
  SPCR |= _BV(SPE);       // turn on SPI in slave mode
  i = 0;                  // buffer empty
  pin = false;
  SPI.attachInterrupt();  // turn on interrupt
}

void loop() {
  static int count;
  if (pin) {
    pin = false;  //reset the pin
    if (count++ < 5) {
      Serial.print(count);
      Serial.print(" : ");

      Serial.println(str);  //print the array on serial monitor
      if (count == 5) {
        delay(1000);
        Serial.println("The end data");
      }
    }
    delay(1000);
    i = 0;                //reset button to zero
  }
}

// Interrupt function
ISR(SPI_STC_vect) {
  char c = SPDR;  // read byte from SPI Data Register
  if (i < sizeof(str)) {
    str[i++] = c;                                   // save data in the next index in the array buff
    if ((c == '\r') || (c == '\n') || (c == '\0'))  //check for the end of the word
      pin = true;
  }
}

更多相似的例子#