Рейтинг@Mail.ru
Интерфейс SPI
Интерфейс SPI

Интерфейс SPI и Arduino

7 комментарии Arduino
Print Friendly, PDF & Email

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

Инструкция по работе Arduino с интерфейсом SPI

Нам понадобится:

1Описание последовательного интерфейса SPI

SPI – Serial Peripheral Interface или «Последовательный периферийный интерфейс» – это синхронный протокол передачи данных для сопряжения ведущего устройства (Master) с периферийными устройствами (Slave). Ведущим устройством часто является микроконтроллер. Связь между устройствами осуществляется по четырём проводам, поэтому SPI иногда называют «четырёхпроводной интерфейс». Вот эти шины:

НазваниеНазначение шины SPI
MOSI (Master Out Slave In)линия передачи данных от ведущего к ведомым устройствам;
MISO (Master In Slave Out)линия передачи от ведомого к ведущему устройству;
SCLK (Serial Clock)тактовые импульсы синхронизации, генерируемые ведущим устройством;
SS (Slave Select)линия выбора ведомого устройства; когда на линии логический "0", ведомое устройство «понимает», что сейчас обращаются к нему.

Существует четыре режима передачи данных (SPI_MODE0, SPI_MODE1, SPI_MODE2, SPI_MODE3), обусловленные сочетанием полярности тактовых импульсов (работаем по уровню HIGH или LOW), Clock Polarity, CPOL, и фазой тактовых импульсов (синхронизация по переднему или заднему фронту тактового импульса), Clock Phase, CPHA. В последнем столбце таблицы приведены поясняющие иллюстрации. На них Sample обозначены моменты, когда данные на линии должны быть готовы и считываются устройствами. Буквой Z отмечено, что состояние данных на линии неизвестно или не важно.

РежимПолярность тактовых импульсов (CPOL)Фаза тактовых импульсов (CPHA)Диаграмма режима
SPI_MODE000Диаграмма сигналов при режиме SPI 0
SPI_MODE101Диаграмма сигналов при режиме SPI 1
SPI_MODE210Диаграмма сигналов при режиме SPI 2
SPI_MODE311Диаграмма сигналов при режиме SPI 3

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

Виды подключения устройств: независимое и каскадное
Виды подключения устройств для работы по интерфейсу SPI: независимое и каскадное

2Реализация интерфейса SPI на платах семейства Arduino

В Arduino шины интерфейса SPI находятся на определённых портах. У каждой платы своё соответствие выводов. Для удобства выводы продублированы и вынесены также на отдельный разъём ICSP (In Circuit Serial Programming, программирование устройства, включённого в цепь, по последовательному протоколу). Обратите внимание, что на разъёме ICSP отсутствует пин выбора ведомого – SS, т.к. подразумевается, что Arduino будет использоваться как ведущее устройство в сети. Но при необходимости вы можете назначить любой цифровой вывод Ардуино в качестве SS.

На рисунке приведено стандартное соответствие выводов шинам SPI для Arduino UNO и Nano.

Реализация интерфейса SPI на платах Arduino UNO и Arduino Nano
Реализация интерфейса SPI на платах Arduino UNO и Arduino Nano

3Стандартная библиотека для работы по интерфейсу SPI

Для Arduino написана специальная библиотека, которая реализует протокол SPI. Она устанавливается вместе со средой разработки Arduino IDE. Подключается она так: в начале программы добавляем #include SPI.h.

Чтобы начать работу по протоколу SPI, нужно задать настройки и затем инициализировать протокол с помощью процедуры SPI.beginTransaction(). Можно выполнить это одной инструкцией: SPI.beginTransaction(SPISettings(14000000, MSBFIRST, SPI_MODE0))

Это значит, что мы инициализируем протокол SPI на частоте 14 МГц, передача данных идёт, начиная с MSB (наиболее значимого бита), в режиме SPI_MODE0.

После инициализации выбираем ведомое устройство, переводя соответствующий пин SS в состояние LOW. Затем передаём ведомому устройству данные командой SPI.transfer(). После передачи возвращаем SS в состояние HIGH.

Временная диаграмма работы интерфейса SPI
Временная диаграмма работы интерфейса SPI

Работа с протоколом завершается командой SPI.endTransaction().

Желательно минимизировать время выполнения передачи между инструкциями SPI.beginTransaction() и SPI.endTransaction(), чтобы не возникло накладок, если другое устройство попробует инициализировать передачу данных, используя другие настройки.

Если вы планируете в своём скетче использовать стандартные пины Arduino, можно не описывать их в начале программы, т.к. они уже определены в самой библиотеке и имеют следующие имена:

#define PIN_SPI_SS    (10)
#define PIN_SPI_MOSI  (11)
#define PIN_SPI_MISO  (12)
#define PIN_SPI_SCK   (13)

Данные пины определены в файле pins_arduino.h , который находится по пути %programfiles%\arduino-(версия)\hardware\arduino\avr\variants\ (если вы устанавливали программу в стандартное расположение). То есть, например, чтобы опустить пин выбора ведомого в состояние "0", можно написать:

digitalWrite(PIN_SPI_SS, LOW);

4Подключение сдвигового регистра к Arduino

Рассмотрим практическое применение интерфейса SPI. Будем зажигать светодиоды, управляя 8-битным сдвиговым регистром по шине SPI. Подключим к Arduino сдвиговый регистр 74HC595. К каждому из 8-ми выходов регистра через ограничительный резистор подключим по светодиоду номиналом 220 Ом. Схема приводится на рисунке.

Схема подключения сдвигового регистра 74HC595 к Arduino
Схема подключения сдвигового регистра 74HC595 к Arduino

5Скетч для управления сдвиговым регистром по интерфейсу SPI

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

#include <SPI.h>

const int pinSelect = 8; // пин выбора регистра

void setup() {
  SPI.begin(); // инициализация интерфейса SPI
  pinMode(pinSelect, OUTPUT); // 
  digitalWrite(pinSelect, LOW); // выбор ведомого устройств (регистра)
  SPI.transfer(0); // очищаем содержимое регистра
  digitalWrite(pinSelect, HIGH); // конец передачи
  Serial.begin(9600);
}

void loop() {
  for (int i=0; i<8; i++) {    
    double L = pow(2, i); // вычисляем активный светодиод
    int leds = round(L); // округляем число до целого
    digitalWrite(pinSelect, LOW); // выбор регистра сдвига
    SPI.transfer(leds); // передаём значение в сдвиговый регистр
    digitalWrite(pinSelect, HIGH); // конец передачи
    Serial.println(leds, BIN); // вывод в двоичном представлении
    delay(125); // задержка 125 мс
  }
  Serial.println("--------");
}

Сначала подключим библиотеку SPI и инициализируем интерфейс SPI. Определим пин 8 как пин выбора ведомого устройства SS. Очистим сдвиговый регистр, послав в него значение "0". Инициализируем последовательный порт.

Чтобы зажечь определённый светодиод с помощью сдвигового регистра, нужно подать на его вход 8-разрядное число. Например, чтобы загорелся первый светодиод – подаём двоичное число 00000001, чтобы второй – 00000010, чтобы третий – 00000100, и т.д. Эти двоичные числа при переводе в десятичную систему счисления образуют такую последовательность: 1, 2, 4, 8, 16, 32, 64, 128 и являются степенями двойки от 0 до 7.

Соответственно, в цикле loop() по количеству светодиодов делаем пересчёт от 0 до 7. Функция pow(основание, степень) возводит 2 в степень счётчика цикла. Микроконтроллеры не очень точно работают с числами типа "double", поэтому для преобразования результата в целое число используем функцию округления round(). И передаём получившееся число в сдвиговый регистр. Для наглядности в монитор последовательного порта выводятся значения, которые получаются при этой операции: единичка «бежит» по разрядам – светодиоды загораются волной.

Числа, посылаемые в сдвиговый регистр 74HC595
Числа, посылаемые в сдвиговый регистр 74HC595

6«Бегущая волна» из светодиодов

Светодиоды загораются по очереди, и мы наблюдаем бегущую «волну» из огоньков. Управление светодиодами осуществляется с помощью сдвигового регистра, к которому мы подключились по интерфейсу SPI. В результате для управления 8-ю светодиодами задействованы всего 3 вывода Arduino. Если бы мы подключали светодиоды напрямую к цифровым портам Arduino, нам бы потребовалось для каждого светодиода использовать отдельный порт.

Мы изучили самый простой пример работы Arduino с шиной SPI. Более подробно рассмотрим работу нескольких сдвиговых регистров при независимом и каскадном подключениях в отдельной статье.

Последнее изменениеПятница, 02 Август 2019 19:26 Прочитано 43549 раз

Поделиться

Print Friendly, PDF & Email

7 комментарии

  • Алекс
    Алекс 16.05.2019 19:01 Комментировать

    Даешь статью про каскадное подключение. Мне прямо очень надо! Спасибо!)))

  • aave1
    aave1 18.05.2019 18:55 Комментировать

    Алекс, обязательно. Просто сейчас совсем нет времени это.

  • Михаил
    Михаил 07.06.2019 02:30 Комментировать

    По поводу розовых и голубых вертикальных линий: очевидно, что розовый цвет отражает передний фронт тактового импульса, а голубой - задний фронт. Не очевидно то, что они вводят читателя в заблуждение. Блуд в том, что в данном применении они извращают понимание фазы. Считывание (latch) текущего бита всегда происходит по фронту импульса: при фазе 0 - по переднему, а при фазе 1 - по заднему. В каждом цикле обработки текущего бита моменту отсечки (latch) ВСЕГДА ПРЕДШЕСТВУЕТ (на половину тактового сигнала) момент выдачи бита (shift). Передний фронт тактового сигнала (в SPI) не его начало, а его середина. Комплект цветных линий на рисунке разумен для режима передачи (mode) SPI 1 и 3. Для режима 0 и 2 весь этот комплект линий должен быть смещён на пол такта влево.

  • Михаил
    Михаил 07.06.2019 13:38 Комментировать

    Хочу обратить внимание читателей на то, что для прояснения принципа функционирования SPI, автором данной странички представлен рисунок белиберды под названием "Временная диаграмма работы интерфейса SPI".
    Что там не так:
    1. передаются 8 бит - значит и импульсов SCLK должно быть 8, а на рисунке их 9 с гаком;
    2. моментов представления текущих битов (на рисунке "скрещивание" MOSI) должно быть 8, а их 9;
    3. принципиально - SPI СИНХРОННАЯ передача, поскольку начало передачи на рисунке соответствует mode0, то отсечка (latch) каждого передаваемого бита должна происходить по переднему фронту импульса (середина текущего такта), а представление текущего бита (shift) - в момент начала текущего такта SCLK.
    На рисунке синхронность процесса передачи отсутствует.

  • aave1
    aave1 07.06.2019 15:20 Комментировать

    Михаил, большое спасибо за замечания! Заменил иллюстрацию, на которой изображена временная диаграмма SPI.

  • Михаил
    Михаил 08.06.2019 00:01 Комментировать

    Если на клетке слона прочтёшь надпись «буйвол», не верь глазам своим. Применительно к SPI, в клетке находится SS, а надпись на клетке - "Slave Select". На самом деле, SS - синхронизация сеанса обмена данными. Синхронизация сеанса об мена данными - это единственная и неотъемлемая роль SS в протоколе интерфейса SPI. Физически SS представляет собой импульс. Передний фронт этого импульса - старт, начало общения Управляющего и Управляемого, а задний фронт - конец общения, начало осмысления. Следует отметить, что импульс SS особенно важен для Управляющего т.к. по переднему фронту импульса SS Управляющий начинает генерировать тактовые сигналы SCLK, каждый из которых синхронизирует обмен одного бита данных.
    Предвидя то, что среди читающих мои строки найдутся непримиримые, укориенелые сторонники "Slave Select", потряхивающие картинкой "Один мастер и три слэйва", хочу их успокоить: - ) В нашей стране извращения не запрещен. Более того, а не пора ли перейти к картинке "Три мастера один слэйв"? Прошу детей к этому не привлекать.

  • Михаил
    Михаил 12.06.2019 01:26 Комментировать

    Поменял "шило на мыло". И в предыдущий рисунок демонстрировал отсутствие синхронизации, и текущий рисунок нагло демонстрирует отсутствие синхронизации. Нарочито моменты смены значения текущего передаваемого бита (shift) не синхронизированы.

Оставить комментарий

Убедитесь, что вы вводите (*) необходимую информацию, где нужно
HTML-коды запрещены