Ich verwende ein Microstick II Board mit dem mitgelieferten PIC32MX250F128B und möchte Audioverarbeitung in Echtzeit durchführen. Ich verwende einen analogen Eingang, um den Eingangston zu erhalten, und einen PWM-Ausgang in Kombination mit einem Tiefpassfilter, gefolgt von einem Verstärker, als meinen Audioausgang (verbunden mit Kopfhörern).
Ich würde gerne eine fortgeschrittenere Signalverarbeitung wie Filterung usw. durchführen. Auf der Microchip-Website heißt es, dass es dafür eine DSP-Bibliothek gibt. Das lib-Handbuch finden Sie hier , aber es enthält nicht viele Informationen, nur einige Funktionsprototypen ...
Zunächst möchte ich eine Tiefpass- und eine Bandpassfilterung durchführen. Bisher habe ich es geschafft, fft mit dem folgenden Code auszuführen:
// 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;
}
Zunächst möchte ich wissen, ob das, was ich tue, richtig ist. Ich meine, es gibt keine ifft-Funktion, führt die zweite fft eine ifft aus? Zweitens kann ich dadurch einen hochfrequenten Ton hören, der aus einer Latenz resultieren kann, die die Änderung des PWM-Tastverhältnisses verzögert OC1RS
. Wie kann ich das beheben? Ich habe gesehen, dass Leute normalerweise Timer2 verwenden, um das Tastverhältnis zu ändern, aber jedes Mal, wenn ich es versucht habe, bekomme ich nichts am Ausgang (kein Ton). Wie kann ich das in meinem Fall umsetzen?
Ich finde, dass dies am besten mit dem DCMI-Tool und der Simulation gelernt wird, obwohl Mipsfir aus irgendeinem Grund nicht simuliert.
mips_fft16/32 macht fft und ifft. Es ist wirklich alles dasselbe. Überträgt Eingaben von einer Domäne zur anderen.
Ihre Implementierung weist Mängel auf. Die Ausgabe der mipsfft ist eine komplexe Zahl, mich wundert das sogar kompiliert. Machen Sie eine Studie über komplexe Zahlen. Ich sehe Ihre fftc-Definition nicht, aber eine Größe von 1 macht im mips_fft16-Aufruf keinen Sinn. Es ist wirklich ziemlich einfach. Wenn Sie eine Sinuswelle in einem Sample-Puffer von 32 erzeugen möchten, geben Sie den Wert (amplitute*32) in din[1].re ein. din[2].re für einen Doppelsinus und so weiter. Also für eine Abtastrate von 16000, 16000/32 = 500 * 1 = 500 Hz. Beachten Sie, dass Ihre Ausgabe in dout[x].re ist. Ein Aufruf von fft/ifft sieht also für eine fft der Größe 32 eher so aus. mips_fft16(dout, din, fftc16c32, scratch, 6);
Denken Sie auch daran, dass die Ausgabe eines fft real und imaginär sein wird. Wenn Sie also nur Spitzen wollen, vergessen Sie nicht, dass A zum Quadrat + B zum Quadrat = C zum Quadrat.
Real und imaginär = Phase der Ausgabe, stark vereinfacht.
OC1RS = (PR2 + 1) * ( ((float)dout2[pos])/1023); // Write new duty cycle
Dies ist überhaupt nicht der richtige Weg, dies zu tun. Das OC1RS-Update kann überall sein, aber ich schlage vor, einen Timer mit einer ausgewählten Abtastrate, sagen wir 16000, einzubauen. Dann füttern Sie OC1RS mit einer Art inkrementierendem Datenzeiger, um durch Ihre Ausgabe zu gehen.
jonk
Drehen
Leon Heller
jonk
Drehen