Forum: Mikrocontroller und Digitale Elektronik ATxMega: DMA arbeitet nicht wie erwartet


von Curby23523 N. (Gast)


Lesenswert?

Hallo,

ich möchte zwei DMA Kanäle (0/1) dazu benutzen, meinen DAC mit Daten zu 
versorgen, aber soweit kommt es noch gar nicht.

Meine Konfiguration sieht folgendermaßen aus:
1
PORTD_DIR = 0xFF;
2
  PORTB_DIR = 0xFF;
3
  PORTC_DIR = 0xFF;
4
  PORTC_OUT = 0;
5
  TCC1_CTRLA = TC_CLKSEL_DIV1_gc;
6
  TCC1_PERBUF = 256;
7
  //TCC1_INTCTRLA = 0x02;
8
  
9
  DMA.CH0.CTRLA = DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_2BYTE_gc;              //DMA Block macht bei jedem Trigger 2 Byte (signle)
10
  DMA.CH0.CTRLB = 0x03;                                  //Interrupt mit höchster Priorität
11
  DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_BLOCK_gc;                      //Nach jedem Block die Quelladresse reseten
12
  DMA.CH0.ADDRCTRL |= DMA_CH_DESTDIR_INC_gc;                        //Quelladresse immer +1
13
  DMA.CH0.ADDRCTRL |= DMA_CH_DESTRELOAD_BURST_gc;                      //Zieladresse nach jedem Burst (2Byte) neu laden
14
  DMA.CH0.ADDRCTRL |= DMA_CH_DESTDIR_FIXED_gc;                      //Zieladresse ist FIX
15
  DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_TCC1_OVF_gc;                      //Triggerevtn ist Timer TCC1 overflow
16
  DMA.CH0.TRFCNT = BUFFER_SIZE*2;                              //buffer_SIZE*2 Bytes
17
  DMA.CH0.REPCNT = 1;                                    //0 WIederholungen - nach 1 Block ist Transaction fertig
18
  DMA.CH0.SRCADDR0 = (DWORD)&DMA_Buffer1 & 0xFF;
19
  DMA.CH0.SRCADDR1 = ((DWORD)&DMA_Buffer1>>8) & 0xFF;
20
  DMA.CH0.SRCADDR2 = ((DWORD)&DMA_Buffer1>>16) & 0xFF;
21
  DMA.CH0.DESTADDR0 = (DWORD)&DACB.CH0DATA & 0xFF;
22
  DMA.CH0.DESTADDR1 = ((DWORD)&DACB.CH0DATA>>8) & 0xFF;
23
  DMA.CH0.DESTADDR2 = ((DWORD)&DACB.CH0DATA>>16) & 0xFF;
24
  
25
  DMA.CH1.CTRLA = DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_2BYTE_gc;              //DMA Block macht bei jedem Trigger 2 Byte (signle)
26
  DMA.CH1.CTRLB = 0x03;                                  //Interrupt mit höchster Priorität
27
  DMA.CH1.ADDRCTRL = DMA_CH_SRCRELOAD_BLOCK_gc;                      //Nach jedem Block die Quelladresse reseten
28
  DMA.CH1.ADDRCTRL |= DMA_CH_DESTDIR_INC_gc;                        //Quelladresse immer +1
29
  DMA.CH1.ADDRCTRL |= DMA_CH_DESTRELOAD_BURST_gc;                      //Zieladresse nach jedem Burst (2Byte) neu laden
30
  DMA.CH1.ADDRCTRL |= DMA_CH_DESTDIR_FIXED_gc;                      //Zieladresse ist FIX
31
  DMA.CH1.TRIGSRC = DMA_CH_TRIGSRC_TCC1_OVF_gc;                      //Triggerevtn ist Timer TCC1 overflow
32
  DMA.CH1.TRFCNT = BUFFER_SIZE*2;                              //buffer_SIZE*2 Bytes
33
  DMA.CH1.REPCNT = 1;                                    //0 WIederholungen - nach 1 Block ist Transaction fertig
34
  DMA.CH1.SRCADDR0 = (DWORD)&DMA_Buffer2 & 0xFF;
35
  DMA.CH1.SRCADDR1 = ((DWORD)&DMA_Buffer2>>8) & 0xFF;
36
  DMA.CH1.SRCADDR2 = ((DWORD)&DMA_Buffer2>>16) & 0xFF;
37
  DMA.CH1.DESTADDR0 = (DWORD)&DACB.CH0DATA & 0xFF;
38
  DMA.CH1.DESTADDR1 = ((DWORD)&DACB.CH0DATA>>8) & 0xFF;
39
  DMA.CH1.DESTADDR2 = ((DWORD)&DACB.CH0DATA>>16) & 0xFF;
40
  
41
  DMA.CTRL = DMA_ENABLE_bm | DMA_DBUFMODE_CH01_gc;
42
  DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;
43
    
44
  DACB.CTRLA = DAC_CH1EN_bm | DAC_CH0EN_bm | DAC_ENABLE_bm;                //Alle kanäle vom DAC aktivieren
45
  DACB.CTRLB = DAC_CHSEL_DUAL_gc;                              //Kanäle getrennt steuern
46
  DACB.CTRLC = DAC_REFSEL_AVCC_gc;                            //VCC als Referenzspannung

Zusätzlich ist das Interrupt definiert mit:
1
#define BUFFER_SIZE 256
2
3
WORD DMA_Buffer1[BUFFER_SIZE];
4
WORD DMA_Buffer2[BUFFER_SIZE];
5
6
ISR(DMA_CH0_vect){
7
  PORTC_OUTTGL = PIN6_bm;
8
  //reloadBuffer(DMA_Buffer1);
9
}
10
11
12
ISR(DMA_CH1_vect){
13
  PORTC_OUTTGL = PIN5_bm;
14
  DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;
15
  //reloadBuffer(DMA_Buffer2);
16
}

Es sollen also aus Buffer1 mit jedem Timerüberlauf zwei Bytes in den DAC 
geschrieben werden. Nach dem Block - ich möchte nur einen machen - soll 
der DMA.CH0 aktiviert werden - ping pong Modus. Der Timer macht jede 
256/14745600 Sekunden einen Überlauf - das macht ca. 57600 Überläufe pro 
Sekunde. 57600 / BUFFER_SIZE = 225. Ich erwarte also eigentlich eine 
Interruptfrequenz von 225Hz, nicht 114kHz.

Meine Beobachtung ist nun die folgende, dass der DMA.CH0 mit einer 
Frequenz von 114kHz ständig ein Interrupt schmeißt. CH1 wird nie 
aktiviert. Meine Parameter scheinen keinen Einfluss auf das Verhalten zu 
haben.

Habe ich ein Verdtänsnisproblem?

Vielen dank schonmal :)

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Das DMA Interrupt Flag muss manuell durch das Schreiben einer 1 an die 
Bitposition gelöscht werden. Ansonsten feuert der Interrupt unentwegt, 
was die hohe Frequenz erklärt.

von Curby23523 N. (Gast)


Lesenswert?

Danke, das hat insofern etwas geholfen, dass der Interrupt jetzt mit 
einer Frequenz von 800Hz statt 114kHz springt. Er sollte aber mit ca. 
25Hz laufen.

Auch wenn ich die Triggerquelle, den Timer, DEUTLICH verlangsamer - 
esbleiben jetzt immernoch 800Hz.

Ich lösche das Interrupt Flag mit dem Schreiben einer 1:
1
ISR(DMA_CH0_vect){
2
  PORTC_OUTTGL = PIN6_bm;
3
  DMA.CH0.CTRLB |= (1<<4);
4
  DMA.CH1.CTRLA |= DMA_CH_ENABLE_bm;
5
  //reloadBuffer(DMA_Buffer1);
6
}
7
8
9
ISR(DMA_CH1_vect){
10
  PORTC_OUTTGL = PIN5_bm;
11
  DMA.CH1.CTRLB |= (1<<4);
12
  DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;
13
  //reloadBuffer(DMA_Buffer2);
14
}

von Bastian W. (jackfrost)


Lesenswert?

Hi,

damit deaktiviere ich den DMA Channel nachdem die Daten gesendet wurden.
Hast du geschaut ob ggf. das Errorbit gesetzt ist ?

In der ISR steht :
1
DMA_TX_CHANNEL.TRFCNT = 0;
2
DMA_TX_CHANNEL.CTRLA &= ~DMA_CH_ENABLE_bm;  // this shouldn't be necessary
3
DMA_TX_CHANNEL.CTRLB |= (DMA_CH_TRNIF_bm | DMA_CH_ERRIF_bm);

Ich lade aber die Adressen für Quelle und Ziel nicht nach. Du willst ja 
den ganzen Buffer senden, und bei jedem Timerüberlauf zwei Byte senden , 
oder ?

Ich sende bei mir immer den ganzen Inhalt des Arrays, aber bei dir 
sollte es auch gehen wenn du die Adressen nicht neu lädtst. Nur am Ende 
müsstest du den Buffer neu laden.

Hier meine Initialisierung, die Quelle weise ich bei mir erst kurz vor 
dem aufrufden zu. :
1
void DMA_TX_Raspi_Init()
2
{
3
  // reset source address
4
  DMA_TX_Raspi_CHANNEL.SRCADDR0 = 0;
5
  DMA_TX_Raspi_CHANNEL.SRCADDR1 = 0;
6
  DMA_TX_Raspi_CHANNEL.SRCADDR2 = 0;
7
8
  // set up destination address
9
  DMA_TX_Raspi_CHANNEL.DESTADDR0 = (((uint16_t) &USART_Netzwerk.DATA) >> 0) & 0xff;
10
  DMA_TX_Raspi_CHANNEL.DESTADDR1 = (((uint16_t) &USART_Netzwerk.DATA) >> 8) & 0xff;
11
  DMA_TX_Raspi_CHANNEL.DESTADDR2 = 0;
12
13
  DMA_TX_Raspi_CHANNEL.ADDRCTRL = DMA_CH_SRCRELOAD_NONE_gc;        // never reload source address
14
  DMA_TX_Raspi_CHANNEL.ADDRCTRL |= DMA_CH_SRCDIR_INC_gc;        // increment source address during transfer
15
  DMA_TX_Raspi_CHANNEL.ADDRCTRL |= DMA_CH_DESTRELOAD_NONE_gc;      // destination address does not need to be reloaded
16
  DMA_TX_Raspi_CHANNEL.ADDRCTRL |= DMA_CH_DESTDIR_FIXED_gc;        // the destination address should be fixed
17
18
  DMA_TX_Raspi_CHANNEL.TRIGSRC = DMA_CH_TRIGSRC_USARTF0_DRE_gc;      // automatically trigger a new burst when UARTF0 is ready
19
  DMA_TX_Raspi_CHANNEL.TRFCNT = 0;                    // reset block size
20
  DMA_TX_Raspi_CHANNEL.REPCNT = 0;                    // do not repeat block transfers
21
  DMA_TX_Raspi_CHANNEL.CTRLA = DMA_CH_SINGLE_bm;            // single shot mode (i.e. one burst transfer per trigger event)
22
  DMA_TX_Raspi_CHANNEL.CTRLA |= DMA_CH_BURSTLEN_1BYTE_gc;        // 1 byte bursts
23
  DMA_TX_Raspi_CHANNEL.CTRLB |= DMA_CH_TRNINTLVL_LO_gc;
24
}

Gruß JackFrost

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.