6
votes

I am trying to derive the frequencies of an audio signal using the FFT library available for the PIC32MZ2064DAB176.

I am using the MPLAB Harmony for configuration.

For the sake of testing, two sine waves of frequencies 1002 Hz and 750 Hz are being used. This is done with the help of an online tone generator tool. I have 1002 Hz on one browser window and 750 Hz on another browser window. The output from the audio O/P jack is fed to the microcontroller ADC after a DC bias.

After performing a DC bias of 1.6 V the signal is sent to the 12-bit ADC. The maximum voltage I am expecting is a 3 V P-P, so I guess a DC bias of 1.6 V will suffice.

The signals are sampled at 48 kHz since I will need to read frequencies up to 20 kHz.

The FFT is a 1024 point FFT.

I am able to get the DC value in the 0th index of the frequency bin.

The formula being used to get the frequency value from the bin is Frequency = index * Sampling Frequency / Number of FFT Points

However, I am getting a high magnitude always in the 1st and the 2nd frequency bins for any value of the input frequency. According to my understanding, for 1002 Hz, the amplitude should be high around the 21st index of the frequency bin and for the 750 Hz signal, the amplitude should be high at around 16th index.

I am enclosing my code, the ADC Harmony configuration screenshot, the result screenshot and the signal input screenshot.

In the code, the array used for the frequency bin is "singleSidedFFT"

Any help in deriving the correct frequency value is greatly appreciated.

    /* 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;
}

I have also tried writing a stand alone FFT function,. The result is the same. Here it is..

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 ADC Config

ADC Frequency Bin

Input Signal

FFT Result 2

Thanks a lot.

1
If you have a large DC value and you are not applying a window function then as well as a high magnitude in bin 0 you'll get a "skirt" from DC up through a number of low frequency bins due to spectral leakage. If you plot the magnitude spectrum you should see better what's going on.Paul R
Oh, I love these kind of questions. However, you might find a better luck on electronics.stackexchange.com due to specifics.Andrejs Cainikovs
Surely you would have looked in all the bins to see the whole spectrum? Clearly you are looking in the wrong ones.Clifford
@Clifford, Yes, I have had a look at all the frequency bins. They are all single digits. Only the bins 1 and 2 have a high valueTed
Solved. The confusion is because of the PIC datasheet. The PIC32MZDA family uC datasheet, the setting for FRC in the ADCSEL<1:0> is 0x11. However in the ADC specific datasheet (DS60001344B) for the uc, the setting for FRC is 0x01. With the value set to 0x11, the sampling frequency was 625 kHz. With the value set to 0x01, the sampling rate is as per requirement which is 48 kHz. Strangely the MPLAB Harmony configurator is using the register values as mentioned in the family datasheet. I am now able to get the indices of the frequencies. Thanks a lot for all the feedback and suggestions :-)Ted

1 Answers

2
votes

Ted uncovered an inconsistency between the datasheet of the microcontroller PIC32MZ Graphics (DA) Family and the version B of the specifications of 12 bit Successive Approximation Register (SAR) Analog-to-Digital Converter (ADC)

In both cases the clock source driving the sampling rate of the ADC is controlled by bits ADCSEL<1:0> of the register ADCCON3.

The datasheet, on page 452, gives the following clock sources:

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

On the contrary, the specifications of the ADC, in version B, on page 14 are:

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

At the same point, version D of the specifications states that:

Refer to the “12-bit High-Speed Successive Approximation Register (SAR)” chapter in the specificdevice data sheet for the ADC Clock source selections.

The MPLAB Harmony ADC configurator complies with this disposition. Nevertheless, adopting the clock settings of the version B solved the sampling issue, suggesting that the family datasheet is not correct.

The sampling rate may also be affected by:

  • CONCLKDIV<5:0> ofADCCON3` : control clock divider
  • ADCDIV<6:0> of ADCxTIME : additionnal divider to define the clock source of indiviual ADC. Or alternatively ADCDIV<6:0> ofADCCON2` for shared ADC.
  • ADCxTIME<9:0> or ADCCON2<25:16>: number of clock ticks.

As the sampling was much higher than expected (625 kHz against 48kHz), the length of the frame (1024 samples = 0.0016s) was comparable to the periods of the input signal (about 1kHz). Most of the amplitude was therefore stored in the first bins of the DFT and applying a window does not solves the problem.

Once the sampling rate is corrected, the DFT features maxima corresponding to the frequencies or the input signal. These frequencies can be accurately identified by applying a window and estimating the frequency of a peak as its mean frequency wih respect to power density