Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
PIC32MZ и Быстрое Преобразование Фурье
Микроконтроллер от компании Microchip далеко не новый. Но пока нам дружественный Китай увы и ах, и такого (415 MIPS на частоте 252 MHz ядра) предложить не может. Ведущие производители микроконтроллеров в Китае - Artery, Nation Semiconductor и другие - освоили пока лишь производство аналогов младших семейств STM32 вплоть до Cortex-M4, и лишь подбираются к старшим семействам.
Быстрое Преобразование Фурье (далее по тексту БПФ) - широко используется в обработке сигналов, а потому является одним из важных критериев производительности для микроконтроллеров. Для ее тестирования использовался "PIC32MZ Embedded Connectivity with Floating Point Unit (EF) Starter Kit" (DM320007) на борту которого установлен микроконтроллер PIC32MZ2048EFH144 (DS60001320F). Среда разработки - MPLAB X IDE 6.0 (User Guide DS50002027) и компилятор MPLAB XC32 C/C++ (User Guide DS50001686) а таже MPLAB Code Configurator v5.0. Последний нужен для удобства и наглядности конфигурирования периферии.
Компилятор XC32 уже содержит в себе библиотеку DSP, функции которой описаны в документе ""32-bit Language Tools Libraries" (DS51685E), в главе 3. Таблица из этого документа приведена ниже.
Таблица 1. DSP функции библиотеки
Функции библиотеки используют форматы данных Q15, Q31. Непосредственно вычислению БПФ посвящены две функции - mips_fft16/mips_fft32. В использовании они идентичны, но работают с разными типами данных Q15/Q31 соответственно. Для их использования в проект достаточно подключить заголовочный файл dsplib_dsp.h
Прототип функции:
mips_fft16
{
int16c *dout // Выходной массив 16-битных комплексных чисел размера N
int16c *din // Входной массив 16-битных комплексных чисел размера N
int16c *fftc // Входной массив 16-битных комплексных чисел размера N/2,
// который содержит коэффициенты для расчета БПФ
int16c *scratch // Массив 16-битных комплексных чисел размера N,
// нужен для промежуточных расчетов
int16 log2N // Размер данных в виде LOG по основанию 2 числа N
// Для 1024 точек это будет 10, для 2024 точек - 11 и т.д.
}
Тип данных int16c определен в заголовочном файле как
Среда разработки уже содержит готовые массивы коэффициентов:
fft16c64 ... fft16c2048, которые определены в заголовочном
файле "fftc.h" поэтому можно использовать их. Для этого
надо включить его в проект и определить fftc как массив констант:
#include "fftc.h"
#define fftc fft16c64 // from fftc.h, for N = 64
Для ускорения работы, можно создать отдельный массив в оперативной памяти и скопировать константы туда. У меня это дало лишь небольшой (до 5%) прирост производительности при расчете БПФ.
Можно также использовать функции mips_fft16_setup/mips_fft32_setup. Они рассчитывают массив коэффициентов для произвольного N и по времени выполняются примерно столько же, как и сам расчет БПФ. Видимо поэтому они помечены как устаревшие (deprecated). Потому как проще и быстрее рассчитать один раз массив коэффициентов и потом использовать его.
Тем не менее, при использовании эту функцию достаточно вызвать всего один раз.
Время вычисления вышеупомянутых функций, которое получено мной экспериментальным путем, приведено в Таблице 2 ниже.
Таблица 2. Время выполнения функций.
Примечательно, что PIC32MZ позволяет рассчитывать БПФ гораздо больших размеров, чем 4096 точек - пока хватает памяти микроконтроллера.
Аналогичные данные приведены в работе Steve Hageman'а "FFT's meet 200MHz, PIC32MZ Microprocessors ", которая датирована 25.04.2016, но для частоты ядра 200 МГц. Мне воспроизвести их на этой частоте ядра не удалось, получилось на 10-25% хуже.
Время выполнения функций вычислялось с помощью таймера ядра, который работает на частоте в 2 раза ниже тактовой. В системе он работает по умолчанию, его не надо специально инициализировать. Так, при частоте ядра 200 МГц с его помощью можно получить разрешение 0,01 мкс. Для этого в начале таймер сбрасывается в ноль командой __CP_CLEAR();. После выполнения фукции, его значение считывается функцией __CP_GET(); и нормируется к тактовой частоте.
Для получения корректных результатов БПФ, на входные данные необходимо наложить оконную функцию. Наиболее широко используются функции Hann'a, Hamming'a, Blackmana. Поскольку они все симметричные, для наложения окна на входные данные размером N достаточно массива коэффициентов размера N/2. Отдельной функции для этих целей библиотека DSP не предоставляет.
Используя перемножение целых 16-ти разрядных чисел, при частоте ядра 252 МГц, наложение окна на комплексный массив из 2048 точек занимает 252 мкс.
Выходной массив данных после БПФ содержит комплексные числа (Re, Im), из которых обычно рассчитывают амплитуду векторов и их фазу. Амплитудный спектр затем обычно пересчитывают в логарифмическую шкалу. Так как амплитуда вектора обычно пропорциональна напряжению, то для расчета мощности используется формула 20LOG(V). Для упрощения вычислений достаточно сначала рассчитать суммы квадратов Re, Im составляющих. Такая операция для частоты ядра 252 МГц и массива из 2048 точек занимает 120 мкс. Квадратный корень мы тут не извлекаем, но для расчета спектральной мощности используем формулу 10LOG(A). Вычисление логарифма от того же массива математической функцией LOG10 занимает 1934 мкс, что существенно. Если большая точность не нужна, то гораздо быстрее использовать таблицу значений логарифма, скажем из 1000 ячеек. Табличный подход хорошо работает и для расчета arctg().
Полагаю, приведенные данные могут полезны широкому круг инженеров ЦОС.