I am using a Microstick II Board with the included PIC32MX250F128B and I would like to perform real-time audio processing. I use an analog input to get the input sound and a PWM output, in combination with a low-pass filter followed by an amplifier, as my audio output (connected to headphones).
I would like to do is to perform more advanced signal processing, like filtering, etc. Microchip website says there is a DSP Library dedicated to that. The lib manual can be found here, but there isn't a lot of information in it, just some function prototypes...
In a first time, I would like to perform low-pass and bandpass filtering. Until now, I managed to perform fft using the following code:
// include header files
#include <plib.h>
#include <p32xxxx.h>
#include <dsplib_dsp.h>
#include <fftc.h>
// Config Bits
#pragma config FNOSC = FRCPLL // Internal Fast RC oscillator (8 MHz) w/ PLL
#pragma config FPLLIDIV = DIV_2 // Divide FRC before PLL (now 4 MHz)
#pragma config FPLLMUL = MUL_20 // PLL Multiply (now 80 MHz)
#pragma config FPLLODIV = DIV_2 // Divide After PLL (now 40 MHz)
#pragma config FWDTEN = OFF // Watchdog Timer Disabled
#pragma config ICESEL = ICS_PGx1 // ICE/ICD Comm Channel Select (pins 4,5)
#pragma config JTAGEN = OFF // Disable JTAG
#pragma config FSOSCEN = OFF // Disable Secondary Oscillator
// Defines
#define fftc fft16c64 // from fftc.h, for N = 64
#define SYSCLK (40000000L)
#define SAMPLES 64
#define PWM_FREQ 48000 // Output PWM frequency
#define DUTY_CYCLE 1
int tab[SAMPLES];
int i = 0;
int analogRead(char analogPIN)
{
AD1CHS = analogPIN << 16; // AD1CHS<16:19> controls which analog pin goes to the ADC
AD1CON1bits.SAMP = 1; // Sampling
while(AD1CON1bits.SAMP); // wait until acquisition is done
while(!AD1CON1bits.DONE); // wait until conversion done
return ADC1BUF0;
}
void adcConfigureManual()
{
AD1CON1CLR = 0x8000; // disable ADC before configuration
AD1CON1 = 0x00E0; // internal counter ends sampling and starts conversion (manual sample)
AD1CON2 = 0; // AD1CON2<15:13> set voltage reference to pins AVSS/AVDD
// Found on the web (todo: check the datasheet)
AD1CON3 = 0x0f01; // TAD = 4*TPB, acquisition time = 15*TAD
}
int main( void)
{
SYSTEMConfigPerformance(SYSCLK);
// Set OC1 to pin 2 with peripheral pin select
RPA0Rbits.RPA0R = 0x0005;
// Configure standard PWM mode for output compare module 1
OC1CON = 0x0006;
for(i = 0; i<SAMPLES; i++)
tab[i] = 0;
// From datasheet:
// PR = [FPB / (PWM Frequency * TMR Prescale Value)] – 1
PR2 = (SYSCLK / PWM_FREQ) - 1;
// Initial duty cycle value
OC1RS = (PR2 + 1) * ((float)DUTY_CYCLE / 100);
T2CONSET = 0x8000; // Enable Timer2, prescaler 1:1
OC1CONSET = 0x8000; // Enable Output Compare Module 1
// Configure pins as analog inputs
ANSELBbits.ANSB3 = 1; // set RB3 (AN5) to analog
TRISBbits.TRISB3 = 1; // set RB3 as an input
TRISBbits.TRISB5 = 0; // set RB5 as an output (note RB5 is a digital only pin)
adcConfigureManual(); // Configure ADC
AD1CON1SET = 0x8000; // Enable ADC
int pos=0, dat = 0;
int log2N = 6; // log2(64) = 6
int N = 1 << log2N; // N = 2^6 = 64
int din[N];
int dout[N];
int dout2[N];
int scratch[N];
while(1)
{
//foo = analogRead 5); // note that we call pin AN5 (RB3) by it's analog number
dat = analogRead(5);
//dat += din[pos]*3/10;
//if(dat > 1023) dat -= 512;
din[pos] = dat;
mips_fft16(dout, din, fftc, scratch, 1);
mips_fft16(dout2, dout, fftc, scratch, 1);
if(++pos >= SAMPLES) pos = 0;
OC1RS = (PR2 + 1) * ( ((float)dout2[pos])/1023); // Write new duty cycle
}
return 0;
}
First of all, I would like to know if what I am doing is correct. I mean, there is no ifft function, does the second fft perform an ifft ?
Secondly, by doing this, I can hear a high frequency sound, which may result from latency that delays the PWM duty cycle change OC1RS
.
How can I fix this ? I saw people usually use Timer2 to change the duty cycle, but each time I tried, I get nothing at the output (no sound).
How can I implement this in my case ?