SPI и Arduino:
Это первая статья из цикла статей, посвящённых шине SPI и работе с ней на платформе Arduino. Тема достаточно обширная, так что начнём с теории.
SPI (Serial Peripheral Interface), или последовательный периферийный интерфейс, был разработан компанией Motorola для организации быстрого и простого в реализации обмена данными между компонентами системы — микроконтроллерами и периферийными устройствами. На шине может быть одно ведущее устройство (master) и несколько ведомых (slave).
Интерфейс использует 4 линии для обмена данными:
- SCLK — Serial Clock: тактовый сигнал (от ведущего)
Другие обозначения: SCK, CLK
Arduino: пин 13 - MOSI — Master Output, Slave Input: данные от ведущего к ведомому
Другие обозначения: SDI, DI, SI
Arduino: пин 11 - MISO — Master Input, Slave Output: данные от ведомого к ведущему
Другие обозначения: SDO, DO, SO
Arduino: пин 12 - SS — Slave Select: выбор ведомого; устанавливается ведущим
Другие обозначения: nCS, CS, CSB, CSN, nSS, STE
Arduino: по умолчанию пин 10
Линия SS обычно для каждого ведомого своя, но некоторых ведомых возможно подключить к одной SS — такой способ используется для каскадного подключения устройств.
Стандартный алгоритм работы SPI таков:
- Ведущий устанавливает низкий уровень на той линии SS, к которой подключен нужный ведомый.
- Ведущий задаёт такт, «дрыгая» уровнем на SCLK, и одновременно с каждым дёрганьем SCLK выставляет нужный уровень на MOSI, передавая ведомому по биту за такт.
- Ведомый на каждый «дрыг» SCLK выставляет нужный уровень на MISO, передавая ведущему по биту за такт.
- Для завершения передачи ведущий устанавливает высокий уровень на SS.
SPI является полнодуплексной шиной — данные передаются одновременно в обе стороны. Типичная скорость работы шины лежит в пределах 1-50 МГц. Благодаря исключительной простоте алгоритма передачи SPI получил широчайшее распространение в самых различных электронных устройствах — например, в датчиках, чипах памяти, радиомодулях, и т.д.
Вообще, у SPI есть четыре режима передачи, которые основаны на комбинации «полярности» тактового сигнала (clock polarity, CPOL) и фазы синхронизации (clock phase, CPHA). Проще говоря, CPOL — это уровень на тактовой линии до начала и после окончания передачи: низкий (0) или высокий (1). А фаза определяет, на фронте или спаде тактового сигнала передавать биты:
- Режим 0: CPOL=0, CPHA=0
Чтение бита происходит на фронте тактового сигнала (переход 0 ⇨ 1), а запись — на спаде (1 ⇨ 0). - Режим 1: CPOL=0, CPHA=1
Чтение — на спаде, запись — на фронте. - Режим 2: CPOL=1, CPHA=0
Чтение — на спаде, запись — на фронте. - Режим 3: CPOL=1, CPHA=1
Чтение — на фронте, запись — на спаде.
Данные по SPI можно передавать либо старшим битом вперёд (по умолчанию для Arduino), либо младшим. Обычно используется первый вариант, но перед началом работы с устройством следует уточнять этот момент в документации.
Кратко о библиотеке SPI
Эта библиотека использует аппаратные возможности AVR для работы по SPI на Arduino, причём только в режиме ведущего (SPI master). Функций в ней совсем немного:
- begin() и end()
Инициализация и завершение работы с SPI. При инициализации линии SCLK (13), MOSI (11) и SS (10) настраиваются на вывод, выставляя на SCK и MOSI низкий, а на SS — высокий уровень. Вызов end() линии не трогает, оставляя в том же состоянии, что и до вызова — просто выключает блок SPI микроконтроллера. - setBitOrder(order)
Устанавливает порядок посылки битов данных (order):
MSBFIRST — первым идёт старший бит посылки (по умолчанию)
LSBFIRST — первым идёт младший бит - setClockDivider(divider)
Устанавливает делитель тактов для SPI относительно основной частоты. Доступны делители 2, 4, 8, 16, 32, 64 и 128. Соответствующие константы имеют имена вида SPI_CLOCK_DIVn, где n — делитель, например, SPI_CLOCK_DIV32. По умолчанию делитель равен 4 — при обычной тактовой частоте МК на Arduino в 16 МГц SPI будет работать на частоте 4 МГц.
На заметку: если устройство поддерживает частоту, скажем, 1.25 МГц, то нужно выставить делитель, соответствующий этой или меньшей частоте — 16, например. - setDataMode(mode)
Задаёт режим работы SPI, используя константы SPI_MODE0 (по умолчанию), SPI_MODE1, SPI_MODE2 и SPI_MODE3. Это те самые режимы c параметрами CPOL и CPHA. - transfer(value)
Осуществляет двустороннюю передачу: передаёт байт value и возвращает байт, принятый от ведомого.
Кроме того, доступны функции shiftIn(miso_pin, sclk_pin, bit_order) и shiftOut(mosi_pin, sclk_pin, order, value), они предоставляют программную полудуплексную передачу данных по SPI — этакие половинки метода transfer(): shiftIn() только принимает, а shiftOut() только передаёт данные. Как видно по их аргументам, они позволяют использовать любые цифровые пины Arduino в качестве линий SPI, но вы сами должны настроить их как входы/выходы, функции shiftIn() и shiftOut() этого не делают.
Ссылки
- Статьи об SPI на Википедии и gaw.ru
- Документация к библиотеке SPI и заметка о подключении
Следующая тема — SPI и Arduino: вывод
UPD 2015-12-10
Про настройку режима передачи SPI.
Код библиотеки содержится в C:\Arduino\hardware\arduino\avr\libraries\SPI\SPI.h
... #define SPI_MODE0 0x00 #define SPI_MODE1 0x04 #define SPI_MODE2 0x08 #define SPI_MODE3 0x0C #define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR ... class SPIClass { public: ... inline static void setDataMode(uint8_t dataMode) { SPCR = (SPCR & ~SPI_MODE_MASK) | dataMode; } ... }; extern SPIClass SPI;
Т.о., при настройке режима передачи SPI — настраиваются 3-й и 2-й биты регистра SPCR:
SPCR (SPI Control Register)
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 SPIE SPE DORD MSTR CPOL CPHA SPR1 SPR0
Т.о., код
SPI.setDataMode(SPI_MODE1);
— приведёт к установке CPOL = 0 and CPHA = 1 (0x04 == 0100).
The Serial Peripheral Interface (SPI)
По теме
1-Wire
Интерфейс I2C
7 комментариев на «“SPI и Arduino: теория”»
Прекрасная статья. Но вот когда дошло дело до реального устройства, я попал в ступор. Есть видеокамера установки которой изменяются через SPI пока все просто, но одно «НО» управление камерой осуществляется по трём проводам:
1. SLD (SPI communication enable, active L)
2. SCL (CLOCK input)
3. SDA (bidirectional DATA line)
Так вот этот SDA он является и SDI и SDO в одном флаконе. Вопрос — как подключить Arduino к этому девайсу, чтобы можно было читать и писать одновременно? Пока что я осилил только запись…
Тут без
поллитрыдатащита не разберёшся.Какую камеру ломаете(модель)? Документация есть?
Камера Watec WAT-910BD. Могу выслать
поллитрадатащит…высылайте=)
Можно на любую почту [AT]robocraft.ru (напр. в разделе «о проекте»)
Можно тут ссылку
Спасибо за проявленное сочувствие и интересэто ссылка на даташит.
Угу…стандартные функции последовательного ввода/вывода — shiftOut и shiftIn — указывая в качестве dataPin в обоих случаях один и тот же вывод(ну и clockPin тоже один на всех ессно) + прям перед выполнением чтения или записи (не в сетапе) переключать dataPin в режим выхода и выхода соответственно:
Так ну первая мысль — соединить MISO и MOSI и при чтении из считанного отбрасывать первые 15 бит адреса (которые туда попадут с MOSI)но сразу видятся проблемы — в стандартной реализации SPI данные получаются только когда отправляются, тоесть нужно будет что-то слать для/во время чтения. Либо нули либо единицы — что сможет передавить камера +наверняка придётся играться с резистором на MOSI — нужно подобрать такой, чтобы не МК не мешал вещать камере (в датащите электрических хр-к камеры нет, так что остаётся славный метод тыка)
Плохой путь.
Полагаю в данном случае будет лучше уйти от использования хардварного SPI и использовать
Только:
1. надо уточнить подходит ли очерёдность дрыганья clockPin и чтения/записи (то же что режим передачи у SPI) спецификации камеры, мне уже лень=))
2. передача у камеры на чтение/запись должна начинаться с передачи дополнительного 0/1, помимо собственно команды/адреса (ручками добавлять)
3. и shiftOut и shiftIn шлют/принимают по одному байту(как я понял из беглого проглядывания) а камере периодически надо больше (запись N-байт в камеру, 15 бит адрес, приём N-байт из камеры)- придётся выполнять shiftOut/shiftIn по несколько раз.
Ну или рисовать свои функции для общения с камерой=)
Ну вот, где то так думал и я и пока что реализовал одностороннюю связь — только на запись в камеру, настройки дрыгания и тому подобное учёл всё работает. Теперь попробую научиться читать…
В любом случае, спасибо за поддержку и скорые ответы.