CraftDuino v2.0
  • - это CraftDuino - наш вариант полностью Arduino-совместимой платы.
  • CraftDuino - настоящий конструктор, для очень быстрого прототипирования и реализации идей.
  • Любая возможность автоматизировать что-то с лёгкостью реализуется с CraftDuino!
Просто добавьте CraftDuino!

SPI и Arduino: теория

SPI и Arduino:
  1. Теория
  2. Вывод
  3. Ввод

Это первая статья из цикла статей, посвящённых шине 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 таков:
  1. Ведущий устанавливает низкий уровень на той линии SS, к которой подключен нужный ведомый.
  2. Ведущий задаёт такт, «дрыгая» уровнем на SCLK, и одновременно с каждым дёрганьем SCLK выставляет нужный уровень на MOSI, передавая ведомому по биту за такт.
  3. Ведомый на каждый «дрыг» SCLK выставляет нужный уровень на MISO, передавая ведущему по биту за такт.
  4. Для завершения передачи ведущий устанавливает высокий уровень на 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 и 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
  • +2
  • 12 мая 2011, 14:19
  • burjui

Комментарии (7)

RSS свернуть / развернуть
+
0
Прекрасная статья. Но вот когда дошло дело до реального устройства, я попал в ступор. Есть видеокамера установки которой изменяются через SPI пока все просто, но одно «НО» управление камерой осуществляется по трём проводам:
1. SLD (SPI communication enable, active L)
2. SCL (CLOCK input)
3. SDA (bidirectional DATA line)

Так вот этот SDA он является и SDI и SDO в одном флаконе. Вопрос — как подключить Arduino к этому девайсу, чтобы можно было читать и писать одновременно? Пока что я осилил только запись…
avatar

mkokorev

  • 12 сентября 2013, 12:35
+
0
Тут без поллитры датащита не разберёшся.
Какую камеру ломаете(модель)? Документация есть?
avatar

Zoltberg

  • 15 сентября 2013, 12:31
+
0
Камера Watec WAT-910BD. Могу выслать поллитра датащит…
avatar

mkokorev

  • 15 сентября 2013, 17:20
+
0
высылайте=)
Можно на любую почту [AT]robocraft.ru (напр. в разделе «о проекте»)
Можно тут ссылку
avatar

Zoltberg

  • 16 сентября 2013, 22:11
+
0
Спасибо за проявленное сочувствие и интерес это ссылка на даташит.
avatar

mkokorev

  • 17 сентября 2013, 08:45
+
0
Угу…
Так ну первая мысль — соединить MISO и MOSI и при чтении из считанного отбрасывать первые 15 бит адреса (которые туда попадут с MOSI)но сразу видятся проблемы — в стандартной реализации SPI данные получаются только когда отправляются, тоесть нужно будет что-то слать для/во время чтения. Либо нули либо единицы — что сможет передавить камера +наверняка придётся играться с резистором на MOSI — нужно подобрать такой, чтобы не МК не мешал вещать камере (в датащите электрических хр-к камеры нет, так что остаётся славный метод тыка)
Плохой путь.
Полагаю в данном случае будет лучше уйти от использования хардварного SPI и использовать стандартные функции последовательного ввода/выводаshiftOut и shiftIn — указывая в качестве dataPin в обоих случаях один и тот же вывод(ну и clockPin тоже один на всех ессно) + прям перед выполнением чтения или записи (не в сетапе) переключать dataPin в режим выхода и выхода соответственно:
pinMode(dataPin, OUTPUT); //на выход
shiftOut(dataPin, clockPin, bitOrder, value) //послали адрес
pinMode(dataPin, INPUT); // переключились на вход
byte incoming = shiftIn(dataPin, clockPin, bitOrder)//и слушаем что нам ответят
Только:
1. надо уточнить подходит ли очерёдность дрыганья clockPin и чтения/записи (то же что режим передачи у SPI) спецификации камеры, мне уже лень=))
2. передача у камеры на чтение/запись должна начинаться с передачи дополнительного 0/1, помимо собственно команды/адреса (ручками добавлять)
3. и shiftOut и shiftIn шлют/принимают по одному байту(как я понял из беглого проглядывания) а камере периодически надо больше (запись N-байт в камеру, 15 бит адрес, приём N-байт из камеры)- придётся выполнять shiftOut/shiftIn по несколько раз.

Ну или рисовать свои функции для общения с камерой=)
avatar

Zoltberg

  • 17 сентября 2013, 10:22
+
0
Ну вот, где то так думал и я и пока что реализовал одностороннюю связь — только на запись в камеру, настройки дрыгания и тому подобное учёл всё работает. Теперь попробую научиться читать…
В любом случае, спасибо за поддержку и скорые ответы.
avatar

mkokorev

  • 17 сентября 2013, 14:26

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.