Программирование DSP Драйвер SPORT для Blackfin, работающий через DMA Tue, February 21 2017  

Поделиться

нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

Драйвер SPORT для Blackfin, работающий через DMA Печать
Добавил(а) microsin   

В этом документе описано использование драйвера устройства SPORT для процессоров Blackfin. Приведен перевод документации по библиотеке компании Analog Devices, реализующей физический драйвер устройства SPORT (ADI_SPORT, документация в файле Blackfin \ docs \ drivers \ sport \ adi_sport.pdf, находящимся в каталоге установки системы VisualDSP++). У драйвера нет никаких аппаратных привязок к определенному типу процессора (поскольку большинство процессоров Blackfin имеют на борту встроенный интерфейс SPORT). Драйвер был протестирован на процессорах ADSP-BF533, ADSP-BF537 и ADSP-BF561, установленных на платах разработчика EZ-Kit Lite.

Драйвер устройства SPORT поддерживает все основные режим SPORT включая stereo mode, аналогичный I2S, и работу в многоканальном режиме (multi-channel). Драйвер SPORT использует DMA для перемещения данных, полагаясь при этом на Менеджер DMA [2] из библиотеки системных служб (SSL) [6], что позволяет достичь максимальной полосы пропускания при обмене данными с устройством SPORT.

В отличие от большинства других драйверов устройств, при определенных условиях драйвер SPORT позволяет дважды открыть одно периферийное устройство SPORT. Это связано с тем, что у одного физического устройства SPORT имеется два практически независимых канала - канал приема и канал передачи. Если SPORT был открыт для двунаправленного обмена данными, то драйвером используются оба канала, и входящий (inbound) и исходящий (outbound), не позволяя никаким другим клиентам открыть то же самое устройство SPORT. Однако, если SPORT был открыт только в одном направлении обмена, либо только входящем, либо только исходящем, то тогда драйвер позволяет еще раз открыть тот же самый SPORT как другое устройство, но в противоположном направлении обмена данными. Например, если SPORT0 был открыт для данных inbound, то SPORT0 можно открыть еще и второй раз для данных outbound; в результате получается эффект наличия двух разных устройств, хотя они работают на одном физическом устройстве SPORT.

Примечание: под термином "клиент" понимается любой программный код, использующий функции SPORT. Обычно это поток RTOS VDK [5] или любой другой операционной системы.

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

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

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

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

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

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

Исходный код. Исходный код драйвера SPORT содержится в файлах Blackfin / lib / src / drivers / sport / adi_sport.c. Весь код написан на языке C. Для драйвера не используется никаких функций, написанных на ассемблере.

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

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

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

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

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

Прерывания. За исключением случаев запрета соответствующими командами драйвера, драйвер устройства SPORT использует привязку по умолчанию групп векторов прерываний (Interrupt Vector Group, IVG) и привязку каналов DMA к периферийным устройствам. Если нужны какие-то изменения в этих настройках, отличающиеся от конфигурации по умолчанию, то эти изменения должны быть сделаны перед открытием драйвера SPORT. Изменения для привязок IVG могут быть сделаны соответствующими вызовами службы Менеджера Прерываний (Interrupt Manager) [3], тогда как изменения привязки каналов DMA к периферийным устройствам можно сделать соответствующими вызовами службы Менеджера DMA [2].

Работа драйвера устройства SPORT может вовлекать использование до 4 прерываний: передачи (transmit), приема (receive), ошибки SPORT и ошибки DMA. Из-за того, что драйвер устройства SPORT использует Менеджер DMA для перемещения данных, разрешение/запрет прерываний и прикрепление/открепление обработчиков прерываний для передачи, приема и ошибки DMA выполняются Менеджером DMA. Драйвер SPORT сам управляет разрешением/запретом и прикреплением/откреплением обработчиков для прерывания ошибки SPORT. Управление этими прерываниями осуществляется следующим образом:

Transmit (Tx) Interrupt, прерывание передачи. Когда драйвер SPORT открыт для исходящего или двунаправленного трафика, Менеджер DMA разрешает и прицепляет прерывание для канала DMA, к которому привязана передача (SPORT Tx). Менеджер DMA запрещает и отцепляет прерывание канала DMA, когда драйвер SPORT закрывается.
Receive (Rx) Interrupt, прерывание приема. Когда драйвер SPORT открыт для входящего или двунаправленного трафика, Менеджер DMA разрешает и прикрепляет прерывание для канала DMA, к которому привязан прием (SPORT Rx). Менеджер DMA запрещает и отцепляет прерывание канала DMA, когда драйвер SPORT закрывается.
DMA Error Interrupt, прерывание ошибки DMA. Когда драйвер SPORT открывается в любом направлении, Менеджер DMA разрешает и прицепляет прерывание DMA error. Это прерывание запрещается и отцепляется, когда драйвер SPORT закрывается, и нет других клиентов Менеджера DMA, использующих то же самое прерывание DMA error.
SPORT Error Interrupt, прерывание ошибки SPORT. Это прерывание разрешается/запрещается и прицепляется/отцепляется, когда клиент передает драйверу команду ADI_DEV_CMD_SET_ERROR_REPORTING. По умолчанию прерывания SPORT error не генерируются, так что если нужна обработка прерываний SPORT error, то это должно быть явно включено командой ADI_DEV_CMD_SET_ERROR_REPORTING. Когда функция adi_dev_Control() принимает эту команду вместе с сопутствующим аргументом TRUE, то прерывание SPORT разрешается и обработчик прерывания подцепляется к цепочке IVG. Когда функция adi_dev_Control() принимает эту команду вместе с сопутствующим аргументом FALSE, то прерывание SPORT error запрещается и обработчик прерывания отцепляется от цепочки IVG. Если прерывание SPORT error разрешено и подцеплено, то оно будет автоматически запрещено и обработчик отцеплен, когда драйвер закрывается (вызовом функции adi_dev_Close()).

DMA (прямой доступ к памяти со стороны периферийного устройства). Драйвер устройства SPORT задействует службу Менеджера DMA для перемещения всех данных через SPORT. Когда драйвер открыт для входящего трафика данных, используется только канал SPORT Rx DMA. Когда драйвер открыт для исходящего трафика данных, используется только канал SPORT Tx DMA. Когда открывается двунаправленный поток данных, то используются оба канала DMA - SPORT Rx и SPORT Tx.

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

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

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

Используемые выводы. Всего интерфейс SPORT использует 8 выводов [1]:

TSCLKx Transmit serial clock, такты бит передачи.
TFSx Transmit frame sync, такты фрейма передачи.
DTxPRI Primary transmit data, первичный канал передачи данных.
DTxSEC Secondary transmit data, вторичный канал передачи данных.
RSCLKx Receive serial clock, такты бит приема.
RFSx Receive frame sync, такты фрейма приема.
DRxPRI Primary receive data, первичный канал приема данных.
DRxSEC Secondary receive data, вторичный канал приема данных.

На процессорах, где используется мультиплексирование выводов, драйвер устройства SPORT использует службу управления портами (Port Control service) [4] для автоматического конфигурирования выводов, чтобы использовать SPORT как это описано ниже:

• Двунаправленный поток данных – все выводы SPORT конфигурируются для использования под функции SPORT.
• Только входящие данные – под функции SPORT конфигурируются выводы RSCLKx, RFSx, DRxPRI и DRxSEC.
• Только исходящие данные – под функции SPORT конфигурируются выводы TSCLKx, TFSx, DTxPRI и DTxSEC.

Выводы конфигурируются автоматически, когда работа драйвера SPORT разрешается командой ADI_DEV_CMD_SET_DATAFLOW.

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

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

Таблица 2. Поддерживаемые направления потока данных для устройства SPORT (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_CIRCULAR Поддерживает метод кольцевого буфера.
ADI_DEV_MODE_CHAINED Поддерживает метод цепочек буферов.
ADI_DEV_MODE_CHAINED_LOOPBACK Поддерживает метод цепочек буферов с переходом на начало цепочки.

Типы буфера. Драйвер SPORT поддерживает перечисленные ниже типы буферов. Поле pAdditionalInfo игнорируется во всех типах структур буферов.

ADI_DEV_CIRCULAR_BUFFER. Это кольцевой буфер.
ADI_DEV_1D_BUFFER. Это линейный одномерный буфер (1D).
ADI_DEV_2D_BUFFER. Это двумерный буфер (2D).

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

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

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

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

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

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

Ниже будут перечислены идентификаторы команд, поддерживаемые драйвером, и будет объяснено значение команды и описание соответствующего каждой команде параметра 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), которые поддерживаются драйвером SPORT.

ADI_DEV_CMD_GET_2D_SUPPORT. Возвратит информацию о том, может ли драйвер поддержать буферы 2D (двумерные буферы). Value равно u32* (указатель, задающий место, где будет сохранена величина TRUE/FALSE).

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

ADI_DEV_CMD_SET_STREAMING. Разрешает/запрещает режим потока для драйвера. Value = TRUE/FALSE.

ADI_DEV_CMD_GET_INBOUND_DMA_CHANNEL_ID. Возвратит идентификатор канала DMA для входящего канала DMA драйвера устройства. Value равно u32 * (место, куда будет сохранен channel ID).

ADI_DEV_CMD_GET_OUTBOUND_DMA_CHANNEL_ID. Возвратит идентификатор канала DMA для исходящего канала DMA драйвера устройства. Value равно u32 * (место, куда будет сохранен channel ID).

ADI_DEV_CMD_SET_INBOUND_DMA_CHANNEL_ID. Устанавливает значение идентификатора канала DMA для входящего канала DMA драйвера устройства. Value = ADI_DMA_CHANNEL_ID (DMA channel ID).

ADI_DEV_CMD_SET_OUTBOUND_DMA_CHANNEL_ID. Устанавливает значение идентификатора канала DMA для исходящего канала DMA драйвера устройства. Value = ADI_DMA_CHANNEL_ID (DMA channel ID).

ADI_DEV_CMD_GET_INBOUND_DMA_PMAP_ID. Возвратит идентификатор привязки периферийного устройства (PMAP ID) для входящего канала DMA драйвера устройства. Value = u32 * (место, где будет сохранено значение PMAP).

ADI_DEV_CMD_GET_OUTBOUND_DMA_PMAP_ID. Возвратит идентификатор привязки периферийного устройства (PMAP ID) для исходящего канала DMA драйвера устройства. Value = u32 * (место, где будет сохранено значение PMAP).

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

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

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

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

[Команды передачи SPORT]

Эти команды управляют поведением канала передачи, к которому относятся сигналы TSCLKx (такты бит передачи), TFSx (такты фрейма передачи), DTxPRI (первичный канал передачи данных) и DTxSEC (вторичный канал передачи данных).

ADI_SPORT_CMD_CLEAR_TX_ERRORS. Очищает все состояния ошибки передачи. Value = NULL (не используется).

ADI_SPORT_CMD_SET_TX_CLOCK_FREQ. Команда устанавливает скорость передачи, т. е. частоту следования бит передачи (Tx Clock frequency). Value = частота следования бит, Гц.

ADI_SPORT_CMD_SET_TX_FRAME_SYNC_FREQ. Команда устанавливает частоту следования синхроимпульсов фреймов передачи. Клиент должен установить частоту следования синхроимпульсов бит передачи (Tx Clock frequency) перед установкой частоты следования синхроимпульсов фреймов передачи.

ADI_SPORT_CMD_SET_TX_CLOCK_SOURCE. Устанавливает источник для синхроимпульсов бит передачи. Value = u16, значение 0: внешние синхроимпульсы (TSCLKx работает как вход), 1 – внутренние (TSCLKx работает как выход).

ADI_SPORT_CMD_SET_TX_DATA_FORMAT. Устанавливает формат данных передачи. Value = u16, значение 0: normal (обычный формат), 1: зарезервировано, 2: ulaw, 3: Alaw.

ADI_SPORT_CMD_SET_TX_BIT_ORDER. Устанавливает порядок следования бит данных передачи. Value = u16, 0: старший бит (MSB) идет первым, 1: младший бит (LSB) идет первым.

ADI_SPORT_CMD_SET_TX_FS_SOURCE. Устанавливает источник для синхроимпульсов фреймов передачи. Value = u16, значение 0: внешние синхроимпульсы (TFSx работает как вход), 1 – внутренние (TFSx работает как выход).

ADI_SPORT_CMD_SET_TX_FS_REQUIREMENT. Устанавливает требования к сигналам синхронизации фрейма (frame sync) передачи. Value = u16, 0: нет генерации frame sync передачи для каждого слова, 1: сигнал frame sync передачи генерируется для каждого слова.

ADI_SPORT_CMD_SET_TX_FS_DATA_GEN. Устанавливает генерацию данных по базе генерации синхроимпульсов фрейма для передачи. Value = u16, 0: данные зависят от генерации frame sync, 1: генерация сигнала фрейма независимо от данных.

ADI_SPORT_CMD_SET_TX_FS_POLARITY. Устанавливает полярность синхроимпульсов фрейма передачи. Value = u16, 0: активная лог. 1 для frame sync, 1: активный лог. 0 для frame sync.

ADI_SPORT_CMD_SET_TX_FS_TIMING. Устанавливает время появления синхроимпульса фрейма передачи. Value = u16, 0: ранний frame sync, 1: поздний frame sync.

ADI_SPORT_CMD_SET_TX_EDGE_SELECT. Устанавливает перепад передачи. Value = u16, 0: данные и внутренний frame sync меняются по фронту нарастания уровня TSCLK, и внешний frame sync по спаду уровня, 1: данные и внутренний frame sync меняются по спаду уровня TSCLK, и внешний frame sync по фронту нарастания уровня.

ADI_SPORT_CMD_SET_TX_WORD_LENGTH. Устанавливает длину слова (фрейма) передачи в битах. Value = u16, задает значение, на 1 меньшее длины слова передачи. Значения 0, 1, и больше 31 недопустимы.

ADI_SPORT_CMD_SET_TX_SECONDARY_ENABLE. Разрешает/запрещает вторичный канал данных передачи. Value = TRUE: канал передачи разрешен, FALSE: запрещен.

ADI_SPORT_CMD_SET_TX_STEREO_FS_ENABLE. Разрешает/запрещает сигналы фрейма передачи стерео (transmit stereo frame sync). Value = TRUE: сигналы frame sync тактируют левый/правый каналы, FALSE: обычный режим (normal mode).

ADI_SPORT_CMD_SET_TX_LEFT_RIGHT_ORDER. Устанавливает порядок следования стереоканалов передачи. Value = u16, 0: первым идет левый канал, 1: первым идет правый канал.

[Команды приема SPORT]

Эти команды управляют поведением канала приема, к которому относятся сигналы RSCLKx (такты бит приема), RFSx (такты фрейма приема), DRxPRI (первичный канал приема данных) и DRxSEC (вторичный канал приема данных).

ADI_SPORT_CMD_CLEAR_RX_ERRORS. Очищает все состояния ошибки приема. Value = NULL (не используется).

ADI_SPORT_CMD_SET_RX_CLOCK_FREQ. Команда устанавливает скорость приема, т. е. частоту следования бит приема (Rx Clock frequency). Value = частота следования бит, Гц.

ADI_SPORT_CMD_SET_RX_FRAME_SYNC_FREQ. Команда устанавливает частоту следования синхроимпульсов фреймов приема. Клиент должен установить частоту следования синхроимпульсов бит приема (Rx Clock frequency) перед установкой частоты следования синхроимпульсов фреймов приема.

ADI_SPORT_CMD_SET_RX_CLOCK_SOURCE. Устанавливает источник для синхроимпульсов бит приема. Value = u16, значение 0: внешние синхроимпульсы (RSCLKx работает как вход), 1 – внутренние (RSCLKx работает как выход).

ADI_SPORT_CMD_SET_RX_DATA_FORMAT. Устанавливает формат данных приема. Value = u16, значение 0: zero fill (заполнение нулями), 1: sign extend (расширение знаком), 2: ulaw, 3: Alaw.

ADI_SPORT_CMD_SET_RX_BIT_ORDER. Устанавливает порядок следования бит данных приема. Value = u16, 0: старший бит (MSB) идет первым, 1: младший бит (LSB) идет первым.

ADI_SPORT_CMD_SET_RX_FS_SOURCE. Устанавливает источник для синхроимпульсов фреймов приема. Value = u16, значение 0: внешние синхроимпульсы (RFSx работает как вход), 1 – внутренние (RFSx работает как выход).

ADI_SPORT_CMD_SET_RX_FS_REQUIREMENT. Устанавливает требования к сигналам синхронизации фрейма (frame sync) приема. Value = u16, 0: нет генерации frame sync приема для каждого слова, 1: сигнал frame sync приема генерируется для каждого слова.

ADI_SPORT_CMD_SET_RX_FS_POLARITY. Устанавливает полярность синхроимпульсов фрейма приема. Value = u16, 0: активная лог. 1 для frame sync, 1: активный лог. 0 для frame sync.

ADI_SPORT_CMD_SET_RX_FS_TIMING. Устанавливает время появления синхроимпульса фрейма приема. Value = u16, 0: ранний frame sync, 1: поздний frame sync.

ADI_SPORT_CMD_SET_RX_EDGE_SELECT. Устанавливает перепад приема. Value = u16, 0: данные и внутренний frame sync меняются по фронту нарастания уровня RSCLK, и внешний frame sync по спаду уровня, 1: данные и внутренний frame sync меняются по спаду уровня RSCLK, и внешний frame sync по фронту нарастания уровня.

ADI_SPORT_CMD_SET_RX_WORD_LENGTH. Устанавливает длину слова (фрейма) приема в битах. Value = u16, задает значение, на 1 меньшее длины слова приема. Значения 0, 1, и больше 31 недопустимы.

ADI_SPORT_CMD_SET_RX_SECONDARY_ENABLE. Разрешает/запрещает вторичный канал данных приема. Value = TRUE: канал приема разрешен, FALSE: запрещен.

ADI_SPORT_CMD_SET_RX_STEREO_FS_ENABLE. Разрешает/запрещает сигналы фрейма приема стерео (receive stereo frame sync). Value = TRUE: сигналы frame sync тактируют левый/правый каналы, FALSE: обычный режим (normal mode).

ADI_SPORT_CMD_SET_RX_LEFT_RIGHT_ORDER. Устанавливает порядок следования стереоканалов приема. Value = u16, 0: первым идет левый канал, 1: первым идет правый канал.

[Команды управления многоканального (Multi-Channel) режима SPORT]

Подробнее про многоканальный режим SPORT см. [1].

ADI_SPORT_CMD_SET_MC_WINDOW_OFFSET. Устанавливает смещение окна. Value = u16 (0 .. 1023).

ADI_SPORT_CMD_SET_MC_WINDOW_SIZE. Устанавливает размер окна. Value = u16, равное ((размер_окна / 8) – 1). Например для размера окна 512 значение должно быть 63.

ADI_SPORT_CMD_SET_MC_CLOCK_RECOVERY_MODE. Устанавливает двукратный режим восстановления тактов (2x clock recovery mode). Value = u16, 0 или 1: bypass (отключено), 2: восстанавливает тактовую частоту 2 МГц из тактов 4 МГц, 3: восстанавливает такты 8 МГц из тактов 16 МГц.

ADI_SPORT_CMD_SET_MC_MODE. Разрешает/запрещает работу в многоканальном режиме. Value = TRUE: режим Multi-Channel разрешен, FALSE: запрещен.

ADI_SPORT_CMD_SET_MC_FS_TO_DATA. Устанавливает взаимозависимость сигналов frame sync и данных. Value = u16, 0: Normal, 1: обратная зависимость (режим H.100).

ADI_SPORT_CMD_SET_MC_FRAME_DELAY. Устанавливает задержку многоканального фрейма. Value = u16 (0 .. 15 тактов).

ADI_SPORT_CMD_SET_MC_TX_PACKING. Устанавливает упаковку передачи. Value = TRUE: разрешено, FALSE: запрещено.

ADI_SPORT_CMD_SET_MC_TX_CHANNEL_ENABLE. Разрешает канал передачи. Значение = u16 (номер канала 0 .. 127).

ADI_SPORT_CMD_SET_MC_TX_CHANNEL_DISABLE. Запрещает канал передачи. Значение = u16 (номер канала 0 .. 127).

ADI_SPORT_CMD_SET_MC_RX_PACKING. Устанавливает упаковку приема. Value = TRUE: разрешено, FALSE: запрещено.

ADI_SPORT_CMD_SET_MC_RX_CHANNEL_ENABLE. Разрешает канал приема. Значение = u16 (номер канала 0 .. 127).

ADI_SPORT_CMD_SET_MC_RX_CHANNEL_DISABLE. Запрещает канал приема. Значение = u16 (номер канала 0 .. 127).

[Команды регистров передачи]

Параметр Value для всех команд = u16 (значение регистра), если не указано нечто другое.

ADI_SPORT_CMD_SET_TCR1. Устанавливает значение регистра управления SPORTx_TCR1.

ADI_SPORT_CMD_SET_TCR2. Устанавливает значение регистра управления SPORTx_TCR2.

ADI_SPORT_CMD_SET_TCLKDIV. Устанавливает регистр делителя тактов передачи SPORTx_TCLKDIV.

ADI_SPORT_CMD_SET_TFSDIV. Устанавливает регистр делителя тактов формирования сигналов фрейма SPORTx_TFSDIV.

ADI_SPORT_CMD_SET_TX16. Устанавливает регистр SPORTx_TX16 (16 младших бит регистра передачи).

ADI_SPORT_CMD_SET_TX32. Устанавливает регистр SPORTx_TX32 (32 бита регистра передачи). Value = u32 (значение регистра).

[Команды регистров приема]

ADI_SPORT_CMD_SET_RCR1. Устанавливает значение регистра управления SPORTx_RCR1. Value = u16 (значение регистра).

ADI_SPORT_CMD_SET_RCR2. Устанавливает значение регистра управления SPORTx_RCR2. Value = u16 (значение регистра).

ADI_SPORT_CMD_SET_RCLKDIV. Устанавливает регистр делителя тактов приема SPORTx_RCLKDIV. Value = u16 (значение регистра).

ADI_SPORT_CMD_SET_RFSDIV. Устанавливает регистр делителя тактов формирования сигналов фрейма SPORTx_RFSDIV. Value = u16 (значение регистра).

ADI_SPORT_CMD_GET_RX16. Получает значение регистра SPORTx_RX16 (16 младших бит регистра приема). Value = u16* (указывает на место в памяти, куда будет сохранено значение регистра).

ADI_SPORT_CMD_GET_RX32. Получает значение регистра SPORTx_RX32 (32 бита регистра приема). Value = u32* (указывает на место в памяти, куда будет сохранено значение регистра).

[Команда регистра состояния]

ADI_SPORT_CMD_GET_STAT. Получает значение регистра состояния SPORTx_STAT. Value = u16* (указывает на место в памяти, куда будет сохранено значение регистра).

[Команды регистров многоканального режима]

ADI_SPORT_CMD_SET_MCMC1. Устанавливает регистр SPORTx_MCMC1. Value = u16 (значение регистра).

ADI_SPORT_CMD_SET_MCMC2. Устанавливает регистр SPORTx_MCMC2. Value = u16 (значение регистра).

ADI_SPORT_CMD_GET_CHNL. Получает значение регистра SPORTx_CHNL. Value = u16* (указывает на место в памяти, куда будет сохранено значение регистра).

ADI_SPORT_CMD_SET_MTCS0. Устанавливает регистр SPORTx_MTCS0. Value – u16 (register value).

ADI_SPORT_CMD_SET_MTCS1. Устанавливает регистр SPORTx_MTCS1. Value = u16 (значение регистра).

ADI_SPORT_CMD_SET_MTCS2. Устанавливает регистр SPORTx_MTCS2. Value = u16 (значение регистра).

ADI_SPORT_CMD_SET_MTCS3. Устанавливает регистр SPORTx_MTCS3. Value = u16 (значение регистра).

ADI_SPORT_CMD_SET_MRCS0. Устанавливает регистр SPORTx_MRCS0. Value = u16 (значение регистра).

ADI_SPORT_CMD_SET_MRCS1. Устанавливает регистр SPORTx_MRCS1. Value = u16 (значение регистра).

ADI_SPORT_CMD_SET_MRCS2. Устанавливает регистр SPORTx_MRCS2. Value = u16 (значение регистра).

ADI_SPORT_CMD_SET_MRCS3. Устанавливает регистр SPORTx_MRCS3. Value = u16 (значение регистра).

[Команды, управляющие выводами]

ADI_SPORT_CMD_SET_PIN_MUX_MODE. Устанавливает зависящий от процессора режим мультиплексирования выводов. Value = значение перечисления типа ADI_SPORT_PIN_MUX_MODE (см. таблицу ниже).

Семейство процессоров Значение перечисления ADI_SPORT_PIN_MUX_MODE Комментарии
ADSP - BF50x ADI_SPORT_PIN_MUX_MODE_0 Режим 0 конфигурации выводов SPORT (по умолчанию). Для SPORT1 порт PG4 используется вторичный канал приема (DR1SEC).
ADI_SPORT_PIN_MUX_MODE_1 Режим 1 конфигурации выводов SPORT. Для SPORT1 порт PG8 используется вторичный канал приема (DR1SEC).
Другие процессоры - Команда не поддерживается.

ADI_SPORT_CMD_SET_HYSTERESIS_ENABLE. Действует только для процессоров ADSP-BF53x и ADSP-BF561. Разрешает/запрещает гистерезис на входных выводах SPORT. Value = TRUE/FALSE (гистерезис разрешен/запрещен).

События callback. Ниже перечислены события для вызовов callback, которые может генерировать драйвер. События поделены на 2 врезки - общие события и события драйвера SPORT. Первая описывает события, которые являются общими для многих драйверов устройств. Другая секция описывает идентификаторы событий (event ID), специфичных именно для драйвера SPORT. Функция 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.

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

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

ADI_DEV_EVENT_SUB_BUFFER_PROCESSED. Оповещает callback-функцию о том, что драйвером устройства был обработан подбуфер, входящий в кольцевой буфер. Value – адрес буфера, предоставленного в вызове функций adi_dev_Read() или adi_dev_Write().

ADI_DEV_EVENT_DMA_ERROR_INTERRUPT. Оповещает callback-функцию о том, что произошла ошибка DMA. Value = NULL (не используется).

Этот идентификатор события уникален для драйвера SPORT.

ADI_SPORT_EVENT_ERROR_INTERRUPT. Оповещает callback-функцию о том, что SPORT генерировал прерывание ошибки. Value = NULL (не используется).

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

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

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

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

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

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

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. Недопустимый хендл к Менеджеру Устройств (Device Manager).

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. Менеджер Прерываний не смог подцепить обработчик прерывания.

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

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. Требуется устройство, отрытое только для двунаправленного трафика.

Код возврата (event ID), перечисленный здесь, поддерживается и обрабатывается только этим драйвером устройства. Этот event ID уникален для драйвера SPORT.

ADI_SPORT_RESULT_BAD_ACCESS_WIDTH. Сделана попытка доступа к одному из регистров SPORTx_TX16, SPORTx_TX32, SPORTx_RX16 или SPORTx_RX32, когда длина поля слова регистра SPORT (SLEN в соответствующем регистре управления), не соответствует ширине регистра, к которому осуществляется доступ.

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

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

Точка входа (Entry Point). Когда драйвер устройства открывается вызовом функции adi_dev_Open(), клиент передает в эту функцию параметр, который идентифицирует специфику открываемого драйвера. Это так называемая точка входа в драйвер (entry point): ADISPORTEntryPoint.

Настройки по умолчанию. Когда SPORT открывается вызовом функции adi_dev_Open(), драйвер устройства сбрасывает регистры управления и состояния SPORT в свои значения по умолчанию, которые были при включении питания или аппаратном сбросе - все нули, как это показано в таблице ниже.

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

Направление данных Регистры, сбрасываемые в 0 Очищаемые ошибки
ADI_DEV_DIRECTION_INBOUND RCR1, RCR2, RCLKDIV, RFSDIV, MRCS0, MRCS1, MRCS2, MRCS3, MCMC1, MCMC2 Все ошибки приема.
ADI_DEV_DIRECTION_OUTBOUND TCR1, TCR2, TCLKDIV, TFSDIV, MTCS0, MTCS1, MTCS2, MTCS3, MCMC1, MCMC2 Все ошибки передачи.
ADI_DEV_DIRECTION_BIDIRECTIONAL RCR1, RCR2, RCLKDIV, RFSDIV, MRCS0, MRCS1, MRCS2, MRCS3, TCR1, TCR2, TCLKDIV, TFSDIV, MTCS0, MTCS1, MTCS2, MTCS3, MCMC1, MCMC2 Все ошибки приема и передачи.

Дополнительные требуемые настройки конфигурации. В дополнение к возможным изменениям настроек по умолчанию драйвера, требуется также указать дополнительную конфигурационную информацию, описывающую метод потока (идентификатор команды ADI_DEV_CMD_SET_DATAFLOW_METHOD, см. врезку "Общие команды").

Того, о чем Вы прочтете здесь, нет в оригинальной документации ADI.

[Проблема синхронного запуска]

Например, если Вам нужно строго синхронно по времени запустить два совершенно разных периферийных устройства, то исключительно средствами драйверов ADI это не получится. Т. е. средствами драйвера невозможно выполнить трюк, описанный в статье [7], когда канал передачи SPORT (работая синхронно с каналом приема SPORT), формирует сигнал выборк/конверсии для АЦП, а канал приема SPORT получает данные от АЦП. Дело в том, что код драйверов универсален, и довольно велик по объему. Из-за наличия конвейера, кэширования кода и данных время выполнения кода драйвера невозможно заранее совершенно четко спрогнозировать, поэтому синхронного запуска двух аппаратных устройств с помощью вызовов API не получится. Это критичный недостаток, когда Вы хотите заставить работать 2 аппаратных устройства совершенно синхронно - например, канал передачи SPORT должен формировать сигнал выборки АЦП синхронно с работой канала DMA на канале приема SPORT.

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

Ниже приведен пример организации синхронного запуска канала приема и канала передачи SPORT. Реализовано то же самое, что и в статье [7], но средствами драйверов ADI. Канал приема и канал передачи работают как два совершенно разные периферийные устройства, но запускаются синхронно с помощью запрета и разрешения DMA. Канал передачи формирует сигнал выборки для АЦП (который определяет момент начала преобразования), а канал приема получает данные от АЦП.

////////////////////////////////////////////////////////////////////////////////
// Это библиотечка для опроса главного АЦП AD7691, использующая драйвер SPORT.
// Как настраиваются буферы: и для передачи, и для приема используется
// кольцевой буфер (с переходом в начало) ADI_DEV_MODE_CIRCULAR.
#include "pins.h"
#include "AD7691.h"
#include "error-codes-app.h"
 
static ADI_DEV_DEVICE_HANDLE DevHandleSPORTrx;
static ADI_DEV_DEVICE_HANDLE DevHandleSPORTtx;
 
static ADI_DEV_CIRCULAR_BUFFER rxbuffer;
static ADI_DEV_CIRCULAR_BUFFER txbuffer;
 
//Адреса регистров DMA_CONFIG каналов DMA 8..19 контроллера DMA1.
static const u32 BF538_DMA_CONFIGaddr[] = 
{
   0xFFC01C08,  /* DMA Channel 8 Configuration Register */
   0xFFC01C48,  /* DMA Channel 9 Configuration Register */
   0xFFC01C88,  /* DMA Channel 10 Configuration Register */
   0xFFC01CC8,  /* DMA Channel 11 Configuration Register */
   0xFFC01D08,  /* DMA Channel 12 Configuration Register */
   0xFFC01D48,  /* DMA Channel 13 Configuration Register */
   0xFFC01D88,  /* DMA Channel 14 Configuration Register */
   0xFFC01DC8,  /* DMA Channel 15 Configuration Register */
   0xFFC01E08,  /* DMA Channel 16 Configuration Register */
   0xFFC01E48,  /* DMA Channel 17 Configuration Register */
   0xFFC01E88,  /* DMA Channel 18 Configuration Register */
   0xFFC01EC8   /* DMA Channel 19 Configuration Register */
};
 
//Адреса регистров счетчика Y каналов DMA 8..19 контроллера DMA1.
static const u32 BF538_DMA_CURR_Y_COUNTaddr[] = 
{
   0xFFC01C38,  /* DMA Channel 8 Configuration Register */
   0xFFC01C78,  /* DMA Channel 9 Configuration Register */
   0xFFC01CB8,  /* DMA Channel 10 Configuration Register */
   0xFFC01CF8,  /* DMA Channel 11 Configuration Register */
   0xFFC01D38,  /* DMA Channel 12 Configuration Register */
   0xFFC01D78,  /* DMA Channel 13 Configuration Register */
   0xFFC01DB8,  /* DMA Channel 14 Configuration Register */
   0xFFC01DF8,  /* DMA Channel 15 Configuration Register */
   0xFFC01E38,  /* DMA Channel 16 Configuration Register */
   0xFFC01E78,  /* DMA Channel 17 Configuration Register */
   0xFFC01EB8,  /* DMA Channel 18 Configuration Register */
   0xFFC01EF8   /* DMA Channel 19 Configuration Register */
};
 
//Буфер приема драйвера SPORT (фактически это 2D буфер, хотя 
// с точки зрения API драйвера буфер рассматривается как кольцевой
// ADI_DEV_MODE_CIRCULAR буфер с двумя подбуферами):
#define SPORT3RX_U32_WORDS 256
static int anwdatarx[2][SPORT3RX_U32_WORDS];
 
//Массив для формирования сигнала SDI (выборка) АЦП AD7691,
// это также ADI_DEV_MODE_CIRCULAR буфер с двумя подбуферами:
static u32 anwdatatx[2][4];
 
//Идентификаторы привязки канала DMA к SPORT:
static ADI_DMA_CHANNEL_ID ChannelIDrx, ChannelIDtx;
//Адреса регистров конфигурации DMA, которые вычисляются
// на основе идентификаторов ChannelIDrx, ChannelIDtx
// и таблицы BF538_DMA_CONFIGaddr:
static u16 *dma_config_rx_reg, *dma_config_tx_reg;
//Адрес регистра DMA счетчика Y, который вычисляется
// на основе ChannelIDrx и таблицы BF538_DMA_CURR_Y_COUNTaddr.
static u16 *dma_curr_y_count_rx_reg;
 
//Глобальный кольцевой буфер, заполняемый оцифровкой сигнала.
#define NUMBUFFERS  1024
#define NUMBUFFMASK (NUMBUFFERS-1)
//Буфер большой, поэтому используется прагма, чтобы память
// буфера не заполнялась при старте сессии отладки, что
// весьма ускоряет загрузку сессии отладчика.
#pragma section ("no_init_sdram", NO_INIT)
int samples[NUMBUFFERS][SPORT3RX_U32_WORDS];
u16 inSB, outSB;     //Индексы кольцевого буфера оцифровки.
 
// Конфигурирование драйвера SPORT под устройство AD7691:
static ADI_DEV_CMD_VALUE_PAIR SPORT_configRX[] =
{
   //Настройка канала приема, в него будут поступать данные АЦП:
   { ADI_SPORT_CMD_SET_RCLKDIV, (void *)SPORT_RCLKDIV }, // делитель для тактов бит приема
   { ADI_SPORT_CMD_SET_RCR1,    (void *)RCR1          }, // регистр управления 1 приема
   { ADI_SPORT_CMD_SET_RCR2,    (void *)RCR2          }, // регистр управления 2 приема
   { ADI_SPORT_CMD_SET_RFSDIV,  (void *)RFSDIV        }, // делитель для тактов фрейма приема
   { ADI_DEV_CMD_SET_DATAFLOW_METHOD, (void *)ADI_DEV_MODE_CIRCULAR },
   { ADI_DEV_CMD_END, (void*)0 }
};
 
static ADI_DEV_CMD_VALUE_PAIR SPORT_configTX[] =
{
   //Настройка канала передачи, он должен формировать выборки SDI для АЦП:
   { ADI_SPORT_CMD_SET_TCLKDIV, (void *)SPORT_TCLKDIV }, // делитель для тактов бит передачи
   { ADI_SPORT_CMD_SET_TCR1,    (void *)TCR1          }, // регистр управления 1 приема
   { ADI_SPORT_CMD_SET_TCR2,    (void *)TCR2          }, // регистр управления 2 приема
   { ADI_SPORT_CMD_SET_TFSDIV,  (void *)RFSDIV        }, // делитель для тактов фрейма передачи
   { ADI_DEV_CMD_SET_DATAFLOW_METHOD, (void *)ADI_DEV_MODE_CIRCULAR },
   { ADI_DEV_CMD_END, (void*)0 }
};
 
static void sportCallbackFunctionRX (void *hArg, u32 Event, void *pArg)
{
   u16 subbuffer;
   
   /* обработка типа сообщения */
   switch (Event)
   {
   case ADI_DEV_EVENT_SUB_BUFFER_PROCESSED:
      subbuffer = ((*dma_curr_y_count_rx_reg)==1)?0:1;
      memcpy(samples[inSB], anwdatarx[subbuffer], SPORT3RX_U32_WORDS * sizeof(int));
      inSB++;
      inSB &= NUMBUFFMASK;
      break;
   default:
      // Ошибка: вызвать исключение пользователя
      VDK::KernelPanic(VDK::kThreadError,
                       (VDK::SystemError)errAD7691sport_dev_callbackRX,
                       Event);
      break;
   }
}
 
static void sportCallbackFunctionTX (void *hArg, u32 Event, void *pArg)
{
   /* обработка типа сообщения */
   switch (Event)
   {
   case ADI_DEV_EVENT_BUFFER_PROCESSED:
   case ADI_DEV_EVENT_SUB_BUFFER_PROCESSED:
      break;
   default:
      // Ошибка: вызвать исключение пользователя
      VDK::KernelPanic(VDK::kThreadError,
                       (VDK::SystemError)errAD7691sport_dev_callbackTX,
                       Event);
      break;
   }
}
 
static void AD7691_prepare_bufferRX (void)
{
   inSB = outSB = 0;
   /* настройка буфера чтения */
   rxbuffer.Data         = &anwdatarx[0];
   rxbuffer.SubBufferCount = 2;
   rxbuffer.SubBufferElementCount = SPORT3RX_U32_WORDS;
   rxbuffer.ElementWidth = sizeof(int);
   rxbuffer.CallbackType = ADI_DEV_CIRC_SUB_BUFFER;
   rxbuffer.pAdditionalInfo = NULL;
}
 
static void setbit (u32 *buf, u16 bitnum)
{
   bitnum &= 0x007F;
   u8 idx = (bitnum >> 5)^3;
   u32 mask = 1 << (bitnum&0x1F);
   buf[idx] |= mask;
}
 
static void fillbuftx (void)
{
///////////////////////////////////////////////////////////////////////////
// В массиве anwdatatx будет настроена 128-битная последовательность
// бит (маска) для синтеза сигнала выборки АЦП (CNVadc).
// В переменной bitstart указывается количество нулей, которое
// будет в маске начиная от старшего бита маски (он находится
// в бите D31 слова anwdatatx[0][0]).
   memset(anwdatatx, 0, 4*sizeof(int));
   //В массиве должно быть param.cnvadc1 последовательных единичек (обычно 49),
   // смещенных вправо от старшего бита anwdatatx на param.cnvadc0 позиций.
   for(u16 bitidx=param.CNVadc0; bitidx < param.CNVadc0+param.CNVadc1; bitidx++)
      setbit(anwdatatx[0], bitidx);
   memcpy(anwdatatx[1], anwdatatx[0], sizeof(int)*4);
}
 
static void AD7691_prepare_bufferTX (void)
{
   /* настройка буфера записи */
   txbuffer.Data         = &anwdatatx[0];
   txbuffer.SubBufferCount = 2;
   txbuffer.SubBufferElementCount = 4;
   txbuffer.ElementWidth = sizeof(int);
   txbuffer.CallbackType = ADI_DEV_CIRC_NO_CALLBACK;
   txbuffer.pAdditionalInfo = NULL;
}
 
#pragma optimize_for_speed
static void AD7691_StartConversion (void)
{ 
   u32 Result;
   //Запуск передачи (и одновременно приема) SPI.
   AD7691_prepare_bufferRX();
   AD7691_prepare_bufferTX();
   Result = adi_dev_Read( DevHandleSPORTrx,
                          ADI_DEV_CIRC,
                         (ADI_DEV_BUFFER*)&rxbuffer );
   if (Result!=ADI_DEV_RESULT_SUCCESS)
   {
      VDK::KernelPanic(VDK::kThreadError, (VDK::SystemError)errAD7691sport_dev_Read, Result);
   }
   Result = adi_dev_Write( DevHandleSPORTtx,
                           ADI_DEV_CIRC,
                          (ADI_DEV_BUFFER*)&txbuffer );
   if (Result!=ADI_DEV_RESULT_SUCCESS)
   {
      VDK::KernelPanic(VDK::kThreadError, (VDK::SystemError)errAD7691sport_dev_Write, Result);
   }
   Result = adi_dev_Control(DevHandleSPORTrx, ADI_DEV_CMD_SET_DATAFLOW, (void *)TRUE);
   if (Result!=ADI_DEV_RESULT_SUCCESS)
   {
      VDK::KernelPanic(VDK::kThreadError, (VDK::SystemError)errAD7691sport_dev_ControlRX2, Result);
   }
   Result = adi_dev_Control(DevHandleSPORTtx, ADI_DEV_CMD_SET_DATAFLOW, (void *)TRUE);
   if (Result!=ADI_DEV_RESULT_SUCCESS)
   {
      VDK::KernelPanic(VDK::kThreadError, (VDK::SystemError)errAD7691sport_dev_ControlTX2, Result);
   }
}
 
static void StopSport (void)
{
VDK::PushCriticalRegion();
   //Остановка передачи:
   *dma_config_tx_reg &= ~DMAEN;
   *pSPORT3_TCR1 &= ~TSPEN;
   //Остановка приема:
   *dma_config_rx_reg &= ~DMAEN;
   *pSPORT3_RCR1 &= ~RSPEN;
VDK::PopCriticalRegion();
}
 
static void RunSport (void)
{
VDK::PushCriticalRegion();
   *dma_config_rx_reg |= DMAEN;
   *pSPORT3_RCR1 |= RSPEN;
   ssync();
   *dma_config_tx_reg |= DMAEN;
   *pSPORT3_TCR1 |= TSPEN;
   ssync();
VDK::PopCriticalRegion();
}
 
void AD7691_Init (void)
{
   static bool sportinitialized = false;
   u32 Result;
   
   if (!sportinitialized)
   {
      // Открытие драйвера SPORT на прием
      Result = adi_dev_Open(
                  adi_dev_ManagerHandle,     // Хендл Менеджера Устройств
                  &ADISPORTEntryPoint,       // Точка входа драйвера SPORT
                  AD7691_SPORT_NUM,          // Номер устройства
                  NULL,                      // Аргумент для callback-функции
                  &DevHandleSPORTrx,         // Указатель на место хендла DM (для драйвера SPORT)
                  ADI_DEV_DIRECTION_INBOUND,
                  adi_dma_ManagerHandle,     // Хендл Менеджера DMA
                  NULL,                      // Менеджер DCB не используется
                  sportCallbackFunctionRX    // Внутренняя callback-функция
                  );
      if (Result!=ADI_DEV_RESULT_SUCCESS)
      {
         VDK::KernelPanic(VDK::kThreadError, (VDK::SystemError)errAD7691sport_dev_OpenRX, Result);
      }
      Result = adi_dev_Control( DevHandleSPORTrx, ADI_DEV_CMD_TABLE, (void*)SPORT_configRX ); 
      if (Result!=ADI_DEV_RESULT_SUCCESS)
      {
         VDK::KernelPanic(VDK::kThreadError, (VDK::SystemError)errAD7691sport_dev_ControlRX1, Result);
      }
      // Открытие драйвера SPORT на передачу
      Result = adi_dev_Open(
                  adi_dev_ManagerHandle,     // Хендл Менеджера Устройств
                  &ADISPORTEntryPoint,       // Точка входа драйвера SPORT
                  AD7691_SPORT_NUM,          // Номер устройства
                  NULL,                      // Аргумент для callback-функции
                  &DevHandleSPORTtx,         // Указатель на место хендла DM (для драйвера SPORT)
                  ADI_DEV_DIRECTION_OUTBOUND,
                  adi_dma_ManagerHandle,     // Хендл Менеджера DMA
                  NULL,                      // Менеджер DCB не используется
                  sportCallbackFunctionTX    // Внутренняя callback-функция
                  );
      if (Result!=ADI_DEV_RESULT_SUCCESS)
      {
         VDK::KernelPanic(VDK::kThreadError, (VDK::SystemError)errAD7691sport_dev_OpenTX, Result);
      }
      Result = adi_dev_Control( DevHandleSPORTtx, ADI_DEV_CMD_TABLE, (void*)SPORT_configTX ); 
      if (Result!=ADI_DEV_RESULT_SUCCESS)
      {
         VDK::KernelPanic(VDK::kThreadError, (VDK::SystemError)errAD7691sport_dev_ControlTX1, Result);
      }
      //Сохранение идентификаторов канала, привязанных к SPORT3:
      Result = adi_dma_GetMapping(ADI_DMA_PMAP_SPORT3_TX, &ChannelIDtx);
      if (Result!=ADI_DEV_RESULT_SUCCESS)
      {
         VDK::KernelPanic(VDK::kThreadError, (VDK::SystemError)errGetMappingSPORT3tx, Result);
      }
      Result = adi_dma_GetMapping(ADI_DMA_PMAP_SPORT3_RX, &ChannelIDrx);
      if (Result!=ADI_DEV_RESULT_SUCCESS)
      {
         VDK::KernelPanic(VDK::kThreadError, (VDK::SystemError)errGetMappingSPORT3tx, Result);
      }
      //Сохранение адресов привязки каналов DMA:
      dma_config_rx_reg       = (u16*)BF538_DMA_CONFIGaddr      [ChannelIDrx-12];
      dma_curr_y_count_rx_reg = (u16*)BF538_DMA_CURR_Y_COUNTaddr[ChannelIDrx-12];
      dma_config_tx_reg       = (u16*)BF538_DMA_CONFIGaddr      [ChannelIDtx-12];
      AD7691_StartConversion();
      sportinitialized = true;
   }
   StopSport();
   fillbuftx();
   RunSport();
}

Обратите внимание, что адреса регистров управления DMA (указатели dma_config_rx_reg, dma_config_tx_reg) вычисляются с помощью вызовов функции adi_dma_GetMapping, возвращающей привязку периферийного устройства к каналу DMA, и массива адресов регистров BF538_DMA_CONFIGaddr. Благодаря функциям StopSport и RunSport гарантируется синхронный запуск канала приема SPORT и канала передачи SPORT.

[Как узнать, какой подбуфер был обработан?]

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

Несмотря на то, что буфер драйвера приема SPORT называется как кольцевой (тип буфера устанавливается командой ADI_DEV_CMD_SET_DATAFLOW_METHOD, см. определение массива команд конфигурации SPORT_configRX[]), он все равно внутренне работает как 2D-буфер, где счетчик Y считает подбуферы. Поэтому если напрямую в callback-функции обратиться к этому регистру, то можно узнать, какая половина буфера (т. е. подбуфер в терминологии ADI) драйвера приема была обработана:

static void sportCallbackFunctionRX (void *hArg, u32 Event, void *pArg)
{
   u16 subbuffer;
   
   /* обработка типа сообщения */
   switch (Event)
   {
   case ADI_DEV_EVENT_SUB_BUFFER_PROCESSED:
      subbuffer = ((*dma_curr_y_count_rx_reg)==1)?0:1;
      memcpy(samples[inSB], anwdatarx[subbuffer], SPORT3RX_U32_WORDS * sizeof(int));
      inSB++;
      inSB &= NUMBUFFMASK;
      break;
   default:
      // Ошибка: вызвать исключение пользователя
      VDK::KernelPanic(VDK::kThreadError,
                       (VDK::SystemError)errAD7691sport_dev_callbackRX,
                       Event);
      break;
   }
} 

Адрес регистра счетчика Y блока DMA (указатель dma_curr_y_count_rx_reg) вычисляется с помощью вызова функции adi_dma_GetMapping, возвращающей привязку периферийного устройства к каналу DMA, и массива адресов регистров BF538_DMA_CURR_Y_COUNTaddr. Значение регистра счетчика используется для установки индекса подбуфера subbuffer.

[Ссылки]

1. ADSP-BF538: интерфейс SPORT.
2. VDK: менеджер DMA.
3. VDK: менеджер прерываний.
4. VDK: служба управления портами.
5. Обзор VisualDSP++ Kernel RTOS (VDK).
6. VDK: драйверы устройств и системные службы процессоров Blackfin.
7. Blackfin: чтение АЦП AD7691 через SPORT с использованием 2D DMA.

 

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


Защитный код
Обновить

Top of Page