Lichtorgel nach Elm-Chan mit xMega

#4404963
Lesenswert?

Hallo,

ich bin gerade dabei die Lichtorgel nach folgendem Projekt nachzubauen:
Beitrag "[C] AVR-Lichtorgel per FFT MEGA8 32 644"

Diese verwendet (wie wahrscheinlich bekannt), die FFT von Elm-Chan:
http://elm-chan.org/works/akilcd/report_e.html

Der Unterschied zu dem Projekt ist, dass ich das Board ATxmega256A3BU 
verwende, welches ich mit 32MHz laufen lasse.
Den Code nach dem Lichtorgel Projekt habe ich nach bestem Wissen 
umgeschrieben in die xMega-Syntax. Jedoch funktioniert es noch nicht.

Was bisher funktioniert:
Das Mikro und die Analogschaltung habe ich angeschlossen bzw. aufgebaut. 
Die Verstärkerschaltung habe ich mit dem Mikrofoneingang am PC getestet, 
sie funktioniert.
Den ADC-Kanal, über den der Ausgang der OP-Schaltung eingelesen wird, 
liefert je nach Spannung (0V - 3.3V) einen Wert zwischen -1100 und ca. 
32753. Diesen Wert lasse ich mir am Display meines Boards ausgeben. Auch 
kann ich sehen, dass dieser Wert schwankt, je nachdem wie fest ich in 
das Mikrofon puste. Ist es ruhig, so liefert der ADV-Wandler einen Wert 
von ca. 26300.

Die 6 LEDs steuere ich direkt über die Outports an, also nicht über eine 
Transistorschaltung. Wenn ich zum Test mal alle 6 "Kanäle" der FFT mit 
Werten belege, so leuchten auch alle LEDs.

Was passiert ist folgendes: die 1. Led leuchtet im ca. 1 Sekunden-Takt 
gnz kurz auf. Die anderen LEDs leuchten alle nicht.
Ich habe bereits die Verstärkung der Mikrofon-Schaltung aufgedreht, 
sowie den THRESHOLD im Code herabgesetzt. Nichts hat geholfen. An F_PWM 
sowie PWM_STEPS habe ich nichts geändert. An den Routinen ffft.h, 
mydefs.h, ffft.S habe ich nichts geändert, bis auf die Anpassung der 
Ports.

Meine Vermutung ist, dass ich den ADC-Wert nicht im richtigen 
Datenformat habe, wie er sein sollte.
Welcher Datentyp müsste er denn sein und welchen Wert müsste er liefern, 
wenn ich meine Schaltung mit 3.3V versorge und der OP im Mittel 1.65V am 
Ausgang (bzw. ADV Eingang) liefert? Welchen Wert erwartet die Routine 
dann als ADC-Wert?

Weiterhin kann ich mir vorstellen, dass ich nicht überall im Code die 
32Mz berücksichtigt habe. An welchen Stellen muss ich dafür eine 
Veränderung vornehmen?

Anbei der Quelltext der größeren Codepassagen, die ich verändert habe.

Original (atmega8) mit 16Mhz:
1
ADMUX = _BV(REFS0)|_BV(ADLAR)|_BV(MUX2)|_BV(MUX0);        // channel
2

3
  do {
4
    #if defined (__AVR_ATmega8__)
5
    ADCSRA = _BV(ADEN)|_BV(ADSC)|_BV(ADFR)|_BV(ADIF)|_BV(ADPS2)|_BV(ADPS1);
6
    #elif defined (__AVR_ATmega32__) || defined (__AVR_ATmega644__)
7
    ADCSRA = _BV(ADEN)|_BV(ADSC)|_BV(ADATE)|_BV(ADIF)|_BV(ADPS2)|_BV(ADPS1);
8
    #endif
9
    while(bit_is_clear(ADCSRA, ADIF));
10
    *buffer++ = ADC - 32768;
11
  } while(--count);
12

13
  ADCSRA = 0;
meine Änderung für Mega und 32Mhz:
1
  ADCA.CTRLB = ADC_RESOLUTION_LEFT12BIT_gc|ADC_CONMODE_bm;  // 12 bit signed
2
  ADCA.REFCTRL |= 0b00010000;        // Ref = VCC/1.6 = 2.06 V
3
  ADCA.PRESCALER = ADC_PRESCALER_DIV128_gc;
4
  ADCA.CTRLA = ADC_ENABLE_bm;  
5
  
6
  do {    
7
          ADCA.CH0.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
8
          ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN4_gc;
9
          ADCA.CH0.CTRL |= ADC_CH_START_bm;
10
          while(!ADCA.CH0.INTFLAGS);
11
          ADC_result= ADCA.CH0.RES;
12
      *buffer++ = ADC_result - 32768;  
13
  } while(--count);

Original (atmega8) mit 16Mhz:
1
OCR1A += (uint16_t) T_PWM;
meine Änderung für Mega und 32Mhz:
1
TCC0.PER += (uint16_t) T_PWM;

Original (atmega8) mit 16Mhz:
1
TCCR1B = (1 << CS10);
2
TIMSK |= (1 << OCIE1A);
meine Änderung für Mega und 32Mhz:
1
TCC0.CTRLA = TC_CLKSEL_DIV1_gc; // Systemtakt  
2
TCC0.CTRLB = 0x00; // Normal-Modus
3
TCC0.INTCTRLA = 0x03; // Höchste Priorität

Original (atmega8) mit 16Mhz:
1
ISR( TIMER1_COMPA_vect )
meine Änderung für Mega und 32Mhz:
1
ISR(TCC0_OVF_vect)

Meine Systemtakt stelle ich folgendermaßen ein:
1
#define F_CPU 32000000
2

3
//Oszillator auf 32Mhz stellen
4
OSC.CTRL |= OSC_RC32MEN_bm;
5
// Warten bis der Oszillator bereit ist
6
while(!(OSC.STATUS & OSC_RC32MRDY_bm));
7
//Schützt I/O Register, Interrupts werden ignoriert
8
CCP = CCP_IOREG_gc;
9
//aktiviert den internen Oszillator
10
CLK.CTRL = CLK_SCLKSEL_RC32M_gc;

Seht ihr, ob etwas nicht passt?
Gerne kann ich auch den ganzen Code posten.
#4405013
Lesenswert?

Highii H. schrieb:

> Den ADC-Kanal, über den der Ausgang der OP-Schaltung eingelesen wird,
> liefert je nach Spannung (0V - 3.3V) einen Wert zwischen -1100 und ca.
> 32753. Diesen Wert lasse ich mir am Display meines Boards ausgeben. Auch
> kann ich sehen, dass dieser Wert schwankt, je nachdem wie fest ich in
> das Mikrofon puste. Ist es ruhig, so liefert der ADV-Wandler einen Wert
> von ca. 26300.

Das kann nicht sein.
Der ADC hat 12 Bit Ausflösung. Damit können sich die Werte nur von 0 bis 
4095 bewegen. Deine Werte sind daher nicht plausibel. Da musst du noch 
mal ran.
#4405034
Lesenswert?

Kommando retour.
Elm Chan hat
1
ADMUX = _BV(REFS0)|_BV(ADLAR)|_BV(MUX2)|_BV(MUX0);        // channel
ADLAR gesetzt und kriegt damit das Ergebnis linksbündig ausgerichtet. 
Damit ist klar, warum er 32768 abzieht: Er verschiebt das Ergebnis in 
den Bereich -32768 bis +32767, wobei nicht alle 16 Bit benutzt werden, 
sondern nur die 10 höchtwertigen Bits zählen.

Wenn du am XMega ADC das Ergebnis nicht linksbündig ausrichten lässt ist 
klar, dass du dann Unsinn im Ergebnis bekommst, wenn du genau dasselbe 
machst. Entscheidend ist hier das ADLAR Bit.

Edit:
Hmm. eigentlich  hast du ja LEFT12BIT gesetzt. Trotzdem sehen deine 
Werte mies aus. Eigentlich sollte sich da ein schön symetrischer Bereich 
ergeben, wenn du (zb mit einem Poti) dem ADC Spannungen von 0 bis zur 
Referenzspannung anbietest.
#4405141
Lesenswert?

Karl H. schrieb:
> Der ADC hat 12 Bit Ausflösung. Damit können sich die Werte nur von 0 bis
> 4095 bewegen. Deine Werte sind daher nicht plausibel. Da musst du noch
> mal ran.

Mh, komisch. Auf dem Display bekomme ich aber 32753 (bei 3.3V) 
angezeigt.

Karl H. schrieb:
> Hmm. eigentlich  hast du ja LEFT12BIT gesetzt. Trotzdem sehen deine
> Werte mies aus. Eigentlich sollte sich da ein schön symetrischer Bereich
> ergeben, wenn du (zb mit einem Poti) dem ADC Spannungen von 0 bis zur

Ich bin davon ausgegangen, dass durch das ADLAR-Bit, aus dem ADV Werte 
zwischen 0 und 65536, für 0V bzw. +5V, herauskommen. Und Elm-Chan mit 
-32768 diesen ADV-Wert zwischen -32768 bis +32767 verschiebt.

Was macht eigentlich der Befehl
1
*buffer++ = ADC - 32768;
Zieht er 32768 von ADC ab, und geht danach im buffer-array eins weiter? 
Oder zieht er vom aktuellen Buffer-Wert 32768 ab und schreibt den neuen 
Wert wieder in den Buffer?
#4405158
Lesenswert?

Highii H. schrieb:

> Ich bin davon ausgegangen, dass durch das ADLAR-Bit, aus dem ADV Werte
> zwischen 0 und 65536, für 0V bzw. +5V, herauskommen.

Ersetzte die 5V durch "Referenzspannung" und wir sind genau dort.

Aber das hast du ja nicht, du sprichst von -1100 bis 32tausend 
irgendwas. Das ist dann  nicht "symetrisch".

Hast du denn deinen ADC getestet, indem du mit einem Poti Spannungen 
zwischen 0V und der Referenzspannung eingeschleust hast und dir die 
Werte angesehen hast, oder hast du einfach mal ein Audiosignal 
draufgeklopft in der Hoffnung, dass schon alles irgendwie funktionieren 
wird?

> Zieht er 32768 von ADC ab, und geht danach im buffer-array eins weiter?

Genau so.

> Oder zieht er vom aktuellen Buffer-Wert 32768 ab und schreibt den neuen
> Wert wieder in den Buffer?

kommt 'buffer' auf der rechten Seite der Zuweisung irgendwie vor?
Nein
Also wird wohl der aktuelle Inhalt von buffer keine Rolle spielen.
#4405193
Lesenswert?

Highii H. schrieb:
> Karl H. schrieb:
>> Aber das hast du ja nicht, du sprichst von -1100 bis 32tausend
>> irgendwas. Das ist dann  nicht "symetrisch".
>
> Ich habe einen kleinen Fehler bei der Anzeige gemacht. Die Werte gehen
> von -112 bis 32752.

Ist immer noch nicht symetrisch.

Wenn der Eingang auf GND liegt, muss am letzten OpAmp die halbe Spannung 
der Referenzspannung anliegen und dein ADC muss (nach der Umrechnerei) 
eine glatte 0 (oder nahe drann) auswerfen.

> ADV mal an GND mal an Vcc (3.3V) gehalten.

So gehts auch. Trotzdem muss das Ergebnis symetrisch rund um 0 sein!
Gast #4405336
Lesenswert?

Highii H. schrieb:
> Anbei der Quelltext der größeren Codepassagen, die ich verändert habe.

Atmega8 und ATXMega unterscheiden sich recht ordentlich - da kann man 
aus wenigen "angepassten" Code-Passagen nicht auf irgendwelche Fehler 
schließen.

Weiß ich auch Erfahrung :-/

Ich habe mein ATMega-Original in großen Teilen "vergessen" und mit 
ATXMega nahezu neu angefangen. Eine "einfache" Portierung halte ich für 
"unwahrscheinlich", lasse mich aber gerne eines Besseren belehren.

Kurz: Schau Dir das Prinzip an und baue es neu auf. Ist nebenbei 
ungemein lehrreich :-)
Gast #4405388
Lesenswert?

Dieter F. schrieb:
> Ich habe mein ATMega-Original in großen Teilen "vergessen" und mit
> ATXMega nahezu neu angefangen. Eine "einfache" Portierung halte ich für
> "unwahrscheinlich", lasse mich aber gerne eines Besseren belehren.

Solange es nur um den Core geht, ist der Xmega zu 99% kompatibel. Ich 
habe selbst schon größere Assemblerprojekte portiert. Die 
Peripheriefunktionen musste man halt austauschen.
Gast #4405409
Lesenswert?

avr schrieb:
> Solange es nur um den Core geht, ist der Xmega zu 99% kompatibel

Fällt mir schwer, das zu glauben. Kannst Du das gezielt an Beispielen 
(Timer, externe Interrupts, USART, SPI. I2C etc.) belegen?

Oder gehören zu Deinen "Core-Funktionen" nur Register-Operationen etc. 
und Sprünge?
Gast #4405492
Lesenswert?

Dieter F. schrieb:
> Oder gehören zu Deinen "Core-Funktionen" nur Register-Operationen etc.
> und Sprünge?

Zu Core gehört der AVR-Core. Das drum herum ist Peripherie. Dazu gehören 
auch Timer, SPI... Die Treiber dafür zu schreiben kostet in der Regel 
auch vergleichsweise wenig Zeit.

Nur Registeroperationen ist gut. Das was ich portiert habe, war immerhin 
ein komplexes Programm. Die Änderungen sind übrigens die gleichen die 
man auch in einem C-Programm machen hätte müssen. Aber wenn man das 
Programm sauber von Peripheriezugriffen trennt, dann geht die Portierung 
sehr schnell.
Gast #4405498
Lesenswert?

avr schrieb:
> Aber wenn man das
> Programm sauber von Peripheriezugriffen trennt, dann geht die Portierung
> sehr schnell.

Blah, blubb - in der Peripherie liegt der Schmackes. Ohne IO geht 
nichts. Da kann der Core die 1.238 te Wurzel aus 5 berechnen - ohne 
Ausgabe spielt das keine Rolle ...
#4405514
Lesenswert?

Dieter F. schrieb:
> avr schrieb:
>> Aber wenn man das
>> Programm sauber von Peripheriezugriffen trennt, dann geht die Portierung
>> sehr schnell.
>
> Blah, blubb - in der Peripherie liegt der Schmackes. Ohne IO geht
> nichts.

Schon richtig.
Aber bei der (in Assembler geschriebenen) FFT sind laut avr-s Aussage 
kaum Probleme zu erwarten.

Daher muss er sich jetzt erst mal darauf konzentrieren, den ADC in den 
Griff zu kriegen. Solange der miese Werte liefert, ist nicht zu 
erwarten, dass die FFT da irgendwas vernünftiges draus macht.
#4407082
Lesenswert?

Karl H. schrieb:
> Daher muss er sich jetzt erst mal darauf konzentrieren, den ADC in den
> Griff zu kriegen. Solange der miese Werte liefert, ist nicht zu
> erwarten, dass die FFT da irgendwas vernünftiges draus macht.

So, den ADC habe ich nun in den Griff bekommen. Der xMega läuft mit 
3.3V. Die mittlere Spannung am ADC ist 1.65V. Der ADC liefert nun bei 0V 
am Eingang einen Wert von -32768. Bei 1.65V liefert er 0 und bei 3.3V 
liefert er +32767.
Leider geht es immernoch nicht. Keine der LED leuchtet auf.

Gibt es jemanden, der bereit wäre, meinen Code mal durchzuschauen?

Ich werde mir jetzt mal die Logik der FFT Routine anschauen. Vielleicht 
verstehe ich ja was sie macht. Leider habe ich absolut keine Ahnung von 
ASM.
#4407259
Lesenswert?

Ah, jetzt geht es (fast). Ich habe aus unerklärlichen Gründen die 
Definition der LED-Ports als Ausgang rausgeschmissen. Dann kann ja auch 
gar keine LED leuchten.
Jedenfalls geht sie die FFT jetzt. Ich muss jetzt nur noch an der 
Verstärkung schrauben. Wenn ich Musik aufdrehe, leuchten die LEDs nur 
sehr schwach, bewegen sich aber zum Beat. Wenn ich jedoch Frequenzen 
direkt auf das Mikro spiele (mit einer einfachen Handy-App), dann 
leuchten sie perfekt auf.
#4407809
Lesenswert?

L. P. schrieb:
> würdest Du hier den Code veröffentlichen?

Ja sehr gerne.
Anbei der Code für die FFT, realisiert auf dem xMega-Board 
atxmega256a3bu.

Die FFT kommt von Elm-Chan:
http://elm-chan.org/works/akilcd/report_e.html

Die ursprüngliche Implementierung ist wurde auf dem Atmega8 gemacht und 
ist hier zu finden:
https://www.mikrocontroller.net/topic/goto_post/1191497

Für Feedback oder Verbesserungsvorschläge bin ich dankbar.
Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren