Como usar SPI no Arduino: comunicação entre duas placas Arduino

Tempo de leitura: 8 minutes

Um microcontrolador usa muitos protocolos diferentes para se comunicar com vários sensores e periféricos. Existem muitos protocolos para comunicação sem fio e com fio, e a técnica de comunicação mais comumente usada é a comunicação serial. A comunicação serial é o processo de enviar dados um bit por vez, sequencialmente, por um canal de comunicação ou barramento. Existem muitos tipos de comunicação serial, como UART, CAN, USB, I2C e SPI.

Neste tutorial, aprendemos sobre o protocolo SPI e como usá-lo no Arduino. Usaremos o protocolo SPI para comunicação entre dois Arduinos. Aqui, um Arduino atuará como mestre e outro como escravo, dois LEDs e botões de pressão serão conectados a ambos os arduinos. Para demonstrar a comunicação SPI, controlaremos o LED do lado mestre pelo botão no lado escravo e vice-versa usando o protocolo de comunicação SPI Serial.

A comunicação SPI é explicada anteriormente em outros microcontroladores:

Comunicação SPI com microcontrolador PIC PIC16F877A (Breve)
Interface de tela de toque TFT LCD de 3,5 polegadas com Raspberry Pi (Breve)
Programação do microcontrolador AVR com pinos SPI (Breve)
Interface do LCD gráfico Nokia 5110 com o Arduino

 

O que é SPI?

SPI (Serial Peripheral Interface) é um protocolo de comunicação serial. A interface SPI foi descoberta pela Motorola em 1970. A SPI tem uma conexão full duplex, o que significa que os dados são enviados e recebidos simultaneamente. Ou seja, um mestre pode enviar dados ao escravo e um escravo pode enviar dados ao mestre simultaneamente. SPI é uma comunicação serial síncrona, o que significa que o relógio é necessário para fins de comunicação.

 

Trabalho da SPI

Um SPI tem uma comunicação mestre/escravo usando quatro linhas. Um SPI pode ter apenas um mestre e pode ter vários escravos. Um mestre geralmente é um microcontrolador e os escravos podem ser um microcontrolador, sensores, ADC, DAC, LCD etc.

Abaixo está a representação do diagrama de blocos de SPI Master com Single Slave.

SPI segue as quatro linhas MISO, MOSI, SS e CLK

  • MISO (Master in Slave Out) – A linha Slave para enviar dados ao mestre.
  • MOSI (Master Out Slave In) – A linha Master para envio de dados aos periféricos.
  • SCK (Serial Clock) – Os pulsos de clock que sincronizam a transmissão de dados gerados pelo mestre.
  • SS (Slave Select) – O mestre pode usar este pino para habilitar e desabilitar dispositivos específicos.

Mestre SPI com vários escravos

Para iniciar a comunicação entre o mestre e o escravo, precisamos definir o pino Slave Select (SS) do dispositivo necessário para LOW, para que ele possa se comunicar com o mestre. Quando está alto, ele ignora o mestre. Isso permite que você tenha vários dispositivos SPI compartilhando as mesmas linhas MISO, MOSI e CLK de mestre. Como você pode ver na imagem acima, existem quatro escravos nos quais o SCLK, MISO, MOSI são comuns conectados ao mestre e o SS de cada escravo é conectado separadamente aos pinos SS individuais (SS1, SS2, SS3) do mestre. Ao definir o pino SS necessário para BAIXO, um mestre pode se comunicar com esse escravo.

 

Pinos SPI no Arduino UNO

A imagem abaixo mostra os pinos SPI presentes no Arduino UNO (na caixa vermelha).

SPI LinePino no Arduino
MOSI11 ou ICSP-4
MISO12 ou ICSP-1
SCK13 ou ICSP-3
SS10

 

Usando SPI no Arduino

Antes de iniciar a programação para comunicação SPI entre dois Arduinos. Precisamos aprender sobre a biblioteca SPI usada no Arduino IDE.

A biblioteca <SPI.h> está incluída no programa para usar as seguintes funções para comunicação SPI.

1. SPI.begin()

USO: Para inicializar o barramento SPI configurando SCK, MOSI e SS para saídas, puxando SCK e MOSI para baixo e SS para alto.

2. SPI.setClockDivider(divider)

USO: Para definir o divisor de relógio SPI em relação ao relógio do sistema. Os divisores disponíveis são 2, 4, 8, 16, 32, 64 ou 128.

Divisores:

  • SPI_CLOCK_DIV2
  • SPI_CLOCK_DIV4
  • SPI_CLOCK_DIV8
  • SPI_CLOCK_DIV16
  • SPI_CLOCK_DIV32
  • SPI_CLOCK_DIV64
  • SPI_CLOCK_DIV128

3. SPI.attachInterrupt(handler)

USO: Esta função é chamada quando um dispositivo escravo recebe dados do mestre.

4. SPI.transfer(val)

USO: Esta função permite enviar e receber dados simultaneamente entre mestre e escravo.

Então, agora vamos começar com uma demonstração prática do protocolo SPI no Arduino. Neste tutorial usaremos dois arduino, um como mestre e outro como escravo. Ambos os Arduino vêm com um LED e um botão de pressão separadamente. O LED mestre pode ser controlado usando o botão do Arduino escravo e o LED do Arduino escravo pode ser controlado pelo botão do Arduino mestre usando o protocolo de comunicação SPI presente no arduino.

 

Componentes necessários

  • Arduino UNO (2)
  • LED (2)
  • Push Button (2)
  • Resistor 10k (2)
  • Resistor 2.2k (2)
  • ProtoBoard
  • Fios de Conexão

 

Diagrama de Circuito de Comunicação Arduino SPI

 

Explicação de programação

Este tutorial tem dois programas, um para o arduino mestre e outro para o arduino escravo. Programas completos para ambos os lados são fornecidos no final deste projeto.

 

Explicação de programação do Master Arduino

1. Em primeiro lugar, precisamos incluir a biblioteca SPI para usar as funções de comunicação SPI.

#include<SPI.h>

2. No void setup()

  • Iniciamos a comunicação serial na taxa Baud 115200.
Serial.begin(115200);
  • Conecte o LED ao pino 7 e pressione o botão ao pino 2 e defina os pinos OUTPUT e INPUT, respectivamente.
pinMode(ipbutton,INPUT);                
pinMode(LED,OUTPUT);
  • Em seguida, começamos a comunicação SPI
SPI.begin();
  • Em seguida, configuramos o Clockdivider para comunicação SPI. Aqui, definimos o divisor 8.
SPI.setClockDivider(SPI_CLOCK_DIV8);
  • Em seguida, defina o pino SS HIGH, pois não iniciamos nenhuma transferência para o arduino escravo.
digitalWrite(SS,HIGH);

3. No void loop():

  • Lemos o status do pino do botão de pressão conectado ao pino 2 (Master Arduino) para enviar esses valores ao escravo Arduino.
buttonvalue = digitalRead(ipbutton);
  • Defina a lógica para definir o valor x (a ser enviado para o escravo) dependendo da entrada do pino 2
if(buttonvalue == HIGH)               
{
  x = 1;
}
else
{
  x = 0;
}
  • Antes de enviar o valor, precisamos BAIXAR o valor de seleção do escravo para iniciar a transferência do mestre para o escravo.
digitalWrite(SS, LOW);
  • Aí vem o passo importante, na instrução a seguir enviamos o valor do botão de ação armazenado na variável Mastersend para o arduino escravo e também recebemos o valor do escravo que será armazenado na variável Mastereceive.
Mastereceive=SPI.transfer(Mastersend);
  • Depois disso, dependendo do valor do Mastereceive, vamos ligar ou desligar o LED do Master Arduino.
if(Mastereceive == 1)                   
  {
    digitalWrite(LED,HIGH);        // Define o pino 7 HIGH
    Serial.println("LED mestre LIGADO");
  }
  else
  {
   digitalWrite(LED,LOW);         // Define o pino 7 LOW
   Serial.println("LED mestre apagado");
  }

Nota: Usamos serial.println () para visualizar o resultado no Serial Motor do Arduino IDE.

 

Explicação de programação do Slave Arduino

1. Em primeiro lugar, precisamos incluir a biblioteca SPI para usar as funções de comunicação SPI.

#include<SPI.h>

2. No void setup()

  • Iniciamos a comunicação serial na taxa Baud 115200.
Serial.begin(115200);
  • Conecte o LED ao pino 7 e pressione o botão ao pino 2 e defina os pinos OUTPUT e INPUT respectivamente.
pinMode(ipbutton,INPUT);           
pinMode(LED,OUTPUT);
  • O passo importante aqui são as seguintes declarações
pinMode(MISO,OUTPUT);

A instrução acima define MISO como OUTPUT (tem que enviar dados para Master IN). Assim, os dados são enviados via MISO do Slave Arduino.

  • Agora ligue o SPI no modo escravo usando o registro de controle do SPI
SPCR |= _BV(SPE);
  • Em seguida, ligue a interrupção para comunicação SPI. Se um dado for recebido do mestre, a rotina de interrupção é chamada e o valor recebido é obtido do SPDR (Registro de dados SPI)
SPI.attachInterrupt();
  • O valor do master é retirado do SPDR e armazenado na variável Slavereceived. Isso ocorre na seguinte função de rotina de interrupção.
ISR (SPI_STC_vect)
{
  Slavereceived = SPDR;                  
  received = true;                       
}

3. Em seguida, em void loop(), configuramos o LED do Arduino Slave para LIGAR ou DESLIGAR dependendo do valor recebido do Escravo.

if (Slavereceived==1)
   {
      digitalWrite(LEDpin,HIGH); // Define o pino 7 como HIGH LED ON
      Serial.println("Slave LED ON");
   } else {
      digitalWrite(LEDpin,LOW);  // Define o pino 7 como LED BAIXO DESLIGADO
      Serial.println("Slave LED OFF");
   }
  • Em seguida, lemos o status do botão Slave Arduino e armazenamos o valor no Slavesend para enviar o valor para o Arduino Mestre, atribuindo valor ao registro SPDR.
buttonvalue = digitalRead(buttonpin); 
if (buttonvalue == HIGH)  {
    x=1;
} else {
    x=0;
}
Slavesend=x;                             
SPDR = Slavesend;

Nota: Usamos serial.println() para visualizar o resultado no Serial Motor do Arduino IDE.

 

Testando o hardware

Abaixo está a imagem da configuração final para comunicação SPI entre duas placas Arduino.

Quando o botão no lado mestre é pressionado, o LED branco no lado escravo liga.

E quando o botão no lado escravo é pressionado, o LED vermelho no lado mestre acende.

O código completo para Arduino Master e Slave é fornecido abaixo

 

Código

Master Arduino Code:
//SPI MASTER (ARDUINO)
// COMUNICAÇÃO SPI ENTRE DOIS ARDUINO

#include<SPI.h>                             // Biblioteca para SPI 
#define LED 7           
#define ipbutton 2
int buttonvalue;
int x;

void setup (void)
{
  Serial.begin(115200);                   // Inicia a comunicação serial na taxa Baud 115200
  
  pinMode(ipbutton,INPUT);                // Define o pino 2 como entrada
  pinMode(LED,OUTPUT);                    // Define o pino 7 como saída
  
  SPI.begin();                           // Inicia a comunicação SPI
  SPI.setClockDivider(SPI_CLOCK_DIV8);   // Define o relógio para comunicação SPI em 8 (16/8 = 2Mhz)
  digitalWrite(SS,HIGH);                 // Configurando Slave Select como HIGH (então o mestre não conecta com o escravo)
}

void loop(void)
{
  byte Mastersend,Mastereceive;          

  buttonvalue = digitalRead(ipbutton);   // Lê o status do pino 2

  if(buttonvalue == HIGH)                // Lógica para definir o valor x (a ser enviado ao escravo) dependendo da entrada do pino 2
  {
    x = 1;
  } else {
    x = 0;
  }
  
  digitalWrite(SS, LOW);                  // Inicia a comunicação com o Slave conectado ao mestre
  
  Mastersend = x;                            
  Mastereceive=SPI.transfer(Mastersend); // Envia o valor do mastersend para o escravo também recebe o valor do escravo
  
  if(Mastereceive == 1)                  // Lógica para definir a saída do LED dependendo do valor recebido do escravo
  {
    digitalWrite(LED,HIGH);              // Define o pino 7 HIGH
    Serial.println("LED mestre LIGADO");
  }
  else
  {
   digitalWrite(LED,LOW);               // Define o pino 7 LOW
   Serial.println("LED mestre apagado");
  }
  delay(1000);
}
Slave Arduino Code:
//SPI SLAVE (ARDUINO)
// COMUNICAÇÃO SPI ENTRE DOIS ARDUINO

#include<SPI.h>
#define LEDpin 7
#define buttonpin 2
volatile boolean received;
volatile byte Slavereceived,Slavesend;
int buttonvalue;
int x;

void setup()
{
  Serial.begin(115200);
  
  pinMode(buttonpin,INPUT);               // Definindo o pino 2 como INPUT
  pinMode(LEDpin,OUTPUT);                 // Definindo o pino 7 como OUTPUT
  pinMode(MISO,OUTPUT);                   // Define MISO como OUTPUT (tem que enviar dados para Master IN

  SPCR |= _BV(SPE);                       // Ligue o SPI no modo Slave
  received = false;

  SPI.attachInterrupt();                  // Interrupção LIGADA é definida para comunicação SPI
}

ISR (SPI_STC_vect)                        // Função de rotina de entrada
{
   Slavereceived = SPDR;                  // Valor recebido do mestre da loja na variável escrava recebido
   received = true;                        // Conjuntos recebidos como verdadeiros
}

void loop()
{ 
   if(received)                            // Lógica para definir LED LIGADO OU DESLIGADO, dependendo do valor recebido do mestre
   {
      if (Slavereceived==1) 
      {
        digitalWrite(LEDpin,HIGH);         // Define o pino 7 como HIGH LED ON
        Serial.println("Slave LED ON");
      } else {
        digitalWrite(LEDpin,LOW);          // Define o pino 7 como LED BAIXO DESLIGADO
        Serial.println("Slave LED OFF");
      }
      
      buttonvalue = digitalRead(buttonpin);  // Lê o status do pino 2
      
      if (buttonvalue == HIGH)               // Lógica para definir o valor de x para enviar ao mestre
      {
        x=1;
      } else {
        x=0;
      }
      
      Slavesend=x                             
      SPDR = Slavesend;                           // Envia o valor x para o mestre via SPDR
      delay(1000);
   }
}