Разработка программного кода для цифровых сигнальных процессоров tms320c6000

Разработка программного кода

для сигнальных процессоров TMS320C6000

Статья открывает новый цикл, в котором будут показаны примеры реализации алгоритмов цифровой обработки сигналов (ЦОС) на базе цифровых сигнальных процессоров (ЦСП) фирмы Т1. Цель предлагаемых материалов — показать технологию программирования ЦСП фирмы Т1 от постановки задачи на разработку программной реализации алгоритма ЦОС до получения бинарного кода для ЦСП, его отладки и оптимизации с помощью аппаратных и программных средств, предоставляемых фирмой Т1. Предлагаемая программная модель реализации алгоритмов ЦОС учитывает требования стандарта еХргезйБР, внедряемого фирмой Т1. Кроме того, модель предполагает максимально возможную переносимость разработанного программного кода между различными семействами ЦСП фирмы Т1.

Игорь ГУК

gii@scanti.ru

В ближайших выпусках цикла будут рассмотрены общие положения ЦОС, рекомендации по написанию программного кода алгоритмов ЦОС на примере фильтра с конечной импульсной характеристикой (в дальнейшем — КИХ-фильтр), использование интегральной среды разработки (ИСР) СС8 для реализации КИХ-фильтра на ЦСП ТМЭ320С6000, тестирование и отладка полученного кода с помощью СС8 и отладочного модуля на основе ЦСП ТМ8320С6211. Использование младшего процессора из серии ТМЭ320С6000 обеспечивает полную совместимость программного кода со всеми старшими ЦСП данного семейства.

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

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

ния могут выступать: электромагнитное поле (радиосигнал), воздух (звуковой сигнал), вода (гидроакустический сигнал), почва (сейсмический сигнал) и т. д.

В ЦОС различают четыре типа сигналов:

• Аналоговый (рис. 1, а) — это сигнал, непрерывный во времени и по значению. Описывается непрерывной (или кусочно-непрерывной) функцией времени x(t). Аргумент и функция могут принимать любые значения из некоторых произвольных интервалов: ^п < t< гтса, Xm^n < x(t) < Xmax.

• Дискретный (рис. 1, б) — это сигнал, дискретный во времени и непрерывный по значению. Представляет собой последовательность чисел, называемых отсчетами. Описывается решетчатой функцией x(nT), где п = 0, 1, 2, 3... — номер отсчета, а Т — интервал между отсчетами, называемый периодом дискретизации. Обратную величину 1/Т называют частотой дискретизации . Решетчатая функция определена только в моменты времени t = nT и может принимать произвольное значе-

ние из некоторого произвольного интервала Xmin < x(nT) < Xmar

• Цифровой (рис. 1, в) — это сигнал, дискретный во времени и квантованный по значению. Описывается решетчатой функцией, которая может принимать только конечное число значений из некоторого конечного интервала. Эти значения называются уровнями квантования, а соответствующая функция — квантованной.

• Цифро-аналоговый (рис. 1, г) — это сигнал, непрерывный во времени и квантованный по значению. Описывается непрерывной (или кусочно-непрерывной) функцией времени xц(t), причем аргумент может принимать любые значения из некоторого интервала t' < t < t", а сама функция — только конечное число значений из некоторого конечного интервала x' < x < x", то есть является квантованной.

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

Обобщенная процедура ЦОС включает три этапа:

• Преобразование входного аналогового сигнала в дискретный сигнал.

• Обработка дискретного сигнала по заданному алгоритму цифровым сигнальным процессором (ЦСП) и формирование выходного дискретного сигнала.

• Преобразование дискретного сигнала в выходной аналоговый.

Х(^ уі»і) У(^

АФЧН х(1^ АЦП х(пТ)  ЦСП ЦАП СФНЧ

1,«і и

Рис. 2. Система цифровой обработки сигналов

-ь,

* -Ь„хх

х,оо —\

Х2

(+)—5а(п) —* г-1 — -н

Хц.»—“

Суммирование Задержка Умножение

Рис. 3. Графическое изображение основных операций цифровой обработки сигналов

Система ЦОС, реализующая процедуру

ЦОС, включает (рис. 2):

• Аналоговый антиэлайсинговый фильтр низкой частоты (АФНЧ). Обеспечивает корректность преобразования аналогового сигнала в дискретный.

• Аналого-цифровой преобразователь (АЦП). Формирует из аналогового сигнала я; (nT) цифровой х(пТ) и выполняет две функции: дискретизацию во времени и квантование по уровням.

• Цифровой сигнальный процессор (ЦСП). Вычисляет по заданному алгоритму выходной отсчет у(пТ) в зависимости от входного отсчета х(пТ).

• Цифро-аналоговый преобразователь (ЦАП). Формирует цифро-аналоговый сигнал ~(^).

• Аналоговый сглаживающий фильтр низкой частоты (СФНЧ). С его помощью сигнал ~(^ преобразуется в аналоговый сигнал у(^.

При анализе дискретных сигналов удобно

пользоваться нормированным временем:

- * пТ

ґ = — = — = п Т Т

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

Основной математической моделью для описания систем ЦОС служит разностное

уравнение (уравнение вход-выход), связывающее входное воздействие х(п) и реакцию системы на него у(п):

N-1 М-1

УЮ^ЬЖп-Ъ-^а^п-Г), (1.2) *=0 1=1

где Ъ{ и а; — весовые постоянные коэффициенты. Конкретный набор коэффициентов определяет алгоритм обработки дискретного сигнала. Описываемая соотношением (1.2) цифровая система является линейной и называется линейной дискретной системой (ЛДС). Реализация ЛДС требует только трех операций:

• умножения на постоянный весовой коэффициент;

• суммирования;

• задержки на один период частоты дискретизации (сдвиг).

Поставив в соответствие каждой операции графический символ (рис. 3) и объединив их в соответствии с разностным уравнением, получим еще одну модель представления ЛДС — структурную схему. Пример реализации структурной схемы для разностного уравнения (РУ) вида

У

показан на рис. 4.

При изучении цифровых систем в качестве испытательных воздействий используются дискретные сигналы, называемые типовыми. Одним из примеров может служить цифровой единичный импульс и0(п-п0), равный единице при п = п0 и равный нулю при остальных значениях п:

, .Л » = »0

=

где п = 0, ±1, ±2, ±3., п0 — целочисленная константа.

Если ЛДС до момента поступления на вход единичного цифрового импульса и0(п-п0) находилась в нулевом состоянии, то есть х(п) = 0 и у(п) = 0 для всех п < По, тогда выходной сигнал у(п) (реакция системы на сигнал и0(п)) называется импульсной характеристикой Н(п) системы ЦОС (ИХ):

У

= й(и).

л<и)=и0(п),

д<я)=0и

у(п)=0 при п<п0

Функционирование ЛДС происходит по тактам, равным по длительности периоду дискретизации. Каждый такт включает несколько этапов:

1 этап — поступление текущего отсчета х(п) на вход системы ЦОС;

2 этап — вычисление текущего выходного отсчета у(п) в соответствии с выражением (1.2);

3 этап — поступление выходного текущего отсчета у(п) на выход системы ЦОС;

4 этап — формирование новой задержанной последовательности отсчетов х(п-г) и у(п-г) в соответствии с правилом:

х

На следующем такте процесс повторяется.

На рис. 5 показан процесс функционирования ЛДС, описываемой РУ (1.3), в случае

Начало

I

Инициализация исходных данных

Считывание из входного файла данные во входной буфер

Считывание текущего отсчета из входного буфера на вход ЛДС

Вычисление текущего выходного отсчета

Запись в выходной буфер текущего выходного отсчета

Формирование новой задержанной последовательности отсчетов

Запись в выходной файл данных из выходного буфера ,

Ю,

Конец

Рис. б. Блок-схема алгоритма функционирования ЛДС

поступления на вход единичного цифрового импульса, при условии п0 = 0 (см. (1.2)). Блок-схема алгоритма функционирования ЛДС показана на рис. 6.

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

Основные требования файловой модели просты и заключаются в следующем:

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

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

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

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

• Каждая функция располагается в отдельном файле и должна быть объявлена в заголовочном файле.

• Обмен данными между функциями осуществляется через указатель на контекстную структуру.

• Программный код функций основывается на базовых операторах языка С.

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

• Обработка массивов осуществляется через указатели на эти массивы, включенные в контекстную структуру.

• Функция таіп() не должна включать программный код, реализующий алгоритм цифровой обработки сигналов. Задача данной функции — заполнить входной буфер данными из файла, вызвать основную функцию алгоритма ЦОС и записать результат из выходного буфера в выходной файл.

Структура файловой модели показана на рис. 7.

Рассмотрим пример программной реализации цифрового устройства (в дальнейшем фильтра), удовлетворяющий требованиям файловой модели и описываемый нерекурсивным разностным уравнением:

ЛГ-1

у

Ы)

где х(п)— входной цифровой сигнал, у(п) — выходной цифровой сигнал, М — масштабирующий коэффициент, Ъ{ — коэффициенты фильтра.

Для генерации программного кода воспользуемся интегрированной средой разработки (ИСР) Visual Studio (VS) версии 5 и выше.

Необходимо запустить VS (рис. 8) и создать консольное приложение. Для этого в меню «File.» нужно выбрать команду «New.» (рис. 9). Появится окно, показанное на рис. 10. На вкладке «Projects» данного окна определяется тип приложения, его расположение на диске и задается имя.

Необходимо выбрать тип «Win32 Console Application», указать в окошке «Location» место размещения консольного приложения (в дальнейшем проекта), в окошке «Project name» ввести его имя и нажать кнопку «OK» (подтвердить ввод). В результате появится окно параметров создаваемого приложения.

Рис. 8. Вид запущенной ИСР VS

Рис. 9. Создание нового проекта

Рис. 10. Создание консольного приложения

Рис. 11. Параметры создаваемого проекта

Рис. 12. Информация о параметрах созданного проекта

Total Commander 6.01 - Informatec Ltda.

Файл Выделение Команды Сеть Вид Конфигурация Запуск

Jala _зс ___id _ie ^jg _jh ~Ji \ .. [programs] 1 155 968 Кб из 5 106

Документы I ‘Project filter I

d \Project\Cpp\std\CPP\filter\* *

Имя

Тип

t*I 1

_j[Debug]

Размер ІТДото

dsp

dsw

neb

<DIR> <DIR>

4 221 535 25 60U

30 03 2005 12 48 30 03.2005 12:52 30.03.2005 12.52 30 03 2005 12 52 30 03 2005 12:52

Рис. 13. Вид ИСР VS после создания проекта

Рис. 14. Служебные файлы консольного приложения

Все настройки необходимо выставить, как показано на рис. 11, и нажать кнопку «Finish». Появится информационное окно, сообщающее о параметрах созданного проекта (рис. 12). Необходимо убедиться, что проект создан с требуемыми характеристиками, и подтвердить ввод.

В результате будет создано консольное приложение, и вид ИСР VS изменится (рис. 13). Станет активным окно « WorcSpace» и в нем две вкладки: «ClassView» и «FileView». Необходимо перейти на вкладку «FileView», где показана структура проекта.

В директории, указанной в окошке «Location» при создании проекта, появится папка, в которой находятся служебные файлы проекта. Эти файлы определяют тип и структуру создаваемого программного кода (рис. 14). Данные файлы можно редактировать, только имея достаточный опыт в создании подобных проектов. На начальном этапе лучше оставить их без изменений.

После создания проекта к нему подключаются заголовочный файл (с расширением *.h) и файлы с функциями, реализующими алгоритм ЦОС (с расширением *.с или *.cpp). Эти файлы являются текстовыми и содержат исходный программный код алгоритма на языке С. Создать файлы можно в произвольном текстовом редакторе, а затем скопировать в папку с проектом и подключить. Или же файлы можно создать непосредственно в ИСР VS при помощи встроенного текстового редактора. В данной статье рассматривается второй вариант.

Для создания файлов с исходным текстом программного кода необходимо в меню «File» выбрать опцию «New» (рис. 9) и на вкладке «Files» указать тип файла (рис. 15).

Для заголовочного файла выбирают тип «С/С++ Header File», а для файлов с функциями — тип «C++ Source File». Затем необходимо указать место размещения файла в окошке «Location» (обычно это папка проекта) и имя файла в окошке «File name». Необходимо также установить флажок в позиции «Add to project» и убедиться, что в окошке, расположенном ниже флажка, имя проекта указано верно. Определив тип, место и имя файла, подтверждают ввод.

Рис. 15. Создание файлов с исходным текстом программного кода

Для реализации файловой модели алгоритма (рис. 16) необходимо создать следующие файлы:

• заголовочный файл <фШг.к>, содержащий объявления функций, массивов, пользовательских типов и т. д.;

• файл «ma.in.cpp» с функцией таіп() (соответствует блокам 0, 2, 3, 9 и 10 в блок-схеме на рис. 6);

• файл «іпй.ерр» с функцией начальной инициализации контекстной структуры (блок 1 на рис. 6);

• файл «гип.ерр» с функцией запуска алгоритма обработки входного буфера (блоки 4, 5, 7 на рис. 6);

*. filter - Microsoft Visual C++

File £d* View [nsert Project guild Tools Window Help

йізіио p - - mm* 31 31

I

^Jxj

155 Workspace 'filter' 1 protect

- filler files

B Source Files

O const , cpp

convolution cpp

^ imt cpp

J] main cpp

_^] run cpp

Fj _j Header Files

M\ filter h

I Resource Files

* S ClatsView I j[) FileView [

ML

Рис. 16. Вид ИСР VS после создания всех необходимых файлов проекта

• файл «сотоїміоп.срр» с функцией, выполняющей операции свертки и формирования новой задержанной последовательности (блоки 6, 8 на рис. 6);

• файл «сопй.срр», со всеми необходимыми глобальными константами, переменными, массивами и т. д.

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

После создания всех необходимых файлов ИСР У8 примет вид, показанный на рис. 16. Можно выбрать мышкой любой файл и в клиентской области ИСР У8 откроется область его редактирования, где необходимо набрать программный код (рис. 17). Вначале поле редактирования пусто. Необходимо набрать текст программного кода, как в обычном текстовом редакторе.

Для программной реализации алгоритма (1.7) необходимо определить тип контекстной структуры со следующими полями:

• указатель на входной буфер;

• указатель на выходной буфер;

• длина входного и выходного буферов;

• указатель на буфер линии задержки;

• указатель на буфер с коэффициентами фильтра;

• длина буферов линии задержки и коэффициентов.

Тип контекстной структуры определяется в файле <фкет.к» следующим образом:

typedef struct { word16 *pInpBuff; word16 *pOutBuff; word16 lenBuff; word16 *pSimplBuff; word16 *pCoeffBuff;

word16 lenFir;

І CONTEXTFILTER;

// Указатель на входной буфер // Указатель на выходной буфер // Длина входного и выходного буферов // Указатель на буфер линии задержки // Указатель на буфер с коэффициентами // фильтра

// Длина буферов линии задержки // и коэффициентов

Рис. U. Редактирование файла с исходным текстом программного кода

Затем в файле «сопвґ.срр» необходимо создать контекстную структуру данного типа и указатель на нее:

CONTEXTFILTER спіх; // Контекстная структура фильтра

CONTEXTFILTER* рСпіх; // Указатель на контекстную // структуру фильтра

Кроме того, в этом же файле создаются массивы:

• для входных данных;

• для выходных данных;

• для линии задержки;

• для буфера коэффициентов.

Создание массива происходит следующим

образом:

шоЫ16 inpBuff[LENBUFF]; // Массив для входных данных шоЫ16 outBuff[LENBUFF]; // Массив для выходных данных шоЫ16 51тр1В1^[Т Е^ТТ.ТЕЯ]; // Массив для линии задержки

Размеры массивов для входных и выходных данных, а также для линии задержки указываются через макросы, которые определяются в файле <фквг.к»:

#define LENBUFF 120 // Длина входного и выходного буферов #define LENFILTER 10 // Порядок (длина) фильтра

Кроме того, в этом же файле вводятся новые имена стандартных типов:

typedef int word32; typedef short word16; typedef char word8;

Этот шаг обусловлен тем, что разрядность стандартных типов часто зависит от используемого компилятора, а при разработке алгоритма для ЦСП необходимо строго контролировать разрядность переменных. При использовании новых имен для стандартных типов всегда точно известна разрядность переменных. Если необходимо использовать другой компилятор и там изменилась разрядность стандартных типов, то это можно легко компенсировать изменением нескольких строк программного кода в заголовочном файле.

Буфер коэффициентов непосредственно инициализируется при создании значениями коэффициентов:

word16 coeffBuffLF[]={1128, -2364, -2960, 4582, 14652, 14652, 4582, -2960, -2364, 1128};

Полный листинг файла «const.cpp»:

#іпс1^е «Шег.Ь»

///////////////////////////////////////////////////////////////////////////////////

// Создание констант /////////////////////////////////////////////////////////////////////////////////// CONTEXTFILTER спіх; // Контекстная структура фильтра CONTEXTFILTER* рСпіх; // Указатель на контекстную // структуру фильтра

///////////////////////////////////////////////////////////////////////////////////

// Создание массивов ///////////////////////////////////////////////////////////////////////////////////

шоЫ16 inpBuff[LENBUFF]; // Массив для входных данных;

шоЫ16 outBuff[LENBUFF]; // Массив для выходных данных;

шоЫ16 simp1Buff[LENFILTER]; // Массив для линии задержки;

// Массив для коэффициентов фильтра

шоЫ16 coeffBuff[] = {1128, -2364, -2960, 4582, 14652, 14652, 4582, -2960, -2364, 1128};

Инициализацию контекстной структуры выполняет функция инициализации initFilter():

#include «filter.h»

void initFilter(CONTEXTFILTER* pCntx){ // Создание локальных переменных word32 i; // Переменная цикла

pCntx->pInpBuff = inpBuff; pCntx->pOutBuff = outBuff; pCntx->lenBuff = LENBUFF; pCntx->pSimplBuff = simplBuff; pCntx->pCoeffBuff = coeffBuff;

pCntx->lenFilter = LENFILTER;

pCntx->mCoeff = MASHTAB;

// Указатель на входной // буфер

// Указатель на выходной // буфер

// Длина входного // и выходного буферов // Указатель на буфер // линии задержки // Указатель на буфер // с коэффициентами // фильтра

// Длина буферов линии // задержки и коэффици-// ентов

// Масштабирующий // коэффициент

// Очистка буфера линии задержки for(i = 0; i < pCntx->lenFilter; i++) { pCntx->pSimplBuff[i] = 0;

ІІ

Обработку буфера осуществляет функция

runFilter():

#include «filter.h»

void runFilter(CONTEXTFILTER* pCntx){

// Локальные переменные

word16* pInpBuff; // Указатель на входной буфер word16* pOutBuff; // Указатель на выходной буфер word16 lenBuff; // Длина входного и выходного буферов

word16* pSimplBuff; // Указатель на буфер линии задержки word16* pCoeffBuff; // Указатель на буфер

// с коэффициентами фильтра word16 lenFilter; // Длина, буферов линии задержки

// и коэффициентов word32 mCoeff; // Масштабирующий коэффициент

word32 count; // Переменная цикла

word32 coeff; // Вспомогательная переменная

// Инициализация локальных переменных pInpBuff = pCntx->pInpBuff; pOutBuff = pCntx->pOutBuff; lenBuff = pCntx->lenBuff; pSimplBuff = pCntx->pSimplBuff; pCoeffBuff = pCntx->pCoeffBuff; lenFilter = pCntx->lenFilter; mCoeff = pCntx->mCoeff;

// Цикл обработки входного буфера for(count = 0; count < lenBuff; count++){

// Чтение входного отсчета coeff = pInpBuff[count];

// Умножение на масштабирующий коэффициент coeff *= mCoeff;

// Нормирование результата coeff >>= 15;

// Запись в буфер задержанных отсчетов pSimplBuff[0] = (word16) coeff;

// Определение выходного отсчета

coeff = convolution(pSimplBuff, pCoeffBuff, lenFilter);

// Запись выходного отсчета pOutBuff[count] = (word16) coeff;

Функции инициализации контекстной структуры и обработки входного буфера должны быть объявлены в заголовочном файле следующим образом:

// Функция инициализации контекстной структуры extern void initFilter(CONTEXTFILTER* pCntx);

// Функция обработки входного буфера extern void runFilter(CONTEXTFILTER* pCntx);

Вычисления выходного отсчета осуществляет функция свертки convolution() (см. рис. 18):

#include «filter.h»

word16 convolution(word16* pSimplBuff, word16* pCoeffBuff, word16 lenFilter){

// Объявление локальных переменных

word16 coeffX;

word16 coeffB;

word32 count;

word32 coeff;

word32 sum;

// Инициализация локальных переменных sum = 0;

// Цикл обработки линии задержки for(count = 0; count < lenFilter; count++){

// Чтение текущего отсчета coeffX = pSimplBuff[count];

// Чтение соответствующего коэффициента фильтра coeffB = pCoeffBuff[count];

// Умножение отсчета на коэффициент coeff = coeffX * coeffB;

// Накопление результата sum += coeff;

}

// Сдвиг линии задержки

for(count = lenFilter — 1; count > 0; count--){

// Чтение предыдущего отсчета coeffX = pSimplBuff[count— 1];

// Запись предыдущего отсчета в текущую ячейку pSimplBuff[count] = coeffX;

}

// Нормирование результата суммирования sum >>= 15;

// Выход из функции return sum;

}

Данная функция также должна быть объявлена в заголовочном файле:

// Функция вычисления выходного отсчета extern word16 convolution(word16*, word16*, word16);

Окончательный вид заголовочного файла «filter.h» будет иметь следующий вид:

///////////////////////////////////////////////////////////////////////////////////

// Подключение внешних библиотек ///////////////////////////////////////////////////////////////////////////////////

#include <stdio.h> // Стандартная библиотека ввода/вывода

///////////////////////////////////////////////////////////////////////////////////

// Определение макросов ///////////////////////////////////////////////////////////////////////////////////

#define LENBUFF 120 // Длина входного и выходного буферов

#define LENFILTER 10 // Порядок (длина) фильтра

#define MASHTAB 28093 // Масштабирующий коэффициент // фильтра

///////////////////////////////////////////////////////////////////////////////////

// Определение новых имен стандартных типов

///////////////////////////////////////////////////////////////////////////////////

typedef int word32; typedef short word16; typedef char word8;

///////////////////////////////////////////////////////////////////////////////////

// Определение пользовательских типов ///////////////////////////////////////////////////////////////////////////////////

// Определение типа контекстной структуры фильтра typedef struct {

word16 *pInpBuff; // Указатель на входной буфер word16 *pOutBuff; // Указатель на выходной буфер word16 lenBuff; // Длина, входного и выходного буферов word16 *pSimplBuff; // Указатель на буфер линии задержки word16 *pCoeffBuff; // Указатель на буфер

// с коэффициентами фильтра word16 lenFir; // Длина буферов линии задержки

// и коэффициентов word32 mCoeff; // Масштабирующий коэффициент і CONTEXTFILTER;

///////////////////////////////////////////////////////////////////////////////////

// Объявление констант и массивов ///////////////////////////////////////////////////////////////////////////////////

extern CONTEXTFILTER cntx; // Контекстная структура // фильтра

extern CONTEXTFILTER* pCntx; // Указатель на контекстную // структуру фильтра

// Массив для входных данных; // Массив для выходных данных; // Массив для линии задержки; // Массив для буфера // коэффициентов;

// Массив для коэффициентов // фильтра

///////////////////////////////////////////////////////////////////////////////////

// Объявление функций ///////////////////////////////////////////////////////////////////////////////////

extern void initFilter(CONTEXTFILTER* pCntx); // Функция

// инициализации // контекстной // структуры

extern void runFilter(CONTEXTFILTER* pCntx); // Функция

// обработки // входного // буфера

extern word16 convolution(word16*, word16*, word16); // Функция

// вычисления // выходного // отсчета

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

extern word16 inpBuff[]; extern word16 outBuff[]; extern word16 simplBuff[]; extern word16 coeffBuff[];

extern word16 coeffBuffLF[];

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

#include «filter.h»

int main(void) {

// Обьявление переменных FILE *pInpFile; // Указатель на входной файл FILE *pOutFile; // Указатель на выходной файл word32 len; // Переменная цикла

// Открыть входной файл файлов для чтения pInpFile = fopen(«inp.dat», «rb»);

// Проверить корректность открытия входного файла if(pInpFile == NULL) {

printf(«ERROR, INPUT FILE!\n»); return 2;

і

// Открыть выходной файл файлов для записи pOutFile = fopen(«out.dat», «wb»);

// Проверить корректность открытия выходного файла if(pOutFile == NULL) {

printf(«ERROR, OUT FILE\n»); return 1;

і

// Инициализировать указатель на контекстную структуру pCntx = &cntx;

// Инициализировать контекстную структуру initFilter(pCntx);

// Инициализировать переменную цикла len = LENBUFF;

// Цикл обработки входного буфера while(len == LENBUFF) {

// Считать данные из файла

len = fread(pCntx->pInpBuff,sizeof(word16), LENBUFF, pInpFile);

// Вызвать функцию обработки входного буфера runFilter(pCntx);

// Записать данные в файл

fwrite(pCntx->pOutBuff, sizeof(word16), len, pOutFile);

і

// Закрыть входной и выходной файлы

fclose(plnpFile);

fclose(pOutFile);

// Выход из программы return 0;

і

В рассматриваемом примере фильтр соответствует требованиям:

• частота дискретизации — 8 000 Гц;

• полоса пропускания — 1 600 Гц;

• полоса задерживания — 2 400 Гц;

• отклонение в полосе пропускания — 1 дБ;

• отклонение в полосе задерживания-------20 дБ;

• число коэффициентов — 10;

• импульсная характеристика — симметричная, с четным количеством коэффициентов. Конкретные значения коэффициентов

фильтра могут быть рассчитаны, например, в программе MatLab или FD-3. Результат расчета в программе FD-3 показан в таблице. На рис. 19 и рис. 20 показаны расчетные амплитудно-частотная и импульсная характеристики фильтра.

В рассмотренной программной реализации используется целочисленное представ-

Таблица. Коэффициенты фильтра

№ п/п Коэффициент с фиксированной точкой Целочисленное представление коэффициентов х

0. 0,034449903800000 1128

1. -0,072134019699999 -2364

2. -0,090314822399999 -2960

3. 0,139855699899999 4582

4. 0,447167894399999 14652

5. 0,447167894399999 14652

6. 0,139855699899999 4582

7. -0,090314822399999 -2960

S. -0,072134019699999 -2364

9. 0,34449903800000 1128

M 0,857374999999999 28093

ление коэффициентов. Для этого необходимо все коэффициенты умножить на нормирующий множитель и округлить результат до целого значения. Чем большая точность представления коэффициентов требуется, тем большее значение нормирующего множителя необходимо устанавливать. В данном примере точность ограничивается 16 разрядами, поэтому нормирующий множитель равен 215-1, так как один разряд знаковый.

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

Все набранные в текстовом редакторе УБ файлы должны быть сохранены нажатием одной из двух (Н или И) клавиш на кнопочной панели.

Программный код, набранный в текстовом виде (файлы с расширением «.Ь» и «.срр»), необходимо преобразовать в исполняемый модуль (файл с расширением «.ехе»). Эта операция называется компиляцией, выполняется компилятором (в данном случае УБ) и включает два этапа:

• трансляция каждого файла с кодом программы (транслируются только файлы с расширением «.срр», заголовочные файлы подключаются автоматически за счет директивы «ШпсЫйе») в объектные модули (файлы с расширением «.оЬр>);

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

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

В VS определены три режима компиляции:

• трансляция отдельного файла и создание его объектного модуля;

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

• трансляция и линковка всех файлов, построение нового исполняемого модуля. Первый режим (рис. 21a) используется для

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

В результате компиляции создается исполняемый модуль (файл с расширением .exe), который находится в папке проекта «debug». Запустить файл на выполнение можно непосредственно из операционной системы. Но лучше всего сделать это в среде VS, нажав клавишу ш на кнопочной панели.

Рис. 19. Амплитудно-частотная характеристика фильтра

Рис. 20. Импульсная характеристика фильтра

filter - Microsoft Visual C + + - [filter.h]

Q Fie E# We» Insert Project Quid loots Window Help

Й ! eg В 0 I * Є  fl"*»»* С

guld fleer .еже

^ Workspace fief' 1 projects)" В fillet files

В £3 Source Fiw Л const cpp *1 convolution cpp 5] wtcpp

*1 mancpp J] runcpp В ^ Header Fies

ill lie hi ~1 Resource Fie*

B^tch Byrid... Ctgan

Start Qebug

Debugger Remote Connection,.,

J Execute Nter.exe Ctrt+F5

Set Active Configuration...

Configurations...

froHe...

Рис. 21. Компиляция программного кода

c

filter Microsoft Visual C + + [filter.hj

В Be ЕЛ View Insert Project Mi loots Window bet

|£) dUO 4jP :• c*|strenght

Workspace Tier' 1 protects) В Cp filter files

В 'bi SouceFtei [j^ const c<

*ll convoki nt cpp Д) maincp _*] runcpp В Header Fie; jfj fAeth 1 Resource Fi

Подключение внешних библиотек

4\' ‘‘E:\Igor\DiObCbK>Cvl"\nnnnDn\nEpCN?iii ян) яЕюуЕрьыиЕютрзшП  \OEp€N* 02 С]р1ЦЫК)...

Press any key to continue

Рис. 22. Запуск проекта на выполнение

Рис. 23. Выявление синтаксических ошибок

Рис. 24. Выявление ошибок линковки

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

На этом построение файловой модели можно считать завершенным. Однако немаловажную роль в разработке программного кода играет процесс отладки. Во-первых, необходимо постараться выявить все ошибки в коде. Во-вторых, необходимо оптимизировать программный код для выбранного ЦСП. Вторая задача будет подробно рассмотрена в третьей статье данного цикла. А сейчас ознакомимся с некоторыми способами поиска ошибок в программном коде.

В процессе создания программного кода могут возникнуть ошибки трех типов:

• синтаксические ошибки;

• ошибки линковки;

• ошибки выполнения.

Синтаксические ошибки вызваны несоответствием требованиям языка программирования. Определяются они на первом этапе компиляции. Если ошибка данного типа произошла, появляется информационное сообщение об ошибке и ее типе в одном из окон ИСР УБ (рис. 23). Необходимо навести курсор на описание выявленной ошибки и щелкнуть мышкой. Курсор переместится к месту выявленной ошибки в тексте программного кода. Однако нужно помнить, что компилятор иногда выставляет курсор не на ошибку, а на строку ниже. Поэтому необходимо проверить не только выделенную строку, но и предыдущие.

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

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

граммы. Они выявляются в процессе работы программы на ПК. К таким ошибкам можно отнести деление на ноль, использование созданной, но не инициализированной переменной, выход за пределы массива. Перечисленные ошибки обычно приводят к аварийному завершению выполнения программы (рис. 25). Для выявления таких ошибок используют запуск программы в режиме отладки. Этот режим задается нажатием клавиши  на кнопочной панели. Выполнение программы останавливается в месте сбоя. Данный вид ошибок самый сложный для устранения, так как выявление места сбоя не гарантирует наличия ошибки именно здесь. Ошибка может произойти в одном фрагменте кода, а проявиться при выполнении совершенно в другом.

В качестве метода поиска ошибки в данном случае можно предложить следующий алгоритм:

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

• Устранить все выявленные ошибки в проверяемой функции.

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

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

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

В рассматриваемом примере реализован цифровой фильтр на базе нерекурсивного разностного уравнения (2.1). Одним из его свойств является то, что его импульсная характеристика (ИХ) конечна и равна (с точностью до нормирующего множителя М) коэффициентам фильтра. Кроме этого, преобразование Фурье от ИХ для любого фильтра есть частотная характеристика (ЧХ). Модуль ЧХ — это амплитудно-частотная характеристика (АЧХ). Поэтому для контроля корректной работы фильтра можно в качестве тестового сигнала использовать единичный цифровой импульс, описываемый выражением (1.4), где п0 = 0. Данный сигнал равен единице при п = 0 и равен нулю при остальных значениях п. Реакция фильтра на него при нулевых

начальных условиях (у(п) и х(п) = 0, для п <0) есть ИХ, а модуль преобразования Фурье от ИХ — это АЧХ.

В начале необходимо сформировать входной сигнал, соответствующий (1.4). Это можно сделать самостоятельно, написав программу генерации файла бинарного типа, содержащего 16-разрядные (для рассматриваемого примера) целые числа. Программа не очень сложная даже для программиста невысокой квалификации. Но лучше воспользоваться специализированным ПО. В частности, автор пользуется программой EDSW. Скачать демо-версию этой программы можно с сайта www.dsp-sut.spb.ru. Программа не только позволяет сгенерировать файл с нужным сигналом, но и предоставляет инструменты для анализа результата. Пример работы программы показан на рис. 26. На левом верхнем графике изображен входной сигнал (единичный цифровой импульс), на левом нижнем — реакция (ИХ), на правом — модуль преобразования Фурье (АЧХ).

Сравнивая результат работы фильтра (рис. 26) с предъявленными требованиями (рис. 19 и рис. 20) видим, что отклонение в полосе пропускания несколько больше заданных требований. Это вызвано квантованием коэффициентов фильтра. Для устранения погрешности можно повысить точность представления коэффициентов фильтра, увеличить его порядок, или изменить структуру фильтра. В каждом конкретном случае решение принимает разработчик, исходя из условий конкретной задачи.

Проект рассмотренного примера можно скачать с сайта www.scanti.ru. Если вы пользуетесь каким-либо другим компилятором

Рис. 26. Анализ результата работы фильтра