Autor Tema: Valores FFT  (Leído 2420 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado hibiscusblau

  • PIC10
  • *
  • Mensajes: 26
Valores FFT
« en: 03 de Diciembre de 2012, 09:00:20 »
Hola a todos,

Estoy trabajando con el codigo FFT de Microchip y tengo un problema con los valores de la FFT, porqué pongo un senos fijo a la entrada del ADC (por ejemplo de 500Hz) y muestro en el hyperterminal los BIN y la frecuencia correspondiente, y cada vez que hago una adquisición de señal, me muestra BINs y frecuencia de peak diferente (con la misma entrada analogica). Por eso no sé qué estoy haciendo mal.

Parametros de la FFT:

Código: [Seleccionar]
#define               FOSC     10000000
#define               PLL         8
//#define           FCY        FOSC*PLL/4
 
#define NUMSAMP         256
#define SAMPLINGRATE    8000
#define SAMPCOUNT       (20000000/SAMPLINGRATE)+1

Leo el señal sinusoidal desde del canal AN3 y muestreo con el TMR3 por eso configuro el ADC de la siguiente forma:


Código: [Seleccionar]
void ADCInit (){
 
        //ADCON1 Register
         //Set up A/D conversrion results to be read in 1.15 fractional number format.
        //All other bits to their default state
        ADCON1bits.FORM = 2;  // 11 Signal fractional, 10 Fractional, 01 Signed integer, 00 Integer 
 
ADCON1bits.SSRC = 2; //Use Timer3 to provide sampling time
                ADCON1bits.ASAM = 1;  // Auto-Sample Start Mode. Allows the ADC to schedule sample/conversion sequences with no intervention by the user or other devices resources
 
        //ADCON2 Register
        ADCON2bits.SMPI = 0;// interrupció x cada mostra/conversio
                               //ADCON3 = 0x1F02; // SAMC  = 11111 (31Tad) i ADCS = 2 => Tad = Tcy*(2+1)/2
 
                               // Dades per una Fs = 8KHz
                               ADCON3bits.ADCS = 63; // Tad = Tcy(ADCS+1)/2 = 1.6usec
 
                    TMR3 = 0x0000;
        PR3 = SAMPCOUNT;  // Load Period value SAMPCOUNT = (Fcy/Fs)+1 (ho he vist algun lloc amb -1)
 
//IFS0bits.T3IF: Timer3 Interrupt Flag Status bit
//1 = Interrupt request has occurred
//0 = Interrupt request has not occurred
        IFS0bits.T3IF = 0;
 
//IEC0bits.T3IE: Timer3 Interrupt Enable bit
//1 = Interrupt request enabled
//0 = Interrupt request not enabled
        IEC0bits.T3IE = 0;
 
 
        //Set up A/D Channel Select Register to convert AN3 on Mux A input
        ADCHS = 0x0003;
 
        //ADCSSL Register
        //Channel Scanning is disabled. All bits left to their default state
        ADCSSL = 0x0000;
 
        //ADPCFG Register
        //Set up channels AN3 as analog input and configure rest as digital
        ADPCFG = 0xFFFF;
        ADPCFGbits.PCFG3 = 0;
 
                               //Clear the A/D interrupt flag bit
        IFS0bits.ADIF = 0;
 
        //Set the A/D interrupt enable bit
        IEC0bits.ADIE = 1;
 
        //Turn on the A/D converter
        //This is typically done after configuring other registers
        ADCON1bits.ADON = 1;
 
        //Start Timer 3
                               //T3CONbits.TON: Timer On Control bit
                               //1 = Starts the timer
                               //0 = Stops the timer
        T3CONbits.TON = 1;
 
                               CountSamples = 0;
}

Después, espero los datos en la interrupción del ADC:


Código: [Seleccionar]
void __attribute__ ((__interrupt__, no_auto_psv)) _ADCInterrupt(void)
{
                //            int i;
                fractional aux;
 
    //Clear the Timer3 Interrupt Flag
    IFS0bits.T3IF = 0;
 
    //Clear the A/D Interrupt flag bit or else the CPU will
    //keep vectoring back to the ISR
    IFS0bits.ADIF = 0;
 
                inputSignal[CountSamples] = ADCBUF0; // mentrestant...
                //sigCmpx[CountSamples].real = ADCBUF0; // mentrestant...
                CountSamples++;
 
                if (CountSamples >= FFT_BLOCK_LENGTH){     
                               ADCFFTMoveSamples(); // moc les mostres del buffer aux al buffer sigCmpx.
                               doFilterFlag = 1; // CountSamples >= 256, ja ho tinc tot
                               CountSamples = 0;       
                }
}


En ADCFFTMoveSamples, copio las muestras dentro de SigCmpx para no reescribir las muestras y que me calcule mal la FFT (actualmente paro la interrupción del ADC cuando entro a calcular la FFT pero en un futuro la idea es que se pueda trabajar en paralelo). Esta función hace lo siguiente:

Código: [Seleccionar]
void ADCFFTMoveSamples(){
                int i;
                for (i = 0; i < FFT_BLOCK_LENGTH; i++){
                               sigCmpx[i].real = inputSignal[i]; //inicialitzo a 0
                               sigCmpx[i].imag = 0; //inicialitzo a 0       
                }
}

Then, when flag doFilterFlag =1, the main calls FFTMotor(), and it does:

Código: [Seleccionar]
char FFTMotor(){
                int i = 0;
                fractional *p_real = &sigCmpx[0].real ; // posa al punter p_real l'@de memòria on es troba sigCmpx[0].real
                fractcomplex *p_cmpx = &sigCmpx[0] ; // posa al punter p_cmpx l'@de memòria on es troba sigCmpx[0]
                char flagAuxCaiguda =0;
//******* paro la interrupció
                IEC0bits.ADIE = 0;                           //paro la interrupció per fer la FFT
//*****baixo flags
                ThereIsARockFlag = 0;
                doFilterFlag = 0;
 
                sprintf(uTxt, "\r\n 1 Inici FFT  \r\n");
                putsUART1((unsigned int *)uTxt);
 
                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. */
//En teoria això divieix per 2? el valor de la variable que apunta p_real. Perquè la variable va de [+1, -1], l'ADC ja l'agafa tipus fractional, per tant divideix x 2.
                               *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[127].real posa a p_real l'@ de memòria de sigCmpx[127].real
                p_real = &sigCmpx[(FFT_BLOCK_LENGTH/2)-1].real ;   /* Set up pointers to convert real array */
 
                // p_cmpx = &sigCmpx[255] posa a p_cmpx l'@ de memòria de sigCmpx[255]
                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 */
                }
 
                FFTComplexIP (LOG2_BLOCK_LENGTH, &sigCmpx[0], (fractcomplex *) __builtin_psvoffset(&twiddleFactors[0]), (int) __builtin_psvpage(&twiddleFactors[0]));
 
                // 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);
 
                // 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, &sigCmpx[0].real, &peakFrequencyBin);
 
                sprintf(uTxt, "\r\n BIN: %d \r\n",peakFrequencyBin );
                putsUART1((unsigned int *)uTxt);
                // Compute the frequency (in Hz) of the largest spectral component
                peakFrequency = peakFrequencyBin*(SAMPLING_RATE/FFT_BLOCK_LENGTH);
 
                sprintf(uTxt, "\r\n Frequencia de Pic: %ld \r\n", peakFrequency);
                putsUART1((unsigned int *)uTxt);
                sprintf(uTxt, "\r\n Fi FFT \r\n");
                putsUART1((unsigned int *)uTxt);
//
                flagAuxCaiguda = FFTDecide();
 
                ADCEnableConversion ();
                IEC0bits.ADIE = 1;
                return flagAuxCaiguda;
}

Alguien sabe qué estoy haciendo mal?

Muchas gracias a todos!

Desconectado hibiscusblau

  • PIC10
  • *
  • Mensajes: 26
Re: Valores FFT
« Respuesta #1 en: 30 de Enero de 2013, 07:17:38 »
Buenas notícias!

Ya he encontrado el fallo, o los fallos! Aquí os los pongo por si alguien le pasa lo mismo que yo:

Correción 1: guardar las muestras en parte real y parte imaginaria

Código: [Seleccionar]
void __attribute__ ((__interrupt__, no_auto_psv)) _ADCInterrupt(void)
{
    IFS0bits.T3IF = 0;    //Clear the Timer3 Interrupt Flag   
    IFS0bits.ADIF = 0; //Clear the A/D Interrupt flag bit or else the CPU will keep vectoring back to the ISR

if(tics%2 == 0 ){
sigCmpx[CountSamples].real = ADCBUF0;
}else{
sigCmpx[CountSamples].imag = ADCBUF0;
CountSamples++;
}
tics++;
if (CountSamples >= FFT_BLOCK_LENGTH){
doFilterFlag = 1;
CountSamples = 0;
tics = 0;
}
}


Corrección 2:

Código: [Seleccionar]
#define FFT_BLOCK_LENGTH 512    /* = Number of frequency points in the FFT */
#define LOG2_BLOCK_LENGTH 9 /* = Number of "Butterfly" Stages in FFT processing */  2^9 = 512

#define SAMPLING_RATE 8000 /* = Rate at which input signal was sampled */
                                        /* SAMPLING_RATE is used to calculate the frequency*/
                                        /* of the largest element in the FFT output vector*/


Saludos!

Desconectado hibiscusblau

  • PIC10
  • *
  • Mensajes: 26
Re: Valores FFT
« Respuesta #2 en: 30 de Enero de 2013, 07:22:28 »
La FFT la realizo asi:

Código: [Seleccionar]
char FFTMotor(){
int i = 0;

double aux = 0;

fractional *p_real = &sigCmpx[0].real ; // posa al punter p_real l'@de memòria on es troba sigCmpx[0].real
fractcomplex *p_cmpx = &sigCmpx[0] ; // posa al punter p_cmpx l'@de memòria on es troba sigCmpx[0]

IEC0bits.ADIE = 0; //paro la interrupció per fer la FFT

sprintf(uTxt, "\r\n entro  a la FFTMOTOR \r\n" );
putsUART1((unsigned int *)uTxt);
ThereIsARockFlag = 0;
doFilterFlag = 0;

p_real = &sigCmpx[(FFT_BLOCK_LENGTH/2)-1].real ; /* Set up pointers to convert real array */  // p_real = &sigCmpx[127].real posa a p_real l'@ de memòria de sigCmpx[127].real
p_cmpx = &sigCmpx[FFT_BLOCK_LENGTH-1]; /* to a complex array. The input array initially has all */ // p_cmpx = &sigCmpx[255] posa a p_cmpx l'@ de memòria de sigCmpx[255]
/* 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   part of each data sample */
(*p_cmpx--).imag = 0x0000;
}


#ifndef FFTTWIDCOEFFS_IN_PROGMEM
FFTComplexIP (LOG2_BLOCK_LENGTH, &sigCmpx[0], &twiddleFactors[0], COEFFS_IN_DATA);
#else
FFTComplexIP (LOG2_BLOCK_LENGTH, &sigCmpx[0], (fractcomplex *) __builtin_psvoffset(&twiddleFactors[0]), (int) __builtin_psvpage(&twiddleFactors[0]));
#endif

sigCmpx[0].real = 0; // remove signal continue

// 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);
// 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, &sigCmpx[0].real, &peakFrequencyBin);

// Compute the frequency (in Hz) of the largest spectral component
aux = 15.63; //((8000)/FFT_BLOCK_LENGTH);
peakFrequency = peakFrequencyBin*15.63; //(SAMPLING_RATE/FFT_BLOCK_LENGTH); //aux;

sprintf(uTxt, "\r\n Frequencia de Pic: %ld \r\n", peakFrequency);
putsUART1((unsigned int *)uTxt);

IEC0bits.ADIE = 0;
return 0;//flagAuxCaiguda;
}