mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ATXmega DMA und ADC einzelne Bytes mit Timer als Trigger


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Toubi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen

Folgendes Problem:

Ich möchte gerne den ADC welcher sich im Freerunning-mode befindet 
periodisch auslesen. Dies gerne mit dem DMA.
Der DMA soll Byte um Byte in einem Buffer schreiben.
Als "Takt" zum Schreiben soll der Overflow eines Timers (TCC1) dienen.

Mein Code sieht aktuell so aus:
  // set TCC1 to 11024Hz overflow, actually 11019.2838Hz (-0.052% error)
  TCC1.CTRLA = 0; // stop if running
  TCC1.CNT = 0;
  TCC1.PER = 1000-1;

  EVSYS.CH0MUX = EVSYS_CHMUX_TCC1_OVF_gc; // trigger on timer overflow


  // reset DMA controller
  DMA.CTRL = 0;
  DMA.CTRL = DMA_RESET_bm;
  while ((DMA.CTRL & DMA_RESET_bm) != 0);
  
  DMA.CTRL      = DMA_CH_ENABLE_bm | DMA_DBUFMODE_CH01_gc; // double buffered with channels 0 and 1
  
  //Bei Double Buffering wird automatisch aus Channel 0 und 1 ein "Pair" gebildet. 
  //Siehe dazu AVR1304.P8
  
  // channel 0
  // **** TODO: reset dma channels
  DMA.CH0.REPCNT    = 1;
  DMA.CH0.CTRLA    =  DMA_CH_BURSTLEN_1BYTE_gc | DMA_CH_SINGLE_bm | DMA_CH_REPEAT_bm; // ADC result is 1 byte (8 bit word)
  DMA.CH0.CTRLB    = 0x1;
  DMA.CH0.ADDRCTRL  = DMA_CH_SRCRELOAD_BURST_gc | DMA_CH_SRCDIR_INC_gc | // reload source after every burst
  DMA_CH_DESTRELOAD_TRANSACTION_gc | DMA_CH_DESTDIR_INC_gc; // reload destination after every transaction
  DMA.CH0.TRIGSRC    = DMA_CH_TRIGSRC_TCC1_OVF_gc;  //DMA0 gets synched by TCC1
  DMA.CH0.TRFCNT    = 32; // always the number of bytes, even if burst length > 1
  DMA.CH0.DESTADDR0  = (( (uint16_t) buffer_a) >> 0) & 0xFF;
  DMA.CH0.DESTADDR1  = (( (uint16_t) buffer_a) >> 8) & 0xFF;
  DMA.CH0.DESTADDR2  = 0;
  DMA.CH0.SRCADDR0  = (( (uint16_t) &ADCB.CH1.RES) >> 0) & 0xFF;
  DMA.CH0.SRCADDR1  = (( (uint16_t) &ADCB.CH1.RES) >> 8) & 0xFF;
  DMA.CH0.SRCADDR2  = 0;

  DMA.CH0.CTRLA    |= DMA_CH_ENABLE_bm;
  //TCC1.INTCTRLA = 0x03;
  TCC1.CTRLA      = TC_CLKSEL_DIV1_gc; // start timer, and in turn ADC
}
...
ISR(DMA_CH0_vect)
{
  
  //Interrupt quittieren
  DMA.CH0.CTRLB |= 0x10;
  TCC1.INTFLAGS |= 0x01;
  PORTF.OUTTGL = 0x01
}

ADC läuft. Dies wurde getestet.
Das Problem ist aktuell, dass der DMA Interrupt nicht nach TCC-Takt / 32 
(wegen 32 Byte Datenlänge im DMA) kommt sondern eigentlich nach jedem 
Byte. Wenn ich den TCC1 Prescaler bzw. dessen Periode reduziere = 
schnellerer DMA Trigger, dann läuft gar nichts mehr.

Wäre froh, wenn jemand kurz über den Code schauen könnte.

Danke.
Gruss
Toubi

Autor: Timo H. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sourceadresse scheint ja zwei Bytes zu umfassen.
Gleichzeitig ist die Burstlänge ein Byte.

Vielleicht da mal schauen?

Autor: Holger K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wäre auch an einer Lösung interessiert.
Hatte mal ein ähnliches Problem.

Bist du bereits weitergekommen Toubi?

Autor: Anno (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie schon angemerkt, hat der ADC-Wert 2 Bytes! => BURSTLEN.
DMA_CH_BURSTLEN_1BYTE_gc ist also falsch.
Dann ist es falsch den ADC im Freerunning Modus zu betreiben,
die DMA aber über Timer zu triggern - passt nicht zusammen.
(Datenkollision bei 8Bit-Prozessor und nicht synchronem Zugriff - 
Schreiben/Lesen).

Richtig: ADC-Convert über Timer triggern. Dazu über einen Eventchannel
gehen - EVSYS_CHMUX0 = EVSYS_CHMUX_TCxy_OVF_gc
und mit ADC_EVCTRL = <SWEEP> | <EVSEL> | <EVACT>) Kanal zum triggern 
einstellen...
<xy>: Die gewünschten Konstanten aus Datenblatt / ioxxx.h einsetzen

...und die DMA über DMA.TRIGSRC= DMA_CH_TRIGSRC_ADCx_CHx_gc
zu triggern.
Evtl. über DMA-Transferinterrupt im Int. das Array auslesen.
Die ADC-Wandlung kannst du abschalten in dem du den CHMUX0 auf 0 setzt..

Weitere Fehler:
- Wenn die DMA kontinuierlich arbeiten soll, muss
DMA.CH0.REPCNT = 0 sein!
- Doublebuffer ist eher nicht gewollt.
- Die richtige Initialisierung des ADCs fehlt hier...
-...und sicher noch einige andere.

Dringender Rat: Das Family-Datasheet zu ADC, DMA und Eventsystem
komplett und eingehend studieren.

Autor: Toubi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Tipps. Ich werde das testen.

Was meinst du mit:
Doublebuffer ist eher nicht gewollt ??

Der Grund für Double Buffering, ist um eine sauberes und schnelles 
Auslesen der Daten zu ermöglichen.
Da ich ein 1kHz signal abtasten und analysieren möchte, ist ein 
schnelles Auslesen erforderlich.

Autor: Anno (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>> Was meinst du mit Doublebuffer ist eher nicht gewollt ??
Doublebuffering verwendet zwei DMA-Kanäle.
Für 1kHz & zwei Bytes ist aber *ein DMA-Kanal völlig ausreichend!*
Bei sehr niedrigem Systemtakt kann man evtl. einige _NOP()s in die 
Hauptschleife / Schleifen einstreuen, damit der Datenbus im Prozessor 
öfter mal unbenutzt ist. Aber zuerst mal ohne probieren!
PS:
Ich habe, bei 32MHz Systemtakt 2 DMA-Kanäle mit 100KHz erfolgreich 
laufen lassen...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.