Решено: FFT частотные бункеры и PIC32 - PullRequest
5 голосов
/ 12 марта 2019

Я пытаюсь получить частоты аудиосигнала, используя библиотеку FFT, доступную для PIC32MZ2064DAB176.

Я использую для настройки MPLAB Harmony.

Для тестирования,используются две синусоидальные волны с частотами 1002 Гц и 750 Гц.Это делается с помощью онлайн-генератора тона.У меня 1002 Гц в одном окне браузера и 750 Гц в другом окне браузера.Выход с аудиоразъема O / P подается на АЦП микроконтроллера после смещения постоянного тока.

После выполнения смещения постоянного тока 1,6 В сигнал отправляется на 12-разрядный АЦП.Максимальное напряжение, которое я ожидаю, составляет 3 В PP, поэтому я предполагаю, что смещения постоянного тока будет 1,6 В.

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

БПФ - это БПФ с 1024 точками.

Я могу получить значение постоянного тока в 0-м индексе частотного бина.

Формула, используемая для получения значения частоты из ячейки: Frequency = index * Частота выборки / Количество точек FFT

Однако я получаю высокую величину всегда в 1-й и2-ые ячейки частоты для любого значения входной частоты.Насколько я понимаю, для 1002 Гц амплитуда должна быть высокой около 21-го индекса частотного бина, а для сигнала 750 Гц амплитуда должна быть высокой около 16-го индекса.

Я прилагаю свой код, снимок экрана конфигурации ADC Harmony, снимок экрана результата и снимок входного сигнала.

В коде массив, используемый для частотного бина, - это "singleSidedFFT"

Любая помощь в получении правильной частотызначение высоко ценится.

    /* FFT */
#define N 1024// Also change the log2N variable below!!
#define SAMPLE_FREQ 48000
#define PI 3.14

// Section: Global Data Definitions
APP_DATA appData;

/* ADC */
long count = 0;

/* FFT */
int16c  fftCoefs[N];
int16c *fftc;
int log2N = 10; 
extern const int16c twiddleFactors[];
long int freqVector[N];
int16c sampleBuffer[N]; //initialize buffer to collect samples
long int singleSidedFFT[N];


void APP_Tasks ( void )
{
    /* Check the application's current state. */
    switch ( appData.state )
    {
        /* Application's initial state. */
        case APP_STATE_INIT:
        {
            bool appInitialized = true;

            if (appInitialized)
            {
                int i;
                fftc = &fftCoefs; /* Stores the twiddle factors */

                // zero the freqVector and singleSidedFFT
                for (i=0; i<N; i++)
                {
                    freqVector = 0;
                    singleSidedFFT = 0;
                    sampleBuffer.re = 0;
                }

                // generate frequency vector this is the x-axis of your single sided fft
                for (i=0; i<N; i++)
                {
                    freqVector = i*(SAMPLE_FREQ/2)/((N/2) - 1);
                }

                /* Calculate the twiddle factors */
                DSP_TransformFFT16_setup(fftc, log2N);
                appData.state = APP_STATE_SERVICE_TASKS;

            }
            break;
        }

        case APP_STATE_SERVICE_TASKS:
        {
            /* Trigger a conversion */
            ADCCON3bits.GSWTRG = 1;

            /* Wait the conversions to complete */
            while (ADCDSTAT1bits.ARDY2 == 0);

            if (count < N)
            {
                sampleBuffer[count].re = ADCDATA2; /* fetch the result */
                sampleBuffer[count].im = 0;
                count++;
            }
            else
            {
                appData.state = APP_STATE_COMPUTE_FREQ;
                count = 0;
            }

            break;
        }

        case APP_STATE_COMPUTE_FREQ:
        {
            APP_ComputeFreq();
            appData.state = APP_STATE_SERVICE_TASKS;
            break;
        }
    }
}


void APP_ComputeFreq(void)
{
    int i;
    int16c dout[N]; //holds computed FFT 
    int16c scratch[N];

    // load complex input data into din
    DSP_TransformFFT16(dout, sampleBuffer, fftc, scratch, log2N);

    // compute single sided fft
    for(i = 0; i < N/2; i++)
    {
        singleSidedFFT = sqrt((dout.re*dout.re) + (dout.im*dout.im));
    }

    LATAbits.LATA6 = ~LATAbits.LATA6;
}

Я также попытался написать отдельную функцию FFT ,.Результат тот же.Вот оно ..

void APP_ComputeFreq_2(void)
{
    int16_t k, t;
    for (k = 0; k < N; k++) 
    { 
        // For each output element
        int16_t sumreal = 0;
        int16_t sumimag = 0;

        for (t = 0; t < N; t++) 
        { 
            // For each input element
            double angle = 2 * M_PI * t * k / N;
            sumreal += sampleBuffer[t].re * cos(angle) + sampleBuffer[t].im * sin(angle);
            sumimag += -sampleBuffer[t].re * sin(angle) + sampleBuffer[t].im * cos(angle);
        }
        singleSidedFFT[k] = sqrt((sumreal * sumreal) + (sumimag * sumimag));
    }
}

Конфигурация АЦП MPLAB Harmony

Корзина частоты АЦП

Входной сигнал

FFT Result 2

Большое спасибо.

1 Ответ

1 голос
/ 19 марта 2019

Тед обнаружил несоответствие между таблицей данных микроконтроллера семейства PIC32MZ Graphics (DA) и версии B спецификаций 12 бит Аналого-цифровой преобразователь (АЦП) регистра последовательных приближений (SAR)

В обоих случаях тактовый источник, управляющий частотой дискретизации АЦП, управляется битами ADCSEL<1:0> регистра ADCCON3. Таблица данных на странице 452 содержит следующие источники синхронизации:

11 = FRC
10 = REFCLK3
01 = System Clock (Tcy)
00 = PBCLK3

Напротив, технические характеристики АЦП в версии B на стр. 14 :

11 = System Clock (TCY)
10 = REFCLK3
01 = FRC Oscillator output
00 = Peripheral bus clock (PBCLK)

В то же время версия D спецификаций утверждает, что:

См. Главу «12-битный регистр высокоскоростного последовательного приближения (SAR)» в листе данных конкретного устройства для выбора источника синхронизации АЦП.

Конфигуратор АЦП MPLAB Harmony соответствует этому положению. Тем не менее, принятие настроек часов версии B решило проблему выборки, предполагая, что таблица данных Familly не верна.

На частоту дискретизации также могут влиять:

  • CONCLKDIV<5:0> of ADCCON3`: делитель тактовых импульсов
  • ADCDIV<6:0> из ADCxTIME: дополнительный делитель для определения источника синхронизации отдельного АЦП. Или альтернативно ADCDIV<6:0> of ADCCON2` для общего АЦП.
  • ADCxTIME<9:0> или ADCCON2<25:16>: количество тактов.

Поскольку выборка была намного выше, чем ожидалось (625 кГц против 48 кГц), длина кадра (1024 выборки = 0,0016 с) была сопоставима с периодами входного сигнала (около 1 кГц). Поэтому большая часть амплитуды была сохранена в первых ячейках DFT, и применение окна не решает проблему.

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

...