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
voiddma_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
Dieter Frohnapfel schrieb:> obwohl ich> das entsprechende Interrupt-Flag (per DMA :-) ) lösche.
Sorry keine echte Vermutung jetzt. Die Idee mit der Rücksetzung in einem
zweiten DMA-Channel hatte ich noch nicht...
Aber hast Du Flaglöschung (1->TRNIF) schon mal ganz regulär via
zugehörigem DMA-Interrupt probiert?
Moby schrieb:> Aber hast Du Flaglöschung (1->TRNIF) schon mal ganz regulär via> zugehörigem DMA-Interrupt probiert?
Ich beziehe mich nicht auf ein DMA-Flag sondern auf das Compare-Flag des
Timers in TCD0.INTFLAGS. Das kann ich so am schnellsten Löschen, da ich
den Interrupt nicht aktivieren wollte - bzw. die Interrupt-Behandlung
vermeiden wollte.
Ich kann mir ehrlich gesagt nicht vorstellen, dass man das Compareflag
explizit per DMA (oder etwa per ISR o.ä.) löschen soll, das wäre
ziemliche Verschwendung von Ressourcen.
Ich würde erwarten dass der DMA durch eine positive Flanke des
Compareflags ausgelöst wird. Aber da müsste man nochmal ganz genau in
die Doku schauen...
Dieter Frohnapfel schrieb:> Compare-Flag des> Timers in TCD0.INTFLAGS. Das kann ich so am schnellsten Löschen,
Warum sollte das denn explizit gelöscht werden müssen?
"A DMA read or write access of the corresponding CCx or
CCxBUF will then clear the CCxIF and release the request."
Warum dann das Rücksetzen im zweiten Channel?
Moby schrieb:> A DMA read or write access of the corresponding CCx or> CCxBUF will then clear the CCxIF and release the request.
Wo hast Du das denn gefunden? Kannst Du mir bitte die Quelle nennen?
-> Hat sich erledigt, habs gefunden - vielen Dank.
Moby schrieb:> Warum dann das Rücksetzen im zweiten Channel?
Weil es bei mir nicht so funktioniert, wie es vorstehend beschrieben ist
:-(
Hallo an alle,
aktuell stelle ich meine Frage mal zurück - ich muss selbst erst noch
mal nachdenken. Wenn ich nicht mehr oder weniger neben der Spur bin
stimmen da Dokumentation und Realität nicht überein.
Vielen Dank für die Antworten.
Gruß
Dieter
Dieter Frohnapfel schrieb:> DMA.CTRL = DMA_CH_ENABLE_bm // DMA Aktivieren
Ist streng genommen der falsche Namen, DMA_ENABLE_bm wäre korrekt,
denn die Aktion bezieht sich nicht auf den Kanal, sondern den
kompletten DMAC.
Ist allerdings beides 0x80.
So ganz verstehe ich nicht, warum das bei dir immer weiter laufen
sollte. Ich habe DMA letztens mal benutzt, um eine Ausgabe auf
einem Pin zu organisieren, der nicht direkt als Ausgangspin für einen
Timer konzpiert ist. Das sieht bei mir so aus:
Der default count ist 1, es wird also jeweils genau ein Byte
übertragen. Das übertragene Byte ist das gleiche ;) (Variable
dmaval), aber Ziel ist einmal das Register zu Setzen und das
andere Mal das zum Löschen eines Portpins.
In den ISRs wird jeweils der eine oder andere DMA-Kanal dann
(einmalig) angestoßen:
1
ISR(TCC0_OVF_vect)
2
{
3
DMA.CH0.CTRLA=DMA_CH_ENABLE_bm;
4
}
5
6
ISR(TCC0_CCA_vect)
7
{
8
DMA.CH1.CTRLA=DMA_CH_ENABLE_bm;
9
// hier passiert noch mehr, jetzt aber uninteressant
10
}
Vielleicht hilft dir das ja als Anregung weiter, scheint mir von
deinem Vorhaben nicht zu weit entfernt zu sein (außer dass ich
richtige ISRs habe, um die Interruptflags zu löschen).
Jörg Wunsch schrieb:> Ist streng genommen der falsche Namen, DMA_ENABLE_bm wäre korrekt,> denn die Aktion bezieht sich nicht auf den Kanal, sondern den> kompletten DMAC.
Hallo Jörg,
da hast Du allerdings Recht - werde ich korrigieren.
Dein Code-Schnipsel unterscheidet sich jetzt nicht so wesentlich von
meinem - abgesehen davon, dass ich eine Timer-Daten-Tablle im single
shot modus mit 2 Byte burstlänge übertragen will.
Es kommt übrigens nichts im CCA-Register an :-( obwohl der DMA arbeitet
(kann ich per JTAG gut beobachten). Mit dem nahezu identischen Code
schreibe ich aber ohne Probleme in das INTFLAGS-Register. Ein Aktivieren
und Behandeln (leer) des Interrupts brachte übrigens auch nichts.
Ich werde wohl als nächstes mal versuchen, CCABUF zu beschreiben. Mal
schauen, ob das funktioniert. Man könnte beiliegende Dokumentation so
deuten, dass bei Output Compare nur der Zugriff auf CCxBUF das
Interrupt-Flag löscht. Mal schauen ...
Gruß
Dieter
Moby schrieb:> "A DMA read or write access of the corresponding CCx or> CCxBUF will then clear the CCxIF and release the request."
Das funktioniert bei mir definitiv nicht - zumindest nicht burst- oder
block-weise.
Markus M. schrieb:> Ich kann mir ehrlich gesagt nicht vorstellen, dass man das Compareflag> explizit per DMA (oder etwa per ISR o.ä.) löschen soll, das wäre> ziemliche Verschwendung von Ressourcen.
Ist es auch, geht aber nicht anders. Ich muss einen Interrupt auslösen
und behandeln, damit das Ganze funktioniert. Habe ich jetzt mit einer
"naked" ISR, die nur ein RETI() enthält, gelöst.
Burst-weise triggern geht in diesem Fall auch nicht, das habe ich
ausschließlich block-weise (bzw. wenn burst- gleich block-länge ist)
hinbekommen.
Das ganze "Verhalten" ist zumindest für mich logisch nicht
nachvollziehbar - ist mir aber mittlerweile egal, da es jetzt (zumindest
im Mini-Test-Programm) funktioniert. Werde es nun in meine Anwendung
einbauen und schauen, ob alles O.K. ist.
Nochmals vielen Dank an alle.
Gruß
Dieter
Dieter Frohnapfel schrieb:> Das ganze "Verhalten" ist zumindest für mich logisch nicht> nachvollziehbar
Nachvollziehbar ist das vermutlich nur im Verilog-Code von Atmel. ;-)
Du kannst natürlich mal probieren, ein Ticket aufzumachen bei denen,
schließlich hast du ja erstmal alles nach Handbuch gemacht. Je
nachdem, wie engagiert der indische Firstlevel-Supporter gerade ist,
könnte es schon sein, dass da mal jemand ernsthaft reinguckt.