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


7 комментариев на «“SPI и Arduino: теория”»

  1. Прекрасная статья. Но вот когда дошло дело до реального устройства, я попал в ступор. Есть видеокамера установки которой изменяются через 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 (напр. в разделе «о проекте»)
      Можно тут ссылку

    • Угу…
      Так ну первая мысль — соединить 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 по несколько раз.

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

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

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна конкурс манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение