Научная статья на тему 'Проектирование с использованием процессоров analog devices. Расширенные возможности симулятора'

Проектирование с использованием процессоров analog devices. Расширенные возможности симулятора Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
160
39
i Надоели баннеры? Вы всегда можете отключить рекламу.

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Сотников Александр

Окончание. Начало в К иТ № 1`2010

i Надоели баннеры? Вы всегда можете отключить рекламу.
iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Текст научной работы на тему «Проектирование с использованием процессоров analog devices. Расширенные возможности симулятора»

Проектирование с использованием процессоров Analog Devices.

Расширенные возможности симулятора

Окончание. Начало в КиТ № 12010

Александр СОТНИКОВ

Компилятор C/C++ процессоров Blackfin

Поскольку создаваемый нами проект будет написан на языке C, целесообразно вначале вкратце рассмотреть основные отличительные черты компилятора C/C++ для процессоров Blackfin. Компилятор С/С++ предназначен для преобразования исходных файлов, написанных на языках С/С++, в исходные файлы на языке ассемблера и представляет собой отдельную утилиту, запускаемую автоматически из IDDE VisualDSP++ или вручную из командной строки ОС.

Он поддерживает стандарт ISO ANSI, однако имеет ряд расширенных возможностей, специфических для процессоров Blackfin, среди которых, например:

• Поддержка встраиваемых ассемблерных вставок при помощи конструкции asm ().

• Поддержка квалификаторов банка памяти bank («имя_банка»), при помощи которых компилятору можно указать на то, что переменные располагаются в разных банках и обращение к ним может быть выполнено одновременно.

• Поддержка квалификаторов секций памяти section («имя_секции»), которые указывают компилятору на то, что переменную необходимо разместить в формируемом объектном файле в определенную секцию.

• Поддержка прагм #pragma — директив, модифицирующих поведение компилятора (например, управляющих выравниванием данных по определенной границе в памяти процессора, указывающих на то, что объявляемая и/или определяемая функция является обработчиком прерывания, и ее адрес должен быть помещен в соответствующий элемент таблицы векторов прерываний, и т. п.).

• Наличие встроенных (built-in) функций, которые распознаются и заменяются компилятором на одну или несколько машинных команд, как если бы это были простые операторы типа “+” или “-”. Встроенные функции позволяют более эффективно использовать аппаратные ресурсы процессора и имеют имена, начинающиеся с builtin_.

Полный перечень расширений стандарта ISO ANSI, реализованных в компиляторе C/C++ процессоров Blackfin, можно найти в документе [1].

Проекты, создаваемые на языке C/C++ для процессоров Blackfin, должны включать в свой состав так называемый исполняемый заголовок C/C++ (CRT, C/C++ run-time header) — код на языке ассемблера, который выполняется после сброса или включения питания процессора. Задача CRT состоит в том, чтобы инициализировать состояние процессора и вызвать функцию _main (код, формируемый компилятором для функций на C/C++; сопровождается меткой, которая состоит из символа подчеркивания и имени функции). Выполнение CRT автоматически гарантирует, что состояние процессора перед вызовом _main подчиняется двоичному интерфейсу прикладных программ (ABI, application binry interface), который обеспечивает переносимость прикладных программ с платформы на платформу.

Код CRT (в последних версиях VisualDSP++ для него используется название startup-код) может быть сконфигурирован в интерактивном режиме и автоматически включен в проект при помощи «мастера» создания проектов. Также пользователь может вручную создать собственный файл с кодом CRT и подключить его на этапе компоновки при помощи соответствующего макроса в файле описания линкера. И, наконец, если пользователь вообще не подключает CRT к проекту, то среда VisualDSP++ автоматически использует на этапе компоновки один из стандартных, предварительно скомпилированных CRT.

Исходный файл на языке ассемблера basiccrt.s, из которого получены стандартные CRT для разных конфигураций и платформ, находится в установочном каталоге VisualDSP++ в папке Blackfin\lib\src\libc\crt.

В остальном, на первый взгляд, процесс программирования на языках C/C++ для процессоров Blackfin не сильно отличается от программирования под другие платформы. Некоторые сложности в освоении у новичков может вызвать разве что организация обработки прерываний, о которой

речь пойдет чуть позднее. Однако для написания действительно эффективного и компактного кода нужно хорошо разбираться в архитектуре процессора и различных нюансах работы компилятора. Так, например, поскольку процессор Blackfin не имеет аппаратной поддержки типов с плавающей точкой и 64-битных данных, то для работы с переменными типа float, double, long long и т. п. используется программная эмуляция, что неизбежно приводит к увеличению объема кода и снижению производительности. Другой пример — это работа с памятью. В языке C используется модель унифицированного адресного пространства, однако на аппаратном уровне доступ к различным областям памяти процессора существенно различается, что может сильно влиять на быстродействие. Подобных примеров можно привести очень много, однако ввиду ограниченного объема вопросы повышения эффективности кода на языках C/C++ для процессоров Blackfin рассматриваться здесь не будут. Заинтересованные читатели могут найти более подробное обсуждение данных вопросов в [1], а также в серии статей [2].

Создание проекта

Рассматриваемый в этой статье проект, как уже отмечалось ранее, будет осуществлять ввод в процессор блока данных через последовательный порт в режиме DMA и вычисление быстрого преобразования Фурье (БПФ). Для упрощения программы мы будем предполагать, что внешнее устройство, которое является источником данных, выдает 32-битные слова, содержащие в старшей половине (биты 31-16) отсчеты синфазной составляющей сигнала, а в младшей половине (биты 15-0) отсчеты квадратурной составляющей сигнала. Данные синфазной и квадратурной составляющих комплексного сигнала будут представлены в знаковом 16-битном формате с фиксированной точкой, который является основным форматом, поддерживаемым процессорами Blackfin. По завершении приема блока из 128 комплексных отсчетов процессор будет генери-

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 О

ИСКРЕ (Выбор фронта ---------------

сигнала тактовой синхронизации)

0 — Выдача внутреннего сигнала кадровой синхронизации по переднему фронту Р5С1_К. Считывание данных и внешнего сигнала кадровой синхронизации по заднему фронту РБСЬК

1 — Выдача внутреннего сигнала кадровой синхронизации по заднему фронту КБСЬК.

Считывание данных и внешнего сигнала

кадровой синхронизации по переднему фронту 1?8С1_К

1_АИР8 (Выбор режима -------

кадровой синхронизации приема)

0 — Кадровая синхронизация с опережением

1 — Кадровая синхронизация с запаздыванием

1_11Р5 (Выбор активного уровня сигнала -кадровой синхронизации приема)

0 — Активный уровень — высокий

1 — Активный уровень — низкий

ИРБИ (Настройка генерации сигнала ------------------------------

кадровой синхронизации приема)

0 — Кадровая синхронизация для каждого слова данных не требуется

1 — Каждое слово данных должно сопровождаться сигналом кадровой синхронизации

L

НвРЕИ (Разрешение приема)

0 — Прием запрещен

1 — Прием разрешен

№С1.К (Выбор источника тактовой синхронизации приема)

0 — Внешняя тактовая синхронизация приема

1 — Внутренняя тактовая синхронизация приема

1ЮТ¥РЕ[1:0] (Тип форматирования данных)

00 — Дополнение нулями

01 — Дополнение знаковым разрядом

10 — Компандирование по р-закону

11 — Компандирование по А-закону

1?1_5В1Т (Порядок приема битов)

0 — Прием начиная с МЭВ

1 — Прием начиная с 1_5В

№С1-К (Выбор источника кадровой синхронизации приема)

0 — Внешний сигнал

1 — Внутренний сигнал 1^8

Рис. 1. Назначение полей регистра SPQRT0_RCR1

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

00000000000

1№Р$Т (Порядок левого/правого каналов) -----------------------------1

0 — Первым следует левый стереоканал

1 — Первым следует правый стереоканал

НвРвЕ (Разрешение использования сигнала кадровой синхронизации в качестве маркера правого/левого стереоканалов) -------------------

0 — Нормальный режим

1 — Сигнал кадровой синхронизации используется в качестве тактового сигнала 1-/1?

0 0 0 0 0

81-ЕМ[4:0] (Длина слова)

00000 — Запрещенная комбинация

00001 — Запрещенная комбинация 00010—11111 — Длина слова минус 1

ЮСвЕ (Разрешение КхЭЕС)

0 — Прием по вторичной линии запрещен

1 — Прием по вторичной линии разрешен

Рис. 2. Назначение полей регистра SPQRT0_RCR2

ровать прерывание, которое сигнализирует о готовности массива входных данных для вычисления БПФ.

Для создания нового проекта вызовите пункт меню File ^ New ^ Project, выберите в открывшемся окне «мастера» создания проектов тип Standard application и задайте имя проекта (например, TestSport) и каталог, в котором он будет располагаться. На вкладке Select Processor укажите процессор ADSP-BF537 и автоматический выбор ревизии кристалла. На вкладках Application Settings и Add Startup Code/LDF оставьте предлагаемые варианты настроек без изменений и завершите создание проекта, нажав кнопку Finish. В результате работы «мастера» создания проектов будет сгенерирован соответствующий файл проекта TestSport.dpj и исходный файл на языке C, TestSport.c, который пока что содержит лишь каркас функции main.

Первое, что необходимо сделать в исходном коде, — это добавить в функцию main() инициализацию соответствующих аппаратных модулей процессора.

Для настройки последовательного порта (для определенности в проекте мы будем использо-

вать SPORT0) на прием необходимо инициализировать четыре регистра: два регистра управления приемом (SPORT0_RCR1 и SPORT0_ RCR2), регистр делителя сигнала тактовой синхронизации приема (SPORT0_RCLKDIV) и регистр делителя кадровой синхронизации приема (SPORT0_RFSDIV).

Регистры управления приемом порта SPORT0 расположены по адресам внутренней памяти 0xFFC00820 и 0xFFC00824, а назначение их полей показано на рис. 1 и 2 соответственно. Для нашего проекта мы выберем режим приема 32-битных данных с внутренней генерацией сигналов кадровой и тактовой синхронизации. Также в регистре SPORT0_RCR1 необходимо установить бит RFSR, который указывает на необходимость генерации сигнала кадровой синхронизации для каждого принимаемого слова. Остальные настройки приема по последовательному порту можно оставить без изменений, поскольку с точки зрения моделирования работы порта в симуляторе они не важны.

Значения частот сигналов тактовой №К5С1К) и кадровой №№5) синхронизации приема определяются при помощи следующих формул:

Frsclk = Fsclk/(2x(SPORTx_RCLKDIV+1)), Frfs = Frslk/(SPORTx_RFSDIV+1),

где FSCLK — частота тактового сигнала системы. В реальной системе значения, записываемые в регистры SPORT0_RCLKDIV и SPORT0_ RFSDIV, необходимо подбирать с учетом конкретного значения FSCLK и требований внешнего устройства, однако для ускорения работы модели мы зададим максимально возможные тактовые частоты сигналов (SPORT0_RCLKDIV= = 0 и SPORT0_RFSDIV = 31).

Для упрощения доступа к регистрам, отображенным во внутренней памяти процессора Blackfin, из программы на языке C можно подключить файл cdefBF537.h. Этот файл и подключаемый внутри него файл cdefBF532.h содержат определения символьных констант, соответствующих указателям на регистры процессора. Так, например, конструкцию *(volatile unsigned short*)0xFFC00820, необходимую для обращения к регистру управления SPORT0_ RCR1, при использовании файла cdefBF537.h можно заменить менее громоздкой и более наглядной записью *pSPORT0_RCR1.

WNR (Направление пересылки)

0 — Чтение из памяти

1 — Запись в память

15 14 13 12 11 10 9 8 7 б 5 4 3 2 1 О

О О

Н-0№[2:0] (Следующая операция) -------------------

0x0 — Остановка 0x1 — Режим автобуферизации 0x4 — Массив дескрипторов 0x6 — Список дескрипторов (малая модель)

0x7 — Список дескрипторов (большая модель)

№817Е (Размер дескриптора) -----------------------

0000 — При работе в режиме с остановкой или в режиме автобуферизации

0001 — 1001 — Размер дескриптора 1010—1111 — Зарезервировано

01_ЕМ (Разрешение прерывания) -----------------------------------------

0 — Запрещение генерации прерывания по завершении пересылки рабочего блока

1 — Разрешение генерации прерывания по завершении пересылки рабочего блока

01_БЕ1_ (Выбор режима генерации прерывания; ---------------------------

распространяется только на двумерный ОМА)

0 — Прерывание по завершении пересылки целого буфера (внешний цикл)

1 — Прерывание по завершению пересылки каждой строки (внутренний цикл)

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

L

□МАЕМ (Разрешение работы канала ОМА)

0 — Канал ОМА отключен

1 — Канал ОМА включен

УУОБ17Е[1:0] (Размер пересылаемых слов]

00 — 8-битные пересылки

01 — 16-битные пересылки

10 — 32-битные пересылки

11 - Зарезервировано

0МА20 (Режим ОМА)

О — Линейный (плнпмепнмй)

Рис. 3. Назначение полей регистра DMAx CONFIG

Таким образом, для настройки последовательного порта нам потребуется добавить в исходный файл строки:

*pSPORT0_RCR1 = RFSRI IRFS I IRCLK;

*pSPORT0_RCR2 = 0x1F;

*pSPORT0_RCLKDIV=0;

*pSPORT0_RFSDIV=31;

Обратите внимание на то, что, пока для последовательного порта не настроен канал DMA и не разрешено соответствующее прерывание, устанавливать в единицу бит RSPEN регистра SPORT0_RCR1 не следует.

По умолчанию за прием данных по последовательному порту SPORT0 в режиме DMA отвечает канал DMA номер 3. Для настройки параметров канала нам потребуется инициализировать содержимое регистров конфигурации (DMA3_CONFIG), начального адреса (DMA3_START_ADDR), счетчика (DMA3_X_COUNT) и модификатора (DMA3_X_MODIFY) канала DMA.

Назначение полей регистра конфигурации канала 3 DMA показано на рис. 3. Для нашего проекта мы выберем одномерный режим DMA с остановкой по завершению пересылки блока данных и генерацией прерывания. Данные в память будут записываться 32-битными словами.

В качестве приемника данных будет использоваться массив данных типа complex_fract16 под названием rx_buf длиной 128 элементов. Тип данных complex_ fract16 представляет собой структуру из двух элементов типа fract16 (знаковое 16-битное число с фиксированной точкой), соответствующих вещественной и мнимой составляющей комплексного числа. Для работы с этими двумя типами данных в исходном файле необходимо подключить заголовочные файлы fract.h и complex.h. Массив rx_buf следует объявить глобально с квалификатором volatile:

volatile complex_fract16 rx_buf[128];

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

• для обращения к регистру процессора или внешнему устройству, отображенным в карте памяти;

• для данных, к которым осуществляется доступ в режиме DMA;

• для объектов (например, глобальных переменных), модифицируемых в обработчиках асинхронных прерываний.

В регистр счетчика DMA записывается полное количество пересылаемых слов, а в регистр модификатора — количество байтов в пересылаемом слове, то есть 4. Таким образом, фрагмент кода, отвечающий за инициализацию канала DMA, будет иметь вид:

*pDMA3_CONFIG = SYNCI WNR I WDSIZE_32 I FLOW_STOP I DI_EN;

*pDMA3_X_COUNT = 128;

*pDMA3_X_MODIFY = 4;

*pDMA3_START_ADDR = (int)rx_buf;

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

Последний этап процедуры инициализации аппаратных средств процессора состоит в настройке прерывания, которое будет генерироваться по заполнению блока данных. Прерывание канала DMA номер 3 по умолчанию направляется на вход IVG9 контроллера событий ядра. Эту настройку можно изменить, записав новое значение в поле регистра SIC_IAR0, соответствующее данному прерыванию.

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

1 — Двумерный

SYNC (Режим переключения между рабочими блоками)

0 — Непрерывное переключение

1 — Синхронное переключение

и зарегистрировать его в таблице векторов прерываний. Для этих целей в библиотеке компилятора C/C++ процессоров Blackfin имеются два механизма. Первый из них — это функция register_handler (IVGx, ISR_ Name), которая ставит в соответствие подпрограмму обслуживания прерывания с именем ISR_Name прерыванию ядра IVGx. Она осуществляет запись адреса функции, обработчика прерывания, в требуемый элемент таблицы векторов прерываний, а также устанавливает в регистре маскирования прерываний ядра (IMASK) бит, соответствующий прерыванию IVGx, и бит глобального разрешения прерываний. В нашем случае вызов функции будет иметь вид:

register_handler(ik_ivg9, Sport0_RX_ISR);

где ik_ivg9 — это символьная константа, определяющая номер прерывания, а Sport0_ RX_ISR — имя функции-обработчика.

Второй механизм — это макрос EX_ INTERRUPT_HANDLER (ISR_Name). Обычно он используется для одновременного объявления и определения обработчика и имеет следующий синтаксис:

EX_INTERRUPT_HANDLER(ISR_Name)

{

//Код обработчика прерывания

}

Также данный макрос может быть использован для объявления прототипа функции-обработчика:

EX_INTERRUPT_HANDLER(ISR_name);

Применение макроса EX_INTERRUPT_ HANDLER указывает компилятору на необходимость сохранения контекста процессо-

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

Для использования описанных выше механизмов в исходном файле необходимо подключить заголовочный файл sys\exception. h.

И, наконец, последний шаг в настройке прерывания — это написание кода функции-обработчика. В нашем случае он будет включать в себя три операции: снятие бита прерывания канала DMA номер 3 (осуществляется записью единицы в соответствующий бит регистра состояния канала DMA), отключение приема по последовательному порту SPORTO и установка в единицу глобально объявляемой переменной-флага DMA_done, которая будет сигнализировать о завершении приема блока данных:

volatile int DMA_done = 0;

EX_INTERRUPT_HANDLER(Sport0_RX_ISR)

{

*pDMA3_IRQ_STATUS = 0x0001;

*pSP0RT0_RCR1 &= 0xFFFE;

DMA_done = 1;

}

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

*pSIC_IMASK = 0x00000020;

*pDMA3_C0NFIG 1= 1;

*pSP0RT0_RCR 1= 1;

Основная часть программы будет включать в себя цикл, в котором осуществляется проверка флага DMA_done, и расчет комплексного БПФ принятого массива данных. Для вычисления БПФ в библиотеке функций цифровой обработки сигналов (ЦОС) компилятора C имеется ряд функций, прототипы которых приведены в заголовочном файле filter. h. Эти функции работают с данными типа fract16 и complex_fract16, и для нашего примера мы воспользуемся функцией cfft_ fr16, которая имеет следующий синтаксис:

void cfft_fr16(const complex_fract16 input[], complex_fract16 output[], const complex_fract16 twiddle_table[], int twiddle_stride, int fft_size, int *block_exponent, int scale_method);

Назначение первых двух и пятого аргумента функции очевидно из их названий. Третий аргумент функции — это указатель на массив, содержащий таблицу синусов и косинусов, которая необходима для вычисления БПФ. Четвертый аргумент, twiddle_stride, позволяет использовать одну

такую таблицу большого размера для вычисления нескольких БПФ разной размерности и должен быть равен отношению размера таблицы к размерности БПФ. Поскольку в нашем случае такая возможность не требуется, этот аргумент должен быть равен единице. Последний аргумент функции указывает вариант масштабирования, используемый для предотвращения переполнения при вычислении функции БПФ. Он может принимать три значения: 1, 2 или 3, что соответствует статическому масштабированию (данные на входе каждого каскада БПФ делятся на 2), динамическому масштабированию (данные на входе каскада БПФ делятся на два, если модуль любого элемента данных больше 0,5) или отсутствию масштабирования. И, наконец, предпоследний аргумент — это указатель на переменную, в которую будет записан масштабирующий множитель для выходного массива. В случае scale_method = 1 и scale_method = 3 возвращаемое значение множителя всегда будет равно log2(fft_size) и 1 соответственно.

Для инициализации массива twiddle_table может быть использована функция:

void twidfftf_fr16(complex_fract16 twiddle_table [], int fft_size);

/* Begin adding your custom code here */ complex_fract16 twiddle_table [128]; complex_fract16 out_buf [128]; int block_exp, i=0;

twidfftrad2_fr16(twiddle_table,128);

//SP0RT0 initialization *pSP0RT0_RCR1 = RFSR I IRFS I IRCLK;

*pSP0RT0_RCR2 = 0x1F;

*pSP0RT0_RCLKDIV = 0;

*pSP0RT0_RFSDIV = 31;

//DMA initialization

*pDMA3_C0NFIG = SYNCIWNRIWDSIZE_32IFL0W_ST0PIDL

EN;

*pDMA3_X_C0UNT = 128;

*pDMA3_X_M0DIFY = 4;

*pDMA3_START_ADDR = (int)rx_buf;

//Interrupt initialization register_handler(ik_ivg9, Sport0_RX_ISR);

//Interrupt, DMA and SP0RT RX enable *pSIC_IMASK = 0 x00000020;

*pDMA3_C0NFIG I= 1;

*pSP0RT0_RCR1 I= 1;

//Wait for DMA completion while (! DMA_done);

cfft_fr16((complex_fract16*)rx_buf, out_buf, twiddle_table,1, 128,&block_exp,0);

for(i=0; i<128; i++)

result[i] = cabs_fr16 (out_buf[i]);

return 0;

}

Листинг 1

которую достаточно вызвать однократно при выполнении процедуры инициализации.

И, наконец, еще одна функция, которая потребуется нам для представления результатов в удобной форме, — это функция вычисления модуля комплексного числа:

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

Моделирование работы программы

После запуска сессии симулятора и успешной компоновки исполняемого файла TestSport.dxe необходимо настроить ввод входных данных для моделирования. В среде VisualDSP++ это делается при помощи «потоков» (streams). Настройка потока осуществляется в диалоговом окне, которое вызывается через команду меню Settings ^ Streams (рис. 4). Нажатие на кнопку Add («Добавить») вызывает новое диалоговое окно Add New Stream («Добавить новый поток»), которое разделено на две части — настройка источника (Source) и приемника (Destination). При моделировании ввода данных источником является текстовый файл, а приемником — внутренняя память или один из поддерживаемых симулятором периферийных модулей процессора ADSP-BF537. При моделировании вывода данных назначение изменяется на противоположное. Для

V.г*.!*4* !>*<-> 1 !

£•_

! rrJ« М

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

/************************************************************** * TestSport.c

************************************************************** #include "cdefBF537.h"

#include "sys\exception.h"

#include "fract.h"

#include "complex.h"

#include "filter.h"

volatile complex_fract16 rx_buf[128]; volatile int DMA_done = 0; fract16 result [128];

EX_INTERRUPT_HANDLER(Sport0_RX_ISR)

I

*pDMA3_IRQ_STATUS = 0x0001;

*pSPORT0_RCR1 &= 0xFFFE;

DMA_done = 1;

}

int main (void)

I

fract16 cabs_fr16(complex_fract16 a);

І а. |Г~Е=~1 Рис. 4. Диалоговое окно Streams

Рис. 5. Настройка потока ввода данных

файла-источника может быть выбран формат данных, а также опции автоматической инициализации потока при загрузке или перезапуске исполняемого файла (флажок Rewind on reset or restart) и автоматического перехода к началу файла при достижении его конца (Circular). В нашем проекте необходимо выбрать настройки источника и приемника, как показано на рис. 5.

Текстовый файл должен содержать текстовые строки с данными в формате, который соответствует настройкам потока, где каждый элемент данных начинается с новой строки. Разработанная нами программа предназначена для работы с данными в формате fract16, и каждое 32-битное слово, принимаемое по последовательному порту, будет содержать как синфазную, так и квадратурную составляющую сигнала. Поэтому целесообразно выбрать шестнадцатеричный формат данных в файле-источнике и записать в каждую строку файла сначала шестнадцатеричное значение квадратурной составляющей отсчета, а затем шестнадцатеричное значение синфазной составляющей. Это может быть сделано, например, в среде MATLAB при помощи скрипта, представленного в листинге 2.

clear

t = 0:1:127;

x = 0.7*(exp(j*2*pi*0.1*t)); s = awgn(x,20); re = fi(real(s),1,16,15); im = fi(imag(s),1,16,15); file = fopen('test.dat','w'); for i = 1:128 fprintf(file,%os%s\n',hex(im(i)),hex(re(i))); end

fclose(file)

Листинг 2

Для наглядного представления входных данных и результатов расчета амплитудного спектра мы воспользуемся средствами графического отображения VisualDSP++. Для создания нового графика необходимо выбрать пункт меню View ^ Debug Windows ^ Plot ^ New. В открывшемся диалоговом окне, которое показано на рис. 6, из выпадающего списка Type («Тип графика») выберите Line Plot («Линейный график») и задайте название графика (например, Input Data) в поле Title. Чтобы добавить на график линию данных синфазной составляющей, нужно нажать на кнопку Browse, выбрать

Рис. 6. Диалоговое окно Plot Configuration

в открывшемся списке позицию rx_buf и закрыть список нажатием кнопки OK. В выпадающем списке типов данных (Data) для построения линейного графика отсутствует возможность выбора знаковых дробных чисел, поэтому выберите в нем пункт short. В поле Name можно задать имя отображаемого набора данных (например, In-Phase). В поле Offset задается смещение первого элемента данных относительно выбранного ранее адреса, и для синфазной составляющей изменять значение по умолчанию не нужно. В поле Count необходимо указать количество элементов данных на графике (в нашем примере — 128). И, наконец, поскольку отсчеты синфазной и квадратурной составляющих сигнала упорядочены в массиве через один, в поле Stride необходимо ввести число 2. При нажатии кнопки Add в списке Data Sets появится позиция с указанным ранее именем. Эти же действия необходимо повторить для добавления на график набора данных квадратурной составляющей. Единственное отличие будет заключаться в том, что в поле Offset потребуется изменить 0 на 1.

Аналогичным образом можно создать график с данными амплитудного спектра. При настройке графика необходимо выбрать из списка адресов позицию, соответствующую массиву result, тип данных short, 0ffset = 0, Count = 128 и Stride = 1.

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

Заключение

В этой статье на примере проекта, реализующего чтение данных по последовательному порту в режиме DMA, были рассмотрены основные принципы создания проектов для процессоров Blackfin на языке C, а также некоторые расширенные возможности симулятора, такие как моделирование ввода/вывода данных и графическое отображение содержимого памяти процессора. Несмотря на то, что рассматриваемый пример является довольно упрощенным и, в некоторой степени, искусственным, он позволяет продемонстрировать такие важные аспекты программирования, как обращение к внутренним регистрам процессора, организация обмена данными в режиме DMA и обработка прерываний. Этот пример может использоваться начинающими разработчиками в качестве отправной точки для создания собственных приложений, более приближенных к реальности. ■

Литература

1. VisualDSP++ 5.0 C/C++ Compiler and Library Manual for Blackfin Processors. Revision 5.2, September 2009 — www.analog.com

2. Anderson A. Programming and 0ptimizing C code — http://www.dspdesignline.com/ 197006981

Рис. 7. Окно VisualDSP++ с графиками входных данных и результатов расчета амплитудного спектра

i Надоели баннеры? Вы всегда можете отключить рекламу.