Интерфейс SPI и Arduino
Изучаем интерфейс SPI и подключаем к Arduino сдвиговый регистр, к которому мы будем обращаться по этому протоколу для управления светодиодами.
Инструкция по работе Arduino с интерфейсом SPI
Нам понадобится:
- Arduino UNO или иная совместимая плата;
- сдвиговый регистр 74HC595;
- 8 светодиодов (к примеру, вот из такого набора);
- 8 резисторов по 220 Ом (рекомендую приобрести набор резисторов с номиналами от 10 Ом до 1 МОм);
- соединительные провода (например, вот такой хороший набор);
- макетная плата;
- персональный компьютер со средой разработки Arduino IDE.
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_MODE0 | 0 | 0 | |
SPI_MODE1 | 0 | 1 | |
SPI_MODE2 | 1 | 0 | |
SPI_MODE3 | 1 | 1 |
Интерфейс SPI предусматривает несколько вариантов подключения ведомых устройств: независимое и каскадное. При независимом подключении к шине SPI ведущее устройство обращается к каждому ведомому устройству индивидуально. При каскадном подключении ведомые устройства срабатывают поочерёдно, как бы каскадом.
2Реализация интерфейса SPI на платах семейства Arduino
В Arduino шины интерфейса SPI находятся на определённых портах. У каждой платы своё соответствие выводов. Для удобства выводы продублированы и вынесены также на отдельный разъём ICSP (In Circuit Serial Programming, программирование устройства, включённого в цепь, по последовательному протоколу). Обратите внимание, что на разъёме ICSP отсутствует пин выбора ведомого – SS, т.к. подразумевается, что Arduino будет использоваться как ведущее устройство в сети. Но при необходимости вы можете назначить любой цифровой вывод Ардуино в качестве SS.
На рисунке приведено стандартное соответствие выводов шинам SPI для Arduino UNO и 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.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 Ом. Схема приводится на рисунке.
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(). И передаём получившееся число в сдвиговый регистр. Для наглядности в монитор последовательного порта выводятся значения, которые получаются при этой операции: единичка «бежит» по разрядам – светодиоды загораются волной.
6«Бегущая волна» из светодиодов
Светодиоды загораются по очереди, и мы наблюдаем бегущую «волну» из огоньков. Управление светодиодами осуществляется с помощью сдвигового регистра, к которому мы подключились по интерфейсу SPI. В результате для управления 8-ю светодиодами задействованы всего 3 вывода Arduino. Если бы мы подключали светодиоды напрямую к цифровым портам Arduino, нам бы потребовалось для каждого светодиода использовать отдельный порт.
Мы изучили самый простой пример работы Arduino с шиной SPI. Более подробно рассмотрим работу нескольких сдвиговых регистров при независимом и каскадном подключениях в отдельной статье.
Поблагодарить автора:
Поделиться
Related items
8 comments
-
Алекс Четверг, 16 May 2019 19:01 Ссылка на комментарий
Даешь статью про каскадное подключение. Мне прямо очень надо! Спасибо!)))
-
aave1 Суббота, 18 May 2019 18:55 Ссылка на комментарий
Алекс, обязательно. Просто сейчас совсем нет времени это.
-
Михаил Пятница, 07 Июнь 2019 02:30 Ссылка на комментарий
По поводу розовых и голубых вертикальных линий: очевидно, что розовый цвет отражает передний фронт тактового импульса, а голубой - задний фронт. Не очевидно то, что они вводят читателя в заблуждение. Блуд в том, что в данном применении они извращают понимание фазы. Считывание (latch) текущего бита всегда происходит по фронту импульса: при фазе 0 - по переднему, а при фазе 1 - по заднему. В каждом цикле обработки текущего бита моменту отсечки (latch) ВСЕГДА ПРЕДШЕСТВУЕТ (на половину тактового сигнала) момент выдачи бита (shift). Передний фронт тактового сигнала (в SPI) не его начало, а его середина. Комплект цветных линий на рисунке разумен для режима передачи (mode) SPI 1 и 3. Для режима 0 и 2 весь этот комплект линий должен быть смещён на пол такта влево.
-
Михаил Пятница, 07 Июнь 2019 13:38 Ссылка на комментарий
Хочу обратить внимание читателей на то, что для прояснения принципа функционирования SPI, автором данной странички представлен рисунок белиберды под названием "Временная диаграмма работы интерфейса SPI".
Что там не так:
1. передаются 8 бит - значит и импульсов SCLK должно быть 8, а на рисунке их 9 с гаком;
2. моментов представления текущих битов (на рисунке "скрещивание" MOSI) должно быть 8, а их 9;
3. принципиально - SPI СИНХРОННАЯ передача, поскольку начало передачи на рисунке соответствует mode0, то отсечка (latch) каждого передаваемого бита должна происходить по переднему фронту импульса (середина текущего такта), а представление текущего бита (shift) - в момент начала текущего такта SCLK.
На рисунке синхронность процесса передачи отсутствует. -
aave1 Пятница, 07 Июнь 2019 15:20 Ссылка на комментарий
Михаил, большое спасибо за замечания! Заменил иллюстрацию, на которой изображена временная диаграмма SPI.
-
Михаил Суббота, 08 Июнь 2019 00:01 Ссылка на комментарий
Если на клетке слона прочтёшь надпись «буйвол», не верь глазам своим. Применительно к SPI, в клетке находится SS, а надпись на клетке - "Slave Select". На самом деле, SS - синхронизация сеанса обмена данными. Синхронизация сеанса об мена данными - это единственная и неотъемлемая роль SS в протоколе интерфейса SPI. Физически SS представляет собой импульс. Передний фронт этого импульса - старт, начало общения Управляющего и Управляемого, а задний фронт - конец общения, начало осмысления. Следует отметить, что импульс SS особенно важен для Управляющего т.к. по переднему фронту импульса SS Управляющий начинает генерировать тактовые сигналы SCLK, каждый из которых синхронизирует обмен одного бита данных.
Предвидя то, что среди читающих мои строки найдутся непримиримые, укориенелые сторонники "Slave Select", потряхивающие картинкой "Один мастер и три слэйва", хочу их успокоить: - ) В нашей стране извращения не запрещен. Более того, а не пора ли перейти к картинке "Три мастера один слэйв"? Прошу детей к этому не привлекать. -
Михаил Среда, 12 Июнь 2019 01:26 Ссылка на комментарий
Поменял "шило на мыло". И в предыдущий рисунок демонстрировал отсутствие синхронизации, и текущий рисунок нагло демонстрирует отсутствие синхронизации. Нарочито моменты смены значения текущего передаваемого бита (shift) не синхронизированы.
-
aave1 Пятница, 01 Ноябрь 2019 19:40 Ссылка на комментарий
Алекс, вот в этой статье разобраны примеры и независимого, и каскадного подключения по SPI: https://soltau.ru/index.php/arduino/item/458-kak-podklyuchit-sdvigovyj-registr-74hc595-k-arduino