Forum: Mikrocontroller und Digitale Elektronik DMA ATXmega128a1


von Ro B. (robret)


Lesenswert?

Hallo,

ich möchte bei einem Atxmega128A1 einen Frequenzgenerator über den DMA 
laufen lassen. Hierzu soll der DMA bei jedem TCC0 Interrupt einen Wert 
an den Port (an welchem ein DAC angeschlossen ist) ausgeben. Das 
funktioniert auch soweit.
Jedoch ist mir aufgefallen, dass die Interruptserviceroutine den 
Prozessor beeinflusst. Wenn die die 3 Zeilen der ISR herrausnehme, kann 
man den Timer ändern wie man will, am die Zeit in der der DMA die Werte 
rausschickt ändert sich nicht.

Kann mir irgendwer weiterhelfen?

void init_TIMER_DMA (void)
{
  PMIC.CTRL=0x07;        //Enable all IRQs

  PORTA.DIR=0xff;

  EVSYS.CH0MUX=0xc0;      // Event0 = TCC0 OVFL
            // initialize TCC0 Counter
  TCC0.CTRLB=0x00;      // normal count
  TCC0.PER=40;        // top count value
  TCC0.CTRLA=0x00;      // counter stop

//Interrupt konfigurieren  (Highlevel Interrupt)
  TCC0.INTCTRLA = 0x03;


  //* initialize DMA chanel 0

  DMA.CTRL = DMA_CH_ENABLE_bm;
        // Aktivieren Single Buffer, Burst    Size 1Byte

        DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_TRANSACTION_gc | 
DMA_CH_SRCDIR_INC_gc | DMA_CH_DESTRELOAD_BURST_gc | 
DMA_CH_DESTDIR_INC_gc;

        DMA.CH0.TRIGSRC = 0x40;   //trigger source TCCo overflow
  DMA.CH0.DESTADDR0 =(uint8_t)(((uint16_t)&PORTA.OUT)>>0)&0xFF;
  DMA.CH0.DESTADDR1 =(uint8_t)(((uint16_t)&PORTA.OUT)>>8)&0xFF;
  DMA.CH0.DESTADDR2 =0;


  sei();
}


ISR(TCC0_OVF_vect)
{
}

void start_dma(void)
{
  DMA.CH0.TRFCNT = 256;
    DMA.CH0.SRCADDR0  =(uint8_t)(((uint16_t)&sinus)>>0) & 0xFF;
  DMA.CH0.SRCADDR1  =(uint8_t)(((uint16_t)&sinus)>>8) & 0xFF;
  DMA.CH0.SRCADDR2  =0;
    DMA.CH0.CTRLA = 0xA4;       // Aktivieren, Wiederholen, 1 Byte, 
Single
    TCC0.CTRLA=0x01;     // start counter
}

von Ro B. (robret)


Lesenswert?

TCC0.CTRLD=0x08;
bei der INIT habe ich noch folgende Zeile vergessen, funktioniert 
trotzdem nicht.

von Gerhard G. (xmega)


Lesenswert?

Hallo,


> DMA.CH0.TRIGSRC = 0x40;   //trigger source TCC0 overflow

hier legst du fest, wann der DMA loslegen soll.

Die Interruptserviceroutine kann leer oder für andere Funktionen im 
Programm genützt werden. Nicht aber zum Starten der DMA-Routine. Sie 
wird hier gestartet: DMA.CH0.TRIGSRC = 0x40.

Mit dem Oszi oder einer Led kann man aber das zeitliche Verhalten der
Interruptserviceroutine testen.


Zum Beispiel:

PORTD.DIRTGL   |= PIN3_bm;  // Toggle Als Ausgang  festlegen


 ISR(TCC0_OVF_vect)
 {
  PORTD.OUTTGL |= PIN3_bm;  // Toggle am Ausgang
 }


Gruß xmega

von Ro B. (robret)


Lesenswert?

Das Problem ist, dass das Timer Overflow Flag nicht zurück gesetzt wird, 
wenn keine ISR gestartet wird. Somit läuft der DMA auf hochtouren. Am 
liebsten würde ich aber ganz auf die ISR verzichten, welchen Sinn hat 
sonst der DMA? dann könnte ich den Port auch in der ISR ansteuern.

von Gerhard G. (xmega)


Angehängte Dateien:

Lesenswert?

Hallo,

Ro Bret schrieb:
> Somit läuft der DMA auf hochtouren

Setze for jedem Ausgabebefehl:

while( !DMA.CH0.CTRLB & (DMA_CH_ERRIF_bm | DMA_CH_TRNIF_bm));

Die Schleife wartet solange, bis der DMA seine Daten gesendet hat.

Siehe auch mein lauffahiges Programm als Anhang!

Gruß xmega

von ro bret (Gast)


Lesenswert?

Vielen Dank für die schnellen Antworten....
habe schon eine andere Lösung, ich nutze den DMA Channel 1 um über den 
selben Timer-EVENT das Interruptflag des Timer zurück zu setzen....so 
funktionierts, ohne reserven der CPU zu verschwenden.

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.