Can you explain the following FFT code ?
I've following code I got from the internet that does FFT calculation on ADC input. Could you explain the code in simple terms what it does and how the input and outputs are defined so I can understand how the code is written to edit it to my use case. Specially the ADC part of the code.
/*
* File: main.c
* Author: irmun
*
* Created on November 15, 2019, 12:52 PM
*/
#include <xc.h>
#include <p30f4013.h>
#include <dsp.h>
#define FFT_BLOCK_LENGTH 256
#define LOG2_BLOCK_LENGTH 8
#define SAMPLING_RATE 10000
unsigned long counter;
fractcomplex sigCmpx[FFT_BLOCK_LENGTH] __attribute__ ((space(ymemory)));
fractcomplex twiddleFactors[FFT_BLOCK_LENGTH/2] __attribute__ ((space(xmemory)));
fractional SquareMagnitude [FFT_BLOCK_LENGTH/2];
int peakFrequencyBin = 0; /* Declare post-FFT variables to compute the */
unsigned long peakFrequency = 0; /* frequency of the largest spectral component */
void start_timer(unsigned int time_value){
T1CONbits.TON = 0;
TMR1 = 0;
PR1 = time_value;
T1CONbits.TON = 1;
return;
}
fractional getADCValue(){
unsigned int ADCValue;
ADCON1bits.SAMP = 1;
while(!ADCON1bits.DONE); //checks if conversion is done
ADCValue = ADCBUF0;
return ADCValue - 16384;
}
void __attribute__((interrupt, auto_psv))_T1Interrupt(void){
if (counter < FFT_BLOCK_LENGTH){
sigCmpx[counter].real = getADCValue();
sigCmpx[counter].imag = 0;
}else{
int i = 0;
fractional *p_real = &sigCmpx[0].real ;
fractcomplex *p_cmpx = &sigCmpx[0] ;
fractional *p_fract = &sigCmpx[0].real ;
for ( i = 0; i < FFT_BLOCK_LENGTH; i++ )/* The FFT function requires input data */
{ /* to be in the fractional fixed-point range [-0.5, +0.5]*/
*p_real = *p_real >>1 ; /* So, we shift all data samples by 1 bit to the right. */
*p_real++; /* Should you desire to optimize this process, perform */
} /* data scaling when first obtaining the time samples */
/* Or within the BitReverseComplex function source code */
p_real = &sigCmpx[(FFT_BLOCK_LENGTH/2)-1].real ; /* Set up pointers to convert real array */
p_cmpx = &sigCmpx[FFT_BLOCK_LENGTH-1] ; /* to a complex array. The input array initially has all */
/* the real input samples followed by a series of zeros */
for ( i = FFT_BLOCK_LENGTH; i > 0; i-- ) /* Convert the Real input sample array */
{ /* to a Complex input sample array */
(*p_cmpx).real = (*p_real--); /* We will simpy zero out the imaginary */
(*p_cmpx--).imag = 0x0000; /* part of each data sample */
}
/* Perform FFT operation */
FFTComplexIP (LOG2_BLOCK_LENGTH, &sigCmpx[0], &twiddleFactors[0], COEFFS_IN_DATA);
/* Store output samples in bit-reversed order of their addresses */
BitReverseComplex(LOG2_BLOCK_LENGTH, &sigCmpx[0]);
/* Compute the square magnitude of the complex FFT output array so we have a Real output vetor */
SquareMagnitudeCplx(FFT_BLOCK_LENGTH, &sigCmpx[0], &sigCmpx[0].real);
sigCmpx[0].real = 0;
sigCmpx[0].imag = 0;
p_fract = &sigCmpx[0].real ; //inicia ponteiro
for( i = 0; i < FFT_BLOCK_LENGTH/2; i++ )
{
SquareMagnitude [i] = *p_fract;
*p_fract++; //incrementa o ponteiro
}
/* Find the frequency Bin ( = index into the SigCmpx[] array) that has the largest energy*/
/* i.e., the largest spectral component */
VectorMax(FFT_BLOCK_LENGTH/2, &SquareMagnitude[0], &peakFrequencyBin);
/* Compute the frequency (in Hz) of the largest spectral component */
peakFrequency = peakFrequencyBin*(SAMPLING_RATE/FFT_BLOCK_LENGTH);
//PWM(peakFrequency*0.00465);
if(peakFrequency < 1500){
LATD = 0x0001;
}else if(peakFrequency > 6000){
LATD = 0x0004;
}else{
LATD = 0x0002;
}
counter = 0;
}
counter++;
IFS0bits.T1IF = 0;
return;
}
void time1_init(void){
T1CONbits.TCS = 0;
T1CONbits.TGATE = 0;
T1CONbits.TCKPS = 0;
IPC0bits.T1IP = 5;
IFS0bits.T1IF = 0;
IEC0bits.T1IE = 1;
start_timer(800);
return;
}
void init_ADC(void){
ADCON1bits.ADSIDL = 0; //continues operation while microcontroller in idle(), 0 so it doesn't
ADCON1bits.FORM = 3; //integer form, 1 for signed integer, 2 for fractional, 3 for signed fractional (sddd dddd dddd 0000)
ADCON1bits.SSRC = 7; //7 for auto-convert, 0 for manual convert
ADCON1bits.ASAM = 0; //0 for sampling begin when SAMP bit set, 1 for samp bit auto-step (start conversion after last one is finished)
ADCON2bits.VCFG = 0; //which voltage references use for A/D
ADCON2bits.CSCNA = 1; //0 to not scan inputs, 1 to scan inputs
ADCON2bits.SMPI = 0; //
ADCON2bits.BUFM = 0;
ADCON2bits.ALTS = 0;
ADCON3bits.SAMC = 1;
ADCON3bits.ADRC = 0;
ADCON3bits.ADCS = 21;
ADCHS = 0;
ADCSSL = 0x0001;
ADPCFG = 0;
IPC2bits.ADIP = 3;
IEC0bits.ADIE = 0;
IFS0bits.ADIF = 0;
ADCON1bits.ADON = 1; //A/D module on, 0 for off
return;
}
int main(void) {
TwidFactorInit (LOG2_BLOCK_LENGTH, &twiddleFactors[0], 0);
init_ADC();
time1_init();
//time2_init();
//PWM_setup();
TRISB = 0x0001; //definir que portas sao input e output
TRISD = 0x0000;
counter = 0;
while(1){
Idle();
}
return 0;
}
post edited by indula - 2019/11/15 01:29:41