Arduino и Serial Peripheral Interface (SPI)
Serial Peripheral Interface (SPI) — синхронный протокол последовательной передачи данных для быстрого обмена между микроконтроллером и периферийными устройствами на коротких расстояниях.
Статья обновлена 18.11.2021 автором Karl Söderby.
Понятия «контроллер» и «периферия» заменили устаревшие термины «master» и «slave». Arduino больше не использует старую терминологию. Таблица соответствий ниже:
| Master/Slave (OLD) | Controller/Peripheral (NEW) |
|---|---|
| Master In Slave Out (MISO) | Controller In, Peripheral Out (CIPO) |
| Master Out Slave In (MOSI) | Controller Out Peripheral In (COPI) |
| Slave Select pin (SS) | Chip Select Pin (CS) |
---
Библиотека SPI
Библиотека SPI входит в состав каждого ядра Arduino — устанавливать её отдельно не нужно. Подробное описание всех функций SPI доступно по ссылкам:
Serial Peripheral Interface (SPI)
При работе по протоколу SPI всегда есть одно устройство-контроллер (как правило, микроконтроллер), которое управляет периферийными устройствами. Все устройства на шине разделяют три общие линии:
- CIPO (Controller In Peripheral Out) — линия, по которой периферия отправляет данные контроллеру.
- COPI (Controller Out Peripheral In) — линия, по которой контроллер отправляет данные периферии.
- SCK (Serial Clock) — тактовый сигнал, синхронизирующий передачу данных; формируется контроллером.
Помимо общих линий, у каждого устройства есть своя:
- CS (Chip Select) — линия выбора устройства. Когда CS переведён в
LOW, устройство активно и общается с контроллером. Когда CS вHIGH— устройство игнорирует шину. Благодаря этому несколько SPI-устройств могут совместно использовать линии CIPO, COPI и SCK.
Что нужно знать перед работой с новым SPI-устройством
Прежде чем писать код для нового SPI-устройства, уточните несколько параметров из даташита:
- Максимальная тактовая частота. Задаётся первым параметром SPISettings. Например, если чип рассчитан на 15 MHz, передайте
15000000. Arduino автоматически выберет наибольшую допустимую скорость, не превышающую указанное значение, при работе с SPISettings. - Порядок битов: MSB или LSB? Задаётся вторым параметром SPISettings: MSBFIRST или LSBFIRST. Большинство SPI-чипов используют MSB first.
- Полярность и фаза тактового сигнала. Определяют, в каком состоянии находится тактовая линия в режиме ожидания (высокий или низкий уровень) и по какому фронту происходит выборка данных. Задаются третьим параметром SPISettings.
- Внимательно читайте даташит. Стандарт SPI достаточно гибкий, и каждый производитель реализует его немного по-своему. Не пренебрегайте документацией.
Режимы передачи SPI
Всего существует четыре режима. Они определяют комбинацию полярности тактового сигнала (CPOL) и фазы (CPHA):
| Mode | Clock Polarity (CPOL) | Clock Phase (CPHA) | Output Edge | Data Capture |
|---|---|---|---|---|
| SPI_MODE0 | 0 | 0 | Falling | Rising |
| SPI_MODE1 | 0 | 1 | Rising | Falling |
| SPI_MODE2 | 1 | 0 | Rising | Falling |
| SPI_MODE3 | 1 | 1 | Falling | Rising |
Как использовать SPI в коде
Определив параметры SPI, вызовите SPI.beginTransaction(), чтобы начать работу с портом SPI. Порт SPI будет настроен согласно вашим параметрам. Самый простой и надёжный способ — вызывать SPISettings непосредственно внутри SPI.beginTransaction():
SPI.beginTransaction(SPISettings(14000000, MSBFIRST, SPI_MODE0));
Если другие библиотеки обращаются к SPI из обработчиков прерываний, они не смогут получить доступ к SPI до тех пор, пока вы не вызовете SPI.endTransaction(). Настройки SPI применяются в начале транзакции, а SPI.endTransaction() их не сбрасывает. Если beginTransaction не будет вызван повторно — настройки сохранятся. Старайтесь сократить время между вызовами до SPI.endTransaction(): это повысит совместимость вашего кода с другими библиотеками, использующими SPI.
При работе с большинством SPI-устройств алгоритм такой: после SPI.beginTransaction() переведите пин Chip Select в LOW, вызовите SPI.transfer() нужное количество раз для передачи данных, затем переведите CS в HIGH и вызовите SPI.endTransaction().
Типичные ошибки
- Неверный режим SPI. Если данные принимаются некорректно, первым делом проверьте CPOL и CPHA в даташите — это самая частая причина проблем.
- Слишком высокая частота. Некоторые компоненты и длинные провода не справляются с высокими скоростями. Попробуйте снизить частоту в
SPISettings. - Забытый CS. Если не перевести CS в
LOWперед транзакцией, устройство просто не отреагирует на данные. - Конфликт библиотек. Если несколько библиотек используют
SPI, убедитесь, что каждая корректно вызываетSPI.beginTransaction()иSPI.endTransaction()— иначе возможны непредсказуемые сбои.
Подробнее о SPI читайте здесь: Wikipedia's page on SPI.