Pull to refresh
242.21
FirstVDS
Виртуальные серверы в ДЦ в Москве

Соединение нескольких устройств через SPI

Reading time11 min
Views33K
image
SPI: Master&Slaves. Источник картинки

Довольно часто при создании различных самоделок на основе микроконтроллера Arduino разработчики как бы «приделывают к телу руки и ноги», то есть присоединяют некую периферию, которая управляется с центрального микроконтроллера. Однако иногда возникают такие ситуации, когда необходимо соединить «два мозга друг с другом», то есть соединить два микроконтроллерa. Об этом мы и поговорим в этой статье.

Для коммуникации существует несколько вариантов, и один из них называется SPI.
SPI (англ. Serial Peripheral Interface, SPI bus — последовательный периферийный интерфейс, шина SPI) — последовательный синхронный стандарт передачи данных в режиме полного дуплекса, предназначенный для обеспечения простого и недорогого высокоскоростного сопряжения микроконтроллеров и периферии. SPI также иногда называют четырёхпроводным (англ. four-wire) интерфейсом.

image
Автор: Cburnett, Источник картинки

В отличие от стандартного последовательного порта (англ. standard serial port), SPI — это синхронный интерфейс, в котором любая передача синхронизирована с общим тактовым сигналом, генерируемым ведущим устройством (процессором). Принимающая (ведомая) периферия синхронизирует получение битовой последовательности с тактовым сигналом. К одному последовательному периферийному интерфейсу ведущего устройства-микросхемы может присоединяться несколько микросхем. Ведущее устройство выбирает ведомое для передачи, активируя сигнал «выбор кристалла» (англ. chip select) на ведомой микросхеме.

Периферия, которая не была выбрана процессором, не будет принимать участия в передаче по SPI.

Интерфейс


В SPI используются четыре цифровых сигнала:

  • MOSI — выход ведущего, вход ведомого (англ. Master Out Slave In). Служит для передачи данных от ведущего устройства ведомому.
  • MISO — вход ведущего, выход ведомого (англ. Master In Slave Out). Служит для передачи данных от ведомого устройства ведущему.
  • SCLK или SCK — последовательный тактовый сигнал (англ. Serial Clock). Служит для передачи тактового сигнала для ведомых устройств.
  • CS или SS — выбор микросхемы, выбор ведомого (англ. Chip Select, Slave Select).

Имена портов интерфейса SPI могут различаться в зависимости от производителя аппаратных средств, при этом возможны такие варианты:

  • MISO: SOMI, SDO (на устройстве), DO, DON, SO, MRSR.
  • MOSI: SIMO, SDI (на устройстве), DI, DIN, SI, MTST.
  • SCLK: SCK, CLK, SPC (SPI serial port clock).
  • SS: nCS, CS, CSB, CSN, NSS, nSS, STE, SYNC.

Синхронизация в SPI


Частота следования битовых интервалов в линиях передачи данных определяется синхросигналом SCK, который генерирует ведущее устройство, ведомые устройства используют синхросигнал для определения моментов изменения битов на линии данных, при этом ведомые устройства никак не могут влиять на частоту следования битовых интервалов.

Как в ведущем устройстве, так и в ведомом устройстве есть счётчик импульсов синхронизации (битов). Счётчик в ведомом устройстве позволяет последнему определить момент окончания передачи пакета. Счётчик сбрасывается при выключении подсистемы SPI, такая возможность всегда имеется в ведущем устройстве. В ведомом устройстве счётчик обычно сбрасывается деактивацией интерфейсного сигнала SS.

Так как действия ведущего и ведомого устройства тактируются одним и тем же сигналом, то к стабильности этого сигнала не предъявляется никаких специальных требований, за исключением ограничения на длительность полупериодов, которая определяется максимальной рабочей частотой более медленного устройства. Это позволяет использовать SPI в системах с низкостабильной тактовой частотой, а также облегчает программную эмуляцию ведущего устройства.

Приём и передача данных в SPI


Передача осуществляется пакетами. Длина пакета, как правило, составляет 1 байт (8 бит), при этом известны реализации SPI с иной длиной пакета, например, 4 бита. Ведущее устройство инициирует цикл связи установкой низкого уровня на выводе выбора подчинённого устройства (SS) того устройства, с которым необходимо установить соединение. При низком уровне сигнала SS:

  • схемотехника ведомого устройства находится в активном состоянии,
  • вывод MISO переводится в режим «выход»,
  • тактовый сигнал SCLK от ведущего устройства воспринимается ведомым и вызывает считывание на входе MOSI значений, передаваемых от ведущего битов и сдвиг регистра ведомого устройства.

image
Автор: Cburnett, Источник картинки

Подлежащие передаче данные ведущее и ведомое устройства помещают в сдвиговые регистры. После этого ведущее устройство генерирует импульсы синхронизации на линии SCLK, что и приводит к взаимному обмену данными. Передача данных идёт бит за битом от ведущего по линии MOSI и от ведомого по линии MISO.

Передача осуществляется, начиная со старших битов, но некоторые производители допускают изменение порядка передачи битов программными методами. После передачи каждого пакета данных ведущее устройство, в целях синхронизации ведомого устройства, может перевести линию SS в высокое состояние.

Режимы работы интерфейса SPI


Возможны четыре режима синхронизации. Режим определяется комбинацией бит CPHA и CPOL:

  • CPOL = 0 — исходное состояние сигнала синхронизации — низкий уровень.
  • CPOL = 1 — исходное состояние сигнала синхронизации — высокий уровень.
  • CPHA = 0 — выборка данных производится по переднему фронту (переключению) сигнала синхронизации. То есть по переключению из основного в противоположное ему.
  • CPHA = 1 — выборка данных производится по заднему фронту (переключению) сигнала синхронизации.То есть по переключению обратно к основному из противоположного.

Для обозначения режимов работы интерфейса SPI принято следующее соглашение:

  • режим 0 (CPOL = 0, CPHA = 0),
  • режим 1 (CPOL = 0, CPHA = 1),
  • режим 2 (CPOL = 1, CPHA = 0),
  • режим 3 (CPOL = 1, CPHA = 1).

Топология систем связи на базе SPI


В простейшем случае к ведущему устройству подключено единственное ведомое устройство и необходим двусторонний обмен данными. В таком случае используется трёхпроводная схема подключения. Интерфейс SPI позволяет подключать к одному ведущему устройству несколько ведомых устройств, причём подключение может быть осуществлено несколькими способами.

Первый способ дает возможность реализовать радиальную структуру связи (топология типа «звезда»), его принято считать основным вариантом подключения нескольких ведомых устройств:

image
Автор: Cburnett, Источник картинки

В этом случае для обмена более чем с одним ведомым устройством, ведущее устройство должно формировать соответствующее количество сигналов выбора ведомого устройства (SS). При обмене данными с ведомым устройством, соответствующий ему сигнал SS переводится в активное (низкое) состояние, при этом все остальные сигналы SS находятся в неактивном (высоком) состоянии. Выводы данных MISO ведомых устройств соединены параллельно, при этом они находятся в неактивном состоянии, а перед началом обмена один из выходов (выбранного ведомого устройства) переходит в активный режим.

Второй способ позволяет выполнять структуру связи типа «кольцо»:

image
Автор: Cburnett, Источник картинки

В данном случае для активации одновременно нескольких ведомых устройств используется один сигнал SS, а выводы данных всех устройств соединены последовательно и образуют замкнутую цепь. При передаче пакета от ведущего устройства этот пакет получает первое ведомое устройство, которое, в свою очередь, транслирует свой пакет следующему ведомому устройству и так далее. Для того чтобы пакет от ведущего устройства достиг определённого ведомого устройства, ведущее устройство должно отправить ещё несколько пакетов.

Преимущества и недостатки интерфейса SPI


Преимущества


  • Полнодуплексная передача данных по умолчанию.
  • Более высокая пропускная способность по сравнению с I²C или SMBus.
  • Возможность произвольного выбора длины пакета, длина пакета не ограничена восемью битами.
  • Простота аппаратной реализации.
  • Используется только четыре вывода, что гораздо меньше, чем для параллельных интерфейсов.
  • Однонаправленный характер сигналов позволяет при необходимости легко организовать гальваническую развязку между ведущим и ведомыми устройствами.
  • Максимальная тактовая частота ограничена только быстродействием устройств, участвующих в обмене данными.

Недостатки


  • Необходимо больше выводов, чем для интерфейса I²C.
  • Ведомое устройство не может управлять потоком данных.
  • Нет подтверждения приёма данных со стороны ведомого устройства (ведущее устройство может передавать данные «в никуда»).
  • Нет определённого стандартом протокола обнаружения ошибок.
  • Отсутствие официального стандарта, что делает невозможным сертификацию устройств.
  • По дальности передачи данных интерфейс SPI уступает таким стандартам, как UART и CAN.
  • Наличие множества вариантов реализации интерфейса.
  • Нет поддержки горячего подключения устройств.

А теперь если мы попробуем обратиться к микроконтроллеру Arduino и откроем одну из стандартных библиотек, идущих в комплекте для библиотеки SPI, под названием Digital Pot Control (управление цифровым потенциометром), то мы сможем с помощью неё организовать общение с подчинённым устройством в рамках этого интерфейса.

Скетч ниже предназначен для управления AD5206, многоканальным цифровым потенциометром. Устройство является достаточно интересным и служит для изменения сопротивления в цепи электронным способом, а не вручную. Если кому интересно, можете почитать подробные спецификации этого потенциометра здесь.

Одним из его применений может быть изменение яркости светодиодов за счёт плавной регулировки сопротивления.

Говоря о технических особенностях этого устройства, можно сказать, что этот цифровой потенциометр имеет возможность установки 256 позиций и является шестиканальным (то есть поддерживает 6 подключаемых устройств, например, светодиодов). Его возможности позволяют заменить механические потенциометры, номиналами 10, 50 или 100 кОм.

Каждый из 6 переменных резисторов потенциометра выведен на корпус микросхемы в виде 3 контактов, и логика подключения к ним ничем не отличается от подключения обычных переменных резисторов.

Назначение пинов на корпусе микросхемы можно посмотреть на рисунке ниже:

image
Источник картинки

Принципиальную схему соединений светодиодов с микросхемой и пинами Arduino Uno можно увидеть на рисунке ниже:

image
Источник картинки

image
Источник картинки

Код довольно простой, он реализует сказанное выше и работает следующим образом: плавно изменяется сопротивление на каждом из 6 каналов, пробегая по каждой из 256 позиций, заставляя светодиоды сначала плавно ярко «разгореться», а потом — также плавно погаснуть. Все требующиеся пояснения содержатся в комментариях к коду.

Пример кода для управления по SPI
/*

  Digital Pot Control

  This example controls an Analog Devices AD5206 digital potentiometer.

  The AD5206 has 6 potentiometer channels. Each channel's pins are labeled

  A - connect this to voltage

  W - this is the pot's wiper, which changes when you set it

  B - connect this to ground.

 The AD5206 is SPI-compatible,and to command it, you send two bytes,

 one with the channel number (0 - 5) and one with the resistance value for the

 channel (0 - 255).

 The circuit:

  * All A pins  of AD5206 connected to +5V

  * All B pins of AD5206 connected to ground

  * An LED and a 220-ohm resisor in series connected from each W pin to ground

  * CS - to digital pin 10  (SS pin)

  * SDI - to digital pin 11 (MOSI pin)

  * CLK - to digital pin 13 (SCK pin)

 created 10 Aug 2010

 by Tom Igoe

 Thanks to Heather Dewey-Hagborg for the original tutorial, 2005

*/

// inslude the SPI library:
#include <SPI.h>

// set pin 10 as the chip select for the digital pot:

const int chipSelectPin = 10;

void setup() {

  // set the chipSelectPin as an output:

  pinMode(chipSelectPin, OUTPUT);

  // initialize SPI:

  SPI.begin();
}

void loop() {

  // go through the six channels of the digital pot:

  for (int channel = 0; channel < 6; channel++) {

    // change the resistance on this channel from min to max:

    for (int level = 0; level < 255; level++) {

      digitalPotWrite(channel, level);

      delay(10);

    }

    // wait a second at the top:

    delay(100);

    // change the resistance on this channel from max to min:

    for (int level = 0; level < 255; level++) {

      digitalPotWrite(channel, 255 - level);

      delay(10);

    }

  }

}

void digitalPotWrite(int address, int value) {

  // take the SS pin low to select the chip:

  digitalWrite(chipSelectPin, LOW);

  delay(100);

  //  send in the address and value via SPI:

  SPI.transfer(address);

  SPI.transfer(value);

  delay(100);

  // take the SS pin high to de-select the chip:

  digitalWrite(chipSelectPin, HIGH);
}


Как мы уже упоминали в самом начале, интерфейс SPI позволяет не только управлять каким-то ещё одним устройством, но и осуществлять полноценную связь в рамках модели ведущий/ведомый между двумя и более ардуинами.

Код, приведённый ниже, работает следующим образом: ардуины связаны друг с другом через SPI. Каждая из них имеет подключённую кнопку-замыкатель. Если происходит нажатие кнопки на ведущем устройстве — включается белый диод на подчинённой ардуине. И, соответственно, если нажимается кнопка на ведомом устройстве, то загорается красный светодиод на ведущей ардуине. Сам пример взят здесь, можете почитать более подробно, если интересно. Все требующиеся пояснения содержатся в комментариях к коду.

Пример связи между 2-мя ардуинами: Master и Slave
Master Arduino Code:
//SPI MASTER (ARDUINO)
//SPI COMMUNICATION BETWEEN TWO ARDUINO
//CIRCUIT DIGEST

#include<SPI.h>                             //Library for SPI
#define LED 7           
#define ipbutton 2
int buttonvalue;
int x;
void setup (void)

{
  Serial.begin(115200);                   //Starts Serial Communication at Baud Rate 115200
 
  pinMode(ipbutton,INPUT);                //Sets pin 2 as input
  pinMode(LED,OUTPUT);                    //Sets pin 7 as Output
 
  SPI.begin();                            //Begins the SPI commnuication
  SPI.setClockDivider(SPI_CLOCK_DIV8);    //Sets clock for SPI communication at 8 (16/8=2Mhz)
  digitalWrite(SS,HIGH);                  // Setting SlaveSelect as HIGH (So master doesnt connnect with slave)
}

void loop(void)
{
  byte Mastersend,Mastereceive;          

  buttonvalue = digitalRead(ipbutton);   //Reads the status of the pin 2

  if(buttonvalue == HIGH)                //Logic for Setting x value (To be sent to slave) depending upon input from pin 2
  {
    x = 1;
  }
  else
  {
    x = 0;
  }
 
  digitalWrite(SS, LOW);                  //Starts communication with Slave connected to master
 
  Mastersend = x;                            
  Mastereceive=SPI.transfer(Mastersend); //Send the mastersend value to slave also receives value from slave
 
  if(Mastereceive == 1)                   //Logic for setting the LED output depending upon value received from slave
  {
    digitalWrite(LED,HIGH);              //Sets pin 7 HIGH
    Serial.println("Master LED ON");
  }
  else
  {
   digitalWrite(LED,LOW);               //Sets pin 7 LOW
   Serial.println("Master LED OFF");
  }
  delay(1000);
}

Slave Arduino Code:
//SPI SLAVE (ARDUINO)
//SPI COMMUNICATION BETWEEN TWO ARDUINO
//CIRCUIT DIGEST
//Pramoth.T

#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);               // Setting pin 2 as INPUT
  pinMode(LEDpin,OUTPUT);                 // Setting pin 7 as OUTPUT
  pinMode(MISO,OUTPUT);                   //Sets MISO as OUTPUT (Have to Send data to Master IN

  SPCR |= _BV(SPE);                       //Turn on SPI in Slave Mode
  received = false;

  SPI.attachInterrupt();                  //Interuupt ON is set for SPI commnucation
 
}

ISR (SPI_STC_vect)                        //Inerrrput routine function
{
  Slavereceived = SPDR;         // Value received from master if store in variable slavereceived
  received = true;                        //Sets received as True
}

void loop()
{ if(received)                            //Logic to SET LED ON OR OFF depending upon the value recerived from master
   {
      if (Slavereceived==1)
      {
        digitalWrite(LEDpin,HIGH);         //Sets pin 7 as HIGH LED ON
        Serial.println("Slave LED ON");
      }else
      {
        digitalWrite(LEDpin,LOW);          //Sets pin 7 as LOW LED OFF
        Serial.println("Slave LED OFF");
      }
      
      buttonvalue = digitalRead(buttonpin);  // Reads the status of the pin 2
      
      if (buttonvalue == HIGH)               //Logic to set the value of x to send to master
      {
        x=1;
        
      }else
      {
        x=0;
      }
      
  Slavesend=x;                             
  SPDR = Slavesend;                           //Sends the x value to master via SPDR
  delay(1000);
}
}


Результат работы кода:

Из интересных моментов в этом примере, на мой взгляд, можно выделить, что используется делитель частоты синхронизации SPI. В данном случае используется делитель на 8, то есть связь между ардуинами осуществляется на скорости в 2 МГц (однако значение по умолчанию обычно равно SPI_CLOCK_DIV4, одна четверть от частоты контроллера).

Всего возможны 7 вариантов делителей частоты:

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

Однако один из самых красивых примеров, который, на мой взгляд, хорошо иллюстрирует взаимодействие между собой множества ардуин, показан всё-таки ниже.

Электронная начинка:


image
Источник картинки

Результат сборки:


В этой самоделке используется взаимодействие мастера и множества слейвов, которые работают синхронно и образуют вращающимися стрелками текущее время. Кому интересно более подробно почитать о проекте, может это сделать здесь.

Правда следует оговориться, что здесь для взаимодействия использован более продвинутый способ, где применяется интерфейс I2C, который тратит в 2 раза меньшее количество линий (2 против 4 у SPI). Хотя теоретически эта самоделка вполне могла быть выполнена и с использованием протокола SPI, однако тогда бы количество соединений критически выросло (видимо, именно поэтому автором и был выбран другой, более лаконичный на количество выводов способ).

С кодом этой реализации можно ознакомиться здесь.

Подытоживая, хочу сказать, что возможности протокола SPI не ограничиваются только перечисленными выше примерами. Однако если попытаться обобщить, то основными его назначениями, на мой взгляд, являются возможность расширить количество доступных контактов в рамках самоделок, а также разгрузить используемые микроконтроллеры, «сбрасывая» выполняющуюся задачу на подчинённых.


НЛО прилетело и оставило здесь промокод для читателей нашего блога:

15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.
Tags:
Hubs:
Total votes 30: ↑25 and ↓5+20
Comments29

Articles

Information

Website
firstvds.ru
Registered
Founded
Employees
51–100 employees
Location
Россия
Representative
FirstJohn