Forum: Mikrocontroller und Digitale Elektronik Lichtorgel nach Elm-Chan mit xMega


von Highii H. (highii)


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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


Lesenswert?

Versuch mal rauszufinden, warum Elm Chan hier
1
    *buffer++ = ADC - 32768;
32768 abzieht. Im Moment seh ich da den Sinn noch nicht.

von Olaf (Gast)


Lesenswert?

> Im Moment seh ich da den Sinn noch nicht.

Vermutlich will er den Nullpunkt verschieben. Kann davon abhaengen was 
der AD-Wandler an Daten liefert.

Olaf

von Karl H. (kbuchegg)


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.

: Bearbeitet durch User
von Highii H. (highii)


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?

von Karl H. (kbuchegg)


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.

von Highii H. (highii)


Lesenswert?

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.
ADV mal an GND mal an Vcc (3.3V) gehalten.

von Karl H. (kbuchegg)


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!

von Highii H. (highii)


Lesenswert?

Karl H. schrieb:
> So gehts auch. Trotzdem muss das Ergebnis symetrisch rund um 0 sein!
Also ist -112 noch zu weit entfernt!?

von Karl H. (kbuchegg)


Lesenswert?

Highii H. schrieb:
> Karl H. schrieb:
>> So gehts auch. Trotzdem muss das Ergebnis symetrisch rund um 0 sein!
> Also ist -112 noch zu weit entfernt!?

Wenn du 16 Bit linksbündig bist (unsigned) UND 32767 abziehst und in 
einer signed Variablen speicherst, dann muss das Ergebnis den kompletten 
Bereich -32768 bis +32767 abdecken!

von Dieter F. (Gast)


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 :-)

von Highii H. (highii)


Lesenswert?

Dieter F. schrieb:
> Kurz: Schau Dir das Prinzip an und baue es neu auf. Ist nebenbei
> ungemein lehrreich

Das mache ich eigentlich auch immer so. Aber durch die FFT blicke ich 
nicht ganz durch. Vor allem was in ASM geschrieben ist.

von avr (Gast)


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.

von Highii H. (highii)


Lesenswert?

avr schrieb:
> 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.

Ok. Ich schaue es mir nochmal an.

von Dieter F. (Gast)


Lesenswert?

avr schrieb:
> Assemblerprojekte

Ja, mag sein - in C sieht das etwas anders aus.

von Dieter F. (Gast)


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?

von avr (Gast)


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.

von Dieter F. (Gast)


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 ...

von Karl H. (kbuchegg)


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.

: Bearbeitet durch User
von Highii H. (highii)


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.

von Highii H. (highii)


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.

von L. P. (lpg)


Lesenswert?

Hi,
würdest Du hier den Code veröffentlichen?

LG.

von Highii H. (highii)


Angehängte Dateien:

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.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.