Введение в SPI (Serial Peripheral Interface)
Назначение и архитектура: Что такое SPI и где он применяется?
Последовательный периферийный интерфейс (SPI) — это синхронный протокол последовательной передачи данных, разработанный компанией Motorola и ставший де-факто стандартом для высокоскоростной связи между микроконтроллерами и периферийными устройствами на коротких расстояниях (в пределах одной печатной платы).
Основной целью SPI является обеспечение простого, но очень быстрого полнодуплексного обмена данными. "Полнодуплексный" означает, что данные могут одновременно передаваться в обоих направлениях — от ведущего к ведомому и от ведомого к ведущему.
Архитектурные основы SPI
Архитектура SPI построена на принципе сдвигового регистра и характеризуется следующими ключевыми особенностями:
-
Синхронная передача: В отличие от UART, протокол SPI является синхронным. Это означает, что для координации передачи каждого бита используется отдельная тактовая линия. Ведущее устройство генерирует тактовый сигнал, и оба устройства — ведущее и ведомое — используют фронты этого сигнала для синхронной отправки и приема битов данных. Это устраняет необходимость в предварительной договоренности о скорости и сложных стартовых/стоповых битах, что и является ключом к высокой производительности.
-
Парадигма "Ведущий-Ведомый" (Master-Slave): Как и в I2C, коммуникация в SPI имеет строгую иерархию.
- Ведущий (Master): Обычно микроконтроллер (например, на Repka Pi). Он инициирует все транзакции, генерирует тактовый сигнал (
SCLK
) и управляет выбором ведомого устройства с помощью линийCS
. - Ведомый (Slave): Периферийное устройство (дисплей, АЦП, память). Оно пассивно и отвечает только тогда, когда ведущий активирует его через его персональную линию
CS
.
- Ведущий (Master): Обычно микроконтроллер (например, на Repka Pi). Он инициирует все транзакции, генерирует тактовый сигнал (
-
Четырехпроводная шина: Стандартная конфигурация SPI требует как минимум четырех сигнальных линий, что обеспечивает высокую пропускную способность:
SCLK
(Serial Clock): Линия тактового сигнала, генерируемая ведущим.MOSI
(Master Out -> Slave In): Линия для передачи данных от ведущего к ведомому.MISO
(Master In -> Slave Out): Линия для передачи данных от ведомого к ведущему.CS
(Chip Select) /SS
(Slave Select): Линия выбора ведомого. Для каждого ведомого устройства на шине требуется своя отдельная линияCS
.
Область применения
Благодаря своей высокой скорости и простоте аппаратной реализации, SPI является предпочтительным интерфейсом для устройств, требующих большой пропускной способности. Основные сферы его применения:
- Работа с памятью: Подключение микросхем Flash-памяти и EEPROM, где требуется быстрая запись и чтение больших объемов данных.
- Дисплеи и графика: Управление TFT/OLED-дисплеями, которые требуют постоянного потока данных для обновления изображения.
- Сенсоры и преобразователи: Взаимодействие с высокоскоростными аналого-цифровыми (АЦП) и цифро-аналоговыми (ЦАП) преобразователями, а также с комплексными датчиками, такими как акселерометры и гироскопы.
- Сетевые интерфейсы: Подключение внешних Ethernet-контроллеров или радиомодулей (например, LoRa, nRF24L01).
- Работа с SD-картами: Хотя SD-карты имеют свой собственный протокол, они также поддерживают режим эмуляции SPI для упрощенного взаимодействия с микроконтроллерами.
В целом, SPI выбирают тогда, когда скорость передачи данных является критически важным параметром, а экономия выводов микроконтроллера не стоит на первом месте.
Конечно. Продолжаем раскрывать фундаментальные принципы SPI, фокусируясь на том, что делает его таким быстрым и как он управляет коммуникацией с несколькими устройствами.
Высокоскоростная синхронная передача: Ключевые преимущества
Главное архитектурное отличие и основное преимущество SPI перед асинхронными протоколами, такими как UART, заключается в его синхронной природе. Наличие выделенной тактовой линии (SCLK
) кардинально упрощает и ускоряет процесс обмена данными.
Принцип синхронной передачи
В SPI нет необходимости в сложной системе стартовых, стоповых битов и битов четности для каждого байта. Вместо этого вся передача подчиняется ритму, задаваемому ведущим устройством на линии SCLK
.
- Общий тактовый сигнал: Ведущий генерирует непрерывную последовательность тактовых импульсов на линии
SCLK
на все время транзакции. - Синхронная работа: Оба устройства, ведущее и ведомое, используют один и тот же сигнал
SCLK
как "метроном". По каждому тактовому импульсу (точнее, по его нарастающему или спадающему фронту, в зависимости от настроек) происходит одно элементарное действие:- Передатчик выставляет на свою линию данных (
MOSI
илиMISO
) следующий бит. - Приемник считывает (сэмплирует) бит с соответствующей линии данных.
- Передатчик выставляет на свою линию данных (
Ключевые преимущества, вытекающие из синхронности
-
Высокая скорость: Отсутствие служебных битов (старт/стоп/четность) внутри каждого байта означает, что каждый тактовый импульс передает один бит полезной информации. Это минимизирует накладные расходы протокола. Так как генерация тактового сигнала происходит аппаратно, его частота может достигать десятков и даже сотен мегагерц (МГц), что обеспечивает пропускную способность, недостижимую для стандартного UART.
-
Простота протокола: Логика передачи предельно проста: "один такт — один бит". Это упрощает аппаратную реализацию SPI-контроллеров, делая их компактными и энергоэффективными.
-
Отсутствие необходимости в точном согласовании скоростей: В отличие от UART, где малейшее расхождение в скорости между устройствами приводит к ошибкам, в SPI такой проблемы нет. Ведомое устройство не имеет собственного генератора скорости; оно полностью подчиняется тактовому сигналу, который ему предоставляет ведущий. Это делает связь чрезвычайно надежной.
-
Полнодуплексный обмен: Архитектура с двумя раздельными линиями данных (
MOSI
иMISO
) позволяет ведущему и ведомому одновременно отправлять и получать данные. С каждым тактовым импульсомSCLK
один бит передается от ведущего к ведомому поMOSI
, и в этот же самый момент один бит передается от ведомого к ведущему поMISO
. Фактически, любая SPI-транзакция — это кольцевой обмен данными между сдвиговыми регистрами двух устройств.
Парадигма "Ведущий-Ведомый" (Master/Slave) и выбор ведомого
Как и I2C, SPI использует строгую иерархическую модель для организации работы на шине.
-
Ведущий (Master): Это всегда одно устройство, которое полностью контролирует шину. В системах с Repka Pi эту роль выполняет сама плата. Ведущий инициирует все коммуникации, генерирует тактовый сигнал
SCLK
и, что самое важное, выбирает, с каким из ведомых устройств он будет общаться в данный момент. -
Ведомый (Slave): Это одно или несколько периферийных устройств (датчики, память и т.д.), подключенных к шине. Они всегда находятся в пассивном режиме, игнорируя активность на линиях
SCLK
иMOSI
до тех пор, пока ведущий не обратится к ним персонально.
Механизм выбора ведомого: Линия CS
(Chip Select)
В отличие от I2C, где адресация встроена в протокол и передается по той же линии данных, в SPI для выбора ведомого используется отдельная аппаратная линия, называемая CS
(Chip Select) или SS
(Slave Select).
- Принцип работы: Каждое ведомое устройство на шине имеет свой собственный, индивидуальный провод
CS
, идущий от ведущего. Эта линия обычно является активной при низком уровне (active-low).- В состоянии покоя ведущий удерживает все линии
CS
на высоком логическом уровне (HIGH
). Все ведомые устройства "спят" и не реагируют на тактовый сигнал. - Когда ведущий хочет начать обмен данными с конкретным устройством (например, Slave_2), он притягивает его персональную линию
CS2
к земле (LOW
). - Только то ведомое устройство, чья линия
CS
перешла в состояниеLOW
, "просыпается" и начинает слушать тактовые импульсы наSCLK
и принимать данные сMOSI
. Все остальные ведомые, чьи линииCS
осталисьHIGH
, продолжают игнорировать активность на шине. - После завершения транзакции ведущий возвращает линию
CS2
в состояниеHIGH
, и ведомое устройство снова "засыпает".
- В состоянии покоя ведущий удерживает все линии
Этот механизм прост, надежен и позволяет избежать конфликтов адресов, но его главный недостаток заключается в том, что для каждого нового ведомого устройства на шине требуется дополнительный GPIO-пин от ведущего, который будет использоваться как его персональная линия CS
.
Основные сигнальные линии: SCLK
, MOSI
, MISO
, CS
Стандартный протокол SPI в конфигурации с одним ведущим и одним ведомым устройством использует четыре основные сигнальные линии. Каждая из них выполняет уникальную и строго определенную функцию. Понимание роли этих линий является ключом к правильному физическому подключению SPI-устройств.
SCLK
(Serial Clock) - Линия тактового сигнала
- Назначение: Синхронизация всей передачи данных.
- Направление: От Ведущего (Master) к Ведомому (Slave). Эта линия всегда управляется ведущим устройством.
- Принцип работы:
SCLK
представляет собой последовательность тактовых импульсов (переходовLOW
->HIGH
->LOW
), которая задает "ритм" обмена данными. По каждому фронту (нарастающему или спадающему, в зависимости от выбранного режима SPI) этого сигнала происходит одно атомарное действие: передача и прием одного бита. Частота сигналаSCLK
напрямую определяет скорость передачи данных по шине.
MOSI
(Master Out -> Slave In) - Линия передачи от Ведущего
- Назначение: Передача данных от ведущего устройства к ведомому.
- Направление: От Ведущего (Master) к Ведомому (Slave).
- Принцип работы: Когда ведущий хочет отправить данные, он выставляет на эту линию последовательность битов, синхронизируя каждый бит с тактовым сигналом на
SCLK
. Ведомое устройство, будучи активированным, считывает эти биты с линииMOSI
по каждому такту.
MISO
(Master In -> Slave Out) - Линия передачи от Ведомого
- Назнашение: Передача данных от ведомого устройства к ведущему.
- Направление: От Ведомого (Slave) к Ведущему (Master).
- Принцип работы: Когда ведущий хочет прочитать данные, ведомое устройство выставляет на эту линию последовательность битов, также синхронизируясь с тактовым сигналом на
SCLK
. Ведущий считывает эти биты с линииMISO
. Важно отметить, что даже во время чтения данных тактовый сигналSCLK
все равно генерируется ведущим.
CS
(Chip Select) / SS
(Slave Select) - Линия выбора ведомого
- Назначение: Активация конкретного ведомого устройства на общей шине.
- Направление: От Ведущего (Master) к Ведомому (Slave).
- Принцип работы: Эта линия работает как "разрешающий" сигнал. В подавляющем большинстве случаев она является активной при низком уровне (active-low).
- Когда напряжение на линии
CS
высокое (HIGH
), ведомое устройство находится в пассивном, высокоимпедансном состоянии. Оно игнорирует любую активность на линияхSCLK
иMOSI
. - Когда ведущий опускает напряжение на линии
CS
до низкого уровня (LOW
), ведомое устройство "активируется", начинает принимать тактовые импульсы и участвовать в обмене данными.
- Когда напряжение на линии
- Подключение нескольких устройств: Если на шине присутствует несколько ведомых устройств, их линии
SCLK
,MOSI
иMISO
подключаются параллельно. Однако каждое ведомое устройство должно быть подключено к своей индивидуальной линииCS
, идущей от ведущего. Ведущий может общаться только с одним устройством в каждый момент времени, активируя его соответствующую линиюCS
.
Протокол SPI: Детальный разбор транзакции
Обмен данными по шине SPI представляет собой синхронную транзакцию, полностью управляемую ведущим устройством. В отличие от асинхронных протоколов, где синхронизация достигается за счет сложных кадровых структур, в SPI эта роль отведена выделенной тактовой линии, что делает протокол одновременно простым и чрезвычайно быстрым.
Роль тактового сигнала (SCLK
- Serial Clock)
Тактовый сигнал на линии SCLK
является "метрономом" для всей SPI-транзакции. Это непрерывная последовательность прямоугольных импульсов (переходов LOW
-> HIGH
-> LOW
), которую генерирует исключительно ведущее устройство (Master). Ведомые устройства (Slave) никогда не управляют этой линией; они лишь используют ее как внешний источник синхронизации.
Функции тактового сигнала
-
Синхронизация передачи битов: Основная функция
SCLK
— указывать точные моменты времени, в которые происходит обмен данными. Протокол определяет, что по каждому тактовому импульсу (или, точнее, по его фронтам) происходит одно элементарное действие:- Передатчик (будь то Master на линии
MOSI
или Slave на линииMISO
) выставляет на свою линию данных состояние следующего бита. - Приемник считывает (сэмплирует) состояние с соответствующей линии данных.
Таким образом, за один полный цикл тактового сигнала (
LOW
->HIGH
->LOW
) по шине гарантированно передается и принимается один бит в каждом направлении. - Передатчик (будь то Master на линии
-
Определение скорости передачи: Скорость SPI-шины напрямую определяется частотой тактового сигнала на линии
SCLK
. Если ведущий генерирует сигнал с частотой 1 МГц, это означает, что по шине будет передаваться 1 миллион бит в секунду (1 Мбит/с). Ведущее устройство может гибко изменять эту частоту, подстраиваясь под максимальные возможности самого медленного ведомого устройства на шине. -
Определение границ транзакции: Активность на линии
SCLK
(наличие тактовых импульсов) является для ведомого устройства индикатором того, что транзакция активна. Генерация тактового сигнала всегда начинается после активации ведомого (перевода его линииCS
вLOW
) и прекращается перед его деактивацией (возвратомCS
вHIGH
).
Фронты тактового сигнала: Нарастающий и Спадающий
Каждый тактовый импульс имеет два фронта:
- Нарастающий фронт (Rising Edge): Момент перехода сигнала с низкого уровня (
LOW
) на высокий (HIGH
). - Спадающий фронт (Falling Edge): Момент перехода сигнала с высокого уровня (
HIGH
) на низкий (LOW
).
Протокол SPI не диктует, по какому именно фронту должны происходить действия. Вместо этого он предлагает четыре стандартных режима (которые мы рассмотрим позже), определяющих, по какому фронту данные выставляются на линию, а по какому — считываются. Ведущий и ведомый должны быть настроены на работу в одном и том же режиме, чтобы корректно интерпретировать данные.
В сущности, линия SCLK
— это дирижерская палочка в оркестре SPI. Она не несет в себе полезной информации, но ее ритм управляет всем процессом, гарантируя, что все участники обмена данными "играют" в унисон.
Конечно. Продолжаем детальный разбор протокола, фокусируясь на линиях, которые непосредственно переносят полезную информацию.
Направление потоков данных
Протокол SPI использует раздельные линии для передачи данных в каждом направлении, что позволяет реализовать полнодуплексный обмен — одновременную отправку и получение информации. Эти линии называются MOSI
и MISO
.
MOSI
(Master Out -> Slave In): От Ведущего к Ведомому
- Назначение: Эта линия предназначена исключительно для передачи данных от ведущего (Master) устройства к ведомому (Slave).
- Принцип работы: Когда ведущий инициирует транзакцию и начинает генерировать тактовые импульсы на
SCLK
, он одновременно выставляет на линиюMOSI
последовательность битов, которые он хочет передать ведомому. Данные передаются, как правило, старшим битом вперед (Most Significant Bit first, MSB). Активированное ведомое устройство синхронно считывает эти биты с линииMOSI
по каждому тактуSCLK
. - Электрическая характеристика: Эта линия всегда управляется ведущим устройством. Выход
MOSI
на ведущем подключается ко входуMOSI
на ведомом.
MISO
(Master In -> Slave Out): От Ведомого к Ведущему
- Назначение: Эта линия предназначена исключительно для передачи данных от ведомого (Slave) устройства к ведущему (Master).
- Принцип работы: Одновременно с тем, как ведущий отправляет данные по
MOSI
, активированное ведомое устройство выставляет на линиюMISO
последовательность битов, которые оно хочет передать в ответ. Ведущий синхронно считывает эти биты с линииMISO
по каждому тактуSCLK
. - Электрическая характеристика: Эта линия управляется ведомым устройством. Однако важно понимать, что ведомое устройство может управлять этой линией (выводить на нее сигнал) только тогда, когда его линия выбора
CS
находится в активном (низком) состоянии. В остальное время выходMISO
на ведомом должен находиться в высокоимпедансном состоянии (Hi-Z), чтобы не мешать другим ведомым устройствам на той же шине.
Концепция сдвигового регистра и полнодуплексный обмен
На самом низком уровне любую SPI-транзакцию можно представить как работу двух соединенных сдвиговых регистров — одного в ведущем и одного в ведомом устройстве.
- Перед транзакцией: Ведущий загружает в свой сдвиговый регистр байт, который хочет отправить. Сдвиговый регистр ведомого содержит байт, который он готов отправить в ответ.
- Во время транзакции: С каждым тактовым импульсом на
SCLK
происходит одно действие:- Данные из регистра ведущего "выталкиваются" бит за битом на линию
MOSI
и попадают в регистр ведомого. - Одновременно данные из регистра ведомого "выталкиваются" на линию
MISO
и попадают в регистр ведущего.
- Данные из регистра ведущего "выталкиваются" бит за битом на линию
- После 8 тактов: Происходит полный обмен. Регистр ведущего теперь содержит байт, который изначально был у ведомого, а регистр ведомого — байт, который был у ведущего.
Даже если одной из сторон нечего передавать (например, ведущий только пишет данные и не ожидает ответа), обмен все равно происходит в обоих направлениях. В этом случае неиспользуемая линия будет просто передавать "мусорные" данные (нули или единицы), которые принимающая сторона должна проигнорировать. Эта особенность делает SPI чрезвычайно эффективным, так как за одну 8-тактовую транзакцию можно и отправить команду, и сразу же получить на нее ответ.
Конечно. Мы подходим к ключевому механизму, который отличает SPI от I2C — к аппаратному способу выбора ведомого устройства.
Выбор ведомого (CS
- Chip Select / SS
- Slave Select)
В отличие от I2C, где адресация является частью протокола и передается по общей шине данных, SPI использует для этой цели выделенные аппаратные линии. Этот подход обеспечивает простоту, высокую скорость и устраняет необходимость в протокольной обработке адресов, но требует большего количества физических подключений.
Принцип работы с активным низким уровнем
Линия выбора ведомого (CS
или SS
) — это индивидуальный управляющий сигнал, который ведущее устройство (Master) отправляет каждому ведомому (Slave). Подавляющее большинство SPI-устройств использует логику активного низкого уровня (active-low) для этого сигнала.
-
Состояние покоя (Deselected / Не выбрано): Когда ведущий не общается с ведомым устройством, он удерживает его персональную линию
CS
в высоком логическом состоянии (HIGH
). В этом состоянии ведомое устройство обязано:- Полностью игнорировать любую активность на линиях
SCLK
иMOSI
. - Перевести свой выход
MISO
в высокоимпедансное состояние (Hi-Z). Это критически важный аспект. Устройство не просто удерживаетMISO
на уровнеHIGH
илиLOW
, оно фактически "отключает" свой передатчик от общей линии. Это позволяет другим ведомым устройствам беспрепятственно использовать шинуMISO
, когда придет их черед.
- Полностью игнорировать любую активность на линиях
-
Активное состояние (Selected / Выбрано): Когда ведущий хочет начать транзакцию с конкретным ведомым, он переводит его линию
CS
в низкое логическое состояние (LOW
). Этот переход является для ведомого командой "проснуться". Только после этого ведомое устройство:- Начинает принимать тактовые импульсы на
SCLK
. - Считывает данные с линии
MOSI
. - Активирует свой передатчик и начинает выставлять данные на линию
MISO
.
- Начинает принимать тактовые импульсы на
После завершения обмена данными ведущий возвращает линию CS
в состояние HIGH
, и ведомое устройство немедленно переходит обратно в пассивный режим.
Подключение нескольких ведомых устройств
Механизм CS
позволяет подключить к одному ведущему устройству несколько ведомых. Схема подключения при этом следующая:
- Общие шины: Линии
SCLK
,MOSI
иMISO
подключаются параллельно ко всем ведомым устройствам. Все устройства слушают один и тот же тактовый сигнал и получают одни и те же данные от ведущего. - Индивидуальные линии выбора: Каждое ведомое устройство подключается к своему уникальному, выделенному GPIO-пину на ведущем устройстве, который будет служить его персональной линией
CS
.
Процесс коммуникации с несколькими устройствами:
Предположим, у нас есть три ведомых устройства: Slave A, Slave B, Slave C с линиями выбора CS_A
, CS_B
, CS_C
.
- Начальное состояние: Ведущий удерживает все три линии
CS_A
,CS_B
,CS_C
в состоянииHIGH
. Все ведомые пассивны. - Обмен с Slave B:
- Ведущий устанавливает
CS_B
в состояниеLOW
.CS_A
иCS_C
остаютсяHIGH
. - Slave B активируется. Slave A и Slave C остаются пассивными.
- Ведущий выполняет транзакцию (генерирует
SCLK
, обменивается данными поMOSI
/MISO
). - После завершения ведущий возвращает
CS_B
в состояниеHIGH
. Slave B "засыпает".
- Ведущий устанавливает
- Обмен с Slave C:
- Ведущий устанавливает
CS_C
в состояниеLOW
.CS_A
иCS_B
остаютсяHIGH
. - Slave C активируется... и так далее.
- Ведущий устанавливает
Главный вывод и одновременно ограничение этого подхода заключается в том, что количество ведомых устройств, которыми может управлять ведущий, напрямую ограничено количеством свободных GPIO-пинов, которые он может выделить под линии CS
.
Конечно. Это одна из самых важных и часто вызывающих путаницу тем в SPI. Мы разберем ее максимально подробно и структурированно.
Режимы SPI (CPOL
и CPHA
)
Протокол SPI не имеет строгой, единой спецификации. Вместо этого он предлагает гибкую модель синхронизации, которая определяется двумя параметрами: полярностью (CPOL
) и фазой (CPHA
) тактового сигнала. Комбинация этих двух параметров формирует четыре стандартных режима работы. Для успешной коммуникации и ведущее (Master), и ведомое (Slave) устройства должны быть настроены на один и тот же режим.
Необходимость в этих режимах исторически возникла из-за того, что разные производители микросхем реализовывали SPI-интерфейс с небольшими отличиями.
CPOL
(Clock Polarity) - Полярность тактового сигнала
Этот параметр определяет базовый, или исходный, уровень тактового сигнала SCLK
в состоянии покоя (когда транзакция неактивна, то есть линия CS
находится в состоянии HIGH
).
-
CPOL = 0
:- Исходный уровень:
LOW
(0V). - Активный уровень:
HIGH
(+3.3V). - Описание: В состоянии покоя линия
SCLK
удерживается на низком уровне. Первый тактовый импульс начинается с переходаLOW
->HIGH
(нарастающий фронт).
- Исходный уровень:
-
CPOL = 1
:- Исходный уровень:
HIGH
(+3.3V). - Активный уровень:
LOW
(0V). - Описание: В состоянии покоя линия
SCLK
удерживается на высоком уровне. Первый тактовый импульс начинается с переходаHIGH
->LOW
(спадающий фронт).
- Исходный уровень:
CPHA
(Clock Phase) - Фаза тактового сигнала
Этот параметр определяет, по какому фронту тактового сигнала происходит считывание (сэмплирование) данных. Он тесно связан с CPOL
.
-
CPHA = 0
:- Считывание данных: Происходит на первом фронте тактового импульса.
- Изменение данных: Происходит на втором фронте тактового импульса.
- Описание: Приемник считывает бит в самом начале тактового цикла. Передатчик должен выставить следующий бит уже в середине цикла, на противоположном фронте.
-
CPHA = 1
:- Считывание данных: Происходит на втором фронте тактового импульса.
- Изменение данных: Происходит на первом фронте тактового импульса.
- Описание: Передатчик выставляет бит в самом начале цикла (на первом фронте), а приемник считывает его в середине цикла (на втором фронте). Это дает данным больше времени на "устаканивание" на линии, что может быть полезно на высоких скоростях.
Разбор четырех стандартных режимов (Mode 0, 1, 2, 3)
Комбинация CPOL
и CPHA
дает четыре уникальных режима работы. Режим, который необходимо использовать, всегда указывается в технической документации (datasheet) на ведомое устройство.
-
Режим 0 (
CPOL = 0
,CPHA = 0
) - Самый распространенный- Исходный уровень
SCLK
:LOW
. - Считывание данных (сэмплирование): Происходит по нарастающему фронту (первый фронт).
- Изменение данных (выставление следующего бита): Происходит по спадающему фронту (второй фронт).
- Исходный уровень
-
Режим 1 (
CPOL = 0
,CPHA = 1
)- Исходный уровень
SCLK
:LOW
. - Считывание данных: Происходит по спадающему фронту (второй фронт).
- Изменение данных: Происходит по нарастающему фронту (первый фронт).
- Исходный уровень
-
Режим 2 (
CPOL = 1
,CPHA = 0
)- Исходный уровень
SCLK
:HIGH
. - Считывание данных: Происходит по спадающему фронту (первый фронт).
- Изменение данных: Происходит по нарастающему фронту (второй фронт).
- Исходный уровень
-
Режим 3 (
CPOL = 1
,CPHA = 1
)-
Исходный уровень
SCLK
:HIGH
. -
Считывание данных: Происходит по нарастающему фронту (второй фронт).
-
Изменение данных: Происходит по спадающему фронту (первый фронт).
-
Практический аспект: При работе с библиотеками, такими как
spidev
в Python, вы не управляете CPOL
и CPHA
напрямую. Вместо этого вы просто устанавливаете номер режима (0, 1, 2 или 3), а библиотека сама настраивает SPI-контроллер на соответствующую комбинацию полярности и фазы. Неправильный выбор режима является одной из самых частых причин получения искаженных или сдвинутых данных при работе с SPI.
Конечно. Мы подходим к самой сути механики SPI — одновременной передаче данных в обе стороны. Я раскрою эту тему в деталях, объясняя не только что происходит, но и как это достигается на аппаратном уровне.
Процесс полнодуплексной передачи данных
Полнодуплексная передача (Full-Duplex) — это фундаментальная характеристика протокола SPI, означающая способность системы одновременно передавать и получать данные. Во время каждого тактового импульса, генерируемого ведущим, один бит информации перемещается от ведущего к ведомому по линии MOSI
, и в этот же самый момент один бит перемещается от ведомого к ведущему по линии MISO
.
Любая SPI-транзакция, независимо от ее цели, по своей природе является обменом данными.
Сдвиговые регистры: Сердце механизма
На аппаратном уровне этот одновременный обмен реализуется с помощью сдвиговых регистров в ведущем и ведомом устройствах. Сдвиговый регистр — это, по сути, временный буфер (обычно размером 8 или 16 бит), в котором хранится байт для передачи.
Можно представить эти два регистра как концы одной замкнутой конвейерной ленты, состоящей из линий MOSI
и MISO
. Линия SCLK
является "мотором", который приводит эту ленту в движение, перемещая биты по одному за каждый такт.
Пошаговый разбор 8-битной транзакции
Рассмотрим, что происходит во время типичной 8-тактовой транзакции.
-
Начальное состояние (Такт 0):
- Ведущий (Master) загружает в свой сдвиговый регистр байт, который он хочет отправить (например, команду
0xA5
, что в двоичной системе10100101
). - В сдвиговом регистре ведомого (Slave) находится байт, который он готов отправить в ответ (например, свой байт статуса
0xB3
, что10110011
).
- Ведущий (Master) загружает в свой сдвиговый регистр байт, который он хочет отправить (например, команду
-
Тактовый импульс 1:
- Ведущий "выталкивает" свой старший бит (
1
) на линиюMOSI
. - Ведомый "выталкивает" свой старший бит (
1
) на линиюMISO
. - Одновременно ведущий считывает бит с
MISO
(1
) и помещает его в младший разряд своего регистра. - Ведомый считывает бит с
MOSI
(1
) и помещает его в младший разряд своего регистра. - Содержимое регистров сдвигается на одну позицию.
- Ведущий "выталкивает" свой старший бит (
-
Тактовые импульсы 2-7:
- Процесс повторяется. С каждым тактовым импульсом следующий бит из ведущего перемещается в ведомый по
MOSI
, а следующий бит из ведомого — в ведущий поMISO
.
- Процесс повторяется. С каждым тактовым импульсом следующий бит из ведущего перемещается в ведомый по
-
Тактовый импульс 8:
- Происходит обмен последними, младшими битами.
- Итог после 8 тактов: Произошел полный обмен. Теперь в сдвиговом регистре ведущего находится значение
0xB3
(то, что было у ведомого), а в регистре ведомого —0xA5
(то, что было у ведущего).
Работа с однонаправленной передачей ("Dummy Bytes")
Что происходит, если ведущему нужно только отправить данные (запись) или только получить их (чтение)? Механизм обмена все равно работает, но одна из сторон передает "пустые", или фиктивные, байты.
-
Только запись (Master -> Slave): Ведущий отправляет по
MOSI
значащие байты (команды, данные). В это же время он продолжает принимать биты, которые ведомый выставляет наMISO
. Чаще всего эти принятые данные являются "мусором" (например,0x00
или0xFF
) и просто игнорируются программным обеспечением ведущего. -
Только чтение (Slave -> Master): Это более сложный случай. Чтобы ведомое устройство могло отправить свой байт, ведущий обязан сгенерировать 8 тактовых импульсов на
SCLK
. А чтобы их сгенерировать, он должен что-то передавать. В этом случае ведущий отправляет поMOSI
фиктивный байт (dummy byte). Значение этого байта обычно не имеет значения (часто используется0x00
), его единственная цель — заставить SPI-контроллер сгенерировать 8 тактов. Пока ведущий передает этот фиктивный байт, он одновременно принимает поMISO
значащий байт от ведомого.
Полный цикл транзакции
Таким образом, полный процесс передачи данных выглядит следующим образом:
- Ведущий активирует ведомое устройство, устанавливая его линию
CS
в состояниеLOW
. - Ведущий загружает первый байт для отправки в свой сдвиговый регистр.
- Ведущий генерирует 8 тактовых импульсов на
SCLK
. - Во время этих 8 тактов происходит полнодуплексный обмен 8 битами.
- Если нужно передать/принять еще байты, шаги 2-4 повторяются.
- Ведущий деактивирует ведомое устройство, возвращая его линию
CS
в состояниеHIGH
. Транзакция завершена.
Конечно. Переходим к аппаратной реализации, связывая абстрактный протокол SPI с конкретным "железом" Repka Pi.
Аппаратная реализация SPI на Repka Pi
После детального разбора протокола SPI необходимо рассмотреть, как он реализован на аппаратном уровне в однокристальной системе (SoC), установленной в Repka Pi. Это понимание позволяет оценить реальные возможности и ограничения системы.
SPI-контроллеры в SoC Allwinner
Как и в случае с UART и I2C, SPI-интерфейс в процессорах Allwinner H5/H6 реализован в виде независимых аппаратных блоков. SPI-контроллер — это специализированная цифровая схема на кристалле SoC, которая полностью отвечает за реализацию протокола.
Ключевые функции, выполняемые аппаратным контроллером:
-
Генерация тактового сигнала: Контроллер самостоятельно генерирует стабильный тактовый сигнал
SCLK
требуемой частоты, освобождая ЦП от этой задачи. -
Управление сдвиговыми регистрами: Вся механика полнодуплексного обмена, описанная ранее, — загрузка данных в передающий регистр и чтение из приемного — происходит аппаратно.
-
Поддержка режимов CPOL/CPHA: Настройка полярности и фазы тактового сигнала выполняется на уровне регистров контроллера, что обеспечивает точное соответствие выбранному режиму SPI.
-
Аппаратное ускорение: Современные SPI-контроллеры, как и UART, оснащены буферами FIFO и могут взаимодействовать с контроллером DMA. Это позволяет ЦП "загружать" в контроллер целые блоки данных для передачи и принимать большие объемы информации с минимальным количеством прерываний, что является ключом к достижению скоростей в десятки и сотни Мбит/с.
В блок-схеме SoC Allwinner H6 эти блоки обозначены как SPI x2
и расположены в секции "Connectivity". Это указывает на наличие двух независимых, полнофункциональных аппаратных SPI-контроллеров.
Конечно. Вот аналогичный раздел, полностью адаптированный под специфику протокола SPI, с сохранением структуры и глубины объяснения.
Аппаратное ускорение: Как SPI-контроллер обеспечивает высокую скорость
Ключевым преимуществом использования аппаратного SPI-контроллера является его способность автономно выполнять высокочастотные протокольные операции, освобождая центральный процессор (ЦП) от крайне ресурсозатратных задач по генерации сигналов. Этот раздел подробно рассматривает механизмы, которые лежат в основе этой эффективности.
Проблема: Программная эмуляция ("Bit-Banging")
Чтобы оценить важность аппаратного контроллера, необходимо сначала понять, какой была бы альтернатива — программная эмуляция протокола SPI. В этом сценарии ЦП пришлось бы взять на себя всю работу по формированию сигналов:
- Захват ресурсов: Процессор должен был бы полностью монополизировать как минимум четыре обычных GPIO-пина (
SCLK
,MOSI
,MISO
,CS
). - Побитовое формирование сигнала: Для передачи всего одного байта процессор должен был бы выполнить в цикле 8 раз строгую последовательность действий:
- Выставить бит данных на
MOSI
. - Сгенерировать импульс на
SCLK
(LOW
->HIGH
->LOW
). - В правильный момент (в зависимости от фазы) считать бит данных с
MISO
.
- Выставить бит данных на
- Соблюдение временных интервалов: Между этими действиями процессор должен был бы выдерживать точнейшие наносекундные задержки, чтобы соответствовать выбранной скорости шины (которая может достигать десятков МГц).
- Управление Chip Select: Вручную управлять состоянием линии
CS
, переводя ее вLOW
в начале транзакции и вHIGH
в конце.
Такой подход губителен для производительности. На высоких скоростях процессор был бы на 100% загружен выполнением этого низкоуровневого цикла. В многозадачной операционной системе, такой как Linux, где планировщик постоянно прерывает процессы, обеспечить стабильность и точность таких быстрых сигналов практически невозможно.
Решение: Делегирование задач аппаратному контроллелю
Аппаратный SPI-контроллер — это, по сути, специализированный модуль, содержащий сдвиговые регистры и генератор тактового сигнала. Процессор не управляет линиями напрямую; он общается с этим "черным ящиком" через набор управляющих регистров, делегируя ему всю рутинную работу.
1. Аппаратная генерация сигналов: Принцип "Записал и забыл"
Когда ядро Linux (драйвер spidev
) хочет передать и/или принять байт, оно выполняет всего одну простую операцию:
- Записывает 8-битное значение в регистр передаваемых данных (
SPI_TX_DATA
).
После этого SPI-контроллер автономно выполняет всю сложную последовательность: генерирует 8 тактовых импульсов на SCLK
, синхронно выталкивает биты из своего передающего регистра на линию MOSI
и одновременно захватывает биты с линии MISO
в свой приемный регистр. Весь этот процесс происходит на аппаратном уровне, параллельно с работой ЦП.
2. Буферы FIFO (First-In, First-Out): Снижение частоты взаимодействия
Даже с аппаратной генерацией, если бы ЦП приходилось загружать каждый байт по отдельности, это все равно требовало бы частого вмешательства. Для решения этой проблемы SPI-контроллеры в SoC Allwinner оснащены аппаратными буферами FIFO (например, 64-байтными, как указано в даташите).
- Что такое FIFO? Это электронная "очередь", работающая по принципу "первым пришел — первым ушел".
- Принцип работы: У контроллера есть отдельные области памяти для приема (RX) и передачи (TX).
- Эффект: Драйвер может "забросить" в передающий (TX) FIFO сразу целый блок данных (например, 64 байта команды для дисплея). SPI-контроллер будет самостоятельно извлекать их из очереди и отправлять на шину. Прерывание будет сгенерировано не после каждого байта, а только тогда, когда в TX FIFO освободится место или вся передача будет завершена. Аналогично, при приеме (RX) прерывание генерируется, когда в RX FIFO накопится определенное количество байт.
- Результат: Количество прерываний, а значит, и переключений контекста для ЦП, кардинально снижается, что является ключевым фактором для достижения высоких скоростей передачи данных (десятки Мбит/с).
3. Механизм прерываний и DMA: От активного опроса к полной автономии
Последний и самый важный элемент — это взаимодействие с ЦП через прерывания и прямой доступ к памяти.
- Что такое прерывание? Это аппаратный сигнал от SPI-контроллера к процессору, который говорит: "Я завершил свою задачу (например, отправил/принял блок данных), мне нужно твое внимание".
- Принцип работы с прерываниями:
- SPI-контроллер завершает операцию.
- Он посылает сигнал на общий контроллер прерываний (GIC).
- GIC уведомляет ЦП, который приостанавливает текущую задачу и переходит к выполнению кода обработчика из драйвера SPI.
- Драйвер определяет, что произошло, и выполняет необходимое действие (например, забирает данные из RX FIFO и помещает их в системный буфер).
- Прямой доступ к памяти (DMA): Для максимальной производительности SPI-контроллер может работать в связке с контроллером DMA. В этом режиме ЦП дает одну команду DMA-контроллеру: "Переместить 4 килобайта данных из этого буфера в оперативной памяти в TX FIFO SPI-контроллера". DMA выполняет всю эту операцию в фоновом режиме, а по завершении генерирует одно-единственное прерывание.
- Результат: Процессор полностью освобождается от задачи пересылки данных и "просыпается" только тогда, когда вся большая транзакция завершена. Это и есть основа эффективной многозадачной работы, позволяющая Repka Pi одновременно общаться с быстрым SPI-дисплеем, обрабатывать сетевые пакеты и выполнять пользовательский код без потери производительности.
Доступные SPI-шины на Repka Pi
Хотя SoC предоставляет два аппаратных контроллера, количество SPI-шин, выведенных на 40-контактную гребенку для пользовательского доступа, определяется схемотехникой платы. Как правило, для общего использования доступна как минимум одна полноценная SPI-шина.
На Repka Pi обычно это SPI0
. Этот контроллер предоставляет стандартный набор из четырех основных линий: SCLK
, MOSI
, MISO
и как минимум одну линию выбора ведомого CS0
.
Множественные линии Chip Select: Особенностью SPI-контроллеров в SoC Allwinner является то, что один и тот же контроллер может аппаратно управлять несколькими линиями CS
. Например, SPI0
может иметь выходы CS0
и CS1
. Это означает, что вы можете подключить два SPI-устройства, используя общие линии SCLK
, MOSI
, MISO
, и управлять ими с помощью разных линий выбора, CS0
и CS1
, которые принадлежат одному и тому же аппаратному блоку.
В операционной системе Linux это будет представлено как два разных "устройства" на одной и той же шине, например:
- Устройство, подключенное к
CS0
, будет доступно как/dev/spidev0.0
. - Устройство, подключенное к
CS1
, будет доступно как/dev/spidev0.1
.
Число до точки (0
) обозначает номер SPI-шины (контроллера), а число после точки (0
или 1
) — номер устройства (линии Chip Select) на этой шине. Это обеспечивает гибкость при подключении нескольких периферийных устройств.
Распиновка (Pinout): Нахождение физических контактов
Правильное физическое подключение является обязательным условием для работы SPI. В отличие от I2C, где всего две линии, для SPI требуется подключение как минимум четырех проводов. Расположение этих пинов на 40-контактной гребенке является фиксированным для каждой модели платы, но может отличаться между ними.
Важное примечание: Как и для любого другого интерфейса, для стабильной работы SPI обязательно необходимо соединять общую землю (GND) между Repka Pi (ведущим) и всеми ведомыми устройствами на шине.
Расположение SCLK
, MOSI
, MISO
, CS0
на Repka Pi 3
На Repka Pi 3 основной SPI-порт (SPI0
) выведен на стандартные для многих одноплатных компьютеров пины.
MOSI
(SPI0_MOSI): Пин 19 (BOARD)MISO
(SPI0_MISO): Пин 21 (BOARD)SCLK
(SPI0_CLK): Пин 23 (BOARD)CS0
(SPI0_CS0): Пин 24 (BOARD)GND
: Любой доступный пин земли (например, Пин 25).
Расположение SCLK
, MOSI
, MISO
, CS0
, CS1
на Repka Pi 4
На Repka Pi 4 основной SPI-порт (SPI0
) также выведен на стандартные пины, но с добавлением второй аппаратной линии выбора ведомого (CS1
), что расширяет возможности подключения.
MOSI
(SPI0_MOSI): Пин 19 (BOARD)MISO
(SPI0_MISO): Пин 21 (BOARD)SCLK
(SPI0_CLK): Пин 23 (BOARD)CS0
(SPI0_CS0): Пин 24 (BOARD)CS1
(SPI0_CS1): Пин 26 (BOARD)GND
: Любой доступный пин земли (например, Пин 25).
Представление в операционной системе: Файлы устройств /dev/spidev*
Операционная система Linux предоставляет унифицированный интерфейс для доступа к аппаратным SPI-шинам из пользовательского пространства. После активации SPI в конфигурации системы (например, через armbian-config
), ядро Linux инициализирует соответствующий SPI-контроллер и создает в системной директории /dev
специальные файлы устройств.
Имя этих файлов имеет строгую структуру: /dev/spidev<bus>.<device>
<bus>
: Это номер SPI-контроллера (шины). ДляSPI0
это будет0
, дляSPI1
—1
, и так далее.<device>
: Это номер ведомого устройства (линии Chip Select) на данной шине. ДляCS0
это будет0
, дляCS1
—1
.
Примеры для Repka Pi 4:
- Файл
/dev/spidev0.0
представляет собой "канал связи" с устройством, подключенным кCS0
на шинеSPI0
. - Файл
/dev/spidev0.1
представляет собой "канал связи" с устройством, подключенным кCS1
на шинеSPI0
.
Роль драйвера spidev
: Эти файлы-устройства управляются универсальным драйвером ядра spidev
. Когда ваша программа (например, использующая библиотеку spidev
для Python) открывает файл /dev/spidev0.0
и выполняет операцию передачи данных, она на самом деле отправляет запрос этому драйверу.
Драйвер spidev
берет на себя всю сложность низкоуровневого управления:
- Принимает от программы высокоуровневую команду (например, "передать массив байт
[0x01, 0x02, 0x03]
и одновременно принять 3 байта в ответ"). - Активирует нужную линию Chip Select (в данном случае
CS0
), переводя ее в состояниеLOW
. - Транслирует команду в последовательность операций с регистрами SPI-контроллера, используя DMA и FIFO для эффективной пересылки данных.
- После завершения транзакции деактивирует линию Chip Select, возвращая ее в
HIGH
. - Возвращает принятые данные обратно в программу.
Таким образом, файловый интерфейс /dev/spidev*
является мощной абстракцией, которая предоставляет программам в пользовательском пространстве простой, безопасный и стандартизированный доступ к высокоскоростной аппаратной шине SPI.
Конечно. Мы подошли к последней главе, посвященной низкоуровневой архитектуре. Мы разберем управляющие регистры SPI-контроллера, чтобы завершить картину, и объясним, почему в реальных условиях мы доверяем эту работу специализированному драйверu ядра.
Архитектура регистров SPI (Низкоуровневый аспект)
Любое взаимодействие с SPI-контроллером на аппаратном уровне сводится к чтению и записи в его аппаратные регистры. Хотя прикладной программист никогда не взаимодействует с ними напрямую, понимание их назначения позволяет в полной мере оценить, какую сложную работу выполняет драйвер операционной системы "под капотом".
Обзор ключевых регистров SPI-контроллера в SoC Allwinner
Ниже описаны четыре основных типа регистров, которые управляют полным циклом SPI-транзакции.
SPI_TX_DATA
(Transmit Data Register) - Регистр передаваемых данных
- Назначение: Данный регистр является аппаратным буфером для исходящих данных. Он служит интерфейсом между системной шиной и передающим сдвиговым регистром SPI-контроллера.
- Принцип работы: Когда драйверу необходимо отправить байт, он записывает его 8-битное значение в этот регистр. Чаще всего,
SPI_TX_DATA
является верхушкой более глубокого аппаратного буфера TX FIFO. SPI-контроллер самостоятельно забирает байт из этого регистра (или очереди FIFO) и начинает его побитовую передачу в соответствии с тактовым сигналом.
SPI_RX_DATA
(Receive Data Register) - Регистр принятых данных
- Назначение: Данный регистр является аппаратным буфером для входящих данных, полученных с линии MISO.
- Принцип работы: После того как SPI-контроллер принял 8 бит по линии
MISO
, он собирает их в байт и помещает в этот регистр (или в очередь RX FIFO). Драйвер должен своевременно считать это значение, прежде чем оно будет перезаписано следующим принятым байтом. - Важный аспект: В силу полнодуплексной природы SPI, этот регистр будет содержать новое значение после каждой 8-тактовой транзакции, даже если операция была задумана как "только запись".
SPI_CTL
(Control Register) - Регистр управления
- Назначение: Этот регистр управляет конфигурацией и режимом работы SPI-контроллера. Записью определенных битовых полей в этот регистр драйвер задает все параметры шины.
- Ключевые управляющие биты:
MASTER/SLAVE Select
: Бит, определяющий роль контроллера (для Repka Pi это всегда Master).CPOL
иCPHA
: Два бита, которые в совокупности устанавливают один из четырех режимов SPI (Mode 0, 1, 2, 3).Clock Divisor
: Группа битов, в которые записывается число-делитель. Контроллер берет высокочастотный системный тактовый сигнал, делит его на это число и получает на выходе требуемую частотуSCLK
(например, 1 МГц, 8 МГц и т.д.).Interrupt Enable
: Биты, разрешающие контроллеру генерировать аппаратные прерывания по различным событиям (например, "TX FIFO пуст" или "RX FIFO содержит данные").CS Control
: Биты, управляющие поведением линий Chip Select.
SPI_STAT
(Status Register) - Регистр статуса
- Назначение: Данный регистр отражает текущее операционное состояние SPI-контроллера и его буферов FIFO. Он содержит набор однобитных флагов, которые драйвер использует для синхронизации операций.
- Ключевые флаги:
TX_EMPTY
: Флаг "Передающий буфер пуст". Сигнализирует о том, что все данные из TX FIFO были отправлены.RX_READY
/RX_NOT_EMPTY
: Флаг "Приемный буфер не пуст". Сигнализирует о том, что в RX FIFO есть данные, готовые для считывания.TX_FIFO_LEVEL
/RX_FIFO_LEVEL
: Группы битов, которые могут указывать на текущий уровень заполнения буферов.BUSY
: Флаг, указывающий на то, что в данный момент идет активная транзакция.
Роль драйвера spidev
в ядре Linux
Прямое управление регистрами SPI из пользовательской программы является нецелесообразным и опасным. Всю эту сложную работу берет на себя драйвер spidev
в ядре Linux.
-
Абстракция и безопасность: Драйвер предоставляет стандартизированный и безопасный файловый интерфейс
/dev/spidev*
. Когда ваша Python-программа вызывает функциюspi.xfer2([...])
, она отправляет ядру одну высокоуровневую команду. -
Реализация транзакции: Получив эту команду, драйвер
spidev
выполняет всю необходимую последовательность низкоуровневых операций:- Конфигурация: Записывает в
SPI_CTL
корректные значения дляCPOL
/CPHA
и делителя скорости, которые вы указали в своей программе (spi.mode
,spi.max_speed_hz
). - Активация: Переводит нужную линию
CS
в низкое состояние. - Передача данных: Загружает ваши данные в
SPI_TX_DATA
(или настраивает DMA для пересылки большого блока), используя прерывания и флаги вSPI_STAT
для эффективного управления потоком. - Прием данных: По мере поступления данных считывает их из
SPI_RX_DATA
и складывает в системный буфер. - Деактивация: После завершения транзакции возвращает линию
CS
в высокое состояние. - Возврат результата: Копирует данные из системного буфера в вашу программу.
- Конфигурация: Записывает в
Таким образом, драйвер spidev
выступает в роли профессионального "пилота", который берет вашу простую команду ("доставить этот груз по этому маршруту") и самостоятельно выполняет все сложные и опасные манипуляции с "приборами" (регистрами), чтобы безопасно и эффективно выполнить задачу.