Komme hier nicht richtig weiter.
Die Datenquelle liefert 16-Bit-Werte in zwei 8-Bit-Häppchen, jeweils mit
einer steigenden Taktflanke.
Die Daten sollen mittels DMA von PORT A gelesen und im Speicher abgelegt
werden.
Der Takt wird vom Timer D0 erzeugt, PWM-Kanal D liefert den externen
Takt, PWM-Kanal C den versetzten Trigger für den DMA-Kanal 0.
Etwa so:
Der DMA schreibt jedoch Blödsinn in den Speicher.
Insbesondere scheint es eine Abhängigkeit vom Burst-Length-Setting zu
geben (bei ansonsten absolut identischer Konfiguration).
Burst-Länge 1 Byte:
7B7B ACAC 7B7B ACAC AC7B 7BAC AC7B 7BAC AC7B 7B7B ACAC 7B7B ACAC 7B7B
Burst-Länge 8 Byte:
7B7B 7B7B 7B7B 7B7B ACAC ACAC ACAC 7B7B 7B7B 7B7B ACAC ACAC ACAC 7B7B
Die Datenquelle liefert 7BAC (oder AC7B, das ist erstmal nicht
entscheidend).
Offensichtlich erfolgt hier keine zuverlässige Synchronisation, der
DMA-Kanal transferiert ein Byte mehrfach.
Möglicherweise liegen noch mehr Dinge in deinem Code im argen, aber
zumindest hierzu kann ich was sagen:
Die Burst-Länge gibt die Größe der Daten an, die bei einem Trigger
kopiert werden sollen. Burst-Transfers von > 1 B verwendet man
beispielsweise zum Kopieren eines ADC Wertes: Der DMA Controller
speichert die 2 Byte der ADC-Messung zwischen und schreibt sie dann mit
1 Byte Zugriffen ins RAM. Das vermeidet, dass Werte gemischt werden,
wenn ein neues ADC-Ergebnis vorliegt (low byte vom alten Wert, high byte
vom neuen).
Für deinen I/O Port sind also nur 1-Byte Bursts sinnvoll (ansonsten
kopierst du das PORTA.IN Register und die nachfolgenden Register), und
immer getriggert durch den Timer.
>> Der DMA schreibt jedoch Blödsinn in den Speicher.> Insbesondere scheint es eine Abhängigkeit vom Burst-Length-Setting zu> geben (bei ansonsten absolut identischer Konfiguration).>> Burst-Länge 1 Byte:>> 7B7B ACAC 7B7B ACAC AC7B 7BAC AC7B 7BAC AC7B 7B7B ACAC 7B7B ACAC 7B7B
Ich vermute, dass du 7BAC sehen möchtest?
Da das mit 1 Byte Bursts nicht zuverlässig funktioniert: hast du die
Setup-/Reaktions-Zeiten deiner datenliefernden Komponente
berücksichtigt?
Wenn das Signal rechteckig ist, wieso triggerst du nicht bei in der
Mitte eines Bits, bei 50% der Periode, wo wie es bei SPI auch üblich
ist?
Des Weiteren kann es sein, dass dein Takt zu groß ist und der DMA mit
dem Kopieren nicht hinterher kommt. Auf das RAM darf nur eine Komponente
zugreifen. Wenn CPU und DMA Peripheral zugreifen wollen, gewinnt immer
die CPU und die DMA Transaktion muss warten (nennt sich
"Arbitrierung/arbitration", steht im Datenblatt).
Heinz Elmann schrieb:> Des Weiteren kann es sein, dass dein Takt zu groß ist und der DMA mit> dem Kopieren nicht hinterher kommt. Auf das RAM darf nur eine Komponente> zugreifen. Wenn CPU und DMA Peripheral zugreifen wollen, gewinnt immer> die CPU und die DMA Transaktion muss warten (nennt sich> "Arbitrierung/arbitration", steht im Datenblatt).
Deinem DMA-freien Beispiel entnehme ich, dass der Takt prinzipiell für
deine Datenquelle ok ist. Allerdings sehe ich, dass du eine
Periodendauer für von 10 Takten verwendest.
Das erhärtet meine Vermutung, dass es ein Arbitrierungsproblem ist. Hast
du zufällig, während die DMA Transaktion läuft, viele
(SRAM-)Speicherzugriffe z.B. kopierst Daten umher? Dann würde die CPU
immer die Arbitrierung gewinnen und der DMA verpasst Ereignisse.
Wenn möglich, erhöhe zum Testen die Timer-Periode auf z.B. 100 Takte und
kopiere weniger/nicht über die CPU währenddessen.
> Die Datenquelle liefert 16-Bit-Werte in zwei 8-Bit-Häppchen, jeweils mit> einer steigenden Taktflanke.
Also dann liest man die Bytes mit fallender Flanke ein (=>
DMA-Trigger)!
Dann könntest du auf den extra DMA-Trigger verzichten.
Wenn der externe Takt ein kleines Puls-Pausen-Verhältnis hat, dann
bekommt die DMA mehr Zeit bis sie tatsächlich kopiert.
Den Trigger für die fallende Flanke erhältst du, wenn die
DMA-Triggersource
auf CCx des PWM-Timers für ext. Takt gelegt wird.
Z.B.: DMA_CH_TRIGSRC_TCE0_CCA_gc
Tipp:
Bei Benutzung der XMega-DMAs kann man vorbeugend einige NOPs in die
Hauptschleife einstreuen. Das sorgt dafür dass der Datenbus
währenddessen für die DMA frei ist.
Herzlichen Dank für die Tips und Hinweise.
Um zu vermeiden, daß auf dem Datenbus irgendwas anderes läuft außer DMA,
habe ich mal von Polling auf Interrupt umgestellt.
Mit obigem Code liest der DMA bspw. folgendes Muster ein:
1313 1313 6767 6767 1313 1313 6767 6767
Eigentlich nicht anders als vorher.
Werden die Timersettings von 20-8-12 auf 100-40-60 geändert, sieht das
dann so aus:
7B7B 7B7B 7B7B 7B7B 7B7B 7B7B 7B7B 7B7B 7B7B 7BAC ACAC ACAC ACAC ACAC
ACAC ACAC ACAC ACAC ACAC
Ich habe mal zwei LA-Screenshots angehangen, auf dem Interface läuft
alles wie vorgesehen, die Bytes werden wie erwähnt mit der steigenden
Taktflanke rausgeschoben.
Irgendwie immer noch so, als würde der DMA sich absolut nicht vom
Timer/Trigger beeindrucken lassen...
Noop schrieb:> Wie kann man sich im 21. Jhd und einem Xmega immernoch Assembler antun?> Ich verstehe es einfach nicht..
Hast Du hier auch was Sinnvolles beizutragen?
Kasper...
Route den Timer auf einen Kanal das Event System und nutze dieses Event
als Trigger für den DMA. Das was du versuchst habe ich schonmal
umgesetzt, es geht.
Hagen R. schrieb:> Route den Timer auf einen Kanal das Event System und nutze dieses> Event> als Trigger für den DMA. Das was du versuchst habe ich schonmal> umgesetzt, es geht.
Du meinst so?
Hab es zum Laufen gebracht.
Woanders hatte sich jemand gefragt, warum ich den DMA-Kanal mit einem
Timer triggere und nicht direkt auf die fallende Flanke am Portpin.
Ist so als direkte Alternative natürlich nicht praktikabel, der Timer
erzeugt ja den Takt.
Trotzdem habe ich den Code mal entsprechend geändert (der Timer macht
weiterhin den Takt).
1
ldi r16,1<<PORT_INVEN_bp|PORT_ISC_FALLING_gc
2
sts (PORTD_PIN3CTRL),r16
3
4
ldi r16,EVSYS_CHMUX_PORTD_PIN3_gc
5
sts (EVSYS_CH0MUX),r16
Hat auf Anhieb allerdings erstmal nichts verbessert.
Also bin ich nochmal durch die Beschreibung des DMAC und habe dann den
entscheidenden Hinweis gefunden.
Das Bit nennt sich Single-Shot Data transfer und macht nach dem
Trigger einen einzelnen Burst-Transfer mit der Burst-Length (hier 1
Byte).
Ich hatte das vorher schon gelesen, aber weil es um maximale
Geschwindigkeit ging, irgendwie ausgeblendet...
Allerdings - mit dem Trigger direkt auf den PWM-Kanal C vom Timer hat es
auch mit diesem Setting nicht richtig funktioniert, es wurden weiterhin
mehrere identische Bytes hintereinander in den Speicher geschrieben.
Egal, im Prinzip geht es nun, es hat sich aber leider gezeigt, daß der
DMA-Transfer trotz aller Optimierung langsamer ist als die Methode "zu
Fuß".
Also lege ich das DMA-Thema erstmal ad acta.