Hallo, Aktuell habe ich das Problem, dass der DMA beim erstem Compare-Ereignis wie gewünscht losläuft - aber nicht mehr aufhört zu Laufen, obwohl ich das entsprechende Interrupt-Flag (per DMA :-) ) lösche. Atmel schweigt sich (soweit ich Lesen konnte) darüber aus, was genau die Übertragung auslöst. Man kann Compare-Channel auswählen - weiß aber nicht (wie in meinem Fall), was genau die Übertragung auslöst. Meine Vorgehensweise: Zunächst habe ich die DMA-Kanäle 0 und 1 (0 für den Compare-Wert, 1 für das Löschen der Interrupt-Flags) initialisiert und aktiviert. Dann habe ich den Compare-Wert vorbelegt und den Timer gestartet. Mit dem ersten Compare startete auch DMA-Kanal 0 - kurz darauf DMA-Kanal 1, da ich diese Reihenfolge über die Priorität festgelegt habe. Prima, bis dahin. Dann beginnt das Elend. Die DMA-Kanäle arbeiten weiter, bis keine Werte mehr zu übertragen sind - ohne auf den nächsten Compare zu Warten - ... . Das Ergebnis ist nicht zufriedenstellend :-( Weiß jemand Rat? Ja, der Code - hier die DMA-Initialisierung ...
1 | void dma_init() |
2 | {
|
3 | DMA.CTRL = 0; // Reset DMA Controller |
4 | DMA.CTRL = DMA_RESET_bm; |
5 | |
6 | while ((DMA.CTRL & DMA_RESET_bm) != 0); |
7 | |
8 | |
9 | DMA.CTRL = DMA_CH_ENABLE_bm // DMA Aktivieren |
10 | | DMA_DBUFMODE_DISABLED_gc // kein double buffer |
11 | | DMA_PRIMODE_CH0123_gc; // Priorität Kanal 0->1->2->3 |
12 | |
13 | DMA.CH0.REPCNT = 0; // Keine Wiederholung |
14 | |
15 | DMA.CH0.CTRLA = DMA_CH_BURSTLEN_2BYTE_gc // Pro Transfer 2 Byte / 1 Wort für das 16-Bit-Register |
16 | | DMA_CH_SINGLE_bm; // Immer nur 1 burst pro Trigger |
17 | |
18 | DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_BLOCK_gc // Die Quell-Adresse wird nach jedem Block-Transfer neu geladen |
19 | | DMA_CH_SRCDIR_INC_gc // Die Quell-Adresse wird nach jedem burst inkrementiert |
20 | | DMA_CH_DESTRELOAD_NONE_gc // Die Ziel-Adresse bleibt unverändert |
21 | | DMA_CH_DESTDIR_FIXED_gc; // Die Ziel-Adresse bleibt unverändert |
22 | |
23 | DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_TCD0_CCA_gc; // Trigger ist Compare-Channel A des Zählers TCD0 |
24 | |
25 | DMA.CH0.DESTADDR0 = (( (uint16_t) &TCD0.CCA) >> 0) & 0xFF; // Die Ziel-Adresse ist das Register mit dem Vergleichswert A des Zähler TCD0 |
26 | DMA.CH0.DESTADDR1 = (( (uint16_t) &TCD0.CCA) >> 8) & 0xFF; |
27 | DMA.CH0.DESTADDR2 = 0; |
28 | DMA.CH0.SRCADDR0 = (( (uint16_t) &data_table + 2) >> 0) & 0xFF; // Die Quell-Adresse + 2 ist die Adresse der Daten-Tabelle |
29 | DMA.CH0.SRCADDR1 = (( (uint16_t) &data_table + 2) >> 8) & 0xFF; // 2 Byte Offset, da der erste Wert beim Zähler-Start mitgegeben wird |
30 | DMA.CH0.SRCADDR2 = 0; |
31 | |
32 | |
33 | DMA.CH1.REPCNT = 0; // Keine Wiederholung |
34 | |
35 | DMA.CH1.CTRLA = DMA_CH_BURSTLEN_1BYTE_gc // Pro Transfer 1 Byte / 1 Wort für das 8-Bit-Flag-Register |
36 | | DMA_CH_SINGLE_bm; // Immer nur 1 burst pro Trigger |
37 | |
38 | DMA.CH1.ADDRCTRL = DMA_CH_SRCRELOAD_NONE_gc // Die Quell-Adresse bleibt unverändert |
39 | | DMA_CH_SRCDIR_FIXED_gc // Die Quell-Adresse bleibt unverändert |
40 | | DMA_CH_DESTRELOAD_NONE_gc // Die Ziel-Adresse bleibt unverändert |
41 | | DMA_CH_DESTDIR_FIXED_gc; // Die Ziel-Adresse bleibt unverändert |
42 | |
43 | DMA.CH1.TRIGSRC = DMA_CH_TRIGSRC_TCD0_CCA_gc; // Trigger ist Compare-Channel A des Zählers TCD0 |
44 | |
45 | DMA.CH1.TRFCNT = 1; // Genau das Interrupt-Flag-Register wird übergeben |
46 | |
47 | DMA.CH1.DESTADDR0 = (( (uint16_t) &TCD0.INTFLAGS) >> 0) & 0xFF; // Die Ziel-Adresse ist das Interrupt-Flag-Register des Zähler TCD0 |
48 | DMA.CH1.DESTADDR1 = (( (uint16_t) &TCD0.INTFLAGS) >> 8) & 0xFF; |
49 | DMA.CH1.DESTADDR2 = 0; |
50 | DMA.CH1.SRCADDR0 = (( (uint16_t) &Flag_del) >> 0) & 0xFF; // Die Quell-Adresse ist die Adresse der Bit-Maske zum Löschen der Flags |
51 | DMA.CH1.SRCADDR1 = (( (uint16_t) &Flag_del) >> 8) & 0xFF; // |
52 | DMA.CH1.SRCADDR2 = 0; |
53 | }
|
Und hier der Aufruf (es werden über data_table Compare-Werte übergeben) ...
1 | TCD0.CTRLA = (TCD0.CTRLA & (~TC1_CLKSEL_gm)); // Timer f. Belichtung ausschalten |
2 | TCD0.CTRLC = 0x00; // Clear WG output at OC0A (Pin0, PORTD) |
3 | |
4 | // TCD0.CCA = *data++; // Den ersten Wert aus dem Zeilenspeicher und als Vergleichswert setzen
|
5 | TCD0.CCA = *data; // Den ersten Wert aus dem Zeilenspeicher und als Vergleichswert setzen |
6 | // TCD0.CCABUF = *data++; // Den zweiten Wert aus dem Zeilenspeicher holen und in den Vergleichspuffer schreiben
|
7 | TCD0.CNT = 0x0000; // Sicherheitshalber ... |
8 | |
9 | |
10 | data_table[(data_size / 2) - 1] = ( last_line_start |
11 | + end_delay |
12 | - data_sum |
13 | - begin_delay ); // Als letzten Wert den nächsten synch-Wert übergeben |
14 | |
15 | DMA.CH0.TRFCNT = data_size; // Die Anzahl per DMA zu übergebender Bytes |
16 | DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm; // DMA starten |
17 | DMA.CH1.CTRLA |= DMA_CH_ENABLE_bm; // DMA starten |
18 | |
19 | TCD0.CTRLA = (TCD0.CTRLA & (~TC1_CLKSEL_gm)) // Timer f. Belichtung starten |
20 | | TC_CLKSEL_DIV2_gc; // |
Gruß Dieter