Как использовать SPI на Arduino?

Со ссылкой на Arduino Uno, Mega2560, Leonardo и аналогичные платы:

  • Как работает SPI?
  • Как быстро работает SPI?
  • Как установить связь между master и slave?
  • Как сделать SPI ведомым?

Обратите внимание: Эта страница являет собой справочный материал SPI Ардуино.

, 👍65

Обсуждение

Можете ли вы ответить на этот связанный с этим вопрос https://arduinoprosto.ru/q/60703/can-serial-be-safely-used-in-an-spi-interrupt-routine, @qwr

1 ответ


Лучший ответ:

110

Введение в SPI

Интерфейс Serial Peripheral Interface Bus (SPI) используется для связи между несколькими устройствами на коротких расстояниях и при высокой скорость.

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


SPI-сигналы

В полноценной системе SPI у вас будет четыре сигнальные линии:

  • Главный выход, ведомый вход (MOSI) — это данные, которые передаются от ведущего к ведомому
  • Главный вход, подчиненный выход (MISO) — это данные, которые передаются от подчиненного устройства к главному.
  • Serial Clock (SCK) — когда переключается и главный, и подчиненный образцы следующего бита
  • Выбор ведомого устройства (SS) — указывает определенному ведомому устройству стать "активным"

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

На этом изображении показан способ обмена данными при отправке одного байта:

Протокол SPI показывает 4 сигнала

Обратите внимание, что три сигнала являются выходными от ведущего устройства (MOSI, SCK, SS), а один является входным (MISO).


Время

Последовательность событий такова:

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

Обратите внимание:

  • Старший бит отправляется первым (по умолчанию)
  • Данные отправляются и принимаются одновременно (полный дуплекс)

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

При использовании библиотеки SPI на Arduino выполнение одной передачи в коде выглядит следующим образом:

 byte outgoing = 0xAB;
 byte incoming = SPI.transfer (outgoing);

Пример кода

Пример только отправки (без учета входящих данных):

#include <SPI.h>

void setup (void)
  {
  digitalWrite(SS, HIGH);  // убедиться, что SS остается высоким
  SPI.begin ();
  } // конец настройки

void loop (void)
  {
  byte c;

  // включить выбор ведомого
  digitalWrite(SS, LOW);    // SS — это контакт 10

  // отправляем тестовую строку
  for (const char * p = "Fab" ; c = *p; p++)
    SPI.transfer (c);

  // отключаем выбор ведомого
  digitalWrite(SS, HIGH);

  delay (100);
  } // конец цикла

Проводка для SPI только для вывода

Приведенный выше код (который только отправляет) может использоваться для управления выходным регистром последовательного сдвига. Это устройства только для вывода, поэтому нам не нужно беспокоиться о входящих данных. В их случае вывод SS можно было бы назвать «хранилищем». или "защелка" pin.

Протокол SPI показывает 3 сигнала

Примерами этого являются последовательный регистр сдвига 74HC595 и различные светодиодные ленты, и это лишь некоторые из них. Например, этот 64-пиксельный светодиодный дисплей управляется микросхемой MAX7219:

64-пиксельный светодиодный дисплей

В этом случае вы можете видеть, что производитель платы использовал немного другие названия сигналов:

  • DIN (вход данных) — это MOSI (выход главного устройства, вход подчиненного устройства)
  • CS (выбор чипа) — это SS (выбор подчиненного устройства)
  • CLK (часы) — это SCK (последовательные часы)

Большинство плат будут следовать аналогичному шаблону. Иногда DIN — это просто DI (ввод данных).

Вот еще один пример, на этот раз плата светодиодного дисплея с 7 сегментами (также на основе микросхемы MAX7219):

7-сегментный светодиодный дисплей

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


Фаза и полярность синхронизации

Существует четыре способа выборки тактового сигнала SPI.

Протокол SPI допускает изменение полярности тактовых импульсов. CPOL — это полярность часов, а CPHA — фаза часов.

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

Они показаны на этом рисунке:

Фаза и полярность часов SPI

Чтобы правильно указать фазу и полярность, обратитесь к техническому описанию вашего устройства. Обычно там будет диаграмма, которая показывает, как сэмплировать часы. Например, из даташита на микросхему 74HC595:

Часы 74HC595

Как вы можете видеть, тактовая частота обычно низкая (CPOL = 0) и выборка производится по переднему фронту (CPHA = 0), поэтому это режим SPI 0.

Вы можете изменить полярность часов и фазу в коде следующим образом (конечно, выберите только один):

SPI.setDataMode (SPI_MODE0);
SPI.setDataMode (SPI_MODE1);
SPI.setDataMode (SPI_MODE2);
SPI.setDataMode (SPI_MODE3);

Этот метод устарел в Arduino IDE версии 1.6.0 и выше. В последних версиях вы меняете режим часов в вызове SPI.beginTransaction, например так:

SPI.beginTransaction (SPISettings (2000000, MSBFIRST, SPI_MODE0));  // 2 MHz clock, MSB first, mode 0

Порядок данных

По умолчанию сначала идет самый старший бит, однако вы можете указать аппаратному обеспечению обрабатывать сначала самый младший бит, например:

SPI.setBitOrder (LSBFIRST);   // сначала младший значащий бит
SPI.setBitOrder (MSBFIRST);   // сначала старший бит

Опять же, это устарело в версиях Arduino IDE 1.6.0 и выше. В последних версиях вы меняете порядок битов в вызове SPI.beginTransaction следующим образом:

SPI.beginTransaction (SPISettings (1000000, LSBFIRST, SPI_MODE2));  // Тактовая частота 1 МГц, первый младший бит, режим 2

Скорость

Настройкой по умолчанию для SPI является использование тактовой частоты системы, разделенной на четыре, то есть один тактовый импульс SPI каждые 250 нс при тактовой частоте ЦП 16 МГц. Вы можете изменить делитель часов с помощью setClockDivider следующим образом:

SPI.setClockDivider (divider);

Где "разделитель" является одним из:

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

Самая быстрая скорость – это "разделить на 2" или один тактовый импульс SPI каждые 125 нс при тактовой частоте ЦП 16 МГц. Следовательно, для передачи одного байта потребуется 8 * 125 нс или 1 мкс.

Этот метод устарел в Arduino IDE версии 1.6.0 и выше. В последних версиях скорость передачи можно изменить в вызове SPI.beginTransaction следующим образом:

SPI.beginTransaction (SPISettings (4000000, MSBFIRST, SPI_MODE0));  // Тактовая частота 4 МГц, старший бит вперед, режим 0

Однако эмпирическое тестирование показывает, что между байтами необходимо иметь два тактовых импульса, поэтому максимальная скорость, с которой байты могут быть тактированы, составляет 1,125 мкс каждый (с делителем тактового сигнала, равным 2).

Подводя итог, каждый байт может быть отправлен с максимальной скоростью один за 1,125 мкс (с тактовой частотой 16 МГц), что дает теоретическую максимальную скорость передачи 1/1,125 мкс или 888 888 байт в секунду (исключая накладные расходы, такие как настройка SS низкий и т. д.).


Подключение к Arduino

Ардуино Уно

Подключение через цифровые контакты с 10 по 13:

Выводы Arduino Uno SPI

Подключение через заголовок ICSP:

Распиновки ICSP — Uno

Заголовок ICSP

Ардуино Atmega2560

Подключение через цифровые контакты с 50 по 52:

Контакты Arduino Mega2560 SPI

Вы также можете использовать заголовок ICSP, аналогичный приведенному выше Uno.

Ардуино Леонардо

В отличие от Uno и Mega, у Leonardo и Micro контакты SPI на цифровых контактах не видны. Единственный вариант — использовать контакты ICSP, как показано выше для Uno. Однако Leonardo и ProMicro не открывают контакт SS и, следовательно, не могут использоваться в качестве ведомого устройства SPI.


Несколько ведомых устройств

Главное устройство может обмениваться данными с несколькими подчиненными устройствами (но только с одним за раз). Он делает это, подтверждая SS для одного ведомого устройства и отменяя его для всех остальных. Ведомое устройство, для которого установлено значение SS (обычно это означает НИЗКИЙ уровень), настраивает свой вывод MISO как выход, чтобы ведомое устройство, и только это ведомое устройство, могло отвечать ведущему. Другие ведомые устройства игнорируют любые входящие тактовые импульсы, если SS не установлен. Таким образом, вам нужен один дополнительный сигнал для каждого подчиненного устройства, например:

Несколько ведомых устройств SPI

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


Протоколы

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

Поэтому было бы логичнее, если бы одна сторона отправляла запрос (например, 4 может означать "перечислить каталог диска"), а затем выполняла передачу (возможно, просто отправляя нули наружу), пока не получит полный ответ. Ответ может заканчиваться символом новой строки или символом 0x00.

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


Как сделать ведомый SPI

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

Настройка оборудования

Соедините два Arduino Unos вместе со следующими контактами, соединенными друг с другом:

  • 10 (СС)

  • 11 (МОСИ)

  • 12 (МИСО)

  • 13 (SCK)

  • +5 В (если требуется)

  • Заземление (для возврата сигнала)

На Arduino Mega контакты 50 (MISO), 51 (MOSI), 52 (SCK) и 53 (SS).

В любом случае, MOSI на одном конце подключен к MOSI на другом, вы не меняете их местами (то есть у вас нет MOSI < -> МИСО). Программное обеспечение настраивает один конец MOSI (ведущий конец) как выход, а другой конец (ведомый конец) как вход.

На некоторых платах (например, Leonardo, ProMicro — см. выше) вывод SS не виден (скорее он подключен к RX_LED). Невозможно переназначить SS на другой контакт, чтобы иметь возможность использовать эти платы в качестве ведомого SPI, поскольку AVR использует фиксированные контакты SPI в соответствии с таблицами данных (PB0-PB3 на 32U4).

Основной пример

#include <SPI.h>

void setup (void)
{

  digitalWrite(SS, HIGH);  // убедиться, что SS пока остается высоким

  // Переводим выводы SCK, MOSI, SS в режим вывода
  // также переводим SCK, MOSI в состояние LOW, а SS в состояние HIGH.
  // Затем переведите оборудование SPI в режим Master и включите SPI.
  SPI.begin ();

  // Немного замедлить мастер
  SPI.setClockDivider(SPI_CLOCK_DIV8);

}  // конец настройки


void loop (void)
{

  char c;

  // включить выбор ведомого
  digitalWrite(SS, LOW);    // SS — это контакт 10

  // отправляем тестовую строку
  for (const char * p = "Hello, world!\n" ; c = *p; p++)
    SPI.transfer (c);

  // отключаем выбор ведомого
  digitalWrite(SS, HIGH);

  delay (1000);  // задержка 1 секунда
}  // конец цикла

Пример подчиненного устройства

#include <SPI.h>

char buf [100];
volatile byte pos;
volatile bool process_it;

void setup (void)
{
  Serial.begin (115200);   // отладка

  // включаем SPI в ведомом режиме
  SPCR |= bit (SPE);

  // нужно отправить на вход master, *slave на выход*
  pinMode (MISO, OUTPUT);

  // готовимся к прерыванию
  pos = 0;   // буфер пустой
  process_it = false;

  // теперь включаем прерывания
  SPI.attachInterrupt();

}  // конец настройки


// Процедура прерывания SPI
ISR (SPI_STC_vect)
{
byte c = SPDR;  // получаем байт из регистра данных SPI

  // добавить в буфер, если есть место
  if (pos < sizeof buf)
    {
    buf [pos++] = c;

    // пример: новая строка означает время обработки буфера
    if (c == '\n')
      process_it = true;

    }  // конец комнаты доступен
}  // конец процедуры прерывания SPI_STC_vect

// основной цикл - ожидание установки флага в процедуре прерывания
void loop (void)
{
  if (process_it)
    {
    buf [pos] = 0;
    Serial.println (buf);
    pos = 0;
    process_it = false;
    }  // конец установленного флага

}  // конец цикла

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

Пример подключения ведущего к ведомому с помощью SPI

Arduino SPI master and slave


Как получить ответ от подчиненного устройства

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

Мастер аналогичен приведенному выше примеру. Однако важным моментом является то, что нам нужно добавить небольшую задержку (примерно 20 микросекунд). В противном случае ведомое устройство не сможет отреагировать на поступающие данные и что-то с ними сделать.

В примере показана отправка "команды". В этом случае "а"; (добавить что-то) или "s" (что-то вычесть). Это должно показать, что ведомое устройство действительно что-то делает с данными.

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

Очень важным моментом является то, что ведомое устройство не может одновременно ответить на входящий байт. Ответ должен быть в следующем байте. Это связано с тем, что биты, которые отправляются, и биты, которые принимаются, отправляются одновременно. Таким образом, чтобы добавить что-то к четырем числам, нам нужно пять переносов, например:

transferAndWait ('a');  // добавить команду
transferAndWait (10);
a = transferAndWait (17);
b = transferAndWait (33);
c = transferAndWait (42);
d = transferAndWait (0);

Сначала мы запрашиваем действие по номеру 10. Но не получаем ответа до следующего перевода (того, что на 17). Однако "а" будет установлен ответ на 10. Наконец, мы отправим "фиктивный" номер 0, чтобы получить ответ для 42.

Мастер (пример)

  #include <SPI.h>

  void setup (void)
    {
    Serial.begin (115200);
    Serial.println ();

    digitalWrite(SS, HIGH);  // убедиться, что SS пока остается высоким
    SPI.begin ();

    // Немного замедлить мастер
    SPI.setClockDivider(SPI_CLOCK_DIV8);
    }  // конец настройки

  byte transferAndWait (const byte what)
    {
    byte a = SPI.transfer (what);
    delayMicroseconds (20);
    return a;
    } // конец передачи и ожидания

  void loop (void)
    {

    byte a, b, c, d;

    // включить выбор ведомого
    digitalWrite(SS, LOW);

    transferAndWait ('a');  // добавить команду
    transferAndWait (10);
    a = transferAndWait (17);
    b = transferAndWait (33);
    c = transferAndWait (42);
    d = transferAndWait (0);

    // отключаем выбор ведомого
    digitalWrite(SS, HIGH);

    Serial.println ("Adding results:");
    Serial.println (a, DEC);
    Serial.println (b, DEC);
    Serial.println (c, DEC);
    Serial.println (d, DEC);

    // включить выбор ведомого
    digitalWrite(SS, LOW);

    transferAndWait ('s');  // команда вычитания
    transferAndWait (10);
    a = transferAndWait (17);
    b = transferAndWait (33);
    c = transferAndWait (42);
    d = transferAndWait (0);

    // отключаем выбор ведомого
    digitalWrite(SS, HIGH);

    Serial.println ("Subtracting results:");
    Serial.println (a, DEC);
    Serial.println (b, DEC);
    Serial.println (c, DEC);
    Serial.println (d, DEC);

    delay (1000);  // 1 секундная задержка
    }  // конец цикла

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

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

Более надежно это можно было бы сделать с помощью прерывания. То есть вы физически подключите SS к одному из входов прерывания (например, на Uno подключите контакт 10 (SS) к контакту 2 (вход прерывания) или используйте прерывание по смене контакта на контакте 10.

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

Подчиненный (пример)

// что делать с поступающими данными
volatile byte command = 0;

void setup (void)
  {

  // нужно отправить на вход master, *slave на выход*
  pinMode(MISO, OUTPUT);

  // включаем SPI в ведомом режиме
  SPCR |= _BV(SPE);

  // включаем прерывания
  SPCR |= _BV(SPIE);

  }  // конец настройки


// Процедура прерывания SPI
ISR (SPI_STC_vect)
  {
  byte c = SPDR;

  switch (command)
    {
    // нет команды? тогда это команда
    case 0:
      command = c;
      SPDR = 0;
      break;

    // добавляем к входящему байту, возвращаем результат
    case 'a':
      SPDR = c + 15;  // добавить 15
      break;

    // вычитаем из входящего байта, возвращаем результат
    case 's':
      SPDR = c - 8;  // вычесть 8
      break;

    } // конец переключателя

  }  // конец процедуры обслуживания прерывания (ISR) SPI_STC_vect

void loop (void)
  {

  // если SPI не активен, очищаем текущую команду
  if (digitalRead (SS) == HIGH)
    command = 0;
  }  // конец цикла

Пример вывода

Adding results:
25
32
48
57
Subtracting results:
2
9
25
34
Adding results:
25
32
48
57
Subtracting results:
2
9
25
34

Вывод логического анализатора

Это показывает время между отправкой и получением в приведенном выше коде:

Время главного и подчиненного SPI


Новые функции в IDE 1.6.0 и более поздних версиях

Версия 1.6.0 IDE несколько изменила способ работы SPI. Вам по-прежнему необходимо выполнить SPI.begin() перед использованием SPI. Это настраивает оборудование SPI. Однако теперь, когда вы собираетесь начать общение с ведомым устройством, вы также выполняете SPI.beginTransaction(), чтобы настроить SPI (для этого ведомого устройства) с правильным:

  • Тактовая частота
  • Порядок битов
  • Фаза и полярность часов

После завершения связи с ведомым устройством вы вызываете SPI.endTransaction(). Например:

SPI.beginTransaction (SPISettings (2000000, MSBFIRST, SPI_MODE0));
digitalWrite (SS, LOW);        // подтверждаем выбор ведомого
byte foo = SPI.transfer (42);  // делаем перевод
digitalWrite (SS, HIGH);       // отменить выбор подчиненного устройства
SPI.endTransaction ();         // транзакция завершена

Зачем использовать SPI?

Я бы добавил один предварительный вопрос: когда/почему вы будете использовать SPI? Необходимость в конфигурации с несколькими ведущими или очень большим количеством подчиненных устройств склонит чашу весов в сторону I2C.

Отличный вопрос. Мои ответы:

  • Некоторые устройства (некоторые) поддерживают только метод передачи SPI. Например, выходной сдвиговый регистр 74HC595, входной сдвиговый регистр 74HC165, драйвер светодиодов MAX7219 и довольно много светодиодных лент, которые я видел. Таким образом, вы можете использовать его, потому что целевое устройство поддерживает только его.
  • SPI — действительно самый быстрый метод, доступный для чипов Atmega328 (и аналогичных). Самая высокая скорость, указанная выше, составляет 888 888 байт в секунду. Используя I2C, вы можете получить только около 40 000 байтов в секунду. Накладные расходы I2C весьма значительны, и если вы пытаетесь создать интерфейс очень быстро, предпочтительным выбором будет SPI. Довольно много семейств микросхем (например, MCP23017 и MCP23S17) на самом деле поддерживают как I2C, так и SPI, поэтому вы часто можете выбирать между скоростью и возможностью подключения нескольких устройств к одной шине.
  • Устройства SPI и I2C поддерживаются аппаратно на Atmega328, поэтому вы могли бы выполнять передачу через SPI одновременно с I2C, что дало бы вам прирост скорости.

Оба метода имеют свое место. I2C позволяет подключать множество устройств к одной шине (два провода плюс заземление), так что это предпочтительный выбор, если вам нужно опрашивать значительное количество устройств, возможно, довольно редко. Однако скорость SPI может быть более актуальной для ситуаций, когда вам нужно быстро выводить (например, светодиодная лента) или быстро вводить (например, преобразователь АЦП).


Ссылки

  • Моя страница о SPI – также содержит подробную информацию о битовом SPI и использовании USART для получения второй аппаратный SPI на чипе Atmega328.

  • Шина последовательного периферийного интерфейса — Википедия

  • Справочные страницы библиотеки Arduino SPI

  • документация SPI в PJRC

  • Протокол SPI — Sparkfun

,

Собираетесь ли вы рассказать о странностях, которые представляет собой SPI Due? Где конфигурация порта SPI привязана к используемому выводу SS, и есть (IIRC) 4 аппаратных контакта SS, назначенных порту SPI?, @Majenko

Еще один момент, касающийся выбора: иногда у вас действительно нет выбора, потому что датчик, который вы хотите/нужно использовать, доступен только как I2C., @Igor Stoppa

«Вы собираетесь рассказать о странностях, связанных с SPI Due?» — я ничего не знаю о SPI Due (кроме предположения, что общий протокол тот же). Вы можете добавить ответ, охватывающий этот аспект., @Nick Gammon

Когда выйдет аудиокнига этого ответа, и будете ли вы сами ее читать ;), @AMADANON Inc.

@AMADANONInc. Возможно, музыкальное видео? Или анимация? Я не уверен, что мой австралийский акцент будет понятен. :П, @Nick Gammon

@NickGammon, я живу в Новой Зеландии и некоторое время учусь на видео EEVBlog, поэтому не думаю, что у меня возникнут проблемы. Музыкальное видео было бы неплохо, но как твое циркулярное дыхание?, @AMADANON Inc.

Я думаю, что в подразделе *"Новые функции в IDE 1.6.0 и выше"* должен быть явный ответ на вопрос *"Что, если я пропущу SPI.beginTransaction?"* (и т. д.). Аналогично, ответьте на вопрос "Подойдут ли старые скетчи?" (обратная совместимость (с некоторыми фиксированными значениями по умолчанию?)) — думаю, да; это то, что я наблюдал вчера, когда пытался использовать Arduino Uno, версию программного обеспечения 1.6.5 и ЦАП (MCP4901); но могут быть оговорки, о которых я не знаю. *"тоже"* может быть *"тоже"*., @Peter Mortensen

Новый SPI имеет SPI.transfer (буфер, размер) https://www.arduino.cc/en/Reference/SPITransfer, @qwr

@PeterMortensen использует Arduino 1.8.8, устанавливая параметры SPI по-старому, все еще работает, @qwr

Запускается ли функция прерывания подчиненного устройства по переднему фронту тактового сигнала?, @c6754

Нет, это будет вызвано **полной** передачей одного байта, поскольку ISR может немедленно получить к нему доступ (SPDR), как показано в приведенном коде., @Nick Gammon

См. мой пост https://www.fpaynter.com/2020/03/arduino-spi-data-exchange-between-two-arduinos-in-a-master-slave-configuration/, чтобы узнать о проблеме новичка., @user3765883

Кто-то пытался отредактировать комментарий о том, что Leonardo и ProMicro не раскрывают контакт SS, поэтому я изменил ответ, чтобы отразить это., @Nick Gammon

Пользователям, которые отклоняют правки о булавке SS, как автор, я с ними не согласен., @Nick Gammon

Я внес поправку в рассматриваемое редактирование, чтобы удалить ссылку на «так что люди задавались вопросом» ... какие люди?, @Nick Gammon