Модуль main.c
/******************************************************************************
*
* УЧЕБНЫЙ ПРОЕКТ
*
* Фильтр с конечной импульсной характеристикой (FIR)
* Фильтр с бесконечной импульсной характеристикой (IIR)
*
* Блочная обработка с использованием функций библиотеки CMSIS DSP Library
*
* Copyright (C) 2015 МИРЭА
* Версия: 1.3
*
******************************************************************************/
/*
ОРГАНЫУПРАВЛЕНИЯ
Джойстик "вверх/вниз" - увеличение/уменьшение частоты генератора синусоидального сигнала
Джойстик "влево" - переключение между типами входного сигнала: синусоидальный / шум / нет сигнала
Джойстик "вправо" - переключение форматов входного сигнала: аналоговый (через АЦП) / цифровой код
Джойстик "выбор" - переключение типов фильтров: КИХ / БИХ.
Кнопка TAMPER - вывод меток времени обработки
*/
/******************************************************************************
ФОРМАТ ЗАПИСИ КОЭФФИЦИЕНТОВ
Для КИХ-фильтра:
{ b0, b1, b2,..., bN }, где N - порядок фильтра; numTaps = N+1 - число коэффициентов
При использовании 16-разрядного формата с фиксированной точкой число коэффициентов должно быть четное
Для БИХ-фильтра:
{ b10, b11, b12, a11, a12, b20, b21, b22, a21, a22,...},
первый индекс - номер звена (каскада) 2-го порядка,
второй индекс - номер коэффициента (5 коэффициентов для одного звена),
по умолчанию коэффициенты a10, a20,... всегда равны 1 и в массив не включаются
*/
#include "stm32_p407.h" //Файл конфигурации отладочной платы STM32-P407
#include "adcdac.h" //Объявления функций для работы с периферийными устройствами
#include "gauge.h" //Процедуры управления и измерения
#include "arm_math.h" //Определения и функции библиотеки CMSIS DSP Library
#include "mathfunc.h" //Прототипы вспомогательных математических функций
#include "values.h" //Числовые значения исходных операндов
|
#include "fir_coef.h" //Значения коэффициентов FIR-фильтра
#include "iir_coef.h" //Значения коэффициентов и усилений IIR-фильтра
//---------------------------------------------------------------------------
// ОБЪЯВЛЕНИЯ ДАННЫХ
// Следующие параметры объявлены в подключаемых файлах коэффициентов:
// numTaps - Число отсчетов/коэффициентов FIR-фильтра (константа)
// FIR_coef[] - Массив коэффициентов FIR-фильтра в формате float
// numStages - Число звеньев IIR-фильтра (константа)
// IIR_coef[] - Массив коэффициентов IIR-фильтра в формате float
// IIR_gain[] - Массив коэффициентов усилений для звеньев IIR-фильтра в формате float
#define FLOAT_PROC //Определение для использования при обработке сигнала
// данных с плавающей точкой
#define BlockSize 1024 //Размер блока обрабатываемых отсчетов
#ifndef FLOAT_PROC //В а р и а н т обработки с фиксированной точкой
q15_t Coeffs[numTaps]; //Буфер 16-разрядных коэффициентов в дробном формате 1.15
int16_t DataIn0[BlockSize]; //Буферы для входных отсчетов
int16_t DataIn1[BlockSize];
int16_t DataOut0[BlockSize]; //Буферы для выходных отсчетов
int16_t DataOut1[BlockSize];
int16_t *DataIn, *DataOut; //Текущие указатели на буферы
arm_fir_instance_q15 SF; //Структура для обработки данных FIR-фильтра
q15_t StateF[numTaps+BlockSize]; //Рабочий массив для обработки данных FIR-фильтра
#else //В а р и а н т обработки с плавающей точкой
float32_t DataIn0[BlockSize]; //Буферы для входных отсчетов
float32_t DataIn1[BlockSize];
float32_t DataOut0[BlockSize]; //Буферы для выходных отсчетов
float32_t DataOut1[BlockSize];
float32_t *DataIn, *DataOut; //Текущие указатели на буферы
arm_fir_instance_f32 SF; //Структура для обработки данных FIR-фильтра
float32_t StateF[numTaps+BlockSize-1]; //Рабочий массив для обработки данных FIR-фильтра
|
float32_t StateI[4*numTaps]; //Рабочий массив для обработки данных IIR-фильтра
arm_biquad_casd_df1_inst_f32 SI; //Структура для обработки данных IIR-фильтра
float32_t GainScale; //Коэффициент масштабирования отсчетов для IIR-фильтра
#endif
int32_t DataIndex; //Индекс текущего входного/выходного отсчета
int32_t TransferBuf; //Номер (0 или 1) буфера для текущего ввода-вывода
int32_t ProcBuf; //Номер (0 или 1) обрабатываемого (обработанного) буфера
int16_t DataChannel1, DataChannel2; //Входные и выходные отсчеты в формате 1.15 с фиксированной точкой
int16_t DataADC; //Прямой отсчет с АЦП для системы калибровки
int32_t AmplOffset = 0; //Смещение нулевого уровня сигнала с АЦП
int32_t ProcessMode = 0; //Режим обработки: 0 - FIR-фильтр, 1 - IIR-фильтр
int32_t HardwareMode = 0; //Конфигурация системы:
// 0: цифровой генератор -> ЦАП--(анал. сигнал)--> АЦП -> обработка
// 1: цифровой генератор -> обработка
//---------------------------------------------------------------------------
// ГЛАВНАЯ ФУНКЦИЯ
int main()
{
//Счетчик для реализации периода изменения тестовой индикации
volatile uint32_t i = 0;
//Инициализация цифровой обработки сигнала
#ifndef FLOAT_PROC //В а р и а н т обработки с фиксированной точкой (только FIR)
for (i = 0; i < numTaps; i++) // Преобразование формата коэффициентов из float в дробный 1.15
Coeffs[i] = float_to_q15(FIR_coef[i]);
arm_fir_init_q15(&SF, numTaps, Coeffs, StateF, BlockSize);
#else //В а р и а н т обработки с плавающей точкой (FIR и IIR)
arm_fir_init_f32(&SF, numTaps, FIR_coef, StateF, BlockSize);
arm_biquad_cascade_df1_init_f32(&SI, numStages, IIR_coef, StateI);
for (i = 0, GainScale = 1.f; i < numStages; i++) GainScale *= IIR_gain[i];
#endif
//Задание 4-х уровней групповых приоритетов и 4-х уровней приоритетов в каждой группе
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//Инициализация тестового индикатора
STM_LEDInit(LED1);
STM_LEDInit(LED2);
|
//Инициализация кнопок
STM_PBInit(BUTTON_TAMPER, BUTTON_MODE_GPIO);
STM_PBInit(BUTTON_WAKEUP, BUTTON_MODE_GPIO);
//Инициализация кодека (ЦАП выходных отсчетов сигнала)
SoundCodecConfig(FS_FIR_IIR); //Аргумент - частота дискретизации, Гц
SoundCodecInterruptConfig(ENABLE);
//Инициализация АЦП
ADCConfig();
//Инициализация генератора входного сигнала
SignalGenConfig();
//Инициализация графического интерфейса, джойстика, системы измерений
GgInterfaceInit();
//ОСНОВНОЙ ЦИКЛ
while (1)
{
if (TransferBuf!= ProcBuf) //Ожидание заполнения буфера
{
TimeMarker3(); //Метка времени выполнения № 3 (НАЧАЛО ОБРАБОТКИ)
DataIn = ProcBuf? DataIn1: DataIn0; //Установка указателей на обрабатываемые буферы
DataOut = ProcBuf? DataOut1: DataOut0;
#ifndef FLOAT_PROC //В а р и а н т обработки с фиксированной точкой
arm_fir_q15(&SF, DataIn, DataOut, BlockSize); //Только FIR-фильтр
#else //В а р и а н т обработки с плавающей точкой
if (!ProcessMode)
arm_fir_f32(&SF, DataIn, DataOut, BlockSize); //FIR-фильтр
else
{ //IIR-фильтр
arm_scale_f32(DataIn, GainScale, DataIn, BlockSize); //Масштабирование отсчетов
arm_biquad_cascade_df1_f32(&SI, DataIn, DataOut, BlockSize); //Фильтрация
}
#endif
ProcBuf ^= 1; //Переключение на другой буфер
TimeMarker4(); //Метка времени выполнения № 4 (КОНЕЦ ОБРАБОТКИ)
}
GgControl(); //Управление частотой и амплитудой генератора сигнала,
// вывод результатов измерений
if (i++ == 0x800) STM_LEDOff(LED1); //Тестовое управление индикатором
if (i == 0x50000) STM_LEDOn(LED1), i = 0;
//Проверка нажатия кнопки WAKEUP завершения программы
if (STM_PBGetState(BUTTON_WAKEUP)) NVIC_SystemReset();
}
//ОКОНЧАНИЕ ОСНОВНОГО ЦИКЛА
}
//---------------------------------------------------------------------------
// ОБСЛУЖИВАНИЕ ПРЕРЫВАНИЯ С ЧАСТОТОЙ ДИСКРЕТИЗАЦИИ
// Данная подпрограмма вызывается из обработчика прерывания..._IRQHandler(),
// реализованного в adcdac.c
// Частота дискретизации формируется модулем процессора SPI/I2S,
// посредством которого по интерфейсу I2S отсчеты сигнала выводятся на внешний звуковой кодек.
// Этот модуль также генерирует прерывания с частотой дискретизации. В обработчике
// прерывания, размещенном здесь, производятся следующие операции:
// - считываются отсчеты входного сигнала с АЦП или цифрового входа;
// - АЦП запускается на следующий цикл преобразования;
// - входные отсчеты размещаются в одном из входных буферов;
// - из выходного буфера с тем же номером извлекаются обработанные отсчеты и передаются кодеку;
// - проверяется заполнение текущего буфера и при необходимости производится их переключение.
void Sample_Handler(void)
{
extern int16_t GeneratorSamples; //Цифровой выход генератора сигналов (в доп. коде)
// параметр объявлен в adcdac.c
if (HardwareMode == 0)
{ //Получение данных с АЦП, преобразование смещенного кода в дополнительный,
// коррекция нулевого уровня
DataADC = ADC_GetConversionValue(ADC1) ^ 0x8000;
DataChannel2 = DataChannel1 = DataADC - AmplOffset;
}
//Получение цифрового отсчета входного сигнала (минуя АЦП)
else DataChannel2 = DataChannel1 = GeneratorSamples;
//Перезапуск АЦП
ADC_SoftwareStartConv(ADC1);
//Сохранение входных отсчетов в текущем буфере, чтение выходных отсчетов
#ifndef FLOAT_PROC //В а р и а н т обработки с фиксированной точкой
if (TransferBuf)
{ DataIn1[DataIndex] = DataChannel1;
DataChannel1 = DataOut1[DataIndex++];
}
else
{ DataIn0[DataIndex] = DataChannel1;
DataChannel1 = DataOut0[DataIndex++];
}
#else //В а р и а н т обработки с плавающей точкой
if (TransferBuf)
{ DataIn1[DataIndex] = q15_to_float(DataChannel1);
DataChannel1 = float_to_q15(DataOut1[DataIndex++]);
}
else
{ DataIn0[DataIndex] = q15_to_float(DataChannel1);
DataChannel1 = float_to_q15(DataOut0[DataIndex++]);
}
#endif
//Проверка достижения конца буфера, если да, переключение на другой буфер
if (DataIndex >= BlockSize) DataIndex = 0, TransferBuf ^= 1;
DataChannel2 = DataChannel1;
//Обработанные значения - в DataChannel1, DataChannel2
}
Пример файла коэффициентов iir_coef.h
#define numStages 4 //Число звеньев второго порядка
float IIR_coef[numStages*5] =
{
1.0000000000000000f, -0.8508242964744567f, 1.0000000000000000f, 1.2679487466812134f, -0.9085670709609985f,
1.0000000000000000f, -1.7376896142959595f, 1.0000000000000000f, 1.4469995498657227f, -0.9191916584968566f,
1.0000000000000000f, -1.1725169420242310f, 1.0000000000000000f, 1.2251321077346802f, -0.9785795807838439f,
1.0000000000000000f, -1.6086072921752930f, 1.0000000000000000f, 1.5572113990783691f, -0.9830712080001831f
};
float IIR_gain[numStages] =
{
0.2331655323505401f,
0.2331655323505401f,
0.7605150938034057f,
0.7605150938034057f
};
Пример файла коэффициентов FIR фильтра см. в предыдущем проекте.
Формирование файлов коэффициентов по результатам синтеза в среде MATLAB
Модуль fir_coef.m
% Формирование заголовочного файла с коэффициентами FIR-фильтра
% из массива Num, экспортируемого из оболочки Filter Design
fname = 'D:\Arm\Work\DspLab_fir_iir\fir_coef.h'; % Имя файла по умолчанию
fid = fopen(fname,'r');
yn = 1;
if fid > 0
fclose(fid);
yn = input('Перезаписать существующий файл? Введите 1 (да) или иное (нет): ');
end;
nt = length(Num);
if yn == 1 && nt > 0
if bitand(nt, 1) % Приведение числа коэффициентов к четному значению
nt = nt + 1; Num(nt) = 0;
end;
fid = fopen(fname,'w');
fprintf(fid, '\n#define numTaps %d\n\n', nt);
fprintf(fid, 'float FIR_coef[numTaps] =\n{\n ');
j = 0;
for i = 1: nt - 1
fprintf(fid, ' %1.16ff,', Num(i));
j = j + 1;
if (j >= 4) j = 0; fprintf(fid, '\n ');
end;
end;
fprintf(fid, ' %1.16ff\n};\n', Num(nt));
fclose(fid);
fprintf(1, 'Файл %s создан\n', fname);
end;
Модуль iir_coef.m
% Формирование заголовочного файла с коэффициентами IIR-фильтра
% из массивов G, SOS, экспортируемых из оболочки Filter Design.
% Матрица SOS имеет размер n x 6 (n - число секций),
% коэффициенты в строке: b0, b1, b2, 1, a1, a2.
% Для DSP требуется 5 коэффициентов: b0, b1, b2, -a1, -a2
fname = 'D:\Arm\Work\DspLab_fir_iir\iir_coef.h'; % Имя файла по умолчанию
fid = fopen(fname,'r');
yn = 1;
if fid > 0
fclose(fid);
yn = input('Перезаписать существующий файл? Введите 1 (да) или иное (нет): ');
end
[ns, nc] = size(SOS);
if (yn == 1 && ns > 0 && nc == 6)
fid = fopen(fname,'w');
fprintf(fid, '\n#define numStages %d\n\n', ns);
fprintf(fid, 'float IIR_coef[numStages*5] =\n{\n ');
for i = 1: ns
for j = 1: 3
fprintf(fid, ' %1.16ff,', SOS(i,j)); %b0, b1, b2
end
fprintf(fid, ' %1.16ff,', -SOS(i,5)); %-a1
fprintf(fid, ' %1.16ff', -SOS(i,6)); %-a2
if i ~= ns fprintf(fid, ',\n ');
else fprintf(fid, '\n');
end
end
fprintf(fid, '};\n');
fprintf(fid, '\nfloat IIR_gain[numStages] =\n{\n ');
for i = 1: ns - 1
fprintf(fid, ' %1.16ff,\n ', G(i));
end
fprintf(fid, ' %1.16ff\n};\n', G(ns));
fclose(fid);
fprintf(1, 'Файл %s создан\n', fname);
end