stromflo
I. XMEGA-C-Tutorial – Teil 1 Einführung,I/O und Timer

Autor: Florian Grotz
Revision: 1.0
Das Werk ist urheberrechtlich geschützt.
Atmel®, das Atmel-Logo und entsprechende Kombinationen, AVR®, XMEGA® und andere sind registrierte Warenzeichen oder Warenzeichen der Atmel Corporation oder ihrer Tochterfirmen.

Vorwort:
Mit der ATxmega Familie bringt Atmel® eine neue Generation von 8-Bit Controllern, bei denen sich auch am Registersatz in Bezug auf ATtiny und ATmega einiges geändert hat. Dieses Tutorial soll keine Musterlösung für ein Projekt sein, sondern Grundkonfigurationen mit der neuen XMEGA® Familie aufzeigen und den Einstieg durch Beispiele erleichtern. Das Werk ist auf Basis von GNU GCC Compilern geschrieben. Da die neue Controllerreihe sehr umfangreich ist, kann nicht auf alle Möglichkeiten eingegangen werden. Dies würde das Werk sprengen. Sehr wichtig ist deshalb, das beim Durcharbeiten die technischen Datenblätter von Atmel zur Verfügung stehen um die nicht beschriebenen bzw. beschriebenen Register noch genauer nachzulesen. Das Tutorial setzt voraus, dass Sie die Programmiersprache C beherrschen und Grundkenntnisse in der Elektronik mitbringen. Ein Oszilloskop ist zwar nicht zwingend erforderlich, einige Beispiele lassen sich aber nur so wirklich überprüfen.

Danksagung:
Vielen Dank an Jens Rapp für die Untersützung bei der Gestaltung des Tutorials.

1. Vorraussetzungen

1.1 Hardware


Die Beispiele sind auf Basis von einem ATxmega32A4U Header Board (X4DIL Modul) von der Firma Reusch Elektronik (1) entstanden. Neben diesem Board können auch andere Entwicklungsboards für das Tutorial verwendet werden (ggf. müssen die Beispiele angepasst werden). Gegenüber ATmega/ATtiny, verfügt der ATxmega nicht mehr über einen so breiten Betriebsspannungsbereich. Die Betriebsspannung beim XMEGA sollte bei 1,6V - 3,3V liegen. Eine Versorgung mit 5V ist nicht direkt möglich. Hierfür muss ein Spannungsregler zwischengeschaltet werden, worauf im Kapitel 1.1.2.2 und Kapitel 1.1.4.1 genauer eingegangen wird.

1.1.1 X4DIL Modul – Technische Daten

Bild "Tutorials:x4dil.jpg"
Das Modul verfügt über eine PDI Schnittstelle, einen Sockel mit externem Quarz und eine USB Schnittstelle. Die Pins werden auf Stiftleisten geführt. Der Controller ATxmega32A4U kommt bisher ohne Bootloader und somit muss der Bootloader einmal über JTAG oder PDI programmiert werden. Das X4DIL Modul wird mit Bootloader ausgeliefert und kann direkt über Flip von Atmel programmiert werden. Am Reset ist bereits ein 10K Widerstand integriert. Um das Baord mit dem PC zu verbinden wird ein USB Kabel Typ (Stecker USB-A, Stecker USB-mini) benötigt.


Das folgende Bild demonstriert das Pinout vom Board:

Bild "Tutorials:xmegaa4u_pinout.png"

Achtung! Am Pin40 liegen 5V an! Siehe 1.2.3.3.

1.1.2 X4DIL – Spannungsversorgungen


1.1.2.1 Spannungsversorgung über USB


Bild "Tutorials:gundschaltung_usb.gif"

In den Beispielschaltungen vom XMEGA-C-Tutorial – Teil 1 wird auf die Versorgung durch die USB Schnittstelle zurückgegriffen. Die Schaltungen dürfen eine maximale Stromaufnahme von 100mA nicht überschreiten.

1.1.2.2 Spannungsversorgung extern


Bild "Tutorials:grundschaltung_spannungsregler.gif"

Neben der Versorgung über USB kann auch eine externe Spannungsquelle verwendet werden.
Achtunng: Spannung muss durch einen Spannungsregler auf 3,3V geregelt werden, außer es wird ein Netzteil mit 3,3V verwendet!
Die genaue Verschaltung eines 3,3V Spannungsreglers finden sie im Kapitel 1.1.4.1.

1.1.3 Grundaufbau auf Lochraster


Um erste Versuche mit dem X4DIL Aufsteckboard , das im Kapitel 1.1.1 vorgestellt wird, durchführen zu können, wird ein Lochrasteraufbau empfohlen. Das Bild zeigt einen möglichen Aufbau eines Testboards. Die USB-Buchse muss wie im Bild unten positioniert sein.

Bild "Tutorials:1.1.3_grundaufbau.png"

Stückliste für Testaufbau:

    7x Taster
    8x LowCurrent LED !
    8x 1K Vorwiderstände für Leds
    1x 10K Widerstand
    2x Stiftleisten 20 polig
    14x Stiftleiste 1 polig
    1x Stiftleiste 16 polig
    2x Stiftleise 4 polig (1x für GND durchverbinden, 1x für VCC durchverbinden)
    2x Buchsenleiste 20polig

Die benötigten Konfigurationen können mit Drahtbrücken auch „jump wires“ genannt, gesteckt werden. Solche Drahtbrücken können entweder selber gefertigt werden oder sind in einigen Onlineshops erhältlich. Die Drahtbrücken sind in den Schaltungen durch etwas dickere Linien in verschiedenen Farben ersichtlich. Die Beispiele im Tutorial können über diesen Aufbau nachvollzogen werden.
Ab Revision 2.0 wird ein Pullup-Widerstand von 100K im X4DIL Modul integriert sein. Der 10K Widerstand am HWB Taster kann, dann entfallen. Das Modul wird zurzeit noch nicht in dieser Revision ausgeliefert.

Die folgende Grafik zeigt die Lötseite vom Testaufbau. Die schwarzen Linien mit Punkten an den Enden stellt die Brücken dar, die eingelötet werden müssen.

Bild "Tutorials:1.1.3_grundaufbau_loetseite.png"

Diese Grafik soll den Aufbau eines eigenen Testboards vereinfachen.

1.1.4 Weitere Hilfestellungen zum Designen von eigener Hardware


1.1.4.1 Spannungsregler 3,3V


Bild "Tutorials:3v3_spannungsversorgung.gif"

1.1.4.2 Levelschifter 5V – 3,3V


Des öfteren benötigen Peripheriebausteine einen Spannungspegel von 5V. Was können wir machen, wenn der ATxmega am Ausgang nur 3,3V liefert. Antwort: Das Level durch einen Level-Shifter auf 5V anheben. Die folgende Schaltung zeigt einen möglichen Aufbau.

Bild "Tutorials:level_shifter.gif"

In diesem Beispiel wird der Level-Shifter mit Hilfe des 4xNAND-Gatters 74LS00 realisiert. Dabei macht man sich zu Nutze, dass der Baustein 74LS00 Spannungen über 2V als High Pegel erkennt. Um am Ausgang kein invertiertes Ergebnis zu erhalten, werden zwei Nandglieder wie im Beispiel gezeigt, verbunden. Dadurch erreicht man eine doppelte Negation, was auch als Bejahung bezeichnet wird. Der 74LS00 wird mit 5V versorgt. Am Ausgang entsteht nun eine Spannung von 5V für die weitere Verarbeitung. Mit Hilfe eines 74LS00 können gleich 2 Level-Shifter aufgebaut werden, da jeweils nur 2-Nand Gatter benötigt werden. Bedeutend schwieriger kann die Situation noch werden, wenn beispielsweise hohe Geschwindigkeiten von Nöten sind. Bei einschlägigen Elektronikhändlern gibt es hierzu fertige IC-Bausteine.

1.1.5 Programmiergerät


Um die ATxmega Controller zu programmieren wird in der Regel ein Programmiergerät benötigt. Die nachfolgende Liste zeigt Programmiergeräte von der Firma Atmel.

    Atmel AVRISP MKII
    Atmel AVR® Dragon
    AVR JTAGICE mkII
    Atmel JTAGICE3

Einige Evalutationsbaords bringen auch eine USB Schnittstelle und einen Bootloader mit um den Controller auch direkt über USB programmieren zu können. Dies ist auch beim unter Punkt 1.1.1 vorgestellten X4DIL Modul der Fall.

1.1.6 Controller aus der XMEGA Reihe


Die neue ATxmega Reihe umfasst natürlich nicht nur einen Controller sondern kommt mit einer ganzen Familie. Die XMEGA Familie wird durch verschiedene Baugrößen (IO Anzahl) und Peripherien eingeteilt. Innerhalb dieser Gruppen unterscheiden sich die Controller vor allem in der Ausstattung vom SRAM und Flash. Im Gegensatz zu den Vorgängern baut der XMEGA auf eine konsequentere Struktur auf, was zu einer höheren Kompatibilität zu anderen XMEGAs führt. An dieser Stelle werden wir lediglich auf den im Tutorial verwendeten ATxmega32A4U Controller genauer eingehen. Der Atxmega32A4U bringt eine integrierte USB Schnittstelle mit.

1.1.6.1 Pinout ATxmega32A4U


Bild "Tutorials:blockdiagramm.png"
„Quelle: Datenblatt 8387C–AVR–3/12, Copyright Atmel“

1.1.6.2 Pinfunktionen


Die Datenblattauschnitte zeigen die Funktionen für die einzelnen Ports bzw. Pins vom Atxmega32A4U. Diese Tabelle vereinfacht das Erstellen einer Hardware.

  • PORTA
Bild "Tutorials:porta.png"
„Quelle: Datenblatt 8387C–AVR–3/12, Copyright Atmel“

  • PORTB
Bild "Tutorials:portb.png"
„Quelle: Datenblatt 8387C–AVR–3/12, Copyright Atmel“

  • PORTC
Bild "Tutorials:portc.png"
„Quelle: Datenblatt 8387C–AVR–3/12, Copyright Atmel“

  • PORTD
Bild "Tutorials:portd.png"
„Quelle: Datenblatt 8387C–AVR–3/12, Copyright Atmel“

  • PORTE
Bild "Tutorials:porte.png"
„Quelle: Datenblatt 8387C–AVR–3/12, Copyright Atmel“

  • PORTR
Bild "Tutorials:portr.png"
„Quelle: Datenblatt 8387C–AVR–3/12, Copyright Atmel“

1.2 Software


Um das Programmieren, Compilieren und Übertragen der Hexfiles auf den Controller zu ermöglichen wird eine Software benötigt.

1.2.1 Atmel Studio


Eine umfangreiche Entwicklungsumgebung bietet der Herseller Atmel. Aktuell ist im Moment Atmel Studio 6.0 (3). Mehr Informationen erhält man auf der Internetseite von Atmel.

1.2.2 Eclipse IDE


Eclipse (5) und das AVR Plugin stellt eine weitere Umgebung zur Entwicklung von Programmen für die AVR Controller dar.

1.2.3 Flashen der Controller mit FLIP


Das X4DIL Modul kann mit dem dem Tool FLIP von Atmel (2) programmiert werden. Das Tool wird von Atmel kostenlos zum Download bereitgestellt. Im folgendem Kapitel wird demonstriert wie einfach damit ein Programm in den Flash des Controllers gelangt. 1.2.3.1 Erste Schritte nach der Installation Nachdem das Tool erfolgreich installiert wurde, muss zunächst aus dem folgenden Beispiel (Blinker) ein Hexfile (.hex) compiliert werden. Hierfür verwenden Sie eine Entwicklungsumgebung, die den ATxmega32A4U untersützt.

1.2.3.1 Erste Schritte nach der Installation


Nachdem das Tool erfolgreich installiert wurde, muss zunächst aus dem folgenden Beispiel (Blinker) ein Hexfile (.hex) compiliert werden. Hierfür verwenden Sie eine Entwicklungsumgebung, die den ATxmega32A4U untersützt.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
void io_init(void);
void clock_init(void);
int main(void){
io_init();
clock_init();
while(1){
//Der Ausgang PC0 wird getoggelt
PORTC.OUT = PIN0_bm;
for (uint8_t i = 0;i<10;i++){
_delay_ms(100);
}
}
return 0;
}
void io_init(void){
PORTC.DIR = PIN0_bm;
}
void clock_init(void){
//Oszillator auf 32Mhz stellen
OSC.CTRL |= OSC_RC32MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC32MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC32M_gc;
}

Auf den Programmcode wird an dieser Stelle nicht genauer eingegangen, da er lediglich der Beschreibung zum Programmieren des Controllers dient.

1.2.3.2 Testaufbau


-Schaltplan

Bild "Tutorials:flip_beispiel.gif"

Um am Ende auch eine Kontrolle zu haben, ob die Übertragung funktioniert hat bzw. das Programm läuft, sollte die Verschaltung der LED mit ausgeführt werden.

Verwendete Aus- und Eingabemöglichkeiten:
2x Taster (Reset u. Boot Taster)
1x LED

– Layout

Bild "Tutorials:1.2.3.1_blinker.png"

Um die Übersichtlichkeit zu verbessern ist das X4DIL Modul hier und in allen weiteren Layouts nicht mehr eingezeichnet.

1.2.3.3 Inbetriebnahme der Schaltung


Achtung:
Vor der Inbetriebnahme sollte die Verschaltung nochmals geprüft werden um Fehler auszuschließen. Da die Schaltungen direkt mit der Spannung vom USB versorgt werden, sollte hierauf besonderes Augenmerk gelegt werden.
Nach der Kontrolle kann das Board mit der USB Schnittstelle des PC's versorgt werden. Beim ersten Starten fordert Sie der PC zur Installation eines Treibers auf. Dieser ist in der Installation von FLIP enthalten. Geben Sie als Installationspfad für den Treiber das Programmverzeichnis von FLIP an. Nach erfolgreicher Treiberinstallation öffnen Sie das Programm FLIP.

Der Pin40 des X4DIL Moduls führt eine Spannung von 5V und darf deshalb nicht direkt mit einem anderen PIN des Moduls verbunden werden!

1.2.3.4 Konfigurationen in FLIP von Atmel


1. Schritt: Den ATxmega32A4U als Device auswählen

Bild "Tutorials:2012-05-08_222014.png"

Bild "Tutorials:2012-05-08_222030.png"

Nachdem der Menüpunkt Select aufgerufen wurde, erscheint Device Selection. Hier muss der ATxmega32A4U ausgewählt und anschließend bestätigt werden.

2. Schritt: Das generierte Hexfile ins Programm laden

Bild "Tutorials:2012-05-08_222615.png"

Nach dem Menüaufruf Load Hex File, kann das Hexfile ausgewählt werden. Hier rufen Sie nun das compilierte Beispiel Blinker.hex auf.

3. Schritt: Den ATxmega32A4U in den Bootmodus bringen

Bevor der Controller programmiert werden kann müssen Sie den Boot Taster gedrückt halten und dabei den Reset Taster kurz drücken. Anschließend beide Taster loslassen.

Schritt 4:Verbinden mit USB Schnittstelle

Bild "Tutorials:2012-05-08_222146.png"

Nach Anklicken des USB Symbols erscheint das angezeigte Fenster. Hier auf Open klicken. Anschließend verbindet sich der PC mit dem X4DIL Modul.

Schritt 5: Controller programmieren

Bild "Tutorials:2012-05-08_222530.png"

Erscheint nun das vorher ausgegraute Fenster klar haben Sie die Sicherheit, dass die Verbindung hergestellt wurde. Mit einem Klick auf Run starten Sie die Programmierung.

Schritt6: Schaltung resetten um in den Programmmodus zu kommen

Hierfür können Sie entweder den Weg über Start Application in FLIP wählen oder die Schaltung extern mit der Reset Taste neustarten. Blinkt nun die LED so haben Sie Ihr erstes Programm auf dem Atxmega32A4U erfolgreich getestet.

2. Bitmaskierungen der ATxmega Register


Mit der Einführung des ATxmega kommt eine neue Schreibweise mit dazu. Die Masken lassen sich durch Ihre Endungen voneinander unterscheiden.

_bm= Bitmaske (wird verwendet um einzelne Bits zu setzen oder zu löschen)
_bp= Bitposition (wird verwendet um einzelne Bitpositionen zu setzen oder zu löschen)
_gm= Gruppenbitmaske (wird verwendet um Konfigruationen zu löschen)
_gc= Bit Gruppenkonfigurationsmaske (wird verwendet um Konfigurationen zu setzen)

2.1 _bm (Bitmaske)


Mit _bm können einzelne Bits des jeweiligen Registers gesetzt oder gelöscht werden. Das folgende Beispiel setzt das CLKSEL1 Bit für den Timer um einen Vorteiler von 2 einzustellen.

//Vorteiler auf 2 setzen
TCD1.CTRLA = TC1_CLKSEL1_bm;

2.2 _bp (Bitposition)


Mit _bp kann eine bestimmte Bitposition angesprochen werden.

//PE0 auf High schalten
PORTE.OUT = (1<<PIN0_bp);

2.3 _gm (Gruppenbitmaske )


Mit _gm können Registerinhalte komplett gelöscht werden.

//Vorteiler löschen
TCD1.CTRLA = ~TC_CLKSEL_gm;

2.4 _gc (Bit Gruppenkonfigurationsmaske )


Mit _gc können Gruppenkonfigurationen durchgeführt werden.

Bild "Tutorials:erklaerung_gc.png"

„Quelle: Datenblatt 8331B–AVR–03/12, Copyright Atmel“

Diese Tabellen stehen bei vielen Registerbeschreibungen im Datenblatt. Hier kann die Bezeichnung der Gruppenkonfigurationsmasken rausgesucht werden.

//2 Vorteiler
TCD1.CTRLA = TC_CLKSEL_DIV2_gc;

3. Clock Einstellungen


Bei ATtiny und ATmega mussten stets Fuse-Bits verändert werden um den Systemtakt umzustellen. Dies kann nun direkt im Programm vorgenommen werden. Es erleichtert den Umgang mit den Controllern damit ungemein. Auch bei neueren Controllern von Atmel wie ATmega32U4 oder ATmega8U2 wurde diese Möglichkeit bereits umgesetzt. Gerade für Anfänger war das Setzen von falschen Fuses immer eine Falle gewesen. Der ATxmega verfügt über einen 32.768kHz, 2Mhz und 32Mhz internen Oszillator. Durch einen einstellbaren Vorteiler, wird die Einstellung von verschiedenen Taktfrequenzen noch flexibler. Nun folgt für einige Möglichkeiten ein Beispiel.

3.1 Interner Oszillator von 2MHz


//Oszillator auf 2Mhz stellen
OSC.CTRL |= OSC_RC2MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC2MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC2M_gc;

3.2 Interner Oszillator von 32MHz


Wer für seine Anwendung ein wenig mehr Rechenleistung haben möchte, kann den Oszillator auf 32Mhz einstellen.

//Oszillator auf 32Mhz stellen
OSC.CTRL |= OSC_RC32MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC32MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC32M_gc;

3.3 Externer Quarz von 16MHz


Neben den internen Taktquellen lassen sich auch externe Taktquellen nutzen, wie das folgende Beispiel zeigt.

//Startzeit von 16CLK und Externer Quarz von 12 bis 16 Mhz
OSC_XOSCCTRL = OSC_XOSCSEL_XTAL_16KCLK_gc | OSC_FRQRANGE_12TO16_gc
// Externen Quarz aktivieren
OSC.CTRL |= OSC_XOSCEN_bm;
//Warten bis der externe Quarz bereit ist
while(!(OSC.STATUS & OSC_XOSCRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
// aktiviert den externen Quarz
CLK.CTRL = CLK_SCLKSEL_XOSC_gc;

4. I/O


Die I/Os werden neben dem Austausch von binären Daten auch als Schnittstelle zu anderen Peripherien verwendet. Wie bereits die bekannte ATmega und ATtiny Familie besitzt natürlich auch der ATxmega die Möglichkeit die herausgeführten Pins als Eingänge oder Ausgänge zu nutzen. Neben dieser Möglichkeit hat der ATxmega auch weit mehr Einstellmöglichkeiten wie Pulldown und Pullup.

4.1 Datenrichtungsregister (DIR)


Über das Register DIR kann die Datenrichtung der einzelnen Pins des jeweiligen Portes bestimmt werden. In der Standardeinstellung sind die Bits auf 0 und somit als Eingänge deklariert. Im Tutorial wird High (logisch 1) für 3,3V und Low (logisch 0) für GND verwendet.

4.1.1 DIR als Ausgang konfigurieren


Setzt alle Bits vom DIR Register des PORTA auf 1 (High).

PORTA.DIR = 0xff;

Setzt Bit0 des Registers DIR vom PORTA auf 1.

PORTA.DIR |= PIN0_bm;

Setzt Bit0 und 1 des DIR Registers vom PORTA auf 1.

PORTA.DIR |= PIN0_bm | PIN1_bm;

Alternative Möglichkeit:
Anstatt der Möglichkeit über Read-Modify-Write (DIR) können die Datenregister auch über (DIRSET) gesetzt werden. DIRSET setzt das jeweilige Bit im DIR Register auf 1 und damit als Ausgang.

Setzt alle Bits des DIR Registers von PORTA auf 1.

PORTA.DIRSET = 0xff;

4.1.2 DIR als Eingang konfigurieren


Setzt alle Bits vom DIR Register des PORTD auf 0 (Low).

PORTD.DIR = 0x00;

Löscht Bit0 des DIR Registers vom PORTD.

PORTD.DIR &= ~PIN0_bm;

Alternative Möglichkeit:
Anstatt der Möglichkeit über Read-Modify-Write (DIR) können die Datenregister auch über (DIRCLR) gelöscht werden. DIRCLR setzt das jeweilige Bit im DIR Register auf 0.

Setzt alle Bits des DIR Registers auf 0.

PORTD.DIRCLR = 0xff;

4.1.3 Datenrichtung toggeln (DIRTGL)


In der neuen Serie von ATxmega ist jetzt auch das direkte toggeln vom Datenrichtungsregister möglich. Da der Begriff toggeln im Tutorial noch häufiger verwendet wird, folgt hier eine kurze Erklärung. Toggeln bedeutet, man invertiert ein oder mehrere Bits. Aus einer 0 wird durch die XOR-Verknüpfung eine 1 und aus einer 1 wird eine 0. Beim ATmega und ATtiny konnte über die Bitmanipulation DDRC = (1«DDC0); das toggeln eines Bits erreicht werden. Diese Möglichkeit steht Ihnen natürlich auch beim ATxmega noch zur Verfügung. Zusätzlich hat der Atxmega aber ein zusätzliches Register, nämlich DIRTGL.

War ein Bit im DIR Register von PORTD zuvor 0 wird es zu 1, war ein Bit 1 wird es zu 0!

PORTD.DIRTGL = 0xff;

Der Zustand von Bit0 und Bit1 von PORTD.DIR wird getoggelt.

PORTD.DIRTGL |= PIN0_bm |PIN1_bm;

4.2 Ausgänge schalten (OUT)


Setzt alle Bits vom OUT Register des PORTA auf 1.

PORTA.OUT = 0xff;

Setzt Bit0 vom Out Register des PORTA.

PORTA.OUT|= PIN0_bm;

Löscht Bit0 vom Out Register des PORTA.

PORTA.OUT &= ~PIN0_bm;

Alternative Möglichkeit:
Anstatt der Möglichkeit über Read-Modify-Write (OUT) können die Ausgänge auch über (OUTSET) gesetzt werden. OUTSET setzt das jeweilige Bit im OUT Register auf 1. Über OUTCLR können die Ausgänge gelöscht werden. OUTCLR setzt das jeweilige Bit im OUT Register auf 0.

Setzt alle Bits vom OUT Register des PORTA auf 1.

PORTA.OUTSET = 0xff;

Setzt alle Bits vom OUT Register des PORTA auf 0.

PORTA.OUTCLR = 0xff;

4.3 Ausgänge toggeln (OUTTGL)


Über OUTTGL können im Ausgangsregister (Out) Bits getoggelt werden. Für das zu toggelnde Bit muss eine 1 in das Register OUTTGL geschrieben werden. War das Bit 0 wird es 1 war es 1 wird es 0. Dieses Register ist beim XMEGA vollständig neu hinzugekommen.

Toggelt alle Bits vom OUT Register des PORTA.

PORTA.OUTTGL = 0xff;

Toggelt Bit0 vom OUT Register des PORTA.

PORTA.OUTTGL |= PIN0_bm;

Hier sieht man sehr schön die Konsequente Arbeitsweise der XMEGAs:

Bild "Tutorials:out.png"

Bild "Tutorials:dir.png"

4.4 Eingänge einlesen (IN)


Eingangssignale werden in Variable abgelegt.

uint_8 value;
value = PORTD.IN

4.5 Multikonfigurationsregister (MPCMASK)


Das Multikonfigurationsregister ermöglicht es Pineinstellungen gleich für mehrere Pins zu übernehmen. Durch die MPCMASK werden 1-8 Pins von einem PORT maskiert. Anschließend werden die Einstellungen mit dem PINxCTRL Register geschrieben. Die Maske wird nach diesem Befehl wieder zurückgesetzt und muss bei wiederholter Verwendung neu gesetzt werden.

Alle Pins eines Ports maskieren.

PORTCFG.MPCMASK=0xff;

Im Beispiel wird nur der Pin0,Pin1 und Pin3 eines Ports maskiert.

PORTCFG.MPCMASK= PIN0_bm|PIN1_bm|PIN3_bm;

4.6 Spezielle Pinkonfigurationen (PINxCTRL)


Bei der XMEGA Familie gibt es eine Reihe verschiedener Pinkonfigurationen. Der Ausschnitt zeigt einige der möglichen Konfigurationen mit deren notwendigen Bitgruppen- konfigurationsmasken.Wurde vorher keine Maske konfiguriert, so wird die Konfiguration nur für das gewählte PINxCTRL Register vorgenommen.

4.6.1 Totem-pole (Gegentaktstufe)


Der Ausgang ist low oder high je nachdem wie das Ausgangsregister (OUT) gesetzt ist. Bei einer 1 wird der Ausgang auf High Pegel gesetzt. Bei einer 0 wird der Ausgang auf einen Low Pegel gesetzt. Definiert man den Pin mit dieser Konfiguration als Eingang und ist kein externer Pullup- oder Pulldownwiderstand vorhanden, so ist der Zustand des Eingangs nicht definiert. Erst wenn ein Signal mit High- oder Lowpegel angelegt wird ist das Eingangspotenzial definiert. Diese Einstellung kann verwendet werden wenn z.B. ein externer Pullup oder Pulldown Widerstand vorhanden ist.

PORTD.PIN0CTRL|=PORT_OPC_TOTEM_gc;

4.6.2 Totem-pole with Pull-down


Ist der Pin als Eingang definiert und wird folgende Konfiguration gewählt, so wird der interne Pulldownwiderstand aktiviert. Liegt am Pin kein Signal an, so wird der Pin durch den Pulldownwiderstand auf GND (Low) gezogen.

PORTD.PIN0CTRL|=PORT_OPC_PULLDOWN_gc;

4.6.3 Totem-pole with Pull-up


Ist der Pin als Eingang definiert und wird folgende Konfiguration gewählt, so wird der interne Pullupwiderstand aktiviert. Liegt am Pin kein Signal an, so wird der Pin durch den internen Pullupwiderstand auf VCC(High) gezogen.

PORTD.PIN0CTRL|=PORT_OPC_PULLUP_gc;

4.6.4 Buskeeper


Wird bei einem Eingang die Konfiguration Buskeeper gewählt, wird der letzte Zustand, der an dem Pin anlag gespeichert, bis ein neues Signal anliegt.

PORTD.PIN0CTRL|=PORT_OPC_BUSKEEPER_gc;

4.6.5 Invertieren


Über das gleiche Register, das unter anderem für die Aktivierung von Pullup und Pulldwon verwendet wird, lassen sich auch die Daten an den Pins invertieren. Das Invertieren kann sowohl für IO's die als Eingang als auch als Ausgang deklariert sind verwendet werden.

//Invertiert die Ausgangs oder Eingangsdaten dieses Pins
PORTD.PIN0CTRL|= PORT_INVEN_bm;

4.6.6 Eingangslevel festlegen


Bei der Verwendung von Interrupts, die im Kapitel 4.7 noch näher beschrieben werden, muss ein Eingangslevel für das Auslösen des Interrupts festgelegt werden. Der folgende Datenblattausschnitt zeigt mögliche Konfigurationen.

Bild "Tutorials:input_sense.png"
„Quelle: Datenblatt 8331B–AVR–03/12, Copyright Atmel“

Stellvertretend für die möglichen Einstellungen zeigt der folgende Code die Konfiguration für eine fallende Flanke.

PORTD.PIN0CTRL = PORT_ISC_FALLING_gc;

Damit haben wir das PINxCTRL Register abgeschlossen und widmen uns jetzt den Interrupts, mit denen das Kapitel Eingangslevel bereits etwas zu tun hat.

Durch diese umfangreichen Konfigurationsmöglichkeiten können externe Komponenten eingespart und der Schaltungsaufbau vereinfacht werden.

4.7 Interruptregister


Interrupts sind grundsätzlich interne oder externe Ereignisse, die den eigentlichen Programmfluss unterbrechen. Jeder PORT des ATxmega besitzt zwei unabhängige Interruptvektoren (INT0 und INT1). Diese werden beim Auslösen eines Interrupts angesprochen.

4.7.1 Register PORTx.INTxMASK


Über die Interruptmaske werden die Pins ausgewählt, die den Interrupt auslösen sollen. Die folgende Zeile wählt alle Pins vom PORTD als Quelle für den Interrupt0 aus.

PORTD.INT0MASK = 0xff;

4.7.2 Register PORTx.INTCTRL


Hier wird dem Interrupt0 ein niedriges Level zur Ausführung zugeordnet. Durch die Level kann die Priorität zur Ausführung von Interrupts beeinflusst werden.

PORTD.INTCTRL |= PORT_INT0LVL_LO_gc;

Mit dem PMIC.CTRL werden die Level von den Interrupts aktiviert. Nur wenn die Freigabe der verwendeten Level erfolgt ist, können die jeweiligen Interrupts ausgeführt werden. In der Beispielzeile werden alle Level freigegeben.

PMIC.CTRL |= PMIC_HILVLEN_bm |PMIC_MEDLVLEN_bm|PMIC_LOLVLEN_bm;

4.7.3 Globale Interruptfreigabe


Um Interrupts grundsätzlich zu aktivieren, muss die Interruptfunktion global freigeben werden.

sei();

4.7.4 Interruptroutine


Tritt ein Interrupt auf, so wird die zugehörige Interruptroutine ausgeführt.

//Interruptroutine
ISR(PORTD_INT0_vect){
PORTE.OUTTGL = 255;
}

4.7.5 Übersicht zur Konfiguration eines Interrupts


Konfigurationsschritte:     Beispiel:
1. Eingang für Interrupt festlegen  
PORTD.DIRCLR = 0x03;
2. Maske zum Festlegen des Eingangslevel
PORTD.MPCMASK = 0x03;
3. Eingangslevel festlegen
PORTD.PIN0CTRL = PORT_ISC_FALLING_gc;
4. Interrupts zuordnen
PORTD.INT0MASK = 0x01;PORTD.INT1MASK = 0x02;
5. Level für Interrupts zuordnen
PORTD.INTCTRL |= PORT_INT0LVL_HI_gc|PORT_INT1LVL_HI_gc;
6. Alle Level für Prioritätensteuerung der Interrupts freigeben
PMIC.CTRL |= PMIC_HILVLEN_bm|PMIC_MEDLVLEN_bm|PMIC_LOLVLEN_bm;
7. Globale Interruptfreigabe
sei();
8. Interruptroutine für INT0 festlegen
ISR(PORTD_INT0_vect){};
9. Interruptroutine für INT1 festlegen
ISR(PORTD_INT1_vect){};

4.8 Ausgabe Taktfrequenz


Über CLKEVOUT kann die Taktfrequenz des Controllers direkt auf einen Pin ausgegeben werden. Der Takt wird auf PC7 ausgegeben. Weitere Ausgabepins können im Datenblatt nachgelesen werden.

PORTCFG.CLKEVOUT = PORTCFG_CLKOUT_PC7_gc;

4.9 Virtuelle Ports (VPORTx)


So richtig mag man sich im ersten Moment nichts darunter vorstellen. Aber eigentlich ist das ganze relativ einfach zu verstehen. Jeder Port des ATxmega kann als virtuelles Portregister konfiguriert werden. Hat man einen virtuellen Port konfiguriert, können bei diesem die selben Pinkonfigurationen wie bei normalen Ports vorgenommen werden. Insgesamt stellt der Atxmega 4 dieser Register zur Verfügung. Durch die Verwendung von virtuellen Ports wird die Flexiblität des Programmcodes erhöht.

Der PORTCwird hier als virtueller Port gemapped.

PORTCFG.VPCTRLA |= PORTCFG_VP0MAP_PORTD_gc;

Anschließend sind Zugriffe über VPORT0.X möglich. Also z.B. VPORT0.DIR |= 0xff; um alle Pins als Ausgänge zu definieren.

4.10 Einfache Beispiele zu der Verwendung von IOs


Folgende Beispiele sollen die Beschreibungen zu den I/Os vertiefen.

4.10.1 (Blinker mit virtuellem Port)


Das Beispiel soll den Einsatz von virtuellen Ports zeigen. Als virtueller Port wird der PORTC definiert. Es wird über Bitmanipulation die LED an PC0 getoggelt. Dadurch ensteht ein Blinker.

4.10.1.1 Schaltplan


Bild "Tutorials:flip_beispiel.gif"

4.10.1.2 Layout


Bild "Tutorials:1.2.3.1_blinker.png"

4.10.1.3 Programmcode


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
void virtual_init(void);
void clock_init(void);
int main(void){
virtual_init();
clock_init();
 
while(1){
//Der Ausgang PC0 wird getoggelt
VPORT0.OUT = PIN0_bm;
//Zeitverzögerung 1s
for(uint8_t i=0;i<10;i++){
_delay_ms(100);
}
}
return 0;
}
void virtual_init(void){
//PORTC wird als Virtueller Port definiert
PORTCFG.VPCTRLA |= PORTCFG_VP02MAP_PORTC_gc;
//Der Virtuelle Port0 wird als Ausgang definiert
VPORT0.DIR = PIN0_bm;
}
//Ausgang auf 0 setzen
VPORT0.OUT = 0x00;
void clock_init(void){
//Oszillator auf 2Mhz stellen
OSC.CTRL |= OSC_RC2MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC2MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC2M_gc;
}

Um das Beispiel auf einem anderen Port zu verwenden, reicht es aus in der Zeile

PORTCFG.VPCTRLA |= PORTCFG_VP02MAP_PORTC_gc;

den PORTC in einen anderen Port zu verändern.

4.10.2 Wechselblinker


Dieses Beispiel zeigt, toggeln von Bits mit dem Register OUTTGL. PC0 und PC1 blinken im Wechsel.

4.10.2.1 Schaltplan


Bild "Tutorials:wechselblinker.gif"

4.10.2.2 Layout


Bild "Tutorials:4.10.2_wechselblinker.png"

Gegenüber zum letzten Beispiel muss lediglich eine einzige Brücke zu einer weiteren LED gezogen werden.

4.10.2.3 Programmcode


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
void io_init(void);
void clock_init(void);
int main(void){
io_init();
clock_init();
while(1){
//PC0 und PC1 wird getoggelt
PORTC.OUTTGL |= PIN0_bm |PIN1_bm;
//Verzögerung
for (uint8_t i = 0;i<10;i++){
_delay_ms(100);
}
}
return 0;
}
void io_init(void){
// Bit0 und Bit1 von PORTC als Ausgang konfigurieren
PORTC.DIR = PIN0_bm|PIN1_bm;
}
// Den Ausgang PC0 auf High schalten
PORTC.OUT = PIN0_bm;
void clock_init(void){
//Oszillator auf 2Mhz stellen
OSC.CTRL |= OSC_RC2MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC2MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC2M_gc;
}

4.10.3 Signale einlesen


Wird Taster T1 betätigt, wird das Signal an PC1 getoggelt. Wird der Taster T2 betätigt, wird das Signal an PC0 getoggelt. Die Taster werden durch eine einfache Routine entprellt.

4.10.3.1 Schaltplan


Bild "Tutorials:einlesen_ausgeben.gif"

4.10.3.2 Layout


Bild "Tutorials:4.10.3_signale_einlesen.png"

4.10.3.3 Programmcode


#include <avr/io.h>
#include <util/delay.h>
//Funktionen
uint8_t entprellung(volatile uint8_t *port, uint8_t pin);
void clock_init(void);
int main(void)
{
clock_init();
// Maske für die Pinkonfiguration
PORTCFG.MPCMASK=0x03;
// Aktivieren vom Pullupwiderstand für die maskierten Pins von PORTD
PORTD.PIN0CTRL=0x38;
// PC0 und PC1 als Ausgang konfigurieren
PORTC.DIR = PIN0_bm | PIN1_bm;
// PC0 und PC1 auf High schalten
PORTC.OUT = PIN0_bm | PIN1_bm;
while(1)
{
//Prüfung ob Taster an PD0 gedrückt wurde
if (entprellung(&PORTD.IN, PIN0_bm))
{
//Zustand von PC0 wird getoggelt
PORTC.OUTTGL = PIN0_bm;
}
//Prüfung ob Taster an PD0 gedrückt wurde
if (entprellung(&PORTD.IN, PIN1_bm))
{
//Zustand von PC1 wird getoggelt
PORTC.OUTTGL = PIN1_bm;
}
}
}
//Entprellung der Taster
uint8_t entprellung(volatile uint8_t *portin, uint8_t pin)
{
if ( !(*portin & pin))
{
//PIN wurde auf GND geschalten
_delay_ms(150);
if ( *portin & pin)
{
//Taster wurde losgelassen
_delay_ms(150);
return 1;
}
}
return 0;
}
void clock_init(void){
//Oszillator auf 2Mhz stellen
OSC.CTRL |= OSC_RC2MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC2MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC2M_gc;
}

4.10.4 Eingang als Buskeeper


Der Eingang PD0 wird als Buskeeper konfiguriert. Wir verbinden dazu den einzelnen Taster mit 3,3V und PD0. Über den Ausgang PC0 lassen wir uns über eine LED das Potenzial am Eingang PD0 abbilden. Wenn der Taster T2 gedrückt wird, liegt am Eingang PD0 High Signal an und die LED leuchtet. Der Zustand wird gespeichert und dadurch bleibt die LED auch nach Losslassen des Tasters an. Erst wenn ein anderes definiertes Potenzial angelegt wird, kann die Schaltung zurückgesetzt werden.

Die Beispielschaltung kann mit einem Reset zurückgesetzt werden.

4.10.4.1 Schaltplan


Bild "Tutorials:buskeeper.gif"

4.10.4.2 Layout


Bild "Tutorials:4.10.4_buskeeper.png"

4.10.4.2 Programmcode


#include <avr/io.h>
#include <util/delay.h>
//Funktionen
uint8_t entprellung(volatile uint8_t *port, uint8_t pin);
void clock_init(void);
int main(void)
{
clock_init();
// Maske für die Pinkonfiguration
PORTCFG.MPCMASK=0x01;
// Aktivieren vom Buskeeper für die maskierten Pins von PORTD
PORTD.PIN0CTRL|=PORT_OPC_BUSKEEPER_gc;
// PC0 als Ausgang konfigurieren
PORTC.DIR = PIN0_bm;
while(1)
{
//PORTD einlesen und an PORTC ausgeben
PORTC.OUT = PORTD.IN;
}
return 0;
}
void clock_init(void){
//Oszillator auf 2Mhz stellen
OSC.CTRL |= OSC_RC2MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC2MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC2M_gc;
}

4.10.5 Taktfrequenz ausgeben


Über den Ausgang PC7 wird die Taktfrequenz ausgegeben. Dadurch kann der Systemtakt z.B. über eine Oszilloskop Messung betrachtet werden. Dies ist eine einfache Möglichkeit, die eingestellte Taktfrequenz zu überprüfen.

4.10.5.1 Schaltplan


Bild "Tutorials:taktausgang.gif"

4.10.5.2 Layout


Bild "Tutorials:4.10.5_clockausgabe.png"

4.10.5.3 Programmcode


#include <avr/io.h>
#include <avr/interrupt.h>
void clock_init(void);
void port_init(void);
int main(void){
clock_init();
port_init();
//Taktfrequenz an PC7 ausgeben
PORTCFG.CLKEVOUT = PORTCFG_CLKOUT_PC7_gc;
while(1){
}
return 0;
}
void port_init(void){
//PC7 als Ausgang definieren
PORTC.DIR = (1<<7);
}
void clock_init(void){
//Oszillator auf 2Mhz stellen
OSC.CTRL |= OSC_RC2MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC2MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC2M_gc;
}

4.10.5.4 Messung über Oszilloskop


Bild "Tutorials:clkout.jpg"

Nachdem das Beispiel auf den Controller gespielt wurde und der Controller in Betrieb ist, wird das folgende Signal auf dem Oszilloskop angzeigt:

Die Frequenz wurde mit 2 Mhz eingestellt und liegt hier laut der Messung bei 2.033Mhz.

4.10.6 Interrupt am PORTA


Der Taster an PA0 löst den Interrupt0 und der Taster PA1 löst den Interrupt1 aus. Im Interrupt0 wird der Ausgang PC0 eingeschalten und der Ausgang PC1 ausgeschalten. Im Interrupt1 wird dieser Vorgang genau umgedreht.

4.10.6.1 Schaltplan


Bild "Tutorials:interrupt_porta.gif"

4.10.6.2 Layout


Bild "Tutorials:4.10.6_interrupt.png"

4.10.6.3 Programmcode


#include <avr/io.h>
#include <avr/interrupt.h>
//Interruptroutine Interrupt0
ISR(PORTA_INT0_vect){
//PC0 auf High schalten
PORTC.OUT = PIN0_bm;
//PC1 auf Low schalten
PORTC.OUT &= ~PIN1_bm;
}
//Interruptroutine Interrupt1
ISR(PORTA_INT1_vect){
//PC1 auf High schalten
PORTC.OUT = PIN1_bm;
//PC0 auf Low schalten
PORTC.OUT &= ~PIN0_bm;
}
void clock_init(void);
int main( void )
{
//clock konfigurieren
clock_init();
//PC0 und PC1 als Ausgang festlegen
PORTC.DIR = PIN0_bm|PIN1_bm;
//PA0 und PA1 als Eingang festlegen
PORTA.DIRCLR = 0x03;
//Maske für das setzen der Pullupwiderstände für PA0 und PA1 setzen
PORTCFG.MPCMASK=0x03;
//Pullupwiderstände und Interrupt bei fallender Flanke festlegen
PORTA.PIN0CTRL= PORT_OPC_PULLUP_gc | PORT_ISC_FALLING_gc;
//PA0 dem Interrupt 0 zuordnen
PORTA.INT0MASK = 0x01;
//PA1 dem Interrupt 1 zuordnen
PORTA.INT1MASK = 0x02;
//Die Level der Interrupts auf Hoch festlegen
PORTA.INTCTRL |= PORT_INT0LVL_HI_gc|PORT_INT1LVL_HI_gc;
//Alle Level für die Interrupts freigeben
PMIC.CTRL |= PMIC_HILVLEN_bm |PMIC_MEDLVLEN_bm|PMIC_LOLVLEN_bm;
//Globale Interruptfreigabe
sei();
//Ausgang PC0 auf High schalten
PORTC.OUT = PIN0_bm;
while (1){
}
return 0;
}
void clock_init(void){
//Oszillator auf 32Mhz stellen
OSC.CTRL |= OSC_RC32MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC32MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC32M_gc;
}

5. Timer


Der Timer bietet für viele Programme eine solide Basis. Beim ATxmega haben die Timer eine Breite von 16-Bit. Auch die Realisierung von einem 32-Bit breiten Timer ist möglich. Die Frequenz von einem Zählschritt des Timers wird durch den Systemtakt und den Vorteiler bestimmt. Das CNT Register wird bis zum Timertopwert hochgezählt. Wird der Timerüberlauf (Timertopwert) erreicht, kann z.B. ein Interrupt ausgelöst werden. Der Timer beginnt wieder von neuem zu zählen. Durch die Vielzahl an Timer Registern ist die Verwendung für PWM, Frequenzausgabe uvm. möglich.

5.1 Timerregister


Register:   Beispiel: Erklärung:
TCxx.CTRLA  
//Vorteiler für Timer TCC0 auf 256 konfigurieren 
TCC0.CTRLA = TC_CLKSEL_DIV256_gc;
Hier wird der Vorteiler Geschwindigkeit der Zählschritte beeinflusst.
Je höher der Vorteiler ist,um so länger dauert ein Zählschritt.
Komplette Beschreibung Siehe:
XMEGA AU MANUAL
Kapitel: 14.12.1
TCxx.CTRLB
//Timer TCD0 für die Frequenzausgabe konfigurieren
TCC0.CTRLB = TC_WGMODE_FRQ_gc;
//CompareA vom Timer TCD0 aktivieren
TCC0.CTRLB = TC0_CCAEN_bm;
Über das Register CTRLB kann der Tirmermodus konfiguriert werden. Auch lässt sich in diesem Register die Freigabe für den Compare Ausgang einstellen.
Komplette Beschreibung Siehe:
XMEGA AU MANUAL
Kapitel: 14.12.2
TCxx.CTRLD
//Eventaktion externer auf und ab Zaehler konfigurieren
TCC0.CTRLD = TC_EVACT_UPDOWN_gc;
Im Register CTRLD können verschiedenen Eventaktionen gewählt werden. Des weiteren kann die Eventquelle konfiguriert werden.
Komplette Beschreibung Siehe:
XMEGA AU MANUAL
Kapitel: 14.12.4
TCxx.CTRLE
//Timer in 8-Bit Modus konfigurieren 
TCC0.CTRLE = TC0_BYTEM_bm;
Über das Register CTRLE lässt sich der Timer auf 8-Bit Modus umstellen.
Komplette Beschreibung Siehe:
XMEGA AU MANUAL
Kapitel: 14.12.5
TCxx.INTCTRLA
 //Timer Überlauf Interrupt mit Interruptlevel High freigeben
TCC0.INTCTRLA = TC_OVFINTLVL_HI_gc;
Das Register INTCTRLA ist für die Freigabe des Timerüberlauf Interrupts zuständig. Außerdem kann ein Timer Error Interrupt konfiguriert werden. Auch wird das Level des Interrupts bestimmt.
Komplette Beschreibung Siehe:
XMEGA AU MANUAL
Kapitel: 14.12.6
TCxx.INTCTRLB
//Timer Compare Interrupt mit Interruptlevel High freigeben
TCC0.INTCTRLB = TC_CCAINTLVL_HI_gc
Das INTCTRLB Register ist ein weiteres Register für die Freigabe von Interrupts bei der Timerkonfiguration. Hier können die Compare Interrupts freigegeben werden. Auch wird hier das Level des Interrupts bestimmt.
Komplette Beschreibung Siehe:
XMEGA AU MANUAL
Kapitel: 14.12.7
TCxx.CTRLFSET
TCxx.CTRLFCLR
//Timer neu starten
TCC0.CTRLFSET = TC_CMD_RESET_gc; 
In diesem Register kann der Timer beispielsweise neu gestartet werden. Außerdem kann die Zählrichtung eingestellt werden.
Komplette Beschreibung Siehe:
XMEGA AU MANUAL
Kapitel: 14.12.8
TCxx.CNT
//Aktuellen Zählerstand des Timers TCC0 in Variable speichern
Temp = TCC0.CNT 
Das Register CNT enthält den aktuellen Zählwert des Timers.
Komplette Beschreibung Siehe:
XMEGA AU MANUAL
Kapitel: 14.12.12
TCxx.PER
//Timertopwert, in diesem Fall nutzt der Timer 
//die vollen 16-Bit aus 
TCC0.PER = 0xffff;
Bis zu diesem Wert zählt der Timer (Timertopwert).
Komplette Beschreibung Siehe:
XMEGA AU MANUAL
Kapitel: 14.12.14

Weitere Registerbeschreibungen finden Sie im XMEGA AU MANUEL.

5.1.1 PWM


Über Pulsweitenmodulation können z.B. LEDs gedimmt oder auch Motoren angesteuert werden. Wird eine PWM konfiguriert, so liefern die PWM-Ausgänge Rechtecksignale. Dabei bleibt die Frequenz und damit die Periodendauer gleich. Allerdings lässt sich das Verhältnis zwischen An und Aus variieren, was dann zu verschiedenen Dimmstufen führt. Ist die Anphase lang und die Ausphase kurz so leuchtet die LED beispielsweise hell, ist die Anphase kurz und Ausphase lang, so leuchtet die LED nur schwach. Gerade bei der Dimmung von LEDs sollte beachtet werden, dass unser menschliches Auge nicht linear ist. So werden die dunkleren Dimmphasen vom Auge besser wahrgenommen, als die Helligkeitsunterschiede in den helleren Bereichen.

Single-Slope-PWM (Fast-PWM) Dual-Slope-PWM (Phase-Correcht-PWM)
- Der Timer zählt das Timerregister CNT von 0 bis zum Timertopwert (PER)- Der Timer zählt das Timerregister CNT zunächst von 0 zum Timpertopwert (PER),anschließend vom Timertopwert(PER) zurück auf 0
Beim Erreichen des Vergleichswertes, der über die jeweiligen Compare Register festgelegt werden kann, wechselt der Status des PWM Ausgangs von 0 auf 1 oder invetiert von 1 auf 0. Beim Erreichen des Vergleichswertes (Compare Register) beim Aufwärtszählen, wechselt der Status des Ausgangs von 0 auf 1 oder von 1 auf 0. Beim Erreichen des Vergleichswertes beim Abwärtszählen wechselt der Status von 1 auf 0 oder von 0 auf 1.

5.2 Beispiele zum Timer


5.2.1 Timer ohne Interrupt


Der Timer wird mit einem Timertopwert und einem Vorteiler konfiguriert. Es wird das CNT Register, das den aktuellen Zählerstand des Timers beinhaltet, ausgelesen. Wenn CNT eine Wertigkeit von 0 aufweist, wird die Variable out um 1 erhöht und ausgegeben. Es wird am PORTA damit binär hochgezählt.

5.2.1.1 Schaltplan


Bild "Tutorials:timer_ohne_interrupt.gif"

5.2.1.2 Layout


Bild "Tutorials:5.2.1_timer_ohne_interrupt.png"

5.2.1.3 Programmcode


#include <avr/io.h>
void clock_init(void);
int main(void) {
clock_init();
// Alle Pins des PORTA werden als Ausgang definiert
PORTA.DIR = 0xff;
//Timertopwert
TCC0.PER = 0xffff;
//Vorteiler von 256
TCC0.CTRLA = TC_CLKSEL_DIV256_gc;
uint8_t out = 0;
//Hauptschleife (Endlosschleife)
while (1) {
//Solange TCC0.CNT den Wert 0 hat, wird das OUt Register um 1 erhöht
if (TCC0.CNT == 0) {
out++;
//Damit je Timerdurchlauf das Out Register nur 1 mal erhöht wird, TCC0.CNT
auf 1 setzen
TCC0.CNT = 1;
}
// der Wert von Out wird an PORTA ausgegeben
PORTA.OUT = out;
}return 0;
}
void clock_init(void){
//Oszillator auf 2Mhz stellen
OSC.CTRL |= OSC_RC2MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC2MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC2M_gc;
}

Durch die Vorgabe von einer Auflösung von 16-Bit und dem Vorteiler von 256, sowie der Oszillatorfrequenz von 2Mhz, lässt sich die Frequenz des Timers berechnen:
Zählfrequenz Timer = Oszillatorfrequenz/Vorteiler → 2Mhz/256 = 7812,5 Hz
Dauer pro Zählschritt = 1/Zählfrequenz Timer → 1/7812,5 Hz = 0,128 ms
Gesamtdauer = Dauer pro Zählschritte x Anzahl der Zählschritt → 0,128 ms x 65536 = ca. 8,4 sec
Da es bis zu einem Timerüberlauf ca. 8,4 Sekunden dauert, wird etwa alle 8,4 Sekunden die Variable out um 1 erhöht.

5.2.2 Timer mit Interrupt


Das folgende Beispiel demonstriert die Verwendung vom Timerüberlauf-Interrupt. Von einem Timerüberlauf spricht man, wenn der Timertopwert (PER) übeschritten wurde. Nach dem Überschreiten, fängt der Timer wieder an das Cnt Register von 0 hochzuzählen. Bei jedem Überlauf werden die Leds an PORTA in der Interruptroutine getoggelt. Die Beschaltung für dieses Beispiel kann von 5.2.1 übernommen werden.

#include <avr/io.h>
#include <avr/interrupt.h>
 
void timer_init(void);
void clock_init(void);
void interrupt_init(void);
void ausgaenge_init(void);
 
void int main(void){
clock_init();
interrupt_init();
timer_init();
ausgaenge_init();
 
while(1){
}
}
void timer_init(){
// Timer Top Wert festlegen Überlauf intterupt alle 2 sekunden
TCC0.PER = 62500;
// Timerüberlauf Interrupt, hohe Priorität
TCC0.INTCTRLA = TC_OVFINTLVL_HI_gc;
// Vorteiler auf 1024 einstellen und Timer starten
TCC0.CTRLA = TC_CLKSEL_DIV1024_gc;
}
void clock_init(void){
//Oszillator auf 32Mhz stellen
OSC.CTRL |= OSC_RC32MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC32MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC32M_gc;
}
void interrupt_init(void){
//Alle Level für die Interrupts freigeben
PMIC.CTRL |= PMIC_HILVLEN_bm | PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm;
//Globale Interruptfreigabe
sei();
}
void ausgaenge_init(void){
//Alle Pins des PORTA auf Ausgang
PORTA.DIR = 0xff;
}
//*Timerüberlauf Interrupt
ISR (TCC0_OVF_vect)
{
//PORTA wird getoggelt
PORTA.OUTTGL = 0xff;
}

5.2.3 Timer mit 5 Interrupts


Nutzt man von einem Timer den Überlauf Interrupt und die 4 Compare Interrupts (CCA,CCB,CCC,CCD) ist es möglich mit einem Timer 5 Interrupts anzusteuern. Durch das toggeln in den Interruptroutinen ergibt sich ein Lauflicht.

5.2.3.1 Schaltplan


Bild "Tutorials:timer_5_interrupts.gif"

5.2.3.2 Layout


Bild "Tutorials:5.2.3_timer_5_interrupts.png"

5.2.3.3 Programmcode


#include <avr/io.h>
#include <avr/interrupt.h>
 
void timer_init(void);
void clock_init(void);
void interrupt_init(void);
void ausgaenge_init(void);
int main(void){
clock_init();
interrupt_init();
timer_init();
ausgaenge_init();
while(1){
}
}
void timer_init(){
// Timer Top Wert festlegen Überlauf intterupt alles 2 sekunden
TCC0.PER = 62500;
// Timerüberlauf Interrupt aktivieren High
TCC0.INTCTRLA = TC_OVFINTLVL_HI_gc;
// Compare Interrupts A,B,C,D aktivieren High
TCC0.INTCTRLB = TC_CCAINTLVL_HI_gc |TC_CCBINTLVL_HI_gc|
TC_CCCINTLVL_HI_gc|TC_CCDINTLVL_HI_gc;
//Interrupt CCA wird ausgeführt wenn TCC0.CNT 3788 ist
TCC0.CCA = 3788;
//Interrupt CCB wird ausgeführt wenn TCC0.CNT 18940 ist
TCC0.CCB = 18940;
//Interrupt CCC wird ausgeführt wenn TCC0.CNT 31280 ist
TCC0.CCC = 31280;
//Interrupt CCD wird ausgeführt wenn TCC0.CNT 50220 ist
TCC0.CCD = 50220;
// Vorteiler auf 1024 einstellen und Timer starten
TCC0.CTRLA = TC_CLKSEL_DIV1024_gc;
}
 
void clock_init(void){
//Oszillator auf 32Mhz stellen
OSC.CTRL |= OSC_RC32MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC32MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC32M_gc;
}
void interrupt_init(void){
//Alle Level für die Interrupts freigeben
PMIC.CTRL |= PMIC_HILVLEN_bm | PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm;
//Globale Interruptfreigabe
sei();
}
void ausgaenge_init(void){
//Alle Pins des PORTE auf Ausgang
PORTA.DIR = PIN0_bm|PIN1_bm|PIN2_bm|PIN3_bm|PIN4_bm;
}
//*Timerüberlauf Interrupt
ISR (TCC0_OVF_vect)
{
//Toggelt Ausgang PA0
PORTA.OUTTGL = 0x01;
}
//*Timer Compare Interrupt bei CNT = 3788
ISR (TCC0_CCA_vect)
{
//Toggelt Ausgang PA4
PORTA.OUTTGL = 0x10;
}
//*Timer Compare Interrupt bei CNT = 18940
ISR (TCC0_CCB_vect)
{
//Toggelt Ausgang PA3
PORTA.OUTTGL = 0x08;
}
//*Timer Compare Interrupt bei CNT = 31280
ISR (TCC0_CCC_vect)
{
//Toggelt Ausgang PA2
PORTA.OUTTGL = 0x04;
}
//*Timer Compare Interrupt bei CNT = 50220
ISR (TCC0_CCD_vect)
{
//Toggelt Ausgang PA1
PORTA.OUTTGL = 0x02;
}

5.2.4 Timer Frequenzausgabe


Eine weitere Möglichkeit die der Timer bietet ist die einfache Ausgabe eines Rechtecksignals auf dem Compare Pin. Die Frequenz wird über den Systemtakt, Vorteiler und den Vergleichswert CCA bestimmt.
f = Systemtakt/(2*Vorteiler*(CCA+1))
In unserem Beispiel ergibt sich die Frequenz dann wie folgt:
f = 32Mhz/(2*4*(65536+1) = ca. 61 Hz

5.2.4.1 Schaltplan


Bild "Tutorials:frequenzausgabe.gif"

4.10.5.2 Layout


Bild "Tutorials:5.2.4_frequenzausgabe.png"

4.10.5.3 Programmcode


#include <avr/io.h>
#include <avr/interrupt.h>
//Funktion für Timer Konfiguration
void timer_init(void);
//Funktion für Clock Konfiguration
void clock_init(void);
int main(void){
//Clock Konfiguration wird aufgerufen
clock_init();
//Timer Konfiguration wird aufgerufen
timer_init();
while(1){
// die Hauptschleife kann sich ausruhen
}
}
void timer_init(void){
//PD0(OCA) als Ausgang definieren
PORTD.DIR = 0x01;
//Vorteiler des Timers einstellen
TCD0.CTRLA = TC_CLKSEL_DIV4_gc;
//Frequenzausgabe auswählen und Compare Ausgang A aktivieren
TCD0.CTRLB = TC_WGMODE_FRQ_gc | TC0_CCAEN_bm;
//Vergleichswert von CCA einstellen
TCD0.CCA = 0xffff;
}
void clock_init(void){
//Oszillator auf 32Mhz stellen
OSC.CTRL |= OSC_RC32MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC32MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC32M_gc;
}

5.2.4.4 Messung mit Oszilloskop


Bild "Tutorials:frequenzausgabe.png"

5.2.5 Timer als Counter (Impulszähler)


In der Praxis kommt es auch häufig vor, dass man Impulse zählen will. Mit der Konfiguration Timer als Counter bietet sich die Möglichkeit, dies zu bewerkstelligen. In diesem Beispiel wird beim Erreichen des Timer-Top-Wertes, der über das Register TCD0.PER definiert wird, ein Interrupt ausgelöst, der eine LED an PA0 toggelt. Bei der Timer Counter Konfiguration können externe Impulse gezählt werden. Der Taster eignet sich nur bedingt, da die Ergebnisse durch das Prellen des Tasters beeinflusst werden. Ein Impuls wird bei einer fallenden Flanke erkannt.

Mögliche Einsatzgebiete:
– Lichtschranke
– Auswertung von Sensoren
– usw.

5.2.5.1 Schaltplan


Bild "Tutorials:impuszaehler.gif"

5.2.5.2 Layout


Bild "Tutorials:5.2.5_impulszaehler.png"

5.2.5.3 Programmcode


#include <avr/io.h>
#include <avr/interrupt.h>
 
void timer_init(void);
void interrupt_init(void);
void io_init(void);
void clock_init(void);
 
int main(void){
clock_init();
interrupt_init();
timer_init();
io_init();
while(1){
}
return 0;
}
void timer_init(void){
//Setze PD0 als Eventchannel
EVSYS.CH0MUX = EVSYS_CHMUX_PORTD_PIN0_gc;
//Setze Timer Modus auf UPDOWN und selektiere CH0 als Eventchannel
TCD0.CTRLD = TC_EVACT_UPDOWN_gc | TC_EVSEL_CH0_gc;
//Legt Timer Topwert und damit auch fest
//bei wievielen Impulsen die Interrupt Routine
//ausgeführt wird
TCD0.PER = 9;
//Timer Interrupt definieren
TCD0.INTCTRLA = TC_OVFINTLVL_HI_gc;
//Clocksource auswählen in dem Fall Event Channel0
TCD0.CTRLA = TC_CLKSEL_EVCH0_gc;
}
void interrupt_init(void){
// Alle Interrupt Level freigeben
PMIC.CTRL |= PMIC_HILVLEN_bm | PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm;
//Globale Interrupt Freigabe
sei();
}
void io_init(void){
//PD0 für Event triggern bei Fallender
//Flanke konfigurieren
PORTD.PIN0CTRL = PORT_ISC_FALLING_gc;
//PD0 Pullup Widerstand aktivieren
PORTD.PIN0CTRL = PORT_OPC_PULLUP_gc;
//PD0 als Eingang definieren
PORTD.DIR &= ~(1<<PIN0);
//PA0 als Ausgang definieren
PORTA.DIR = 0x01;
}
void clock_init(void){
//Oszillator auf 32Mhz stellen
OSC.CTRL |= OSC_RC32MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC32MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC32M_gc;
}
//Interruptroutine bei Timer Overflow
ISR (TCD0_OVF_vect)
{
//PA0 toggeln
PORTA.OUTTGL = 0x01;
}

5.2.6 Beispiel zur Single Slope PWM


Mit diesem Beispiel werden 6 Ausgänge von PORTD gedimmt. Es wird ein fester Wert vorgeben.
Berechnung-PWM-Frequenz
Die Auflösung der Single Slope PWM wird über das Register PER festgelegt.
2-Bit PWM —> PER = 0x0003;
4-Bit PWM —> PER = 0x000F;
8-Bit PWM —> PER = 0x00FF;
10-Bit PWM —> PER = 0x03FF;
12-Bit PWM —> PER = 0x0FFF;
16-Bit PWM —> PER = 0xFFFF;
Die eingestellte Auflösung beeinflusst die PWM Frequenz.
PWM-Frequenz = Systemtakt / (Vorteiler*(PER + 1)
Berechnung der PWM-Frequenz für das folgende Programm:
Auflösung: 16-BIT —> 0xFFFF —> 65536
Systemtakt: 32Mhz
Vorteiler: 4
–> 32Mhz/(4*(65536+1) = 122Hz
Bei einer Auflösung von 16-BIT und dem kleinsten Vorteiler von 1 kann eine maximale Frequenz von 488 Hz erzielt werden.
Mögliche Verwendungszwecke:
– Dimmen von Leds
– Ansteuerung von Motoren
– usw.

5.2.6.1 Schaltplan


Bild "Tutorials:timer_pwmt.gif"

5.2.6.2 Layout


Bild "Tutorials:5.2.6_single_slope_pwm.png"

5.2.6.3 Programmcode


#include <avr/io.h>
//Konfiguriert die clock
void clock_init(void);
//Konfiguriert die Datenrichtung
void dir_init(void);
//Konfiguriert Timer0 von PORTD
void timer0_init(void);
//Konfiguriert Timer1 von PORTD
void timer1_init(void);
int main(void)
{
clock_init();
dir_init();
timer0_init();
timer1_init();
// Setzt alle PWM Ausgänge auf 4095
TCD1.CCA = 0x0fff;
TCD1.CCB = 0x0fff;
TCD0.CCA = 0x0fff;
TCD0.CCB = 0x0fff;
TCD0.CCC = 0x0fff;
TCD0.CCD = 0x0fff;
 
while(1){
}
return 0;
}
void clock_init(void){
//Oszillator auf 32Mhz stellen
OSC.CTRL |= OSC_RC32MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC32MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC32M_gc;
}
void dir_init(void){
PORTD.DIR |= (1<<PIN0)|(1<<PIN1)|(1<<PIN2)|(1<<PIN3)|(1<<PIN4)|(1<<PIN5);
}
void timer0_init(void){
//PWM Modus auf Single Slope festlegen und Compare Ausgänge freigeben
TCD0.CTRLB = TC_WGMODE_SINGLESLOPE_gc|TC0_CCAEN_bm|TC0_CCBEN_bm|TC0_CCCEN_bm|
TC0_CCDEN_bm;
/*Timertopwert*/
TCD0.PER = 0xffff;
/*4 Vorteiler auf 4 einstellen*/
TCD0.CTRLA = TC_CLKSEL_DIV4_gc;
}
void timer1_init(void){
//PWM Modus auf Single Slope festlegen und Compare Ausgänge freigeben
TCD1.CTRLB = TC_WGMODE_SINGLESLOPE_gc|TC1_CCAEN_bm|TC1_CCBEN_bm;
/*Timerstartwert*/
TCD1.PER = 0xffff;
/*4 Vorteiler*/
TCD1.CTRLA = TC_CLKSEL_DIV4_gc;
}

5.2.6.4 Messung Oszilloskop


Bild "Tutorials:single_slope_pwm.png"

Dieses Signal kann an jedem der 6 PWM Ausgänge nun gemessen werden.

5.2.7 Beispiel zur Dual Slope PWM


Hier wird der Unterschied zur Single Slope PWM nochmals näher gebracht. Der Aufbau der Schaltung kann vom vorhergehenden Beispiel bestehen bleiben. Am Programmcode wird lediglich der Modus umgestellt.

5.2.7.1 Programmcode


#include <avr/io.h>
//Konfiguriert die clock
void clock_init(void);
//Konfiguriert die Datenrichtung
void dir_init(void);
//Konfiguriert Timer0 von PORTD
void timer0_init(void);
//Konfiguriert Timer1 von PORTD
void timer1_init(void);
int main(void)
{
clock_init();
dir_init();
timer0_init();
timer1_init();
// Setzt alle PWM Ausgänge auf 4095
TCD1.CCA = 0x0fff;
TCD1.CCB = 0x0fff;
TCD0.CCA = 0x0fff;
TCD0.CCB = 0x0fff;
TCD0.CCC = 0x0fff;
TCD0.CCD = 0x0fff;
 
 
while(1){
}
return 0;
}
void clock_init(void){
//Oszillator auf 32Mhz stellen
OSC.CTRL |= OSC_RC32MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC32MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC32M_gc;
}
void dir_init(void){
PORTD.DIR |= (1<<PIN0)|(1<<PIN1)|(1<<PIN2)|(1<<PIN3)|(1<<PIN4)|(1<<PIN5);
}
void timer0_init(void){
//PWM Modus auf Dual Slope festlegen und Compare Ausgänge freigeben
TCD0.CTRLB = TC_WGMODE_DSBOTTOM_gc|TC0_CCAEN_bm|TC0_CCBEN_bm|TC0_CCCEN_bm|
TC0_CCDEN_bm;
/*Timertopwert*/
TCD0.PER = 0xffff;
/*4 Vorteiler auf 4 einstellen*/
TCD0.CTRLA = TC_CLKSEL_DIV4_gc;
}
void timer1_init(void){
//PWM Modus auf Dual Slope festlegen und Compare Ausgänge freigeben
TCD1.CTRLB = TC_WGMODE_DSBOTTOM_gc|TC1_CCAEN_bm|TC1_CCBEN_bm;
/*Timerstartwert*/
TCD1.PER = 0xffff;
/*4 Vorteiler*/
TCD1.CTRLA = TC_CLKSEL_DIV4_gc;
}

5.2.7.2 Messung Oszilloskop


Bild "Tutorials:dual_slope_pwm.png"

Da der Timer bei der DualSlope PWM einmal bis zum Timertopwert hoch und anschließend wieder runter zählt, halbiert sich die PWM-Frequenz gegenüber der Single-Slope PWM.

5.2.8 Sekundentakt über Real-Time-Counter erzeugen


Neben den bereits in den Beispielen vorgestellten Timer bringt der ATxmega32A4U auch einen Realtimecounter mit. Als Taktquelle wird ein im Controller integrierter internen Oszillator verwendet. Durch die 1024kHz Taktquelle und die Möglichkeiten vom Vorteiler kann so relativ einfach ein Sekundentakt generiert werden. Der Realtimecounter ist 16-Bit breit.
Mögliche Verwendungszwecke:
– Uhr
– Zeitschaltuhr
– Zeitrelais
– usw.

5.2.8.1 Schaltplan


Bild "Tutorials:rtc_16-bit.gif"

5.2.8.2 Layout


Bild "Tutorials:5.2.8_rtc.png"

5.2.8.3 Programmcode


#include <avr/io.h>
#include <avr/interrupt.h>
 
void clock_init(void);
void rtc_1kHz_init(void);
void rtc_init(void);
void interrupt_init(void);
int main(void) {
//32Mhz Taktfrequenz konfigurieren
clock_init();
//RTC über Internen Oszillator konfigurieren
rtc_1kHz_init();
//RTC Timer konfigurieren
rtc_init();
//Interrupt freigeben
interrupt_init();
//PA0 als Ausgang konfigurieren
PORTA.DIR = PIN0_bm;
//Hauptschleife (Endlosschleife)
while (1) {
 
}return 0;
}
 
void interrupt_init(void){
//Alle Interrupt Level freigeben
PMIC.CTRL |= PMIC_HILVLEN_bm | PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm;
//Globale Interruptfreigabe
sei();
}
void rtc_init(void){
//Timertopwert einstellen
RTC.PER = 0;
//Timeroverflow Interrupt mit Interrupt Priorität Hoch einstellen
RTC.INTCTRL |= RTC_OVFINTLVL_HI_gc;
//Timerregister CNT auf 0 stellen
RTC.CNT = 0;
}
void clock_init(void){
//Oszillator auf 32Mhz stellen
OSC.CTRL |= OSC_RC32MEN_bm;
// Warten bis der Oszillator bereit ist
while(!(OSC.STATUS & OSC_RC32MRDY_bm));
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
//aktiviert den internen Oszillator
CLK.CTRL = (CLK.CTRL & ~CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC32M_gc;
}
void rtc_1kHz_init(void)
{
//Schützt I/O Register, Interrupts werden ignoriert
CCP = CCP_IOREG_gc;
// Internen RTC Oszillator aktivieren
OSC.CTRL |= OSC_RC32KEN_bm;
// Internen Oszillator mit 1024khz für RTC verwenden
CLK.RTCCTRL = CLK_RTCSRC_RCOSC_gc|CLK_RTCEN_bm;
//Vorteiler auf 1024
RTC.CTRL = RTC_PRESCALER_DIV1024_gc;
// Warten bis Synchronisierung zwischen Takt und RTC abgeschlossen ist
while(RTC.STATUS & RTC_SYNCBUSY_bm);
};
//Wird jede Sekunde einmal ausgeführt
ISR(RTC_OVF_vect)
{
//PA0 toggeln
PORTA.OUTTGL = PIN0_bm;
}

5.2.8.4 Messung mit Oszilloskop


Bild "Tutorials:rtc_timer.png"

Die Zeitbasis wurde auf 1 Sekunde festgelegt. Dadurch wird der genaue Sekundentakt schön sichtbar.

6. Links/Referenzen


6.1 Weblinks zu verwendeten Tools


(1) X4DIL – Tutorial Board – Reusch Elektronik http://produkte.reworld.eu/x4dil.htm

(2) FLIP von Atmel http://www.atmel.com/tools/FLIP.aspx

(3) Atmel Studio 6.0 von Atmel http://www.atmel.com/microsite/atmel_studio6/

(4) Dokumente zu ATxmega32A4U von Atmel http://www.atmel.com/devices/ATXMEGA32A4U.aspx?tab=documents

(5) Eclipse IDE http://www.eclipse.org/

Schaltpläne sind mit Splan 7.0 von Abacom erstellt http://www.abacom-online.de/html/splan.html

Die Lochraster-Layouts wurden mit Blackboard Breadboard erstellt http://blackboard.serverpool.org/Home.html

6.2 Downloads zum Tutorial


Programmbeispiele XMEGA-C-Tutorial Teil 1

Layouts XMEGA-C-Tutorial Teil1(erstellt in Blackboard Breadboard 1.0)
nach oben