Kampis Elektroecke

XMega – USART

XMega Blockdiagramm

Der USART stellt eine einfache und effektive Möglichkeit dar seine Programme zu “debuggen” oder Daten zwischen verschiedenen Mikrocontrollern bzw. zu einem PC zu senden. 

USART konfigurieren:

In diesem Beispiel zeige ich, wie der USART mit einer Baudrate von 19200 Baud bei einem Takt von 2 MHz genutzt werden kann. Zuerst muss der Wert für das entsprechende BAUDCTRL-Register berechnet werden. Die Formel ist dem Datenblatt des Herstellers zu entnehmen (CLK2X = 0):

BSCALE \geq 0 BSCALE < 0
BSEL = \frac{1}{2^{BSCALE}} \cdot \left ( \frac{Clock}{16 \cdot Baudrate} - 1 \right ) BSEL = \frac{Clock}{2^{BSCALE}\cdot Baudrate \cdot 16}-1

Der Wert für BSCALE wird vorgegeben und soll -5 betragen. Daraus ergibt sich bei einer Taktrate von 2 MHz ein Wert für das Baudratenregister von 176,33 bzw. 0xB0. Der berechnete Wert wird nun in das USARTC0.BAUDCTRL-Register geschrieben.

Das Baudratenregister besteht aus einem Teil A und einen Teil B. In Teil A werden die höhsten fünf Byte und in Teil B die restlichen 8 Byte des berechneten Wertes gespeichert. Die übrigen fünf Bits von Teil B beinhalten den Scale Factor (ein Wert von 7 bis -8).

Für den Empfang von Zeichen soll der Receive-Interrupt des USART-Moduls genutzt werden. Dieser wird nachfolgend mit er Prioriät Low aktiviert und das entsprechende Interruptflag gelöscht.

Anschließend wird der Sende- und Empfangszweig des USARTs aktiviert.

 Zum Schluss wird noch die Größe eines Datenbytes auf 8 Bit gesetzt,

Die Kodierungen für die einzelnen Zeichengrößen können der nachfolgenden Tabelle entnommen werden.

Jetzt muss noch die Parität, sowie die Anzahl der Stopbits eingestellt werden.

Da ich den Modus 8N1 verwenden möchte (8 Datenbits, keine Parität und 1 Stopbit) müssen die entsprechenden Stellen im CTRLC-Register gelöscht werden um die restlichen Einstellungen vorzunehmen.

Als letztes wird noch der Tx-Pin vom USART als Ausgang und der Rx-Pin als Eingang eingestellt werden. Laut Datenblatt sind dies die Pins C2 und C3.

Nun ist der USART fertig konfiguriert und bereit für den Einsatz…

Zeichen senden:

Da der USART nun fertig eingerichtet ist, kann man über ihn Zeichen zu einen Computer senden. Um ein Zeichen (in diesem Fall ein G) zu senden, wird der folgende Code verwendet.

Die erste Zeile prüft ob das Flag, welches angibt ob das Datenregister leer ist, gesetzt ist. Wenn das Bit gesetzt ist, ist das Datenregister leer. Die ganze while-Schleife bedeutet also:

Solange das Bit USART_DREIF im USARTC0 Status-Register (USARTC0.STATUS) NICHT (!) gesetzt ist wird die Schleife ausgeführt.

Da die Schleife keinen Inhalt hat, sprich nichts zwischen “{“und “}” steht, wird solange eine Endlosschleife ausgeführt bis das Bit gesetzt wird, also die Schleifenabfrage unwahr wird. Dies Schleife sorgt dafür, dass kein neues Byte gesendet werden kann, solange der Controller die Übertragung noch nicht abgeschlossen hat. Dadurch werden Übertragungsfehler vermieden. Anschließend wird der Buchstaben G in das Datenregister vom USARTC0 geladen.

Text senden:

Einen Text zu senden bedarf etwas mehr Code, da es sich bei dem Text um ein Array, also eine Verkettung mehrerer Zeichen, handelt. Es soll der Text Hello, World gesendet werden. Das kann so gemacht werden…

Was macht dieser Code nun?
In der ersten Zeile wird ein Pointer erzeugt, der auf die Zeichenkette mit dem zu sendenden Text zeigt.


Wichtig:

In C werden Zeichenketten mit einem \0 abgeschlossen (Nullterminiert), sodass man recht leicht das Ende einer Zeichenkette erkennen kann. Einzelne Character verfügen über kein Abschlusszeichen.


Die nachfolgende while-Schleife ist so lange aktiv, bis der Zeiger auf die Terminierung der Zeichenkette zeigt. Innerhalb dieser Schleife wird so lange gewartet, bis das Datenregister leer ist. Danach wird das aktuelle Zeichen, auf das der Zeiger zeigt im Datenregister gespeichert.

Nach dem Kopiervorgang wird die Position, auf die der Zeiger zeigt, um eins erhöht, indem die Adresse des Zeigers um 1 erhöht wird. Jetzt beginnt die Schleife wieder von vorne. Zu guter letzt wird noch ein CR + LF gesendet, damit das Terminal am Computer nach jeder Übertragung in eine neue Zeile wechselt.

Schauen wir uns nun noch an, wie Texte empfangen werden können…

Text empfangen:

Zum Empfangen eines Zeichens, bzw. ganzer Texte soll der Rx-Interrupt des USART verwendet werden. Dadurch springt der Mikrocontroller automatisch in die Empfangsroutine und ein Programm wird nicht blockiert, während es auf eine Eingabe wartet.

Dazu müssen zuerst die Interrupts aktiviert werden. Da in diesem Beispiel lediglich die Priorität Low verwendet werden soll, reicht es, wenn auch nur diese aktiviert wird. Dies geschieht mit Hilfe des Interruptcontrollers.

Die empfangenen Daten sollen in einem Ringpuffergespeichert werden. Unter einem Ringpufferversteht man ein “kreisförmiges” Array, wobei die Kreisform durch eine Verknüpfung von Anfang und Ende des Arrays entsteht. Das Prinzip lässt sich sehr gut durch eine Grafik von Wikipedia veranschaulichen.

Der Ringpuffer benötigt folgende Elemente und ist als Struktur definiert.

  • Ein Array, aus dem Daten gelesen, bzw. in das Daten geschrieben werden können
  • Einen Lesezeiger
  • Einen Schreibzeiger
  • Einen Zeiger auf den Anfang des jeweiligen Datenarrays
  • Einen Zeiger auf das Ende des jeweiligen Datenarrays
  • Die Anzahl der Bytes im Puffer
  • Die Größe des Puffers

Vor der ersten Nutzung muss der Ringpuffer initialisiert werden, sprich es müssen die einzelnen Zeiger mit Werten gefüllt werden. Dies soll über eine entsprechende Init()-Funktion erfolgen.

Damit der Ringpuffer genutzt werden kann, müssen sowohl ein Datenarray, als auch das jeweilige Ringpufferobjekt deklariert werden.

Anschließend wird der Puffer initialisiert.

Jetzt fehlt nur noch eine Funktion um Daten zum Puffer hinzuzufügen. Diese Funktion muss folgende Aufgaben erledigen:

  • Speichern des Datenbytes
  • Zähler erhöhen
  • Prüfen ob der Zähler am Ende des Arrays angekommen ist und wenn ja, den Zähler auf den Startwert zurücksetzen

Daraus resultiert die gezeigte Funktion.

Jetzt kann die Interruptroutine zum Empfangen programmiert werden. Der Prototyp einer ISR sieht im Atmel Studio folgendermaßen aus.

Der Ausdruck USARTC0_RXC_vect legt den jeweiligen Interruptvektor, hier der zum Empfangen beim USART, fest. In der ISR wird das empfangene Datenbyte dann in den Puffer geschrieben. Damit man am Terminal auch was sieht, habe ich zusätzlich ein Echo eingebaut.

Sobald der Buffer voll ist, soll das Programm einen Zeilenwechsel und anschließend den Inhalt des Puffer senden. Dazu wird erst einmal eine Funktion benötigt, die feststellt ob der Puffer voll ist. Dies lässt sich durch einen Vergleich zwischen der Puffergröße und dem aktuellen Zählerstand realisieren.

Wenn der Puffer voll ist, soll eine neue Zeile, also ein CR + LF, gesendet werden.

Jetzt wird nur noch eine Funktion benötigt um ein einzelnes Zeichen aus dem Puffer auszulesen. Diese Funktion muss folgendes können:

  • Lesen eines Datenbytes aus dem Speicher
  • Zähler verringern
  • Prüfen ob der Zähler am Anfang des Arrays angekommen ist und wenn ja, den Zähler auf den Startwert zurücksetzen
  • Datenbyte zurückgeben

Daraus ergibt sich die folgende Funktion:

Diese Funktion wird nun in die ISR eingebaut, sodass jedes Byte im vollen Puffer gesendet wird. Zusätzlich wird am Ende ein weiterer Zeilenwechsel gesendet.

Das Programm kann nun auf den Mikrocontroller übertragen werden. Im Terminal erscheint nun zuerst der Hello, World-String und sobald Eingaben getätigt werden, werden diese im Puffer gespeichert. In der zweiten Zeile ist die Antwort bei einem vollen Puffer.

Das komplette Beispielprojekt gibt es in meinem GitLab-Repository zum Download.

Last Updated on

28 Kommentare

  1. Hallo,

    erstmal vielen Dank für dieses Tutorial.

    Ich bräuchte eine Schaltung um die USART-Ports an
    [code]
    …zwei XBee Funkmodule mit meinem Computer verbunden. Soll er aber über RS232 oder USB mit dem Computer…
    [/code]

    zu betreiben.
    Vielen Dank im Voraus.

    kwr4711

    1. Hallo,

      XBee Funkmodule kannst du direkt mit dem Raspberry verbinden. Für die Anbindung an den PC verwende ich ein FT232 Modul. Dieses stellt einen Wandler von UART nach USB bereit + 3,3V Spannungsquelle.
      Einfach beim XBee 3,3V + GND an das Raspberry anschließen und Tx an Din vom Funkmodul und Rx an Dout vom Funkmodul. Beim FT232 dasselbe nochmal.

      Gruß
      Daniel

    1. Hallo,

      stimmt….das wollte ich auch noch schreiben. Das ist bisher komplett untergegangen.
      Im Prinzip musst du nur nach einem Rx Interrupt das Datenregister auslesen und alles zu einem String zusammenbauen.
      Sobald ich etwas Zeit habe schreibe ich dazu noch was.
      Danke für den Hinweis. Das ist mir komplett entfallen!

      Gruß
      Daniel

  2. Nicht vergessen die Variable “counter” in der Funktion “void Send_UART(char data[])” zu initialisieren, da sonst evtl. die Ausgabeschleife nie durchlaufen werden kann!

    counter = 0x00;

  3. Hi,
    echt ein super Tutorial. Allerding brauchte ich etwas Hilfe ich soll das ganze mit Bluetooth(BTM 232) realisieren und habe noch nie mit C gearbeitet und auch noch nie mit einem ATXMega sondern nur mit ATMega. Mein Problem ist jetzt wie kann ich damit empfangen und senden.
    Hab das oben schon versucht hat aber irgendwie nicht funktioniert. Kann auch sein das ich zu blöd bin.
    Für Hilfe wäre ich dankbar.

  4. Hey,
    super Sache mit dem Tutorial. Leider hab ich ein kleines Problem mit der Kommunikation und hoffe, dass du mir vielleicht dabei weiterhelfen kannst.

    Ich habe dein Bsp.Code zu “Hallo World” verwendet. 1 x XBee sitzt am ATxMega32A4, der zweite sitzt am Sparkfun XBee Explorer USB. Das Problem ist nur, dass ich keine Kommunikation zwischen den beiden in der Konstellation erhalte.

    Vorneweg: Mit Arduino und XBee Explorer funktioniert ohne Probleme, nur nicht mit dem Xmega32A4.

    So bald ich dein Bsp. via DragonBoard auf den XMega lade, kann ich am EmpfangsPin des Xbee 3,3V messen.

    PS: Die Diskussion über den MPU6050 würde mich brennend interessieren (kann leider die Diskussion zwischen Dir und einem User nicht mehr finden, weiß jedoch, dass Ihr weiter per mail kommunizieren wolltet.), da ich in näherer Zeit auch ein MPU6050 (Beschleunigungssensor) mit dem XMega32A4 auslesen und über Xbee an ein PC schicken möchte.

    Bin für jede Hilfe sehr Dankbar.

    Gruß
    Bexx

    1. Hey,

      die Verkabelung hast du schon geprüft? Tx vom XMega muss an DIN vom XBee und
      DOUT muss an Rx.
      Hast du im Programm auch darauf geachtet, dass du den richtigen USART
      verwendest?
      Schick mir am besten mal einen Schaltplan und dein Programm zu. Dann gucke ich
      mal drüber.

      Gruß
      Daniel

  5. Hallo Daniel,

    Ich finde diese Artikel sind sehr schoen geschrieben. Ich habe mich lange nicht mehr mit AVRs (oder C) beschaeftigt und wollte mal wieder ddamit rumspielen :-)

    Ich habe immer direkt in Assembler geschrieben weil man dann genau weiss was los ist. Ich finde, dass hoehere Sprachen einem zu Ineffizienz verleiten. Manche meinen jedoch, dass sie einem von Kodierfehler bewahren :-)
    Ich weiss, dass dies kein C Kurs ist, denoch finde ich, dass man immer Effizienz im Hinterfopf haben sollte, und wissen was fuer Code erzeugt wird…

    Zu diesem Code faellt mir folgendes ein. In C sind Strings NULL-terminiert, d.h. der String der ueberreicht wird ist ‘Hallo Welt’. Die Funktion strlen() macht nichts anderes, als diesen String char fuer char nach dem zu durchsuchen. Warum also den String zwei Mal durchlesen? Und warum zwei zusaetzliche Variablen verwenden? Da gehen register drauf, es werden Sachen auf Stacks gepusht und gepopt usw….

    Ich empfehle an dieser Stelle The C Programming Language, Kernighan & Ritchie. Das beste Buch ueberhaupt um eine Sprache zu lernen.

    Natuerlich werden durch Code wie folgendes die ganzen Securityprobleme verursacht. Aber die Verwendung von strlen() ist nicht besser :-)

    void Send_UART(char *data)
    {
    char c;

    while(c = *data++) // c ist data[0]. Danach wird data um eins erhoeht
    // und zeigt auf den naechsten Buchstaben
    // wenn c == ”, also 0, hoert die Schleife auf.

    // C Strings hoeren DESWEGEN mit einem 0 auf, damit man Code so
    // schreiben kann. :-)

    {
    while (!(USARTC0.STATUS & USART_DREIF_bm));
    USARTC0.DATA = c;
    }

    while (!( USARTC0.STATUS & USART_DREIF_bm));
    USARTC0.DATA = 0x0A;
    while (!( USARTC0.STATUS & USART_DREIF_bm));
    USARTC0.DATA = 0x0D;
    }

    1. Arrrgh! Meine backslash-zeros und Formatierung wurden geloescht! :-)

      Es waren zwei im Paragraphen ueber NULL-terminerte Strings. :
      ‘Hallo WeltNULL’
      nach dem NULL zu durchsuchen

      Und eins in der dritten Komentarzeile:
      // Wenn c == NULL, also 0, hoert die Schleife auf.

    2. Hey,

      danke für deine Anmerkung.
      Klar es gibt effizientere Wege und der Punkt Effizienz ist auch wichtig (gerade bei größeren Programmen).
      Bei diesem Beispiel habe ich einfach nicht an die strlen-Funktion gedacht ;)
      Aber das Buch was du da empfiehlst ist ein sehr gutes Buch…hab da mal reinschauen dürfen und kann nicht meckern :)

      Gruß
      Daniel

  6. Hey super tutoria, super Beschreibungen, super Codebeispiele!
    Hast du auch ein Codebeispeil mit dem man Zeichen oder ein Wort über ein Terminal empfangen kann?

    1. Hey,

      Danke für das Lob :)
      Nein habe ich noch nicht geschrieben und ausprobiert, da ich im Moment keine Hardware besitze auf der ich das testen kann.

      Gruß
      Daniel

  7. “Soll er aber über RS232 oder USB mit dem Computer verbunden werden sind andere Schaltungen zu benutzen (hierbei hilft Google oder falls ihr gar nicht weiter wisst, könnt ihr mir auch eine E-Mail schreiben).”

    Hallo Kampi,

    super Einführung!!
    kannst du mir sagen was ich genau einstellen muss wenn ich eine RS232 Verbindung benutze ?:)

    Beste Grüße!

      1. Danke für deine schnelle Antwort.
        Ja sowas habe ich bereits. @MAX232

        Die Frage war eher auf den Code bezogen bezüglich der RS232 Verbindung.

        Kannst du mir vllt. noch sagen ob man den oszi noch extra einstellen muss?

        Der Code hat leider nicht bei mir funktioniert aber mein Verständnis dafür schon weitergebracht.

        Grüße Lukas

        1. Hallo Lukas,

          extra einstellen musst du dafür nichts.
          Den Pegelwandler musst du einfach nur entsprechend verdrahten und mit dem PC verbinden.
          Sicher das die Probleme mit dem Programm vom Code her kommen und nicht wegen einer falschen Verdrahtung (sehr beliebter Fehler :) ).

          Gruß
          Daniel

  8. Hallo Daniel,

    danke, dass du es angeboten hast, fragen zu stellen.
    Ich verwende auch das xplained Board und habe verstanden und ausprobiert, wie man damit Zeichen Senden kann. ich schaff es leider nicht, damit auch sachen zu empfangen (z. B. über J1) und dann auszuwerten (blinken)
    kannst du mir vielleicht weiterhelfen? (wenn das nicht zu Zeitaufwendig ist)

    Gruß
    :-)

    1. Hallo Dustin,

      ich habe leider keinen fertigen Code zum Empfangen über UART und kann dir deswegen auch nur begrenzt helfen (hab da im Moment auch nicht soviel Zeit mich dahinter zu klemmen).
      Im Grunde musst du nur den USART Recieve Interrupt aktivieren und dann in der ISR des Interrupts das Zeichen aus dem Datenbuffer einlesen und zu einem String zusammen fügen.
      Schau mal hier:

      https://kampis-elektroecke.de/wp-content/uploads/2012/12/UART_Empfangen.zip

      Hier habe ich sowas ähnliches für das Raspberry Pi aber ohne ISR geschrieben.
      Im Grunde kannst du das Mainprogramm in deine ISR übernehmen. Es empfängt bis zu 255 Zeichen bzw. bis zu dem Zeichen 0x0D. Sobald eine der beiden Bedingungen erfüllt wurden, wird der Text in eine Textdatei geschrieben (diesen Teil lässt du weg, aber du könntest den Text dann z.B. in einen anderen String kopieren).
      Du musst also nur die Funktionen “WriteFile()” und “UART_Init()” weglassen. Den Rest solltest du eigentlich mit ein paar Anpassungen auf den Problem übertragen können.
      Den Rest, sprich den Interrupt aktivieren, musst du selber raus finden ;) (gibt aber genug im Internet dazu :)

      Viel Erfolg!
      Gruß
      Daniel

        1. Hallo Andreas,

          vielen Dank für deine Mühe!
          Sowas weiß ich wirklich zu schätzen. Ich habe den Code gedownloadet und veröffentliche ihn direkt bei mir (die Dropbox Links laufen irgendwann aus, bzw. funktionieren nicht mehr).
          Solange es funktioniert reicht es doch :)
          Verbessern kann man dann immer noch!

          Gruß
          Daniel

  9. Servus erstmal,
    super Seite die mich viel weiter gebracht hat, allerdings bekomme ich es grade nicht hin den inhalt einer speicherzelle (egal ob char oder sonst etwas) zu senden. Gibt es dafür eine andere Funktion als die Send_UART da diese ja “nur” einen String sendet?!

    Liebe Grüße,
    Klaus

    1. Hey,

      C bietet bereits eine Option an, einen Zahlenwert in einen String zu konvertieren:

      http://www.cplusplus.com/reference/cstdlib/itoa/

      Eine Ausgabe machst du (am PC und am Mikrocontroller) immer über Strings, daher sendet das Programm nur Strings. Wenn du also Registerwerte senden willst, musst du das Register lesen, den Wert in einen String umwandeln und dann senden.
      Empfangen geht ähnlich….String empfangen, mit der Funktion atoi in eine Zahl umwandeln, Zahl weiter nutzen.
      Hoffe das hilft dir weiter :)

      Gruß
      Daniel

  10. Hallo Kampi,
    oben bei der Berechnung für das Baudratenregister (BSEL) fehlt noch die “minus 1” am Ende.

    Super Seite, hab mir schon viele Ideen geholt

    LG Andreas

Schreibe einen Kommentar

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