Miso mosi что это
Данная библиотека позволяет Ардуино взаимодействовать с различными SPI-устройствами, выступая при этом в роли ведущего устройства.
Краткое введение в интерфейс SPI (Serial Peripheral Interface)
Последовательный периферийный интерфейс (SPI) — это синхронный протокол последовательной передачи данных, используемый для связи микроконтроллера с одним или несколькими периферийными устройствами. Интерфейс SPI отличается относительно высокой скоростью и предназначен для связи близко расположенных устройств. Он также может использоваться для взаимодействия двух микроконтроллеров.
Согласно протоколу SPI, одно из взаимодействующих устройств (обычно микроконтроллер) всегда является ведущим и контролирует ведомые периферийные устройства. Как правило, все взаимодействющие устройства объединены тремя общими линиями:
- MISO (Master In Slave Out) — линия для передачи данных от ведомого устройства (Slave) к ведущему (Master),
- MOSI (Master Out Slave In) — линия для передачи данных от ведущего устройства (Master) к ведомым (Slave),
- SCK (Serial Clock) — тактовые импульсы, генерируемые ведущим устройством (Master) для синхронизации процесса передачи данных.
Помимо перечисленных, на каждое устройство отводится отдельная линия:
- SS (Slave Select) — вывод, присутствующий на каждом ведомом устройстве. Он предназначен для активизации Мастером того или иного периферийного устройства.
Периферийное устройство (Slave) взаимодействует с ведущим (Master) тогда, когда на выводе SS присутствует низкий уровень сигнала. В противном случае данные от Master-устройства будут игнорироваться. Такая архитектура позволяет взаимодействовать с несколькими SPI-устройствами, подключенными к одной и той же шине: MISO, MOSI и SCK.
Перед тем, как отправлять данные новому SPI-устройству, необходимо выяснить о нем несколько основных моментов:
- Сдвиг данных должен осуществляться, начиная со старшего бита (MSB) или с младшего бита (LSB)? Порядок следования данных контролируется функцией SPI.setBitOrder().
- При отсутствии тактовых импульсов линия SCK должна находиться в высоком или низком уровне? Считывание данных происходит по фронту или по спаду тактового импульса? Эти режимы работы контролируются функцией SPI.setDataMode().
- Какова должна быть скорость передачи данных по SPI? Этот параметр контролируется функцией SPI.setClockDivider().
Поскольку стандарт SPI является открытым, его реализация в разных устройствах может немного отличаться. Поэтому при написании программ, особое внимание необходимо уделять даташиту того или иного устройства.
Грубо говоря, существует четыре режима передачи данных, отличающиеся условием сдвига данных (по фронту или по спаду синхро-импульсов — так называемая фаза), а также уровнем сигнала, в котором должна находится линия SCK при отсутствии синхро-импульсов (полярность). Различные комбинации фазы и полярности, формирующие четыре режима передачи данных, сведены в таблицу:
| Режим | Полярность (CPOL) | Фаза (CPHA) |
| SPI_MODE0 | 0 | 0 |
| SPI_MODE1 | 0 | 1 |
| SPI_MODE2 | 1 | 0 |
| SPI_MODE3 | 1 | 1 |
Для изменения режима передачи данных служит функция SPI.setDataMode().
Каждое SPI-устройство налагает определенные ограничения на максимальную скорость SPI-шины. Для корректной работы периферийных устройств в библиотеке предусмотрена функция SPI.setClockDivider(), позволяющая изменять тактовую частоту шины (по умолчанию 4 МГц).
После правильной настройки всех параметров SPI, останется только выяснить, какие регистры периферийного устройства отвечают за те или иные его функции. Как правило, это описано в даташите устройства.
Для получения дополнительной информации об интерфейсе SPI, см. страницу Википедии.
Соединения
Ниже в таблице приведены номера выводов, использующиеся шиной SPI в тех или иных моделях Ардуино:
| Плата Arduino | MOSI | MISO | SCK | SS (slave) | SS (master) |
| Uno или Duemilanove | 11 или ICSP-4 | 12 или ICSP-1 | 13 или ICSP-3 | 10 | — |
| Mega1280 или Mega2560 | 51 или ICSP-4 | 50 или ICSP-1 | 52 или ICSP-3 | 53 | — |
| Leonardo | ICSP-4 | ICSP-1 | ICSP-3 | — | — |
| Due | ICSP-4 | ICSP-1 | ICSP-3 | — | 4, 10, 52 |
Обратите внимание, что на всех платах выводы MISO, MOSI и SCK соединены с одними и теми же контактами разъема ICSP. Такое расположение может быть удобно при создании универсальных плат расширения, работающих на всех моделях Ардуино.

Особенности работы вывода SS в Ардуино на базе AVR
У всех моделей Ардуино на основе микроконтроллеров AVR есть вывод SS, который используется в режиме работы Slave (например, при управлении Ардуино внешним ведущим устройством). Однако, в библиотеке реализован только режим работы Master, поэтому в этом режиме вывод SS должен быть сконфигурирован как выход. В противном случае SPI может аппаратно переключиться в режим Slave, что приведет к неработоспособности функций библиотеки.
Для управления выводом SS периферийных устройств можно использовать любой из доступных выводов. Например, на плате расширения Arduino Ethernet для взаимодействия со встроенной SD-картой и контроллером Ethernet по SPI используются выводы 4 и 10 соответственно.
Расширенные возможности SPI на Arduino Due
Существуют некоторые особенности работы с интерфейсом SPI на платах Arduino Due. Помимо основных функций и методов, применимых ко всем платам Ардуино, в библиотеке SPI предусмотрено несколько дополнительных методов. Эти методы реализовывают аппаратные возможности микроконтроллеров SAM3X и предоставляют разработчику расширенные возможности:
- автоматический управление процессом выбора ведомого устройства;
- автоматическое управление конфигурациями интерфейса SPI для различных устройств (тактовая частота, режим передачи данных и т.д.). Благодаря этому каждое из ведомых устройств может иметь собственный набор настроек, автоматически применяемых в начале передачи.
В Arduino Due есть три отдельных вывода (4, 10 и 52) для управления линиями SS периферийных устройств.
Функции
- begin()
- end()
- setBitOrder()
- setClockDivider()
- setDataMode()
- transfer()
- Использование расширенных возможностей SPI на Arduino Due
Примеры
- BarometricPressureSensor: Считывание показаний датчика об атмосферном давлении и температуре через SPI
- SPIDigitalPot: Управление цифровым потенциометром через SPI
SPI -Serial Peripheral Interface, краткое руководство
SPI — (Serial Peripheral Interface) эспиай, последовательный периферийный интерфейс иногда называемый 4-х проводным интерфейсом, является последовательным синхронным интерфейсом передачи данных. Изобретён компанией Motorola в середине 1980-x. В отличие от I2C и UART, SPI требует больше сигналов для работы, но может работать на более высоких скоростях. Не поддерживает адресацию, вместо этого используется сигнал SS (slave select — выбор ведомого), который также иногда называется CS (chip select), CE (chip enable) или SE (slave enable). Поддерживает только одного ведущего на шине. Ведущий устанавливает скорость обмена данными и другие параметры, такие как полярность и фаза тактирования. Обмен данными происходит в режиме полного дуплекса, что означает устройства на шине могут одновременно передавать и принимать данные. Интерфейс использует следующие сигналы (в номенклатуре AVR, для получения точного названия сигналов обратитесь к технической документации микросхемы, с которой работаете):
- MISO (master in slave out) — вход ведущего, выход ведомого
- MOSI (master out slave in) — выход ведущего, вход ведомого
- SCK (serial clock) — сигнал тактирования
- SS (slave select) — сигнал выбор ведомого.
Несмотря на то, что интерфейс называется 4-х проводным, для подключения нескольких ведомых понадобится по одному проводу SS для каждого ведомого (в полнодуплексной реализации). Сигналы MISO, MOSI и SCK являются общими для всех устройств на шине. Ведущий посылает сигнал SS для того ведомого, обмен данными с которым будет осуществляться. Простыми словами, все ведомые, кроме выбранного ведущим будут игнорировать данные на шине. SS является инверсным (active-low), что означает что ведущему необходимо прижать эту линию для выбора ведомого.
Подключение:
SPI на Arduino:
Arduino UNO/Piranha UNO/Arduino ULTRA
На Arduino UNO/Piranha UNO/Arduino ULTRA выводы аппаратного SPI расположены на 10, 11, 12 и 13 выводах, а так же эти выводы соединены с колодкой ICSP (in circuit serial programmer):

| Сигнал | Вывод |
|---|---|
| SS | 10 |
| MOSI | 11 |
| MISO | 12 |
| SCK | 13 |
Arduino MEGA
На Arduino MEGA выводы аппаратного SPI расположены на 50, 51, 52 и 53 выводах, а так же эти выводы соединены с колодкой ICSP (in circuit serial programmer):

| Сигнал | Вывод |
|---|---|
| SS | 53 |
| MOSI | 51 |
| MISO | 50 |
| SCK | 52 |
Пример для Arduino
В этих примерах мы соединим две Arduino по SPI по следующей схеме:

В одну плату необходимо загрузить скетч ведущего, а в другую скетч ведомого. Для проверки работы необходимо открыть проследовательный монитор той платы, в которую загружен скетч ведомого.
Arduino UNO в качестве ведущего:
#include "SPI.h" // Создаём переменную байтового массива byte data[] ; // Создаём переменную строки в формате языка Си char* cstring = " World!"; // создаём переменную с числом 168496141 в шестнадцатеричной системе счисления long i = 0x0A0B0C0D; void setup() < // Инициируем интерфейс SPI в режиме ведущего SPI.begin(); // Устанавливаем логическую "1" на выводе Slave Select pinMode(SS, HIGH); >void loop() < // Прижимаем вывод Slave Select (устанавливаем логический "0"), // тем самым активируя передачу данных с подключенным // к этому выводу ведомым digitalWrite(SS, LOW); // Начинаем передачу данных, передавая функции объект настроек шины // SPISettings( Скорость в Гц, Порядок передачи битов, Режим шины) SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); // Передаём массив data for (int i = 0; i < sizeof(data); i++) < SPI.transfer(char(data[i])); >// Передаём один байт SPI.transfer(','); // Передаём строку в формате си for (char* p = cstring; char c = *p; p++) < SPI.transfer(c); >// Передаём один байт - заголовок начала целого числа // Как пример: байт 0xAD обозначает заголовок целого числа типа long SPI.transfer(0xAD); // Побайтово передаём целое число for (int j = 0; j < sizeof(i); j++) < byte b = i >> 8 * j; SPI.transfer(b); > // Передаём байт конца пакета // Как пример: байт 0xAF обозначает конец пакета SPI.transfer(0xAF); // Завершаем передачу данных SPI.endTransaction(); // Завершаем работу с ведомым digitalWrite(SS, HIGH); delay(1000); >
Arduino UNO в качестве ведомого:
#include "SPI.h" // Определяем размер буфера #define BUFFER_SIZE 100 // Создаём буфер для данных SPI char buff[BUFFER_SIZE]; // Создаём переменную индекса буфера // volatile - указания для препроцессора о том, // что переменная может измениться при прерывании // и с ней не стоит производить никаких оптимизаций // при компилировании volatile uint8_t index = 0; // Создаём флаг готовности данных volatile bool data_ready = false; // Создаём переменную для полученного целого числа long i; // Создаём индекс заголовка целого числа volatile uint8_t int_index = 0; void setup (void) < // Инициируем работу с последовательным портом Serial.begin(9600); // Устанавливаем вывод MISO в режим выхода pinMode(MISO, OUTPUT); // Устанавливаем режим ведомого в контрольном регистре SPI (SPI Control Register) SPCR |= _BV(SPE); // Подключаем прерывание SPI SPI.attachInterrupt(); >// Вызываем функцию обработки прерываний по вектору SPI // STC - Serial Transfer Comlete ISR(SPI_STC_vect) < // Получаем байт из регистра данных SPI byte c = SPDR; // Добавляем байт в буфер if (index < sizeof(buff)) < buff[index++] = c; // Как пример: байт 0xAD обозначает заголовок целого числа типа long if (c == 0xAD) // Записываем положение целого числа в байтовом массиве int_index = index; // Как пример: байт 0xAF обозначает конец пакета if (c == 0xAF) // Устанавливаем флаг готовности данных для обработки data_ready = true; >> void loop(void) < // Если установлен флаг готовых данных if (data_ready == true) < // Обнуляем индекс index = 0; // Создаём строку и записываем в неё полученный буфер String message = String(buff); // Форматируем строку, убирая из неё заголовок // целого числа и всё после заголовка message = message.substring(0, int_index - 1); // Выводим отформатированную строку в последовательный порт Serial.println(message); // Обнуляем переменную для хранения полученного целого числа i = 0; // Записываем целое число через указатель на элемент массива /* Пояснение: * *(выражение) - разыменовывание указателя * (long *) - приводим последующее выражение к типу указателя на long * buff+int_index - прибавляем к указателю на первый элемент * массива buff индекс следующего после заголовка элемента массива, */ тем самым получая указатель на наше целое число i = *((long *)(buff + int_index)); // обнуляем флаг готовности данных data_ready = false; Serial.print("long integer over SPI is: "); // Выводим целое число в последовательный порт Serial.println(i); >>
После соединения двух Arduino по SPI и загрузки соответствующих скетчей, мы будем получать следующее сообщение в мониторе последовательного порта ведомого микроконтроллера раз в секунду:
Hello, World! long integer over SPI is: 168496141
SPI на Raspberry Pi
На Raspberry Pi выводы аппаратного SPI расположены на выводах GPIO7, GPIO8, GPIO9, GPIO10, GPIO11:

Перед работой с SPI необходимо его включить. Сделать это можно из эмулятора терминала командой sudo raspi-config -> Interfacing options -> Serial -> No -> Yes -> OK -> Finish или из графической среды в главном меню -> Параметры -> Raspberry Pi Configuration -> Interfaces -> SPI
Подробное описание как это сделать можно посмотреть по ссылке Raspberry Pi, включаем I2C, SPI
Пример работы с SPI на Python:
# Импортируем библиотеку для работы с SPI import spidev # Создаём объeкт spi spi = spidev.SpiDev() # Открываем устройство SPI (/dev/spidev0.0) spi.open(0, 0) # Ограничиваем скорость до 1 МГц spi.max_speed_hz = 1000000 # Выводим байтовую строку spi.writebytes(b'Hello, World!') # Выводим заголовок целого числа spi.writebytes([0xAD]) # Создаём целое число в шестнадцатеричной системе счисления i = 0x0A0B0C0D # Конвертируем число в список байтов, # указывая формат little endian b = i.to_bytes(4, byteorder='little') # Передаём число по SPI spi.writebytes(b) # Передаём байт конца пакета spi.writebytes([0xAF])
В отличие от Arduino для Raspberry не существует простых решений для работы в режиме ведомого. Подробней ознакомиться с работой чипа BCM Raspberry можно в технической документации на официальном сайте, стр. 160.
Для проверки работы сценария можно подключить Raspberry по SPI к Arduino со скетчем из примера выше через преобразователь уровней или Trema+Expander Hat:

Подробнее о SPI
Параметры
Существуют четыре режима работы SPI, зависящие от полярности (CPOL) и фазы (CPHA) тактирования:
| Режим | Полярность | Фаза | Фронт тактирования | Фронт установки бита данных |
|---|---|---|---|---|
| SPI_MODE0 | 0 | 0 | Спадающий | Нарастающий |
| SPI_MODE1 | 0 | 1 | Нарастающий | Спадающий |
| SPI_MODE2 | 1 | 0 | Нарастающий | Спадающий |
| SPI_MODE3 | 1 | 1 | Спадающий | Нарастающий |
В Arduino IDE для установки режима необходимо передать функции, возвращающей объект настроек параметр режима работы SPI_MODE, например:
SPISettings(1000000, MSBFIRST, SPI_MODE0)
Для выбора режима работы SPI на Raspberry Pi необходимо вызвать дескриптор объекта SpiDev().mode и присвоить ему битовые значения CPOL и CPHA, например:
# 0b11 соответствует режиму MODE3 spi.mode = 0b11
Скорость передачи данных
Скорость передачи данных устанавливается ведущим и может меняться «на лету». Программист в силах указать лишь максимальную скорость передачи данных.
Последовательный интерфейс SPI (3-wire)
SPI (3-wire) — популярный интерфейс для последовательного обмена данными между микросхемами. Интерфейс SPI, наряду с I 2 C, относится к самым широко-используемым интерфейсам для соединения микросхем. Изначально он был придуман компанией Motorola, а в настоящее время используется в продукции многих производителей. Его наименование является аббревиатурой от ‘Serial Peripheral Bus’, что отражает его предназначение — шина для подключения внешних устройств. Шина SPI организована по принципу ‘ведущий-подчиненный’. В качестве ведущего шины обычно выступает микроконтроллер, но им также может быть программируемая логика, DSP-контроллер или специализированная ИС. Подключенные к ведущему шины внешние устройства образуют подчиненных шины. В их роли выступают различного рода микросхемы, в т.ч. запоминающие устройства (EEPROM, Flash-память, SRAM), часы реального времени (RTC), АЦП/ЦАП, цифровые потенциометры, специализированные контроллеры и др.
Главным составным блоком интерфейса SPI является обычный сдвиговый регистр, сигналы синхронизации и ввода/вывода битового потока которого и образуют интерфейсные сигналы. Таким образом, протокол SPI правильнее назвать не протоколом передачи данных, а протоколом обмена данными между двумя сдвиговыми регистрами, каждый из которых одновременно выполняет и функцию приемника, и функцию передатчика. Непременным условием передачи данных по шине SPI является генерация сигнала синхронизации шины. Этот сигнал имеет право генерировать только ведущий шины и от этого сигнала полностью зависит работа подчиненного шины.
Существует три типа подключения к шине SPI, в каждом из которых участвуют четыре сигнала (их основное и альтернативные обозначения см. в табл. 1). Самое простое подключение, в котором участвуют только две микросхемы, показано на рисунке 1. Здесь, ведущий шины передает данные по линии MOSI синхронно со сгенерированным им же сигналом SCLK, а подчиненный захватывает переданные биты данных по определенным фронтам принятого сигнала синхронизации. Одновременно с этим подчиненный отправляет свою посылку данных. Представленную схему можно упростить исключением линии MISO, если используемая подчиненная ИС не предусматривает ответную передачу данных или в ней нет потребности. Одностороннюю передачу данных можно встретить у таких микросхем как ЦАП, цифровые потенциометры, программируемые усилители и драйверы. Таким образом, рассматриваемый вариант подключения подчиненной ИС требует 3 или 4 линии связи. Чтобы подчиненная ИС принимала и передавала данные, помимо наличия сигнала синхронизации, необходимо также, чтобы линия SS была переведена в низкое состояние. В противном случае, подчиненная ИС будет неактивна. Когда используется только одна внешняя ИС, может возникнуть соблазн исключения и линии SS за счет жесткой установки низкого уровня на входе выбора подчиненной микросхемы. Такое решение крайне нежелательно и может привести к сбоям или вообще невозможности передачи данных, т.к. вход выбора микросхемы служит для перевода ИС в её исходное состояние и иногда инициирует вывод первого бита данных.
Рис. 1. Простейшее подключение к шине SPI
При необходимости подключения к шине SPI нескольких микросхем используется либо независимое (параллельное) подключение (рис. 2), либо каскадное (последовательное) (рис. 3). Независимое подключение более распространенное, т.к. достигается при использовании любых SPI-совместимых микросхем. Здесь, все сигналы, кроме выбора микросхем, соединены параллельно, а ведущий шины, переводом того или иного сигнала SS в низкое состояние, задает, с какой подчиненной ИС он будет обмениваться данными. Главным недостатком такого подключения является необходимость в дополнительных линиях для адресации подчиненных микросхем (общее число линий связи равно 3+n, где n-количество подчиненных микросхем). Каскадное включение избавлено от этого недостатка, т.к. здесь из нескольких микросхем образуется один большой сдвиговый регистр. Для этого выход передачи данных одной ИС соединяется со входом приема данных другой, как показано на рисунке 3. Входы выбора микросхем здесь соединены параллельно и, таким образом, общее число линий связи сохранено равным 4. Однако использование каскадного подключения возможно только в том случае, если его поддержка указана в документации на используемые микросхемы. Чтобы выяснить это, важно знать, что такое подключение по-английски называется ‘daisy-chaining’.
Рис. 2. Независимое подключение к шине SPI
Рис. 3. Каскадное подключение к шине SPI
Протокол передачи по интерфейсу SPI предельно прост и, по сути, идентичен логике работы сдвигового регистра, которая заключается в выполнении операции сдвига и, соответственно, побитного ввода и вывода данных по определенным фронтам сигнала синхронизации. Установка данных при передаче и выборка при приеме всегда выполняются по противоположным фронтам синхронизации. Это необходимо для гарантирования выборки данных после надежного их установления. Если к этому учесть, что в качестве первого фронта в цикле передачи может выступать нарастающий или падающий фронт, то всего возможно четыре варианта логики работы интерфейса SPI. Эти варианты получили название режимов SPI и описываются двумя параметрами:
- CPOL — исходный уровень сигнала синхронизации (если CPOL=0, то линия синхронизации до начала цикла передачи и после его окончания имеет низкий уровень (т.е. первый фронт нарастающий, а последний — падающий), иначе, если CPOL=1, — высокий (т.е. первый фронт падающий, а последний — нарастающий));
- CPHA — фаза синхронизации; от этого параметра зависит, в какой последовательности выполняется установка и выборка данных (если CPHA=0, то по переднему фронту в цикле синхронизации будет выполняться выборка данных, а затем, по заднему фронту, — установка данных; если же CPHA=1, то установка данных будет выполняться по переднему фронту в цикле синхронизации, а выборка — по заднему). Информация по режимам SPI обобщена в таблице 2.
Ведущая и подчиненная микросхемы, работающие в различных режимах SPI, являются несовместимыми, поэтому, перед выбором подчиненных микросхем важно уточнить, какие режимы поддерживаются ведущим шины. Аппаратные модули SPI, интегрированные в микроконтроллеры, в большинстве случаев поддерживают возможность выбора любого режима SPI и, поэтому, к ним возможно подключение любых подчиненных SPI-микросхем (относится только к независимому варианту подключения). Кроме того, протокол SPI в любом из режимов легко реализуется программно.
Cравнение с шиной I 2 C
Как уже упоминалось, для стыковки микросхем не меньшей популярностью пользуется 2-проводная последовательная шина I 2 C. Ниже можно ознакомиться с преимуществами, которая дает та или иная последовательная шина.
| Преимущества шины SPI | Преимущества шины I2C |
| Предельная простота протокола передачи на физическом уровне обуславливает высокую надежность и быстродействие передачи. Предельное быстродействие шины SPI измеряется десятками мегагерц и, поэтому, она идеальна для потоковой передачи больших объемов данных и широко используется в высокоскоростных ЦАП/АЦП, драйверах светодиодных дисплеев и микросхемах памяти | Шина I 2 C остается двухпроводной, независимо от количества подключенной к ней микросхем. |
| Все линии шины SPI являются однонаправленными, что существенно упрощает решение задачи преобразования уровней и гальванической изоляции микросхем | Возможность мультимастерной работы, когда к шине подключено несколько ведущих микросхем. |
| Простота программной реализации протокола SPI. | Протокол I2C является более стандартизованным, поэтому, пользователь I2C-микросхем более защищен от проблем несовместимости выбранных компонентов. |
Производные и совместимые протоколы
Протокол MICROWIRE компании National Semiconductor полностью идентичен протоколу SPI в режиме 0 (CPOL = 0, CPHA = 0).
Отличие этого интерфейса состоит в том, что вместо полнодуплексной передачи по двум однонаправленным линиям здесь выполняется полудуплексная передача по одной двунаправленной линии DQ.
Более высокоуровневый протокол, чем SPI, позволяющий автоматизировать передачу данных без участия ЦПУ.
Кроме того, интерфейс SPI является основой для построения ряда специализированных интерфейсов, в т.ч. отладочный интерфейс JTAG и интерфейсы карт Flash-памяти, в т.ч. SD и MMC.
Табл. 1. Электрические сигналы шины SPI
| Ведущий шины | Подчиненный шины | ||||
| Основное обозначение | Альтернативное обозначение | Описание | Основное обозначение | Альтернативное обозначение | Описание |
| MOSI | DO, SDO, DOUT | Выход последовательной передачи данных | MOSI | DI, SDI, DIN | Вход последовательного приема данных |
| MISO | DI, SDI, DIN | Вход последовательного приема данных | MISO | DO, SDO, DOUT | Выход последовательной передачи данных |
| SCLK | DCLOCK, CLK, SCK | Выход синхронизации передачи данных | SCLK | DCLOCK, CLK, SCK | Вход синхронизации приема данных |
| SS | CS | Выход выбора подчиненного (выбор микросхемы) | SS | CS | Вход выбора подчиненного (выбор микросхемы) |
Табл. 2. Режимы SPI
| Режим SPI | 0 | 1 | 2 | 3 |
| CPOL | 0 | 1 | 0 | 1 |
| CPHA | 0 | 0 | 1 | 1 |
| Временная диаграмма первого цикла синхронизации |
Соединение нескольких устройств через SPI

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

Автор: 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).
- 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 значений, передаваемых от ведущего битов и сдвиг регистра ведомого устройства.
Автор: Cburnett, Источник картинки
Подлежащие передаче данные ведущее и ведомое устройства помещают в сдвиговые регистры. После этого ведущее устройство генерирует импульсы синхронизации на линии SCLK, что и приводит к взаимному обмену данными. Передача данных идёт бит за битом от ведущего по линии MOSI и от ведомого по линии MISO.
Передача осуществляется, начиная со старших битов, но некоторые производители допускают изменение порядка передачи битов программными методами. После передачи каждого пакета данных ведущее устройство, в целях синхронизации ведомого устройства, может перевести линию SS в высокое состояние.
Режимы работы интерфейса SPI
Возможны четыре режима синхронизации. Режим определяется комбинацией бит CPHA и CPOL:
- CPOL = 0 — исходное состояние сигнала синхронизации — низкий уровень.
- CPOL = 1 — исходное состояние сигнала синхронизации — высокий уровень.
- CPHA = 0 — выборка данных производится по переднему фронту (переключению) сигнала синхронизации. То есть по переключению из основного в противоположное ему.
- CPHA = 1 — выборка данных производится по заднему фронту (переключению) сигнала синхронизации.То есть по переключению обратно к основному из противоположного.
- режим 0 (CPOL = 0, CPHA = 0),
- режим 1 (CPOL = 0, CPHA = 1),
- режим 2 (CPOL = 1, CPHA = 0),
- режим 3 (CPOL = 1, CPHA = 1).
Топология систем связи на базе SPI
В простейшем случае к ведущему устройству подключено единственное ведомое устройство и необходим двусторонний обмен данными. В таком случае используется трёхпроводная схема подключения. Интерфейс SPI позволяет подключать к одному ведущему устройству несколько ведомых устройств, причём подключение может быть осуществлено несколькими способами.
Первый способ дает возможность реализовать радиальную структуру связи (топология типа «звезда»), его принято считать основным вариантом подключения нескольких ведомых устройств:

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

Автор: Cburnett, Источник картинки
В данном случае для активации одновременно нескольких ведомых устройств используется один сигнал SS, а выводы данных всех устройств соединены последовательно и образуют замкнутую цепь. При передаче пакета от ведущего устройства этот пакет получает первое ведомое устройство, которое, в свою очередь, транслирует свой пакет следующему ведомому устройству и так далее. Для того чтобы пакет от ведущего устройства достиг определённого ведомого устройства, ведущее устройство должно отправить ещё несколько пакетов.
Преимущества и недостатки интерфейса SPI
Преимущества
- Полнодуплексная передача данных по умолчанию.
- Более высокая пропускная способность по сравнению с I²C или SMBus.
- Возможность произвольного выбора длины пакета, длина пакета не ограничена восемью битами.
- Простота аппаратной реализации.
- Используется только четыре вывода, что гораздо меньше, чем для параллельных интерфейсов.
- Однонаправленный характер сигналов позволяет при необходимости легко организовать гальваническую развязку между ведущим и ведомыми устройствами.
- Максимальная тактовая частота ограничена только быстродействием устройств, участвующих в обмене данными.
Недостатки
- Необходимо больше выводов, чем для интерфейса I²C.
- Ведомое устройство не может управлять потоком данных.
- Нет подтверждения приёма данных со стороны ведомого устройства (ведущее устройство может передавать данные «в никуда»).
- Нет определённого стандартом протокола обнаружения ошибок.
- Отсутствие официального стандарта, что делает невозможным сертификацию устройств.
- По дальности передачи данных интерфейс SPI уступает таким стандартам, как UART и CAN.
- Наличие множества вариантов реализации интерфейса.
- Нет поддержки горячего подключения устройств.
Скетч ниже предназначен для управления AD5206, многоканальным цифровым потенциометром. Устройство является достаточно интересным и служит для изменения сопротивления в цепи электронным способом, а не вручную. Если кому интересно, можете почитать подробные спецификации этого потенциометра здесь.
Одним из его применений может быть изменение яркости светодиодов за счёт плавной регулировки сопротивления.
Говоря о технических особенностях этого устройства, можно сказать, что этот цифровой потенциометр имеет возможность установки 256 позиций и является шестиканальным (то есть поддерживает 6 подключаемых устройств, например, светодиодов). Его возможности позволяют заменить механические потенциометры, номиналами 10, 50 или 100 кОм.
Каждый из 6 переменных резисторов потенциометра выведен на корпус микросхемы в виде 3 контактов, и логика подключения к ним ничем не отличается от подключения обычных переменных резисторов.
Назначение пинов на корпусе микросхемы можно посмотреть на рисунке ниже:

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

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

Источник картинки
Код довольно простой, он реализует сказанное выше и работает следующим образом: плавно изменяется сопротивление на каждом из 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 // 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 //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 #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
Электронная начинка:

Источник картинки
Результат сборки:
В этой самоделке используется взаимодействие мастера и множества слейвов, которые работают синхронно и образуют вращающимися стрелками текущее время. Кому интересно более подробно почитать о проекте, может это сделать здесь.
Правда следует оговориться, что здесь для взаимодействия использован более продвинутый способ, где применяется интерфейс I2C, который тратит в 2 раза меньшее количество линий (2 против 4 у SPI). Хотя теоретически эта самоделка вполне могла быть выполнена и с использованием протокола SPI, однако тогда бы количество соединений критически выросло (видимо, именно поэтому автором и был выбран другой, более лаконичный на количество выводов способ).
С кодом этой реализации можно ознакомиться здесь.
Подытоживая, хочу сказать, что возможности протокола SPI не ограничиваются только перечисленными выше примерами. Однако если попытаться обобщить, то основными его назначениями, на мой взгляд, являются возможность расширить количество доступных контактов в рамках самоделок, а также разгрузить используемые микроконтроллеры, «сбрасывая» выполняющуюся задачу на подчинённых.
НЛО прилетело и оставило здесь промокод для читателей нашего блога:
— 15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS .
- Блог компании FirstVDS
- Программирование микроконтроллеров
- Разработка под Arduino