Miso mosi что это

от admin

SPI -Serial Peripheral Interface, краткое руководство

SPI — (Serial Peripheral Interface) эспиай, последовательный периферийный интерфейс иногда называемый 4-х проводным интерфейсом, является последовательным синхронным интерфейсом передачи данных. Изобретён компанией Motorola в середине 1980-x. В отличие от I2C и UART, SPI требует больше сигналов для работы, но может работать на более высоких скоростях. Не поддерживает адресацию, вместо этого используется сигнал SS (slave select — выбор ведомого), который также иногда называется CS (chip select), CE (chip enable) или SE (slave enable). Поддерживает только одного ведущего на шине. Ведущий устанавливает скорость обмена данными и другие параметры, такие как полярность и фаза тактирования. Обмен данными происходит в режиме полного дуплекса, что означает устройства на шине могут одновременно передавать и принимать данные. Интерфейс использует следующие сигналы (в номенклатуре AVR, для получения точного названия сигналов обратитесь к технической документации микросхемы, с которой работаете):

  • MISO (master in slave out) — вход ведущего, выход ведомого
  • MOSI (master out slave in) — выход ведущего, вход ведомого
  • SCK (serial clock) — сигнал тактирования
  • SS (slave select) — сигнал выбор ведомого.

Несмотря на то, что интерфейс называется 4-х проводным, для подключения нескольких ведомых понадобится по одному проводу SS для каждого ведомого (в полнодуплексной реализации). Сигналы MISO, MOSI и SCK являются общими для всех устройств на шине. Ведущий посылает сигнал SS для того ведомого, обмен данными с которым будет осуществляться. Простыми словами, все ведомые, кроме выбранного ведущим будут игнорировать данные на шине. SS является инверсным (active-low), что означает что ведущему необходимо прижать эту линию для выбора ведомого.

Подключение:

SPI на Arduino:

Arduino UNO/Piranha UNO/Arduino ULTRA

На Arduino UNO/Piranha UNO/Arduino ULTRA выводы аппаратного SPI расположены на 10, 11, 12 и 13 выводах, а так же эти выводы соединены с колодкой ICSP (in circuit serial programmer):

Сигнал Вывод
SS 10
MOSI 11
MISO 12
SCK 13

Arduino MEGA

На Arduino MEGA выводы аппаратного SPI расположены на 50, 51, 52 и 53 выводах, а так же эти выводы соединены с колодкой ICSP (in circuit serial programmer):

Сигнал Вывод
SS 53
MOSI 51
MISO 50
SCK 52

Пример для Arduino

В этих примерах мы соединим две Arduino по SPI по следующей схеме:

В одну плату необходимо загрузить скетч ведущего, а в другую скетч ведомого. Для проверки работы необходимо открыть проследовательный монитор той платы, в которую загружен скетч ведомого.

Arduino UNO в качестве ведущего:

Arduino UNO в качестве ведомого:

После соединения двух Arduino по SPI и загрузки соответствующих скетчей, мы будем получать следующее сообщение в мониторе последовательного порта ведомого микроконтроллера раз в секунду:

SPI на Raspberry Pi

На Raspberry Pi выводы аппаратного SPI расположены на выводах GPIO7, GPIO8, GPIO9, GPIO10, GPIO11:

Перед работой с SPI необходимо его включить. Сделать это можно из эмулятора терминала командой sudo raspi-config -> Interfacing options -> Serial -> No -> Yes -> OK -> Finish или из графической среды в главном меню -> Параметры -> Raspberry Pi Configuration -> Interfaces -> SPI

Подробное описание как это сделать можно посмотреть по ссылке Raspberry Pi, включаем I2C, SPI

Пример работы с SPI на Python:

В отличие от Arduino для Raspberry не существует простых решений для работы в режиме ведомого. Подробней ознакомиться с работой чипа BCM Raspberry можно в технической документации на официальном сайте, стр. 160.

Для проверки работы сценария можно подключить Raspberry по SPI к Arduino со скетчем из примера выше через преобразователь уровней или Trema+Expander Hat:

Подробнее о SPI

Параметры

Существуют четыре режима работы SPI, зависящие от полярности (CPOL) и фазы (CPHA) тактирования:

Режим Полярность Фаза Фронт тактирования Фронт установки бита данных
SPI_MODE0 0 0 Спадающий Нарастающий
SPI_MODE1 0 1 Нарастающий Спадающий
SPI_MODE2 1 0 Нарастающий Спадающий
SPI_MODE3 1 1 Спадающий Нарастающий

В Arduino IDE для установки режима необходимо передать функции, возвращающей объект настроек параметр режима работы SPI_MODE, например:

Для выбора режима работы SPI на Raspberry Pi необходимо вызвать дескриптор объекта SpiDev().mode и присвоить ему битовые значения CPOL и CPHA, например:

Скорость передачи данных

Скорость передачи данных устанавливается ведущим и может меняться «на лету». Программист в силах указать лишь максимальную скорость передачи данных.

Описание интерфейса SPI

Интерфейс SPI — это один из самых популярных на сегодняшний день последовательных интерфейсов. Он был придуман фирмой Motorola и очень быстро завоевал популярность благодаря своей исключительной простоте и высокой скорости. При этом, SPI, наверное, нельзя назвать в полной мере интерфейсом, скорее это просто принцип связи, поскольку всё, что подразумевается под SPI, — это логика передачи данных между двумя устройствами («Ведущий»-«Ведомый»), физике же уделяется гораздо меньшее внимание, она реализуется, можно сказать, «по обстоятельствам», а никакого протокола нижнего уровня вообще нет, тут каждый производитель придумывает что-то своё.

Ну что ж, — с главного и начнём. Итак, в чём же тут логика? Логика в том, что данные передаются последовательно, побитно, при этом считывание и установка данных разделены во времени с помощью специального синхросигнала на специальной шине. Эта шина называется шиной тактирования (или шиной синхронизации), а суть разделения заключается в том, что считывание и установка данных происходят по противоположным фронтам генерируемых на шине синхроимпульсов. Такое, чётко разделённое во времени, чередование установок и считываний даёт возможность использовать один и тот же регистр и для приёма, и для передачи данных. Ранее (когда память была маленькой и дорогой, операционки хранились на дискетах, а по полям бегали мамонты ) это было серьёзным преимуществом, более того, именно под это на самом деле изначально и затачивался SPI, однако сейчас никаких проблем с обьёмом памяти нет и большинство устройств спокойно могут позволить себе иметь отдельные входной и выходной регистры.

Устройство, управляющее шиной тактирования (то есть генерирующее на ней синхроимпульсы), является «Ведущим» или «Мастером». Собственно, «Master» управляет всем обменом данными, — он решает: когда начинать обмен, когда заканчивать, сколько бит передать и т.д. Второе устройство, участвующее в обмене, является «Ведомым» или «Slave». В SPI, в отличии от, например, того же I2C, «Slave» совсем бесправен, он вообще никак не может влиять на шину тактирования и никак не может сообщить мастеру, что не успевает или, наоборот, что уже готов к обмену. То есть «Мастер» сам должен знать: когда, что и на какой скорости спросить у «Слэйва», чтобы тот смог ему ответить.

Всего, для полнодуплексного обмена (в обе стороны одновременно), в интерфейсе SPI используются 4 линии (смотрим рисунок): SCLK, MOSI, MISO и SS.

  1. SCLK — шина тактирования (на этой линии мастер генерирует синхроимпульсы)
  2. MOSI (Master Out, Slave In) — выход ведущего, вход ведомого (по этой линии мастер передаёт данные слэйву)
  3. MISO (Master In, Slave Out) — вход ведущего, выход ведомого (по этой линии мастер принимает данные от слэйва)
  4. SS (Slave Select) — выбор ведомого (с помощью этой линии мастер управляет сеансами обмена)

«1» и «0» кодируются уровнем напряжения на шинах данных (MOSI, MISO) в обычной положительной логике, то есть высокий уровень напряжения на шине соответствует «единице», а низкий уровень соответствует «нулю». При этом, то, каким образом организуется установка на шинах этих уровней, — нигде не оговаривается, то есть выходы передатчиков могут быть как «push-pull», так и «с открытым коллектором». Высокий уровень обычно соответствует напряжению питания микросхемы (то есть если мы имеем дело с пятивольтовыми микрухами, то высокий уровень — это напряжение, близкое к пяти вольтам, если речь идёт о микрухах, питающихся от 3,3В, то высокий уровень — это напряжение, близкое к 3,3В).

Сигнал SS отмечает начало и конец сеанса обмена. Этот сигнал обычно инверсный, то есть во время сеанса обмена данными мастер должен устанавливать на линии SS низкий уровень, а при отсутствии обмена — высокий. Наличие сигнала SS позволяет мастеру организовать подключение к нескольким слэйвам, используя один и тот же синхросигнал и одни и те же шины данных, без каких-либо дополнительных протоколов (вариант такого подключения показан на рисунке слева). Правда тут есть один минус: в этом случае мастеру придётся к каждому слэйву подключаться по отдельной линии SS (чтобы управлять сеансами обмена с каждым слэйвом независимо друг от друга), что увеличивает общее количество используемых проводов.

Названия линий, в общем-то, не являются каким-то стандартом и могут отличаться в зависимости от производителя (например, вместо MOSI, MISO и SCLK линии могут называться DI, DO и SC, или SI, SO и CLK, линия SS может называться CS или RESET).

Более того, линий не обязательно должно быть четыре, — иногда их может быть только три, например, если данные передаются только в одном направлении или вместо двух однонаправленных шин данных используется одна двунаправленная. Очевидно, что в последнем случае возможен только полудуплексный обмен, то есть в один момент времени можно только передавать или только принимать данные (а передавать и принимать одновременно — нельзя).
То есть, ни по названию линий, ни по уровням напряжения на них, ни даже по их количеству, однозначно идентифицировать SPI нельзя, зато это отлично можно сделать по самому методу передачи данных, по тому как происходит их установка на шину и считывание.

Как я уже упоминал, — данные передаются побитно, а установка и чтение данных происходит по противоположным фронтам сигнала тактирования. Момент чтения данных в англоязычной литературе называется latch (фиксация, защёлкивание), а момент установки данных на шину — shift (сдвиг). Сдвигом момент установки называется в силу особенностей большинства последовательных интерфейсов. Обычно никто не передаёт данные по одному биту, как правило, их посылают пачками по 8 и более бит (размер пачки чаще всего всё же кратен восьми). В связи с этим, на выходе передатчика делают сдвиговый регистр, куда загружают сразу всю пачку передаваемых бит, при этом значение младшего или старшего бита этого сдвигового регистра устанавливается на шине данных (смотря как передаём — младшим или старшим битом вперёд), а для установки на шине следующего передаваемого бита — достаточно «сдвинуть» этот регистр. Так устроены передатчики и в SPI, и в I2C, и в привычном RS232, и много где ещё (так просто аппаратно удобнее). Ну, ладно, — вернёмся к нашему SPI.

Логический уровень сигнала на шине тактирования в неактивном состоянии (когда нет передачи данных) называют полярностью и обозначают CPOL (то есть, если при отсутствии передачи на шине SCLK низкий уровень, то CPOL=0, а если в это время на шине SCLK высокий уровень, то CPOL=1). Порядок чередования считываний и сдвигов называют фазой и обозначают CPHA (если по первому фронту на SCLK происходит считывание, то CPHA=0, а если по первому фронту на SCLK происходит сдвиг, то CPHA=1).

В зависимости от сочетания значений CPOL и CPHA различают 4 режима работы интерфейса SPI, которые так и обозначают Mode0, Mode1, Mode2 и Mode3. Ниже приведена картинка, иллюстрирующая как происходит установка и чтение данных, в зависимости от выбранного режима.

Хотелось бы подчеркнуть, что SS — это именно линия управления сеансом обмена, а не просто линия выбора слэйва. Разница тут в том, что если считать SS просто линией выбора слэйва, то при подключении мастера к одному единственному слэйву возникает соблазн этой линией не управлять, а жёстко закоротить её на общий провод (типа чтоб слэйв всегда был выбран). Однако, логика слэйва обычно такова, что начало сеанса сопровождается различными подготовительными процедурами, такими как загрузка данных в выходной сдвиговый регистр и сброс счётчика импульсов, а выполнять какие-то действия (в соответствии с принятыми по SPI командами от мастера) слэйв начинает только после завершения сеанса обмена. Кроме того, вам ведь вполне может понадобиться несколько сеансов общения (например, если в первом сеансе вы посылаете команды, а в следующем хотите получить отчёт о результате их выполнения). Думаю понятно, что если жёстко притянуть линию SS к общему проводу, то ни о каком распознавании начала и конца сеанса обмена (начало распознаётся по спаду на линии SS, а конец — по подъёму) не может быть и речи, соответственно весь обмен данными будет нарушен. Так что важность сигнала SS не стоит недооценивать.

Ну и напоследок скажу, что наиболее популярными являются режимы Mode0 и Mode3.

Более подробно о том, как происходит обмен, что должен уметь SPI-мастер и как это программно реализовать на микроконтроллере (на примере контроллеров PIC и AVR) можно почитать в статье «Программная реализация ведущего шины SPI»

Обзор шины SPI и разработка драйвера ведомого SPI устройства для embedded Linux (Часть первая, обзорная)

В этой статье я хочу провести краткий обзор шины SPI (интерфейса, широко распространённого во встраиваемой технике, используемого для подключения различных устройств) и попытаюсь описать процесс создания драйвера протокольного уровня SPI устройства для Linux. Данный документ не претендует на роль полного руководства, а скорее преследует цель указать нужное направление. Так как статья не вошла в размер одного топика, мне пришлось разбить её на две части.

0. Вместо введения

Что это за статья?
Эта статья представляет собой компиляцию информации из различных источников, вольный перевод некоторых частей документации, а также мои собственные комментарии, дополнения и описания возникших проблем.

  1. Что такое SPI?
  2. Обзор SPI подсистемы в Linux
  3. Разработка userspace протокольного SPI драйвера с использованием spidev
  4. Разработка протокольного SPI драйвера уровня ядра
  5. Документация

Первый подраздел описывает работу шины SPI, данная часть статьи конкретно к Linux никак не привязана, поэтому её можно читать тем, кому Linux не интересен, а нужно лишь получить информацию об этом интерфейсе.

Второй подраздел описывает структуры и механизмы лежащие в основе работы с SPI в Linux, его нужно прочесть для понимания того, о чём пойдёт речь в третьей и четвёртой частях.

Если вас не интересует мои переводы и дополнения, можете смело переходить сразу к пятой части, там можно найти информацию о том, где получить всю необходимую информацию по данному вопросу.

Если вы видите ссылки в названии какой-либо структуры или функции, можете открыть её в новой вкладке, так вы сможете попасть непосредственно на описание данной структуры/функции в официальной документации к ядру Linux.

Ошибки
Я не волшебник, я только учусь. Если найдёте какие-либо ошибки или неточности, пожалуйста, сообщите мне.

1. Что такое SPI?

Аббревиатура SPI означает «Serial Peripheral Interface» или в русском варианте «последовательный периферийный интерфейс». Название говорит само за себя, данный интерфейс используется для работы с различными периферийными устройствами. Например, это могут быть различные ЦАП/АЦП, потенциометры, датчики, расширители портов ввода/вывода (GPIO), различная память и даже более сложная периферия, такая как звуковые кодеки и контроллеры Ethernet.

С технической точки зрения SPI — это синхронная четырёхпроводная шина. Она представляет собой соединение двух синхронных сдвиговых регистров, которые является центральным элементом любого SPI устройства. Для соединения используется конфигурацию ведущий/ведомый. Только ведущий может генерировать импульсы синхронизации. В схеме всегда только один ведущий (в отличие от той же шины I2C, где возможен вариант с более чем одним ведущим), количество ведомых может быть различно. В общем случае выход ведущего соединяется со входом ведомого, и наоборот, выход ведомого соединяется со входом ведущего. При подаче импульсов синхронизации на выход SCK, данные выталкиваются ведущим с выхода MOSI, и захватываются ведомым по входу MISO. Таким образом если подать количество импульсов синхронизации соответствующее разрядности сдвигового регистра, то данные в регистрах обменяются местами. Отсюда следует что SPI всегда работает в полнодуплексном режиме. А вот нужны ли нам данные, полученные от устройства при записи какого-либо параметра, это уже другой вопрос. Часто бывает что данные полученные от устройства при записи в него данных являются мусором, в таком случае их просто игнорируют, но мы их получим вне зависимости от нашего желания.

Контроллер SPI, как правило, реализуется периферийным блоком в MCU или eMPU. В большинстве чипов он может работать как в режиме ведущего, так и в режиме ведомого. Но на данный момент Linux поддерживает только режим ведущего (Master).

Существует несколько способов включения SPI устройств.

image

Простейший из них вы видите на рисунке выше (спасибо Wikipedia за рисунки под свободной лицензией GFDL). В данном случае к ведущему все ведомые подключаются параллельно, за исключением сигнала выбора ведомого (

CS). Для каждого ведомого необходим отдельный сигнал выбора ведомого (на рисунке они обозначены как SSx). Для сигналов выбора ведомого могут использоваться как специально предназначенные для этого выходы SPI-контроллера, так и порты ввода/вывода общего назначения (GPIO) микроконтроллера.

  • MOSI — Master Output, Slave Input (выход ведущего, вход ведомого). Данный сигнал предназначен для последовательной передачи данных от ведущего к ведомому. Также может называться SDO, DO и т.п.
  • MISO — Master Input, Slave Output (вход ведущего, выход ведомого). Данный сигнал предназначен для последовательной передачи данных от ведомого к ведущему. Может называться SDI, DI и т.п.
  • SCK — Serial Clock (сигнал синхронизации). Используется для синхронизации при передаче данных. Также может иметь название SCLK, CLK и др.

CS — Chip Select (выбор микросхемы). С помощью данного сигнала происходит активация ведомого устройства. Обычно он является инверсным, то есть низкий уровень считается активным. Иногда его называют

Частным случаем независимого подключения является вариант с одним единственным ведомым. В таком случае может возникнуть желание подтянуть сигнал

CS к земле, чтобы устройство всегда было в активном состоянии. Но делать это крайне не рекомендуется, так как ведомое устройство может использовать сигнал CS для инициализации или для других служебных целей.

Основное неудобство при независимом подключении ведомых в том, что для каждого из ведомых необходим отдельный сигнал

CS. Каскадная схема подключения, в зарубежной литературе называемая «daisy-chain» (можно перевести как «гирлянда»), лишена такого недостатка.

image

Как видно из рисунка выше, здесь используется общий сигнал выбора ведомого для всех ведомых. Выход каждого из ведомых соединяется со входом следующего. Выход последнего ведомого соединяется со входом ведущего, таким образом образуется замкнутая цепь. При таком подключении можно считать что последовательно соединённые устройства образуют один большой сдвиговый регистр. Соответственно, данные можно записать во все устройства «за один присест», предварительно собрав нужный пакет, объединяющий данные для каждого из устройств в порядке соответствующем физическому порядку соединения. Но тут есть один тонкий момент. Во-первых, все микросхемы должны поддерживать такой тип подключения; во-вторых, ядро Linux не поддерживает такой тип подключения, так что если всё же захотите его использовать, то вам придётся модифицировать существующие драйвера, либо же написать собственные.

  • CPOL (Clock Polarity) — определяет начальный уровень (полярность) сигнала синхронизации.
    CPOL=0 показывает, что сигнал синхронизации начинается с низкого уровня, так что передний фронт является нарастающим, а задний — падающим.
    CPOL=1, сигнал синхронизации начинается с высокого уровня, таким образом передний фронт является падающим, а задний — нарастающим.
  • CPHA (Clock Phase) — фаза синхронизации, определяет по какому из фронтов синхронизирующего сигнала производить выборку данных.
    CPHA=0 показывает что необходимо производить выборку по переднему фронту, а
    CPHA=1 показывает что выборку данных необходимо производить по заднему фронту.

CS и MISO не показаны. Но в данном случае они не так интересны, например, сигнал

CS представляет собой просто «провал» на всём протяжении передачи данных.

2. Обзор SPI подсистемы в Linux

Драйверы SPI в Linux делятся на две части. Первая — это драйверы SPI контроллеров, которые работают непосредственно с железом конкретно взятого контроллера. Такие драйверы определяют как настроить контроллер, какие действия предпринять при переходе в режим пониженного энергопотребления (suspend) и выходе из него(resume), выбор следующей передачи (spi_transfer) из очереди передач в сообщении (spi_message, об очередях чуть ниже) и отправка его непосредственно в порт, также определяется как активировать/деактивировать конкретное устройство посредством CS (функции cs_activate/cs_deactivate). В этой статье я не буду описывать данный тип драйверов. Как правило, они уже реализованы для тех MCU/eMPU на которые существует порт Linux, и лезть в них руками надо только в том случае, если вам нужна какая-то специфичная функция, вроде Chip Select Decoding, для возможности активации нужного ведомого устройства посредством внешней логики. Иногда это бывает полезно, например, в случае недостатка GPIO.

Вторая часть — это протокольные драйверы, используемые для работы с различными ведомыми устройствами, которые подключены к шине SPI. Данные драйверы называют «протокольными», потому что они лишь отправляют и получают различные данные от ведомых устройств, при этом не работая напрямую с каким-либо оборудованием. Именно данный тип драйверов нам наиболее интересен, так как позволяет добавить поддержку интересующего ведомого устройства в систему, его то мы и рассмотрим.

Большинство протокольных драйверов представляет собой модули ядра. Например, если устройство представляет собой аудиокодек подключаемый по SPI, то драйвер будет также использовать функции предоставляемые ALSA, а программы (например, madplay) смогут работать с ним посредством символьного устройства /dev/audio, не имея ни малейшего понятия о том как он аппаратно устроен и к какой шине подключен.

Также ядро предоставляет протокольный драйвер общего назначения, называемый spidev, с интерфейсом в виде символьного устройства. Он позволяет совершать полудуплексные обращения к ведомому SPI-устройству посредством стандартных системных вызовов read() и write(), устанавливать режим работы, а также производить полнодуплексный обмен данными посредством ioctl() вызовов.

  • userspace драйверы, работающие в пространстве пользователя и представляющие собой обычные программы на любом языке, работающие с SPI устройством посредством чтения/записи соответствующего символьного устройства spidev.
  • драйверы, работающие в пространстве ядра и предоставляющие интерфейс для userspace посредством файлов устройств в каталоге /dev, либо с помощью атрибутов в каталоге устройства в sysfs.

transfers — связанный список передаваемых сегментов в транзакции (передач);
spi — указатель на spi устройство, в очереди которого стоит данное сообщение;
is_dma_maped — если данный флаг «истина», то предоставлены оба, dma и cpu виртуальные адреса для каждого буфера передачи;
complete — обратный вызов, вызываемый для извещения об окончании транзакции;
context — аргумент для обратного вызова complete();
actual_length — полное число байт, которые были переданы во всех успешных предачах;
status — 0 в случае успеха, либо отрицательное значение с errno в случае ошибки;

Структура spi_message используется для выполнения атомарной последовательности передач данных, каждая из которых представлена структурой spi_transfer. Последовательность передач «атомарна» в том смысле, что шина SPI не может быть использована для передачи другого сообщения spi_message до тех пор, пока не будет полностью отправлено предыдущее. На некоторых системах, многие такие последовательности могут быть выполнены как единая запрограммированная DMA передача. На всех системах данные сообщения ставятся в очередь, и могут быть завершены уже после транзакций с другими устройствами. Все обращения к отдельно взятому ведомому устройству всегда выполняются в FIFO порядке.

Структура struct spi_transfer описывает отдельную передачу в связанном списке сообщения и определяет пару буферов для чтения/записи.

tx_buf — указатель на буфер данных в пространстве памяти ядра, которые необходимо передать, либо NULL;
rx_buf — указатель на буфер данных в пространстве памяти ядра, в который данные следует считать, либо NULL;
len — размер буферов rx и tx в байтах;
tx_dma — DMA адрес tx_buf, используется если установлен параметр spi_message.is_dma_mapped;
rx_dma — DMA адрес rx_buf, используется если установлен параметр spi_message.is_dma_mapped;
speed_hz — устанавливает скорость для передачи, отличную от установленной по-умолчанию для устройства. Если данное значение равно 0, то используется скорость по-умолчанию, указанная в поле max_speed_hz структуры spi_device.
bits_per_word — устанавливает количество бит на слово, отличное от определённого по умолчанию. Если данное значение равно 0, то используется значенние по-умолчанию, указанное в поле bits_per_word структуры spi_device.
delay_usecs — время ожидания в микросекундах, после того как был отправлен последний бит передачи и перед тем как сменить состояние chipselect’а, либо начать передачу следующей передачи в очереди. Будьте крайне осторожны с данным параметром, нужно смотреть в какой части драйвера контроллера реализуется задержка. Например, для чипов серии at91 она реализована в обработчике прерывания, так что её использование чревато последствиями.

При инициализации структуры spi_transfer существует очень важный момент, они обязательно должны быть выделены в области памяти доступной для DMA через kmalloc, kzalloc и иже с ними. Если master-драйер использует dma, то при использовании статически объявленных массивов драйвер будет падать при попытке передачи.

При передаче данных по SPI количество записанных бит всегда равно количеству считанных. Протокольные драйверы всегда должны предоставлять указатели на буферы tx_buf и/или rx_buf. В некоторых случаях они могут предоставлять DMA адреса для передаваемых данных.

Возможность переопределения скорости передачи данных и количества бит на слово для каждой передачи в отдельности зависит от конкретной реализации драйвера и аппаратных возможностей контроллера. Например, для контроллера SPI в чипах серии at91 возможность переопределения полей speed_hz и bits_per_word не предусмотрена, поэтому они должны быть всегда установлены в 0, иначе вы получите ошибку при попытке передачи данных.

Если указатель на tx_buf установлен как NULL, то SPI контроллер будет выталкивать нули при заполнении буфера rx_buf. В случае, когда rx_buf установлен в NULL, считываемые данные будут игнорироваться. Количество выталкиваемых (и захватываемых) байтов всегда равно len. Попытка вытолкнуть только часть слова приведёт к ошибке. (Например, при попытке выталкивании трёх байт и длине слова 16 бит или 20 бит, в первом случае будет использовано 2 байта на слово, во втором — 4 байта).

Данные для передачи всегда хранятся в порядке специфичном для данной аппаратной платформы. При отправке/считывании данных происходит автоматическое конвертирование порядка байт из специфичного для SPI (обычно big-endian, за исключением случая когда выставлен параметр SPI_LSB_FIRST) в аппаратно-специфичный порядок для данного CPU. Например, если параметр bits_per_word равен 16, то буферы будут занимать по 2N байт, и содержать по N слов с длиной 16 бит каждое, хранящемся в байтовом порядке, специфичным для данного CPU.

В том случае, если размер слова не является степенью двойки, то представление слова в памяти включает дополнительные биты. Слова, хранящиеся в памяти для протокольного драйвера всегда являются выровненными по правому краю (right-justified), так что дополнительные биты всегда будут являться старшими разрядами.
Для наглядности снова приведу осциллограмму:
image
В данном случае tx-буфер содержит значение 0xf98e, установленное значение bits_per_word соответствует 12 битам на слово. Устройство работает в SPI_MODE_0. На рисунке синяя линия соответствует выходу MOSI контроллера, а жёлтая — SCK. Здесь хорошо видно что при отправке пришло только 0x098e, старшие четыре бита были отброшены, так как они считаются дополнительными. Если совсем просто, то одно 12-битное слово занимает в памяти два байта, а разница между размером слова в памяти и его действительным размером составляет 2*8 — 12 = 4 бита, которые отбрасываются при передаче.

  • Если передача не последняя в сообщении, то флаг cs_change используется для того, чтобы chipselect на короткое время стал неактивным в середине сообщения. Такое переключение cs может быть необходимо для завершения команды ведомого. Это позволяет с помощью одного spi_message выполнять сразу группу передач для одного ведомого.
  • Когда передача является последней в сообщении, ведомый может оставаться выбранным до тех пор, пока не начнётся передача следующего сообщения. Начало передачи сообщения другому устройству деактивирует текущий cs. Для работы некоторых устройств транзакции должны состоять из очереди spi_message сообщений, где содержимое последующего сообщения зависит от результата предыдущих. В таком случае транзакция целиком завершается переведением cs в неактивное состояние.

SPI не поддерживает какого-либо механизма автоматического обнаружения устройств. К тому же, в большинстве случаев, SPI устройства не предусматривают горячее подключение/отключение, поэтому они, как правило, просто распаиваются непосредственно на плате. В связи с этим данные устройства считаются специфичными для конкретной платы (board-specific). Параметры для таких устройств указываются в файле платы: arch/. /mach-*/board-*.c.
Например, вот так будет выглядеть установка параметров для аудиокодека tlv320aic23b для отладочной платы SK-AT91SAM9260:

где modalias – название драйвера ядра, отвечающего за обслуживание устройства (в нашем случае “tlv320aic23b”);
chip_select – номер соответсвующего chip select’а;
max_speed_hz – максимальная частота в Гц;
mode – режим SPI, определяемый константами SPI_MODE_0… SPI_MODE_3, также через операцию битового “или” могут быть добавлены флаги SPI_CS_HIGH (устанавливает активным высокий уровень для chipselect-а ), SPI_NO_CS (передача данных без активации CS в принципе). Полный список возможных флагов можно посмотреть в описании структуры spi_device;
bus_num – номер шины (как правило, соответсвует номеру SPI контроллера в даташите на MCU/eMPU).
Также структура spi_board_info содержит следующие поля, не инициализированные в примере выше:
const void *platform_data – данное поле предназначено для хранения указателя на данные специфичные для конкретного драйвера;
void *controller_data – для некоторых контроллеров необходима информация о настройке устройства, например, DMA;
int irq – зависит от подключения устройства.

Все поля структуры spi_board_info устанавливают соответствующие поля структуры spi_device.
В случае необходимости установки параметров для других SPI устройств, в масив добавляются ещё аналогичые элементы.
Данные структуры хранят информацию, которая не может быть всегда определена драйверами. Информация, которая может быть определена функцией probe() драйвера (например, количество бит на слово), в данную структуру не включается.

Стоит заметить, что всё же существует возможность горячего подключения ведомых SPI устройств. В этом случае используют функцию spi_busnum_to_master() для получения указателя на структуру spi_master по номеру шины SPI и дальнейшего перебора устройств на шине. Но данная тема выходит за рамки данной статьи.

Знакомство с шиной SPI

Сегодня мы начинаем знакомство с шиной SPI (Serial Peripheral Interface).

Данная шина очень широко используется в электронике. Она очень удобна, являясь синхронной и полнодуплексной, поэтому, применяется во многих схемах для общения между различными цифровыми усторйствами — датчиками, контроллерами, драйверами и прочими устройствами.

Ещё одним важнейшим фактором необходимости нашего с ней знакомства является то, что данная шина аппаратно организована в контроллерах AVR.

Мало того, хотим мы этого или не хотим, с интерфейсом SPI мы с вами общаемся уже давным-давно, как только начали первый раз прошивать наш контроллер, так как прошивается он именно посредством данного интерфейса.

Поэтому хочется познакомиться с данной шиной поближе.

Давайте откроем техническую документацию на контроллер Atmega8, откроем страницу, где изображена распиновка данного контроллера и посмотрим, что от 16 до 19 ножки и находятся выводы шины SPI

image00

Теперь немного подробнее о данных выводах

SS (chip select) — это ножка выбора устройства. Если на ведомом устройстве на данной ножке установится низкий уровень, то данное устройство будет откликаться и обмениваться информацией по шине SPI, если высокий, то не будет.

MOSI (master output slave input) — это ножка выхода ведущего устройства и входа ведомого устройства.

MISO (master input slave output) — наоборот, выход ведомого, вход ведущего.

SCK — ножка синхронизации. Ко всем устройствам, участвующим в обмене информации по данной шине, подаются синхроимпульсы с определённой частотой.

Вот схема реализации шины SPI в контроллере Atmega8

image01

Как в любой шине, здесь имеется ряд регистров, в которых хранится определённая информация.

Нам интересен SHIFT REGISTER, через него и происходит обмен информации. Как только на ножке синхронизации будет определённый фронт, или нисходящий или восходящий, в зависимости от настройки, данные регистры у ведомого и ведущего устройства обменяются информацией, причем не всей информацией, а только одним битом. Данные регистры сдвинутся влево и старшие биты из каждого регистра уйдут в младшие биты такого же регистра сопряженного устройства. То есть ведомый передаст свой старший бит через ножку MOSI ведущему, который его запишет в освободившийся засчет сдвига влево младший бит, а ведомый свой вытесненный засчет сдвига передаст старший бит через ножку MISO в младший бит ведущего. Вот так и идёт обмен, то есть за 8 полных циклов тактирования они полностью обменяются байтами

image02

Как только все 8 бит одного байта информации передадутся, определённый регистр нам просигнализирует о том, что данный процесс закончен. Вернее, определённый бит определённого регистра.

Также в блок-схеме мы видим делитель, на вход которого поступают тактовые импульсы и уже потом разделённые на определенное значение поступают по цепочке на ножку SCK и уже оттуда подаются на ведомое устройство на одноимённую ножку. Этим и обеспечивается синхронность работы устройств. Частота тактовых импульсов выбирается из наименьшей поддерживаемой всеми устройствами, участвующими в обмене.

Я говорю во множественном числе, так как может быть не только два устройства в данной цепи. Как это обеспечивается при условии, что у устройств нет никаких адресов, я сейчас и расскажу.

Существует несколько способов обмена информацией между несколькими устройствами, то есть когда на одно ведущее устройство приходится несколько ведомых. Мы рассмотрим два самых распространённых из них.

Первый способ — радиальный (нажмите на картинку для увеличения изображения)

image03

Здесь мастер направляет данные к определённому устройству, включая на ножке SS логический 0. При данном способе возможно выбрать только одно устройство, также потребуются несколько свободных ножек портов контроллера.

Есть ещё один интересный способ — кольцевой или каскадный (нажмите на картинку для увеличения изображения)

image05_502

Здесь мы видим, что ножки выбора все запараллелены и обмен идёт по кругу. Тем самым скорость падает, засчёт того, что увеличивается круг передачи, но зато экономятся лапки портов.

Всё это мы в очередных занятиях изучим подробнее, когда будем использовать определённые устройства в наших проектах.

Ну, вроде со схемотехникой передачи данных по шине SPI мы разобрались.

Теперь разберёмся, как же данным процессом управлять на уровне аппаратных регистров контроллера AVR.

Данные регистры мы видим в блок-схеме выше на странице.

У Atmega8 существуют следующие регистры для обслуживания шины SPI.

SPDR (SPI Data Register) — регистр данных, в блок-схеме это DATA BUFFER. В этот регистр мы будем заносить байт для последующей его передачи на ведомое устройство и из него же будем читать байт информации, пришедший с ведомого устройства. Также не обязательно что у нас контроллер будет ведущим устройством. Впоследствии мы соберём схему из двух контроллеров, один из которых будет ведомым. Так что именно в этом регистре будет находиться байт и для отправки и для приёма.

SPCR (SPI Control Register) — управляющий регистр

image06

Данный регистр включает в себя следующие биты:

SPIE (SPI Interrupt Enable) — бит, который разрешает прерывания.

SPE (SPI Enable) — бит, включающий шину SPI.

DORD (Data Order) — бит, устанавливающий порядок отправки бит, Если он установлен в 1, то первым отправляется младший бит, если в 0 — старший.

MSTR (Master/Slave Select) — бит, который назначает устройство ведущим либо ведомым. При установке данного бита 1 устройство будет ведущим.

CPOL (Clock Polarity) — полярность синхронизации, определяет, при каком фронте синхронизирующего импульса будет инициироваться режим ожидания

image08

Если данный бит в 1, то режим ожидания будет у нас при восходящем фронте, а если в 0, то при нисходящем.

CPHA (Clock Phase) — бит, отвечающий за фазу тактирования, то есть по какому именно фронту будет осуществляться передача бита.

image09

Посмотрим диаграммы передачи данных в зависимости от установки CPOL и CPHA

image10

Вот такая вот интересная зависимость. Порой мы иногда видим в технических характеристиках какого-нибудь устройства, что оно, к примеру, может работать в режиме SPI 0:0 и SPI 1:1, вот это как раз и касается настройки этих битов.

SPR1, SPR0 (SPI Clock Rate Select) — это биты, отвечающие за значение делителя частоты синхронизации, работают совместно с битом SPI2X, находящемся в регистре статуса. Он также управляющий, так как восьми бит в управляющем регистре под все настройки не хватило, а в статусном много свободных.

SPSR (SPI Status Register) — статусный регистр

image07

SPI2X (Double SPI Speed Bit) — бит, удваивающий скорость, работающий совместно с битами SPR1 и SPR0 управляющего регистра.

Посмотрим зависимость частоты от данных трёх битов

image11

SPIF (SPI Interrupt Flag) — Флаг прерывания. Установку данного бита в единицу мы ждём. когда принимаем байт. Как только байт от другого устройства появится полностью у нас в буфере, то данный флаг установится. Данный флаг работает только в случае установки бита, разрешающего прерывания, а также разрешения глобальных прерываний.

WCOL (Write COLlision Flag) — флаг конфликта, или коллизий, установится в том случае, если во время передачи данных будет конфликт битов, если во время передачи данных выполнится попытка записи в регистр данных.

Ну теперь мы, можно сказать, немного познакомились с интерфейсом SPI.

В следующем занятии мы уже попытаемся данную шину использовать на практике, подключив к нашему контроллеру какое-нибдуь ведомое устройство.

Купить программатор можно здесь (продавец надёжный) USBASP USBISP 2.0

Читать:
Какие рации используют военные

Похожие публикации