Найти в Дзене
Электроника, ESP32, Arduino

SPI - Arduino связываем между собой платы

Данная статья носит больше практический характер - у нас есть несколько плат Arduino и нам необходимо организовать между ними связь. Для чего это может быть нужно? Иногда может не хватить ног. Понятно что в данном случае можно использовать сдвиговые регистры, мультиплексоры и другие решения, но в данном случае мы получаем дополнительные ноги в комплекте с дополнительным мозгами, а главный плюс - у нас появляются дополнительные интерфейсы. Вторая причина такого объединения - недостаток вычислительной мощности в плане реализации многозадачности. Например у нас есть сложный проект в котором необходимо производить много вычислений - и использовать механизм прерываний не представляется возможным.
Для сборки тестового примера (код которого потом вы сможете легко подправить под свои задачи) потребуются:
2 платы Ардуино NANO (UNO)
4 светодиода
4 резистора 220 Om
4 потенциометра 10 ком
Макетная плата и макетные перемычки.

Схема подключения:

Организация связи между двумя платами  Ардуино.
Организация связи между двумя платами Ардуино.

Левая плата сконфигурирована программно в режиме мастера - правая в режиме ведомого устройства. Потенциометры R1 и R2 - управляют яркостью зеленых светодиодов HL3 и HL4. Потенциометры R3 и R4- управляют яркостью красных светодиодов HL1 и HL2. Сам тестовый стенд можно питать от USB компьютера или повербанка.

SPI Arduino
SPI Arduino

У нас как бы получилась одна большая двухпроцессорная Arduino. C 2-мя интерфейсами UART, с двумя шинами I2C и множеством аналоговых и цифровых пинов. К одной из плат можно подключить какой-нибудь датчик и полностью возложить на неё обработку информации, а со второй платы мы можем пользоваться уже обработанными данными (забирая их по SPI), отдавать команды исполнительным механизмам или транслировать их по радиоканалу.

Для того чтобы разобраться как работает данный код - необходимо в общих чертах представлять как работает интерфейс SPI

Шина SPI состоит из четырех линий:
MOSI (Master Out Slave In) – передача данных от ведущего к ведомому.
MISO (Master In Slave Out) – передача данных от ведомого к ведущему.
SS (Slave Select) – выбор ведомого устройства.
SCK (Serial ClocK) – передача тактового сигнала от ведущего к ведомому.

Одно из устройств на шине называется ведущим (master), а все остальные - ведомыми (slave). Master инициализирует передачу со slave, выставляя низкий уровень на его линии SS, после чего производится одновременная двухсторонняя передача данных по линиям MOSI и MISO с тактированием по линии SCK. 

Конфигурация плат, какая из них будет ведущим устройством:

-3

а какая ведомым:

-4

выполняется программно - при этом нужно понимать что штатных средств (набора библиотек, включающий в себя некоторые функции (вроде pinMode) для превращения платы Arduino в ведомое устройство нет.

Для настройки ведущего и ведомого использует регистр управления SPI - SPCR - (Control Register).

регистр управления SPI - SPCR - (Control Register)
регистр управления SPI - SPCR - (Control Register)

Bit 7 - SPIE: SPI Interrupt Enable - Разрешение прерывания SPI
Установка бита SPIE в состояние 1 приводит к установке бита SPIF регистра SPSR и, при разрешении глобального прерывания, к выполнению прерывания SPI.

Bit 6 - SPE: SPI Enable - Разрешение SPI
Установка бита SPE в состояние 1 разрешает подключение SS, MOSI, MISO и SCK к выводам PB4, PB5, PB6 и PB7.

Bit 5 - DORD: Data Order - Порядок данных
При установленном в состояние 1 бите DORD передача слова данных происходит LSB вперед. При очищенном бите DORD первым передается MSB слова данных.

Bit 4 - MSTR: Master/Slave Select - Выбор режима ведущий/ведомый
При установленном в состояние 1 бите MSTR SPI работает в ведущем режиме и при очищенном бите в ведомом режиме.

Bit 3 - CPOL: Clock Polarity - Полярность тактового сигнала
SCK в режиме ожидания находится на высоком уровне при установленном в состояние 1 бите CPOL и на низком уровне при сброшенном бите CPOL. См. рис. 40 и 41.

Bit 2 - CPHA : Clock Phase - Фаза тактового сигнала

Работа этого бита отражена на рис. 40 и 41.

Bits 1,0 - SPR1, SPR0: SPI Clock Rate Select 1 and 0 - Выбор частоты тактового сигнала, биты 1 и 0

Выбор частоты тактового сигнала, биты
Выбор частоты тактового сигнала, биты

В данном случае мы работаем с платой Arduino именно как с микроконтроллером на языке C/C++.

Еще одно важное замечание касается настройки выводов SPI:

Настройка выводов SPI
Настройка выводов SPI

Если мы настраиваем плату Arduino как ведущее устройство (Master) - то выводы MOSI, SCK, SS как выходы необходимо настроить самостоятельно - причем в качестве выхода SS может использоваться любой цифровой пин. (SS у мастера может быть и несколько).
Если мы настраиваем плату Arduino как ведомое устройство (Slave) - то нам необходимо настроить на выход только вывод MISO.
Все остальные выходы микроконтроллер настроит самостоятельно в зависимости от роли которую мы ему назначим.

Упрощенный механизм работы интерфейса SPI показан на этом рисунке:

Обмен информацией между регистрами ведущим и ведомым устройствами
Обмен информацией между регистрами ведущим и ведомым устройствами

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

Один ведущий (MASTER) и несколько ведомых SLAVE устройств на одной шине
Один ведущий (MASTER) и несколько ведомых SLAVE устройств на одной шине

Если мы подадим логический 0 на линию SS2 то обмен байтами произойдет между Arduino Master и Arduino Slave 2. то есть на каждое дополнительное подключаемое устройство потребуется один дополнительный цифровой PIN.

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

Для этого можно применить небольшую хитрость:
- мастер отправляет байт, но мы не читаем и не обрабатываем его;
- в ведомом устройстве поступление этого "мусорного" байта отслеживаем по прерыванию и тут же происходит загрузка байта с нулевым индексом;
- далее ведем обмен массивами между ведущим и ведомым устройствами один в один - поэлементно.
В коде это будет выглядеть следующим образом:

На мастере:

Код мастера для обмена массивами из нескольких байт
Код мастера для обмена массивами из нескольких байт

На ведомом устройстве (SLAVE) :

Код ведомого для обмена массивами из нескольких байт
Код ведомого для обмена массивами из нескольких байт

Разумеется, что для Вам для практического применения разбираться во всех этих тонкостях совсем не обязательно. Вы можете просто увеличить размеры массивов под необходимый Вам объем информации - и продолжать программировать используя привычные функций среды Arduino IDE.

Используя данную технологию можно объединять любые платы Arduino и получать самодельные платформы под любые нестандартные задачи.
К примеру объединим 2 платы Ардуино на базе микроконтроллера ATmega328PB. Фишка данной платформы в том, что в нем есть два аппаратных SPI и второй сидит на аналоговых пинах.

Две платы с микроконтроллером ATmega328PB
Две платы с микроконтроллером ATmega328PB

Получившийся гибрид будет обладать следующими характеристиками:
- Порты с поддержкой PWM 18
- Аппаратные интерфейсы UART 4
- 2 свободных интерфейса SPI
- 2 шины I2C (что может быть актуально при необходимости подключить два датчика у которых нет возможности аппаратной смены адреса)
- 36 цифровых пинов 8 из которых могут работать как аналоговые

Данную плату пока я видел только у компании Амперка.
Пример скетча можно скачать по этой ссылке.
Как выглядит сама плата и её характеристики можно посмотреть
здесь

Примеры кода скачивайте по этой ссылке

Работу тестового примера и узнать дополнительную информацию можно посмотрев видео:


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