Драйвер SPI для Blackfin, работающий по прерываниям Печать
Добавил(а) microsin   

Здесь приведен перевод документации по библиотеке компании Analog Devices, реализующей физический драйвер устройства SPI (ADI_SPI) без использования каналов DMA (документация в файле Blackfin\docs\drivers\spi\adi_spi_int.pdf, находящемся в каталоге установки системы VisualDSP++). У драйвера нет никаких аппаратных привязок к определенному типу процессора (поскольку большинство, если не все процессоры Blackfin имеют на борту встроенный интерфейс SPI). Достоинство этой версии драйвера состоит в том, что она поддерживает передачу данных в обоих направлениях (ADI_DEV_DIRECTION_BIDIRECTIONAL), а аналогичная версия драйвер с поддержкой DMA предоставляет передачу данных только в одном направлении (см. [6]).

Примечание: имеется аналогичный драйвер SPI, использующий каналы DMA, см. файл Blackfin\docs\drivers\spi\adi_spi_dma.pdf (см. также перевод соответствующей документации [6]). Примеры кода, использующие драйвер SPI с прерываниями и драйвером SPI с DMA, можно найти в поддиректориях Blackfin\Examples\ каталога установки VisualDSP++ (ищите файлы *.c и *.cpp, в которых подключается заголовочный файл adi_spi.h).

Драйвер устройства SPI был разработан для предоставления простого интерфейса к периферийным устройствам SPI процессора Blackfin. Команды, события и коды возврата в драйвере устройств могут быть использованы в программах приложений с целью установки обмена данными через SPI. Драйвер устройства SPI поддержан версиями как с использованием DMA, так и с использованием прерываний. Предоставляются два модуля исходного кода adi_spi_dma.c и adi_spi_int.c, где находятся соответственно версии драйвера SPI с поддержкой DMA и с поддержкой прерываний.

В этом документе описывается только версия драйвера SPI, работающая по прерываниям. Чтобы выбрать версию драйвера SPI с поддержкой прерываний, код приложения должен:

· Подключить файл кода adi_spi_int.c к списку файлов проекта.
· Использовать точку входа драйвера с прерываниями ADISPIIntEntryPoint для открытия драйвера SPI.

[Используемые файлы]

Все упомянутые здесь файлы находятся в поддиректориях каталога установки системы программирования VisualDSP++. Указанные пути для подключаемых файлов начинаются с директории %ProgramFiles%\Analog Devices\VisualDSP 5.0\Blackfin\include, а для файлов исходного кода с директории %ProgramFiles%\Analog Devices\VisualDSP 5.0.

Подключаемые файлы. Исходный код драйвера подключает следующие заголовочные файлы:

services/services.h. Здесь содержатся все определения, прототипы функций и т. д. для всей библиотеки Системных Служб (System Services Library, SSL).

drivers/adi_dev.h. Здесь содержатся все определения, прототипы функций и т. д. для Менеджера Устройств (Device Manager) и общая информация по модели Драйвера Устройства.

drivers/spi/adi_spi.h. Здесь содержатся коды всех команд, событий и значений возврата для API драйвера устройства SPI.

Исходный код. Исходный код драйвера SPI содержится в файлах Blackfin/lib/src/drivers/spi/adi_spi.c и adi_spi_int.c (файл adi_spi_int.c по сути только заглушка, он только подключает код модуля adi_spi.c). Весь код написан на языке C. Для драйвера не используется никаких функций, написанных на ассемблере.

Код драйвера SPI не использует никакие низкоуровневые физические драйверы устройств, он сам фактически является таким драйвером.

[Ресурсы, требуемые для драйвера SPI]

Драйверы устройств обычно требуют для своей работы некоторое количество системных ресурсов (уровни прерываний IVG, каналы DMA, память). В этом разделе описаны ресурсы, которые требует для себя драйвер SPI.

За исключением специально указанных ниже случаев, этот драйвер использует Системные Службы для доступа к любой требуемой аппаратуре процессора и управления этой аппаратурой. Информация в этой секции может быть полезна в определении ресурсов, которые нужно выделить для драйвера SPI со стороны Системных Служб, наподобие количества обработчиков прерывания или количества используемых каналов DMA и т. п. (в этой реализации драйвера каналы DMA не используются).

По той причине, что в Драйверах Устройств и Системных Службах не используется динамическое выделение памяти, вся память для Драйверов Устройств и Системных Служб должна быть явно предоставлена приложением. Библиотека Драйверов Устройств и Системных Служб предоставляет специальные макросы, которые можно использовать в приложении для вычисления необходимого количества базовой памяти и/или количества дополнительной памяти, которая требуется для поддержки нужного функционала служб. Память для Менеджера Устройств и Системных Служб предоставляется специальными функциями инициализации соответствующего API (adi_xxx_Init(), где вместо xxx указывается мнемонический идентификатор службы. Например для Менеджера Устройств это будет функция adi_dev_Init()).

Прерывания. Этот драйвер SPI использует только одно прерывание - SPI interrupt. Оно может быть сконфигурировано для поддержки либо прерывания передачи, либо прерывания приема.

DMA. Эта версия драйвера SPI не использует DMA. Версию без DMA целесообразно использовать для коротких пересылок блоков данных неопределенной длины и одиночных байт, когда не ведется интенсивный обмен с устройством SPI, и нужно передавать данные в обоих направлениях (версия драйвера SPI с DMA [6] поддерживает только однонаправленную передачу).

Таймер. Драйвер SPI не использует ресурсы таймера.

Часы реального времени (RTC). Драйвер устройства SPI не использует какие-либо службы часов реального времени.

Программируемые флаги. Драйвер SPI не использует явно какие-либо выводы процессора (GPIO, компания Analog Devices почему-то называет их программируемыми флагами). Однако, на процессоре вход и выход SPI может быть мультиплексирован с программируемыми выводами GPIO. Пользователь должен сам убедиться в том, что эти выводы, используемые для SPI, не конфликтовали с использованием выводов GPIO, и наоборот.

Используемые выводы. Всего интерфейс SPI для соединения с внешним миром использует 4 сигнальных провода - 2 вывода данных MOSI и MISO, 1 вывод управления выборкой устройства SPISS и один вывод тактирования бит CLK. Подробнее про общее описание SPI и его сигналы см. [3]. Аппаратное описание периферийного устройства SPI и его используемые выводы в процессоре ADSP-BF538 см. в статье [4].

Внимание: даже если Вы сконфигурировали драйвер SPI для работы только на ввод данных (указали ADI_DEV_DIRECTION_INBOUND в вызове функции adi_dev_Open при открытии драйвера), и рассчитываете использовать ножку MOSI для своих целей (например, как ножку GPIO, работающую на вход), то имейте в виду, что драйвер SPI все равно ошибочно сконфигурирует ножку MOSI как выход. Это логическая ошибка в реализации драйвера SPI, по крайней мере для процессора семейства Stirling (модуль adi_spi.c, функция spiSetPortControl). То же самое касается и регистра управления переключением периферия/GPIO (регистр PORTxIO_FER), ножка MOSI будет ошибочно сконфигурирована под управление периферийным устройством SPI. Поэтому конфигурируйте режим ножек портов GPIO только после того, как сконфигурировали драйвер SPI, но не наоборот. Либо исправляйте исходный код spiSetPortControl (я так и поступил).

[Функции, поддерживаемые драйвером SPI]

Направление потоков данных. Драйвер поддерживает приведенные в таблице ниже варианты настройки для направления потока данных. ADI_DEV_DIRECTION это тип перечисления enum, задающий варианты направления данных (определен в заголовочном файле Blackfin\include\drivers\adi_dev.h).

Таблица 2. Поддерживаемые направления потока данных для устройства SPI (Dataflow Directions).

ADI_DEV_DIRECTION Описание
ADI_DEV_DIRECTION_INBOUND Поддерживает прием данных в устройство.
ADI_DEV_DIRECTION_OUTBOUND Поддерживает передачу данных из устройства.
ADI_DEV_DIRECTION_BIDIRECTIONAL Поддерживаются оба направления данных.

Методы поддержки потока данных. Поддерживаемые методы организации потока данных перечислены в таблице ниже. ADI_DEV_MODE это тип перечисления enum, задающий варианты предоставления буферов для данных (определен в заголовочном файле Blackfin\include\drivers\adi_dev.h).

Таблица 3. Поддерживаемые методы организации потока данных.

ADI_DEV_MODE Описание
ADI_DEV_MODE_CHAINED Поддерживает метод цепочек буферов.
ADI_DEV_MODE_CHAINED_LOOPBACK Поддерживает метод цепочек буферов с переходом на начало цепочки.
ADI_DEV_MODE_SEQ_CHAINED Поддерживает последовательный метод ввода/вывода потока данных через цепочку буферов.
ADI_DEV_MODE_SEQ_CHAINED_LOOPBACK Поддерживает последовательный метод ввода/вывода потока данных через цепочку буферов с переходом в начало цепочки.

Типы буфера. Эта версия драйвера поддерживает одномерный линейный буфер ADI_DEV_1D_BUFFER, и одномерный линейный последовательный буфер ADI_DEV_SEQ_1D_BUFFER. Поле pAdditionalInfo структур буферов ADI_DEV_1D_BUFFER и ADI_DEV_SEQ_1D_BUFFER игнорируется.

Идентификаторы команд. В этой секции перечислены команды, поддерживаемые драйвером. Команды делятся на 3 секции. Первая описывает команды, поддерживаемые напрямую Менеджером Устройств. Следующая секция описывает общие поддерживаемые драйвером команды (относящиеся ко всем драйверам, не только к SPI). Последняя секция описывает специфические для драйвера SPI команды.

Команды посылаются в драйвер устройства через функцию adi_dev_Control(). Она принимает 3 аргумента:

DeviceHandle. Этот параметр типа ADI_DEV_DEVICE_HANDLE, который уникально идентифицирует драйвер устройства. Это хендл, предоставленный клиенту при вызове функции adi_dev_Open().

CommandID. Этот параметр типа u32, он задает идентификатор команды.

Value. Этот параметр типа void *, смысл которого зависит от значения идентификатора команды.

Ниже будут перечислены идентификаторы команд, поддерживаемые драйвером, и будет объяснено значение команды и описание соответствующего каждой команде параметра Value.

Команды, перечисленные ниже, поддерживаются напрямую Менеджером Устройств, и не передаются драйверу для обработки. Таким образом, все драйверы устройств поддерживают эти команды.

ADI_DEV_CMD_TABLE. Команда предоставляет таблицу пар команд, которые будут переданы драйверу. Value это указатель на таблицу пар команд (ADI_DEV_CMD_VALUE_PAIR *).   

ADI_DEV_CMD_END. Обозначает конец таблицы пар команд. Value игнорируется.

ADI_DEV_CMD_PAIR. Команда передает одну пару команда-значение. Value это указатель на одну пару команда-значение (ADI_DEV_CMD_PAIR *).

ADI_DEV_CMD_SET_SYNCHRONOUS. Эта команда разрешает/запрещает синхронный режим работы драйвера. Value – TRUE/FALSE.

Идентификаторы команд, описанные в этой секции, являются общими для многих драйверов устройств. Ниже в списке перечислены всех общие идентификаторы команд (command ID), которые поддерживаются драйвером SPI.

ADI_DEV_CMD_SET_DATAFLOW_METHOD. Задает используемый метод потока данных устройства. Value – одно из поддерживаемых значений перечисления ADI_DEV_MODE (таблица 3).

ADI_DEV_CMD_SET_DATAFLOW. Разрешает/запрещает поток данных через устройство. Value – TRUE/FALSE.

ADI_DEV_CMD_GET_PERIPHERAL_DMA_SUPPORT. Позволяет вызывающему коду определить, поддерживается ли драйвер периферийным DMA. В ответ на эту команду драйвер всегда вернет FALSE. Value – u32*, указывает на ячейку, куда помещается ответ (TRUE/FALSE).

ADI_DEV_CMD_SET_ERROR_REPORTING. Разрешает/запрещает сообщения от ошибках со стороны драйвера устройства. Value – TRUE/FALSE.

Идентификаторы команд, перечисленные ниже, поддерживаются и обрабатываются драйвером SPI, они уникальны именно для этого драйвера устройства.

ADI_SPI_CMD_SET_BAUD_RATE. Устанавливает скорость обмена (baud rate). Value = частота следования тактов в Гц.

ADI_SPI_CMD_SET_SLAVE_FLAG. Устанавливает регистр флагов подчиненного устройства (SPI slave flag register). Value = значение для регистра.

ADI_SPI_CMD_SET_CONTROL_REG. Напрямую устанавливает значение регистра управления SPI (SPIx_CTL). Value = значение для регистра.

ADI_SPI_CMD_SET_BAUD_REG. Напрямую устанавливает значение регистра скорости. Value = значение для регистра.

ADI_SPI_CMD_ENABLE_SLAVE_SELECT. Разрешает сигнал выборки подчиненного устройства. Value = номер подчиненного устройства.

ADI_SPI_CMD_DISABLE_SLAVE_SELECT. Запрещает сигнал выборки подчиненного устройства. Value = номер подчиненного устройства.

ADI_SPI_CMD_SELECT_SLAVE. Делает выборку подчиненного устройства. Value = номер подчиненного устройства.

ADI_SPI_CMD_DESELECT_SLAVE. Снимает выборку подчиненного устройства. Value = номер подчиненного устройства.

ADI_SPI_CMD_SET_EXCLUSIVE_ACCESS. Предотвращает постороннее использование этого устройства SPI. Value = TRUE означает эксклюзивный доступ, FALSE означает разрешение общего доступа.

ADI_SPI_CMD_SET_TRANSFER_INIT_MODE. Устанавливает значение TIMOD (принцип запуска процедуры переноса данных).

Value = 0 - запуск при чтении ядром регистра SPI_RDBR (приходящие данные под управлением прерываний).
Value = 1 - запуск при записи ядром регистра SPI_TDBR (исходящие данные под управлением прерываний).
Value = 2 - запуск чтением DMA регистра SPI_RDBR (приходящие данные под управлением DMA).
Value = 3 - запуск записью DMA регистра SPI_TDBR (исходящие данные под управлением DMA).

ADI_SPI_CMD_SEND_ZEROS. Настраивает отправку нулей, когда регистр передачи SPI_TDBR пуст. Value =TRUE означает отправку нулей, FALSE означает отправку последних данных, записанных в регистр.

ADI_SPI_CMD_SET_GET_MORE_DATA. Устанавливает режим получения данных, когда предыдущие данные еще не прочитаны. Value = TRUE означает, что старые данные будут перезаписываться новыми (gets more date), FALSE - новые приходящие данные будут отбрасываться.

ADI_SPI_CMD_SET_PSSE. Устанавливает управляющий бит PSSE. Value = TRUE означает, что SPISS разрешен, FALSE - SPISS запрещен.

ADI_SPI_CMD_SET_MISO. Разрешает/запрещает MISO. Value = TRUE - MISO разрешен, FALSE - MISO запрещен.

ADI_SPI_CMD_SET_WORD_SIZE. Устанавливает размер слова фрейма в битах. Value = 8 .. 16.

ADI_SPI_CMD_SET_LSB_FIRST. Команда настраивает очередность следования бит по старшинству (MSB/LSB). Value = TRUE - младший бит слова (LSB) будет отправлен первым, FALSE - первым будет отправлен MSB.

ADI_SPI_CMD_SET_CLOCK_PHASE. Устанавливает формат передачи (фаза тактов). Value = TRUE - начало с переключением, FALSE - переключение посередине.

ADI_SPI_CMD_SET_CLOCK_POLARITY. Устанавливает полярность тактов. Value = TRUE - активный уровень лог. 0, FALSE - активный уровень лог. 1.

ADI_SPI_CMD_SET_MASTER. Устанавливает режим работы в качестве главного (master) или подчиненного устройства (slave). Value = TRUE - master, FALSE – slave.

ADI_SPI_CMD_SET_OPEN_DRAIN_MASTER. Управляет битом WOM в регистре управления SPIx_CTL. Value = TRUE - открытый сток (open drain) выходов master, FALSE - обычные выходы.

ADI_SPI_CMD_EXECUTE_DUMMY_READ. Выполняет фиктивное чтение SPI. Value = 8 - 8-битное чтение, 16 - 16 битное чтение.

ADI_SPI_CMD_PAUSE_DATAFLOW. Ставит на паузу или снимает с паузы поток данных. Value = TRUE - пауза, FALSE - снятие с паузы.

ADI_SPI_CMD_SET_TRANSFER_TYPE_BIG_ENDIAN. Переключает тип перемещения данных между little endian и big endian [5]. Value =TRUE – Big endian, FALSE – Little endian (установка по умолчанию).

ADI_SPI_CMD_SET_PIN_MUX_MODE. Устанавливает специфичный для процессора режим мультиплексирования выводов. Value = значение из перечисления типа ADI_SPI_PIN_MUX_MODE.

Семейство процессоров Значение перечисления ADI_SPI_PIN_MUX_MODE Комментарии
ADSP - BF50x ADI_SPI_PIN_MUX_MODE_0 Режим mode 0 (по умолчанию) мультиплексирования выводов SPI. Для SPI0 порт PF15 используется как выборка подчиненного устройства 3 (SPI0 SSEL3). Для SP1 порт PG1 используется как выборка подчиненного устройства 2 (SPI1 SSEL2), PG0 как выборка подчиненного устройства 3 (SPI1 SSEL3).
ADI_SPI_PIN_MUX_MODE_1 Режим mode 1 мультиплексирования выводов SPI. Для SPI0 порт PH0 используется как выборка подчиненного устройства 3 (SPI0 SSEL3). Для SP1 порт PG1 используется как выборка подчиненного устройства 2 (SPI1 SSEL2), PH1 как выборка подчиненного устройства 3 (SPI1 SSEL3).
ADI_SPI_PIN_MUX_MODE_2 Режим mode 2 мультиплексирования выводов SPI. Для SPI0 порт PF15 используется как выборка подчиненного устройства 3 (SPI0 SSEL3). Для SP1 порт PH2 используется как выборка подчиненного устройства 2 (SPI1 SSEL2), PG0 как выборка подчиненного устройства 3 (SPI1 SSEL3).
ADI_SPI_PIN_MUX_MODE_3 Режим mode 3 мультиплексирования выводов SPI. Для SPI0 порт PH0 используется как выборка подчиненного устройства 3 (SPI0 SSEL3). Для SP1 порт PH2 используется как выборка подчиненного устройства 2 (SPI1 SSEL2), PH1 как выборка подчиненного устройства 3 (SPI1 SSEL3).
Другие процессоры - Команда не поддерживается.

События callback. Ниже перечислены события для вызовов callback, когда они разрешены для генерации драйвером. События поделены на 2 врезки - общие события и события драйвера SPI. Первая описывает события, которые являются общими для многих драйверов устройств. Другая секция описывает идентификаторы событий (event ID), специфичных именно для драйвера SPI. Функция callback приложения должна быть подготовлена для обработки каждого из событий в этих врезках.

Callback-функция имеет тип ADI_DCB_CALLBACK_FN. В неё передается 3 параметра:

ClientHandle. Этот параметр имеет тип void*, его значение было передано драйверу устройства как параметр функции adi_dev_Open().

EventID. Это значение типа u32, которое указывает идентификатор события (event ID).

Value. У этого параметра тип void*, и смысл этого значения зависит от контекста события (от специфического значения event ID).

Во врезках ниже перечислены идентификаторы event ID, которые может генерировать драйвер устройства, и значение параметра Value для каждого event ID.

Имеется только одно общее событие - ADI_DEV_EVENT_BUFFER_PROCESSED. Оно оповещает callback-функцию, что буфер I/O цепочки или последовательный буфер I/O был обработан драйвером устройства. Это событие также используется для оповещения о том, что кольцевой буфер был обработан, если драйверу было указано генерировать вызов callback при завершении обработки всего кольцевого буфера. Value – для цепочечного или последовательного метода потока данных это значение параметра CallbackParameter, которое было предоставлено в буфере, который был передан в API-функцию adi_dev_Read() или adi_dev_Write(). Для кольцевого метода Value это адрес буфера, который был передан в API-функцию adi_dev_Read() или adi_dev_Write().

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

ADI_SPI_EVENT_TRANSMISSION_ERROR. Означает, что произошло детектирование события TXE. Обратите внимание, что в версии этого драйвера с управлением по прерываниям (не DMA версия) это событие может быть сгенерировано как часть нормального функционирования. Например, если осуществляется обычный ввод/вывод, и драйвер был привязан на обработку только входных буферов, или осуществляет последовательный ввод/вывод и в настоящий момент обрабатывает входящий буфер, то периферийное устройство SPI генерирует это событие автоматически. Таким образом, драйвер подавляет это событие и не вовлекает запуск callback-функции пользователя, избегая тем самым чрезмерных вызовов callback. Value не используется.

ADI_SPI_EVENT_RECEIVE_ERROR. Было детектировано событие RBSY. Value не используется.

ADI_SPI_EVENT_MODE_FAULT_ERROR. Было детектировано событие MODF. Value не используется.

ADI_SPI_EVENT_TRANSMIT_COLLISION_ERROR. Было детектировано событие TXCOL. Value не используется.

ADI_SPI_EVENT_ERROR_INTERRUPT. Была сгенерирована ошибка SPI. ВНИМАНИЕ: это событие больше не используется и заменено индивидуальными событиями, которые были описаны выше. Value не используется.

ADI_SPI_EVENT_WRITE_BUFFER_PROCESSED. Был обработан буфер передачи. Value – для метода потока данных цепочки Value будет значением параметра CallbackParameter, которое было предоставлено в буфере и было передано вместе с ним в функцию adi_dev_Write().

ADI_SPI_EVENT_READ_BUFFER_PROCESSED. Был обработан буфер приема. Value – для метода потока данных цепочки Value будет значением параметра CallbackParameter, которое было предоставлено в буфере и было передано вместе с ним в функцию adi_dev_Read().

ADI_SPI_EVENT_SEQ_BUFFER_PROCESSED. Завершена обработка последовательного буфера. Value – для методов последовательного ввода/вывода потока данных Value это значение CallbackParameter, которое было предоставлено в буфере, переданном в функцию adi_dev_SequentialIO().

[Коды возврата]

Все API-функции драйвера устройства возвращают статус, показывающий успешное выполнение функции или показывающий, какая произошла ошибка. Эта секция перечисляет коды возврата, которые драйвер SPI может возвратить приложению. Значение возврата ADI_DEV_RESULT_SUCCESS показывает успешное завершение, в то время как другое значение показывает ошибку или какой-то другой информативный результат. ADI_DEV_RESULT_SUCCESS всегда соответствует нулевому значению кода возврата. Все другие коды возврата соответственно будут ненулевые.

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

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

if (adi_dev_Xxxx(...) == ADI_DEV_RESULT_SUCCESS)
{
   // Нормальная обработка, ошибки нет
   ...
}
else
{
   // Обработка ошибки
   ...
} 

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

ADI_DEV_RESULT_SUCCESS. Выполнение функции было успешным.

ADI_DEV_RESULT_NOT_SUPPORTED. Эта функция не поддерживается драйвером.

ADI_DEV_RESULT_DEVICE_IN_USE. Запрошенное устройство уже используется.

ADI_DEV_RESULT_NO_MEMORY. Недостаточное количество доступной памяти.

ADI_DEV_RESULT_BAD_DEVICE_NUMBER. Недопустимый номер устройства.

ADI_DEV_RESULT_DIRECTION_NOT_SUPPORTED. Устройство не может быть открыто в заданном направлении.

ADI_DEV_RESULT_BAD_DEVICE_HANDLE. Недопустимый хендл для драйвера устройства.

ADI_DEV_RESULT_BAD_MANAGER_HANDLE. Недопустимый хендл для Менеджера Устройств.

ADI_DEV_RESULT_BAD_PDD_HANDLE. Недопустимый хендл для физического драйвера устройства.

ADI_DEV_RESULT_INVALID_SEQUENCE. Запрошенное действие не находится в допустимой последовательности.

ADI_DEV_RESULT_ATTEMPTED_READ_ON_OUTBOUND_DEVICE. Приложение сделало попытку предоставить входящий буфер для устройства, открытого только для исходящего трафика.

ADI_DEV_RESULT_ATTEMPTED_WRITE_ON_INBOUND_DEVICE. Приложение сделало попытку предоставить исходящий буфер для устройства, открытого только для входящего трафика.

ADI_DEV_RESULT_DATAFLOW_UNDEFINED. Еще не был декларирован метод поддержки потока данных.

ADI_DEV_RESULT_DATAFLOW_INCOMPATIBLE. Этот метод поддержки потока данных несовместим с запрошенным действием.

ADI_DEV_RESULT_BUFFER_TYPE_INCOMPATIBLE. Устройство не поддерживает этот тип предоставленного буфера.

ADI_DEV_RESULT_CANT_HOOK_INTERRUPT. Менеджер Прерываний [1] не смог подцепить обработчик прерывания.

ADI_DEV_RESULT_CANT_UNHOOK_INTERRUPT. Менеджер Прерываний [1] не смог отцепить обработчик прерывания.

ADI_DEV_RESULT_NON_TERMINATED_LIST. Предоставленная цепочка буферов не завершена NULL.

ADI_DEV_RESULT_NO_CALLBACK_FUNCTION_SUPPLIED. Потребовалась, но не была предоставлена callback-функция.

ADI_DEV_RESULT_REQUIRES_UNIDIRECTIONAL_DEVICE. Требуется, чтобы устройство было открыто только для одного направления трафика - либо только входящего, либо только для исходящего трафика.

ADI_DEV_RESULT_REQUIRES_BIDIRECTIONAL_DEVICE. Требуется, чтобы устройство было открыто только для двунаправленного трафика - и входящего, и исходящего.

Приведенные ниже коды возврата поддерживаются драйвером SPI, они уникальны для этого драйвера.

ADI_SPI_RESULT_ALREADY_EXCLUSIVE. Эксклюзивный доступ не предоставлен.

ADI_SPI_RESULT_BAD_SLAVE_NUMBER. Ошибочный номер подчиненного устройства (slave number), ожидается значение номера в диапазоне 1 .. 7.

ADI_SPI_RESULT_BAD_TRANSFER_INIT_MODE. Ошибочное значение режима инициализации передачи.

ADI_SPI_RESULT_BAD_WORD_SIZE. Ошибочное значение размера фрейма данных (ожидается 8 или 16).

ADI_SPI_RESULT_BAD_VALUE. Передано ошибочное значение (ожидалось TRUE или FALSE).

ADI_SPI_RESULT_DATAFLOW_ENABLED. Недопустимо, потому что поток данных уже активен.

ADI_SPI_RESULT_NO_VALID_BUFFER. Нет буфера для перемещения данных.

ADI_SPI_RESULT_BAD_BAUD_NUMBER. Ошибочное значение для определения скорости передачи.

ADI_SPI_RESULT_RW_SEQIO_MISMATCH. Недопустимое использование Read/Write() и SequencialIO(). Функции Read/Write() и SequentialIO() не могут использоваться одновременно.

[Открытие и конфигурирование драйвера SPI]

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

Точка входа. Когда драйвер устройства SPI открывается вызовом функции adi_dev_Open(), клиент передает в эту функцию параметр, идентифицирующий определенный открываемый драйвер устройства. Этот параметр называется точкой входа (entry point). Для драйвера SPI с управлением по прерыванием точку входа определяет глобальная переменная - структура ADISPIIntEntryPoint типа ADI_DEV_PDD_ENTRY_POINT, которая находится в исходном коде драйвера SPI (Blackfin\lib\src\drivers\spi\adi_spi.c).

Настройки по умолчанию. В таблице 4 описаны настройки по умолчанию и их возможные значения для драйвера SPI. Если настройки по умолчанию не подходят для имеющейся системы, клиент должен использовать соответствующие идентификаторы команд (command ID), чтобы правильно сконфигурировать драйвер. Настройки, не перечисленные в таблице, не определены по умолчанию.

Таблица 4. Настройки по умолчанию.

Опция По умолчанию Возможные значения Command ID
Режим запуска обмена данными 0x00 (начало обмена осуществляется чтением регистра приема SPI_RDBR) 0x01 (начало обмена осуществляется записью регистра передачи SPI_TDBR) ADI_SPI_CMD_SET_TRANSFER_INIT_MODE
Отправка нулей (Send Zero) 0 (отправлять последнее записанное слово данных) 1 (отправлять нули) ADI_SPI_CMD_SEND_ZEROS
Как быть с необработанными данными (get more data) 0 (отбрасывать новые поступающие данные) 1 (принимать данные, перезаписывая ими старые) ADI_SPI_CMD_SET_GET_MORE_DATA
Разрешение выборки подчиненного устройства 0 (запрет) 1 (разрешение) ADI_SPI_CMD_SET_PSSE
Разрешение MISO 0 (MISO запрещен) 1 (MISO разрешен) ADI_SPI_CMD_SET_MISO
Количество бит фрейма данных 0 (8 бит) 1 (16 бит) ADI_SPI_CMD_SET_WORD_SIZE
Первым передавать младший (LSB) бит 0 (первым передается и принимается старший бит, MSB) 1 (первым передается и принимается младший бит, LSB) ADI_SPI_CMD_SET_LSB_FIRST
Фаза тактов 1 (выборка подчиненного устройства осуществляется программно) 0 (выборка подчиненного устройства осуществляется аппаратно) ADI_SPI_CMD_SET_CLOCK_PHASE
Полярность тактов 0 (для CLK активный уровень лог. 1) 1 (для CLK активный уровень лог. 0) ADI_SPI_CMD_SET_CLOCK_POLARITY
Master/Slave 0 (подчиненное устройство, slave) 1 (главное устройство, master) ADI_SPI_CMD_SET_MASTER
Использовать для выходов master открытый коллектор 0 (обычные, двухтактные выходы) 1 (выходы с открытым коллектором) ADI_SPI_CMD_SET_OPEN_DRAIN_MASTER

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

Таблица 5. Дополнительные требуемые настройки.

Опция Возможные значения Command ID
Метод потока данных См. выше "Методы поддержки потока данных" ADI_DEV_CMD_SET_DATAFLOW_METHOD

[Что следует учесть при работе с аппаратурой]

Драйвер устройства SPI не требует какой-либо аппаратной конфигурации. По умолчанию функции сигналов SPI работают как GPIO, и сигналы выборки подчиненных устройств мультиплексируются с сигналами других периферийных устройств. Когда функция adi_pdd_Open() открывает этот драйвер, то драйвер установит соответствующие биты PORTx_FER, чтобы сконфигурировать GPIO для использования SPI.

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

[Работа с потоком данных]

Необходимо обратить внимание на выбор версии драйвера SPI, либо версии с поддержкой DMA, либо версии с поддержкой прерываний - в зависимости от требований приложения. Для приложений, которые постоянно только читают большие объемы данных от подключенного через SPI устройства, или только записывают большие объемы данных, лучше подойдет версия драйвера, работающая через DMA. Для приложений, которые периодически читают и/или записывают через SPI небольшие объемы данных, может лучше подойти версия драйвера, работающая по прерываниям.

Что следует учесть для версии драйвера с прерываниями: это часто хороший выбор для двунаправленного (дуплексного) обмена, когда требуется обмен данными в полном дуплексе или в полудуплексе. Для полного дуплекса, когда Blackfin одновременно отправляет и получает данные, подойдет обычный метод потока данных с цепочкой (chained dataflow). Для полудуплекса, где Blackfin отправляет данные, и после того, как данные были отправлены, читает данные, может быть лучшим выбором метод последовательного ввода/вывода (sequential I/O dataflow). Примеры ниже иллюстрируют оба метода.

Обычный метод потока. Здесь описывается работа драйвера SPI с использованием метода chained dataflow (поток данных с цепочкой буферов). В этом описании входящие буферы были предоставлены функцией adi_dev_Read(), а исходящие буферы функцией adi_dev_Write(). Когда используется обычный ввод/вывод, драйвером поддерживаются раздельные очереди буфера; одна очередь для входящих буферов, и другая очередь для исходящих буферов. Как только поток данных был разрешен драйвером, драйвер автоматически управляет функционированием SPI следующим образом:

· Если у драйвера есть входящие буферы, куда надо сохранять данные, драйвер заполняет эти буферы принимаемыми данными. Когда каждый буфер заполнен, драйвер оповещает код приложения вызовом callback-функции, если приложение запросило такое оповещение (поле CallbackParameter для буфера содержит значение, не равное NULL). Если у драйвера нет входящих буферов для сохранения данных, но исходящие буферы все еще обрабатываются, то драйвер будет отбрасывать любые приходящие данные.

· Если у драйвера есть исходящие буферы для отправки данных через SPI, то драйвер отправляет данные, находящиеся в этих буферах. Когда каждый из буферов полностью отправлен, то драйвер оповещает об этом приложение вызовом callback-функции, если приложение запросило такое оповещение (т. е., как и для буферов приема, если поле CallbackParameter буфера передачи содержит значение, не равное NULL). Если у драйвера нет исходящих буферов, но входящие буферы все еще обрабатываются, то в процессе приема драйвер отправляет данные через SPI в соответствии с настройкой SZ (Send Zero), находящейся в регистре управления SPI (SPIx_CTL).

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

Пример кода ниже показывает использование драйвера SPI с методом chained dataflow. Данные отправляются и принимаются через SPI одновременно. Этот пример просто получает принятые данные и как эхо отправляет их обратно через SPI.

#define INBOUND ((void *)0)
#define OUTBOUND ((void *)1)
 
#define BUFFER_COUNT (4)
#define DATA_COUNT (512)
 
static u16 Data[DATA_COUNT * BUFFER_COUNT];
static ADI_DEV_1D_BUFFER Buffer[BUFFER_COUNT];
static ADI_DEV_DEVICE_HANDLE SPIHandle;
 
/*********************************************************************
Функция: Callback (функция обратного вызова драйвера)
Описание: обрабатывает и заново ставит в очередь буферы
*********************************************************************/
static void Callback(void *AppHandle, u32 Event, void *pArg)
{
   ADI_DEV_1D_BUFFER *pBuffer;
   u32 Result;
   switch (Event)
   {
   // Входной буфер обработан:
   case ADI_SPI_EVENT_READ_BUFFER_PROCESSED:
      pBuffer = pArg; //избегаем приведения типа
      // гарантируем, что за один раз мы предоставляем драйверу один буфер:
      pBuffer->pNext = NULL;
      // помечаем буфер как исходящий, и отправляем его:
      pBuffer->pAdditionalInfo = OUTBOUND;
      Result = adi_dev_Write(SPIHandle, ADI_DEV_1D, (ADI_DEV_BUFFER *)pBuffer);
      break;
   // Выходной буфер обработан:
   case ADI_SPI_EVENT_WRITE_BUFFER_PROCESSED:
      pBuffer = pArg; //избегаем приведения типа
      // гарантируем, что за один раз мы предоставляем драйверу один буфер:
      pBuffer->pNext = NULL;
      // помечаем буфер как входящий, и предоставляем его для заполнения данными:
      pBuffer->pAdditionalInfo = INBOUND;
      Result = adi_dev_Read(SPIHandle, ADI_DEV_1D, (ADI_DEV_BUFFER *)pBuffer);
      break;
   }
}

/*********************************************************************
Функция: DriverApp (демонстрационное приложение)
Описание: открывает, конфигурирует и запускает драйвер
*********************************************************************/
void DriverApp(void)
{
   // Таблица команд для драйвера:
   ADI_DEV_CMD_VALUE_PAIR SPIConfig[] =
   {
      { ADI_DEV_CMD_SET_DATAFLOW_METHOD, (void *)ADI_DEV_MODE_CHAINED }, // метод chained dataflow
      { ADI_SPI_CMD_SET_BAUD_REG,        (void *)0x7ff                }, // запуск с некоторой скоростью бит
      { ADI_SPI_CMD_SET_WORD_SIZE,       (void *)16                   }, // размер слова 16 бит
      { ADI_SPI_CMD_SET_MASTER,          (void *)TRUE                 }, // режим master
      { ADI_SPI_CMD_ENABLE_SLAVE_SELECT, (void *)4                    }, // внешнее устройство подключено к выборке SS 4
      { ADI_SPI_CMD_SELECT_SLAVE,        (void *)4                    }, // разрешить внешнее устройство SPI
      { ADI_DEV_CMD_END, NULL }
   };
   u32 Result;
   int i;
   // Открытие версии драйвера SPI, работающей по прерываниям:
   Result = adi_dev_Open(adi_dev_ManagerHandle, &ADISPIIntEntryPoint, 0, 0, &SPIHandle,
                         ADI_DEV_DIRECTION_BIDIRECTIONAL, NULL, NULL, Callback);
   // Конфигурирование SPI:
   Result = adi_dev_Control(SPIHandle, ADI_DEV_CMD_TABLE, SPIConfig);
   
   // Подготовка половины буферов для входящих данных:
   for (i = 0; i < BUFFER_COUNT/2; i++)
   {
      Buffer[i].Data              = &Data[i * DATA_COUNT];
      Buffer[i].ElementCount      = DATA_COUNT;
      Buffer[i].ElementWidth      = 2;
      Buffer[i].CallbackParameter = &Buffer[i];
      Buffer[i].pNext             = &Buffer[i+1];
      Buffer[i].pAdditionalInfo   = INBOUND;
   }
   
   // Подготовка половины буферов для исходящих данных:
   for (i = BUFFER_COUNT/2; i < BUFFER_COUNT; i++)
   {
      Buffer[i].Data              = &Data[i * DATA_COUNT];
      Buffer[i].ElementCount      = DATA_COUNT;
      Buffer[i].ElementWidth      = 2;
      Buffer[i].CallbackParameter = &Buffer[i];
      Buffer[i].pNext             = &Buffer[i+1];
      Buffer[i].pAdditionalInfo   = OUTBOUND;
   }
   Buffer[BUFFER_COUNT-1].pNext = NULL;
   
   // Предоставление драйверу входящих буферов:
   Result = adi_dev_Read(SPIHandle, ADI_DEV_1D, (ADI_DEV_BUFFER *)&Buffer[0]);
   // Предоставление драйверу исходящих буферов:
   Result = adi_dev_Write(SPIHandle, ADI_DEV_1D, (ADI_DEV_BUFFER *)&Buffer[BUFFER_COUNT/2]);
   // Разрешить поток данных:
   Result = adi_dev_Control(SPIHandle, ADI_DEV_CMD_SET_DATAFLOW, (void *)TRUE);
   
   while (1) ;
}

Метод потока с последовательным вводом/выводом. Здесь описывается работа драйвера SPI с использованием метода потока последовательного ввода/вывода (sequential I/O dataflow). Когда используется последовательный ввод/вывод, то функции adi_dev_Read() и adi_dev_Write() не используются. Вместо этого используется функция adi_dev_SequentialIO(). В этом изложении сконфигурированные как входящие буферы предоставляются вызовом функции adi_dev_SequentialIO(), и сконфигурированные как исходящие буферы также предоставляются вызовом функции adi_dev_SequentialIO(). Когда используется последовательный ввод/вывод, то и для ввода, и для вывода используется одна очередь буферов. Как только поток данных разрешен для драйвера, драйвер будет автоматически управлять функционированием SPI следующим образом:

· Если буфер на вершине очереди входящий, то драйвер заполняет его данными, принятыми через SPI. По мере этого приема данных драйвер передает данные, как это было сконфигурировано полем SZ (Send Zero) регистра управления SPI (SPIx_CTL). Когда входящий буфер заполнен данными, драйвер оповещает приложение об этом событии вызовом callback-функции, если приложение запросило такое оповещение (поле CallbackParameter для буфера содержит значение, не равное NULL). Затем драйвер переходит к следующему буферу в очереди.

· Если буфер на вершине очереди исходящий, то драйвер передает его данные через SPI. По мере этой передачи драйвер отбрасывает приходящие через SPI данные. Когда исходящий буфер полностью передан, драйвер оповещает приложение об этом событии вызовом callback-функции, если приложение запросило такое оповещение (поле CallbackParameter для буфера содержит значение, не равное NULL). Затем драйвер переходит к следующему буферу в очереди.

· Когда очередь буферов опустошена, драйвер автоматически запрещает передачу / прием через SPI, пока приложение не предоставит драйверу новый буфер (или буферы), тогда драйвер автоматически заново разрешит SPI и возобновит обмен данными.

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

#define SENSOR_CHANNEL (2)
 
static u8 InboundData;
static ADI_DEV_SEQ_1D_BUFFER InboundBuffer;
 
static u8 OutboundData;
static ADI_DEV_SEQ_1D_BUFFER OutboundBuffer;
 
static ADI_DEV_DEVICE_HANDLE SPIHandle;
 
/*********************************************************************
Функция: Callback (функция обратного вызова драйвера)
Описание: обрабатывает принятые данные, после чего запрашивает новые
*********************************************************************/
static void Callback(void *AppHandle, u32 Event, void *pArg)
{
   ADI_DEV_1D_BUFFER *pBuffer;
   u32 Result;
   switch (Event)
   {
   // Буфер обработан:
   case ADI_SPI_EVENT_SEQ_BUFFER_PROCESSED:
      pBuffer = pArg;   //избегаем приведения типа
      // Обработка принятых данных:
      ProcessData (pBuffer);
      // Гарантируем, что мы передадим в сенсор то, что надо, после чего 
      // запросим получение данных:
      OutboundBuffer.Buffer.pNext = (ADI_DEV_1D_BUFFER *)&InboundBuffer;
      InboundBuffer.Buffer.pNext = NULL;
      // Заново ставим буферы в очередь:
      Result = adi_dev_SequentialIO(SPIHandle, ADI_DEV_SEQ_1D, (ADI_DEV_BUFFER *)&OutboundBuffer);
      break;
   }
}
 
/*********************************************************************
Функция: DriverApp (демонстрация обмена данными)
Описание: открывает, конфигурирует и запускает драйвер
*********************************************************************/
void DriverApp(void)
{
   ADI_DEV_CMD_VALUE_PAIR SPIConfig[] =
   {
      { ADI_DEV_CMD_SET_DATAFLOW_METHOD, (void *)ADI_DEV_MODE_SEQ_CHAINED }, // метод последовательной цепочки
      { ADI_SPI_CMD_SET_BAUD_REG,        (void *)0x7ff                    }, // запуск на некоторой скорости бит
      { ADI_SPI_CMD_SET_WORD_SIZE,       (void *)16                       }, // во фрейме 16 бит
      { ADI_SPI_CMD_SET_MASTER,          (void *)TRUE                     }, // режим master
      { ADI_SPI_CMD_ENABLE_SLAVE_SELECT, (void *)4                        }, // устройство подключено к выборке SS 4
      { ADI_SPI_CMD_SELECT_SLAVE,        (void *)4                        }, // разрешить устройство
      { ADI_DEV_CMD_END, NULL }
   };
   
   u32 Result;
 
   // Открыть драйвер SPI, версия с управлением по прерываниям:
   Result = adi_dev_Open(adi_dev_ManagerHandle, &ADISPIIntEntryPoint, 0, 0, &SPIHandle,
                         ADI_DEV_DIRECTION_BIDIRECTIONAL, NULL, NULL, Callback);
   // Конфигурирование SPI:
   Result = adi_dev_Control(SPIHandle, ADI_DEV_CMD_TABLE, SPIConfig);
 
   // Подготовка исходящего буфера (без callback):
   OutboundData = SENSOR_CHANNEL;
   OutboundBuffer.Buffer.Data = &OutboundData;
   OutboundBuffer.Buffer.ElementCount = 1;
   OutboundBuffer.Buffer.ElementWidth = 1;
   OutboundBuffer.Buffer.CallbackParameter = NULL;
   OutboundBuffer.Buffer.pNext = (ADI_DEV_1D_BUFFER *)&InboundBuffer;
   // Подготовка входящего буфера (callback будет вызван при завершении заполнения буфера):
   InboundData = SENSOR_CHANNEL;
   InboundBuffer.Buffer.Data = &OutboundData;
   InboundBuffer.Buffer.ElementCount = 1;
   InboundBuffer.Buffer.ElementWidth = 1;
   InboundBuffer.Buffer.CallbackParameter = &OutboundBuffer;
   InboundBuffer.Buffer.pNext = NULL;
 
   // Предоставление драйверу цепочки буферов:
   Result = adi_dev_SequentialIO(SPIHandle, ADI_DEV_1D, (ADI_DEV_BUFFER *)&OutboundBuffer);
   // Разрешить обмен данными:
   Result = adi_dev_Control(SPIHandle, ADI_DEV_CMD_SET_DATAFLOW, (void *)TRUE);
 
   while (1) ;
}

[Ссылки]

1. VDK: менеджер прерываний.
2. VDK: драйверы устройств и системные службы процессоров Blackfin.
3. Интерфейс SPI.
4ADSP-BF538: интерфейс SPI.
5. Порядок следования байт (endianness).
6. Драйвер SPI для Blackfin, работающий через DMA.