Kampis Elektroecke

XMega – DMA

XMega Blockdiagramm

Mit Hilfe des DMA-Controllers können sowohl kleine als auch große Datenmengen mit einer minimalen CPU Interaktion kopiert werden. Dadurch ist es z. B. möglich hochfrequente Messergebnisse des ADCs in den Speicher zu schreiben, ohne das die CPU blockiert wird. In diesem Teil des Tutorials zeige ich, wie mit Hilfe des DMA-Controllers der ADC ausgelesen und das Resultat automatisch zum DAC kopiert wird, der das Ergebnis als analoge Spannung ausgibt.

DAC und ADC konfigurieren:

Für dieses Projekt werden, neben dem DMA, auch der DAC und der ADC benötigt. Die Konfiguration des DAC soll folgendermaßen aussehen:

  • DACB
  • 1 V Referenzspannung
  • Kanal 0

Und für den ADC:

  • ADCA
  • 1 V Referenzspannung
  • Ungetriggerter Modus
  • Unsigned 12-Bit Modus
  • Taktvorteiler 32
  • Pin 0, Kanal 0
  • Single-ended mit Gain x1

Damit sind die benötigten Peripheriegeräte einsatzbereit. Nun muss der DMA-Controller konfiguriert werden…

Konfiguration des DMA-Controllers:

Mit dem DMA können Datenblöcke mit einer Größe von 1 Byte bis 64 kB ohne CPU Unterstützung übertragen werden. Da die CPU und der DMA-Controller den gleichen Datenbus verwenden, wird jeder Datenblock in kleinere Segmente zerlegt. Der DMA-Controller meldet dann einen Datentransfer an, wodurch der Datenbus von der CPU getrennt und vom DMA-Controller kontrolliert wird. Während dieser Phase sendet der DMA-Controller eine bestimmte Menge Daten und übergibt danach den Bus wieder an die CPU.

Diese Transferart wird Burst transfer genannt und der DMA-Controller kann 1, 2, 4 oder 8 Bytes in einem Burst übertragen. Eine komplette DMA transaction besteht zudem aus 1256 Blöcken. Über das REPCNT-Register kann eine DMA-Übertragung mit identischen Einstellungen bis zu 255x wiederholt werden. Der Wert 0 gibt dabei unbegrenzte Wiederholungen an.

In diesem Beispiel sollen die Daten vom ADC an den DAC gesendet werden. Der ADC besitzt eine Auflösung von
12-Bit, was einer Datenmenge von 2 Byte entspricht. Ich habe mich für eine Burst-Länge von 2 Byte entschieden, wodurch das komplette ADC Ergebnis in einem Rutsch kopiert werden kann.

Zuerst werden ein paar allgemeine Einstellungen am DMA-Controller und dem verwendeten Kanal (hier Kanal 0) vorgenommen. Es werden der DMA-Controller und die Interrupts für eine komplette DMA-Übertragung mit der Priorität Niedrig aktiviert.

Die Blockänge (hier 2 Byte) wird über das TRFCNT-Register des DMA-Kanals festgelegt und im CTRLA-Register des jeweiligen Kanals wird die Länge des Burst (hier 2 Byte) eingestellt.

Als nächstes wird der DMA so konfiguriert, dass die Übertragung direkt durch den ADC angestoßen wird. Dazu wird der Auslöser (hier Kanal 0 des ADCA) in das TRIGSRC-Register des DMA-Kanals geschrieben:

Als Quell- und Zieladresse werden die Datenregister des verwendeten ADC-Kanals, bzw. des verwendeten DAC-Kanals angegeben:

Jetzt muss noch der Addressmodus über das ADDRCTRL-Register des DMA-Kanals konfiguriert werden. Der DMA-Controller ist in der Lage sowohl die Quell-, als auch die Zieladresse bei jedem Burst- oder Blocktransfer, bzw. jeder abgeschlossenen Übertragung zu erhöhen, bzw. zu verringern oder neu zu laden. Da in diesem Beispiel permanent aus dem Datenregister des ADC gelesen und in das Datenregister des DAC geschrieben werden soll, wird hier der Reload-Modus verwendet. Zudem muss nach jedem gesendeten Byte der Adresszeiger des DMA-Controllers sowohl für die Quell-, als auch für die Zieladresse erhöht werden.

Jetzt müssen noch die entsprechenden Interruptlevel freigegeben und die globalen Interrupts aktiviert werden.

Der DMA-Kanal wird dann durch das Setzen des ENABLE-Bits in den Bereitschaftsmodus gesetzt.

In diesem Modus wartet der DMA-Controller auf ein Triggersignal (falls einer ausgewählt worden ist, ansonsten kann es auch per Software ausgelöst werden) und sobald ein Signal kommt, wird die Übertragung gestartet.

Sobald der ADC mit der Wandlung fertig ist, wird die Übertragung mittels DMA angestoßen. Der DMA-Controller generiert nach einer erfolgreichen Übertragung einen Transaction Complete-Interrupt. In der jeweiligen ISR muss dann das Interruptflag gelöscht und der DMA-Kanal erneut aktiviert werden.

Die ISR ist das einzige mal wo die CPU aktiv werden muss. Alles andere läuft vollkommen automatisch ab.

Der DMA im Einsatz:

Zum Abschluss soll das Programm noch getestet werden. Dafür habe ich mein XMega-A3BU Xplained verwendet, da dieses Board über einen integrierten analogen Lichtsensor verfügt und dieser direkt mit dem ADC ausgelesen werden kann. Die analoge Spannung des DAC kann dann über Pin 2 des Headers J2 abgegriffen werden. Das ganze sieht dann folgendermaßen aus…

Huch… Woher kommt den dieses seltsame Dreieckssignal in der Ausgangsspannung vom DAC?

Das abgebildete Dreieckssignal resultiert aus der Netzfrequenz (50 Hz) der Leuchtstoffröhren, die diesen Raum beleuchten. Der ADC arbeitet so schnell, dass das Glimmern als Belichtungsdifferenz in der analogen Spannung des Lichtsensors sichtbar wird.

Das komplette Projekt findet ihr in meinem GitLab-Repository.

Last Updated on

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.