Kampis Elektroecke

XMega – Eventsystem

XMega Blockdiagramm

In diesem Tutorial stelle ich das Eventsystem des XMega vor. Bei der Beispielanwendung handelt es sich um eine modifizierte Version meines DMA-Beispiels. In diesem Beispiel soll das Eventsystem genutzt werden, um per Tastendruck eine ADC-Messung auszulösen und nach der Messung wird das das Ergebnis dann per DMA an eine bestimmte Speicheradresse geschrieben.

Über das Eventsystem können verschiedene Peripheriekomponenten des Mikrocontrollers direkt miteinander verbunden werden. Die Funktionsweise hat Atmel sehr schön in den offiziellen Datenblättern der XMega-Mikrocontroller visualisiert.

Die Verbindung erfolgt über ein Event Routing Network, welches über 8 Kanäle verfügt, wobei jeder Kanal durch einen Eingang (z. B. einen bestimmten Pin eines Ports) und mind. einem Ausgang definiert ist. Das besondere am Eventsystem ist, dass es sich bei den Events nicht um Interrupts handelt und diese somit nicht von der CPU bearbeitet werden. Das Eventsystem arbeitet zudem auch unabhängig vom Debugger und kann die Events auch weiterleiten, wenn die CPU und die Peripherie durch den Debugger pausiert sind. Das Eventsystem verfügt zudem auch noch über digitale Filter, mit denen Signale, die kürzer sind als eine bestimmte Anzahl Taktperioden, herausgefiltert werden können.

Die Konfiguration des ADCs und des DMA-Controllers kann größtenteils aus dem DMA-Beispiel übernommen werden:

ADCA.REFCTRL = 0x00;
ADCA.PRESCALER = ADC_PRESCALER0_bm | ADC_PRESCALER1_bm;
ADCA.CTRLA |= ADC_ENABLE_bm;
	
PORTA.DIR &= ~(0x01 << 0x00);
ADCA.CH0.CTRL = ADC_CH_INPUTMODE0_bm;
ADCA.CH0.MUXCTRL = 0x00;

DMA.CTRL = DMA_ENABLE_bm;

DMA.CH0.CTRLB = PMIC_LOLVLEN_bm;
DMA.CH0.TRFCNT = 2;
DMA.CH0.CTRLA = DMA_CH_BURSTLEN0_bm;
DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_ADCA_CH0_gc;
	
DMA.CH0.SRCADDR0 = ((uintptr_t)&ADCA.CH0.RES) & 0xFF;
DMA.CH0.SRCADDR1 = (((uintptr_t)&ADCA.CH0.RES) >> 0x08) & 0xFF;
DMA.CH0.SRCADDR2 = (((uintptr_t)&ADCA.CH0.RES) >> 0x0F) & 0xFF;

DMA.CH0.DESTADDR0 = ((uintptr_t)&DMA_DestinationBuffer) & 0xFF;
DMA.CH0.DESTADDR1 = ((uintptr_t)&DMA_DestinationBuffer >> 0x08) & 0xFF;
DMA.CH0.DESTADDR2 = ((uintptr_t)&DMA_DestinationBuffer >> 0x0F) & 0xFF;
	
DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_BURST_gc | DMA_CH_DESTRELOAD_BURST_gc | DMA_CH_SRCDIR_INC_gc | DMA_CH_DESTDIR_INC_gc ;

PMIC.CTRL = PMIC_LOLVLEN_bm;
sei();
	
DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;

Der ADC wird in diesem Beispiel nicht im Freerun-Modus betrieben. Stattdessen soll bei jedem Tastendruck eine Messung durchgeführt werden, weshalb das CTRLB-Register des ADCs nicht konfiguriert werden muss. Die Zieladresse des DMA-Controllers ist jetzt auch nicht mehr der DAC, sondern der Speicherbereich eines Arrays, welches die Daten des ADCs halten soll.

uint8_t DMA_DestinationBuffer[2];

Alles was jetzt noch fehlt ist die Anbindung des ADCs und des Tasters an das Eventnetzwerk. Die Anbindung des ADCs an das Eventnetzwerk wird über das EVCTRL-Register gesteuert. Der ADC wird so konfiguriert, dass er bei einem Triggersignal über Kanal 7 des Eventsystems nur Kanal 0 verwendet:

ADCA.EVCTRL = (ADC_EVSEL2_bm | ADC_EVSEL1_bm | ADC_EVSEL0_bm) | ADC_EVACT0_bm;

Mit dieser Konfiguration werden die Eventkanäle 0 – 3 als Eingänge für den ADC genutzt, wobei allerdings nur Kanal 0 eine Messung auslösen kann (ADC_EVACT0). Da keine Sweep-Optionen gesetzt worden sind, wird lediglich Kanal 0 ausgelesen.

Jetzt muss nur noch der Taster an das Eventnetzwerk angebunden werden. Bei meinem verwendeten XPlained ist der Taster SW0 an Pin E.5 angebunden. Dieser Pin wird zuerst als Eingang deklariert. Zusätzlich wird die Input Sense Configuration für fallende Flanken konfiguriert, da der SW0 gegen Masse schaltet:

PORTE.DIRCLR = (0x01 << 0x05);
PORTE.PIN5CTRL = PORT_ISC_FALLING_gc;

Die Eingangsquelle für das Eventnetzwerk wird im MUX-Registers des jeweiligen Kanals gesetzt. Das Bitmuster kann der Tabelle 6-3 aus dem Datenblatt entnommen werden.

Daraus ergibt sich der folgende Code:

EVSYS.CH7MUX = EVSYS_CHMUX_PORTE_PIN5_gc;
EVSYS.CH7CTRL = (EVSYS_DIGFILT2_bm | EVSYS_DIGFILT1_bp | EVSYS_DIGFILT0_bm);

Ich habe zusätzlich noch einen digitalen Filter verwendet, damit das Prellen des Tasters reduziert, bzw. verhindert wird. Das Signal des Tasters muss für 8 Taktzyklen anliegen, damit es an das Eventsystem weitergeleitet wird.

Damit ist das Programm vollständig. Jetzt wird bei jedem Tastendruck ein Sample des ADCs vollautomatisch aufgenommen und an eine feste Speicherstelle geschrieben. Das ganze Beispiel läuft mit einer minimalen CPU-Belastung, da die CPU lediglich in der Interruptroutine des DMA-Kanals den Kanal wieder aktivieren muss. Das Triggern des ADCs läuft parallel zur CPU über das Eventsystem und die Daten werden dann mittels DMA kopiert, sobald der ADC seine Messung beendet hat.

Das komplette Projekt steht in meinem GitHub-Repository zum Download bereit.

Schreibe einen Kommentar

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