|
|
AVR-Tutorial: UARTWie viele andere Controller besitzen die meisten AVRs einen UART (Universal Asynchronous Receiver and Transmitter). Das ist eine serielle Schnittstelle, die meistens zur Datenübertragung zwischen Mikrocontroller und PC genutzt wird. Zur Übertragung werden zwei Pins am Controller benötigt: TXD und RXD. Über TXD ("Transmit Data") werden Daten gesendet, RXD ("Receive Data") dient zum Empfang von Daten.
[Bearbeiten] HardwareUm den UART des Mikrocontrollers zu verwenden, muss der Versuchsaufbau um folgende Bauteile erweitert werden: Auf dem Board vom Shop sind diese Bauteile bereits enthalten, man muss nur noch die Verbindungen zwischen MAX232 (IC2) und AVR herstellen wie im Bild zu sehen.
[Bearbeiten] Software[Bearbeiten] UART konfigurierenAls erstes muss die gewünschte Baudrate im Register UBRR festgelegt werden. Der in dieses Register zu schreibende Wert errechnet sich nach der folgenden Formel:
Beim AT90S4433 kann man den Wert direkt in das Register UBRR laden, beim ATmega8 gibt es für UBRR zwei Register: UBRRL (Low-Byte) und UBRRH (High-Byte). Bei Baudraten über etwa 3900 Bit/s (gilt nur bei Verwendung eines Takts von 16 MHz) steht in UBRRH 0, da der berechnete Wert kleiner als 256 ist und somit in UBRRL alleine passt. Beachtet werden muss, dass das Register UBRRH vor dem Register UBRRL beschrieben werden muss. Der Schreibzugriff auf UBRRL löst das Neusetzen des internen Taktteilers aus.
WICHTIGER HINWEIS 1 Es empfiehlt sich statt der oben genannten Formel, die Formel der Codebeispiele zu verwenden:
Beispiel: Bei einem ATMega mit 16MHz und 115200 Baud ist der Wert laut Datenblatt UBBRL=8. Rechnet man mit der Formel UBRRL=(F_CPU / (UART_BAUDRATE* 16L) - 1) ergibt sich ein Wert von 7,680555 und im UBRRL Register steht somit eine 7 statt einer 8. Die Verwendung der Formel aus dem Codebeispiel ergibt 8.180555 und im UBRRL Register steht somit der richtige Wert - nämlich 8 WICHTIGER HINWEIS 2 Auf Grund permanent wiederkehrender Nachfrage sei hier AUSDRÜCKLICH darauf hingewiesen, dass bei Verwendung des UART im asynchronen Modus dringend ein Quarz oder Ouarzoszillator verwendet werden sollte! Der interne RC-Oszillator der AVRs ist recht ungenau! Damit kann es in Ausnahmefällen funktionieren, muss es aber nicht! Auch ist der interne Oszillator temperaturempfindlich. Damit hat man dann den schönen Effekt, dass eine UART-Schaltung die im Winter noch funktionierte, im Sommer den Dienst verweigert. Außerdem muss bei der Berechnung von UBRR geprüft werden, ob mit der verwendeten Taktfrequenz die gewünschte Baudrate mit einem Fehler von <1% generiert werden kann. Das Datenblatt bietet hier sowohl die Formel als auch Tabellen unter der Überschrift des U(S)ART an.
Siehe auch Baudratenquarz Wer es ganz einfach haben will, nimmt die folgenden Macros. Die rechnen sogar den Fehler aus und brechen die Assemblierung ggf. ab. Das ist dann praktisch idiotensicher.
Wer dennoch den internen RC-Oszillator verwenden will, muss diesen kalibrieren. Näheres findet man dazu im Datenblatt, Stichwort Register OSCCAL. Um den Sendekanal des UART zu aktivieren, muss das Bit TXEN im UART Control Register UCSRB auf 1 gesetzt werden. Danach kann das zu sendende Byte in das Register UDR eingeschrieben werden - vorher muss jedoch sichergestellt werden, dass das Register leer ist, die vorhergehende Übertragung also schon abgeschlossen wurde. Dazu wird getestet, ob das Bit UDRE ("UART Data Register Empty") im Register UCSRA auf 1 ist. Genaueres über die UART-Register findet man im Datenblatt des Controllers. An dieser Stelle sei noch folgendes angemerkt: Das UDRE-Bit sagt nichts darüber aus, ob der Controller immer noch damit beschäftigt ist, Daten zu senden. Da das Senderegister mehrfach gepuffert ist, wird UDRE bereits gesetzt, obwohl das letzte Zeichen den AVR noch nicht komplett verlassen hat. Dies kann insbesondere bei der Verwendung von Sleep-Modes ein Problem werden, wenn der Controller schlafen gelegt wird, bevor das letzte Zeichen versendet wurde, da dies gezwungenermassen zu einem Frame-Error beim Empfänger führen wird. Um sicher zu gehen, dass der UART nicht mehr beschäftigt ist, kann das Bit TXC ("UART Transmit complete") getestet werden. Dieses wird jedoch wirklich erst nach dem Senden eines Zeichens gesetzt, beinhaltet also auch nach dem Systemstart eine 0, obwohl der Controller nichts sendet. Der ATmega8 bietet noch viele weitere Optionen zur Konfiguration des UARTs, aber für die Datenübertragung zum PC sind im Normalfall keine anderen Einstellungen notwendig. [Bearbeiten] Senden von ZeichenDas Beispielprogramm überträgt die Zeichenkette "Test!" in einer Endlosschleife an den PC. Hinweis Wenn man das nachfolgende Programm laufen lässt und Hyperterminal startet, scheint es problemlos zu funktionieren. Wenn man aber das RS232 Kabel zwischenzeitlich abzieht und wieder ansteckt wird es oft passieren, dass nur noch wirre Zeichen auf dem PC erscheinen. Das liegt daran, dass der PC aus einem ununterbrochen Zeichenstrom nicht den Anfang eines Zeichens erkennen kann. Darum muss in solchen Fällen periodisch eine kleine Pause von der Länge mindestens eines Zeichens eingelegt werden, damit der PC sich wieder synchronisieren kann.
Der Befehl rcall serout ruft ein kleines Unterprogramm auf, das zuerst wartet bis das Datenregister UDR von der vorhergehenden Übertragung frei ist, und anschließend das in zeichen (=r17) gespeicherte Byte an UDR ausgibt. Bevor serout aufgerufen wird, wird zeichen jedesmal mit dem ASCII-Code des zu übertragenden Zeichens geladen (so wie in Teil 4 bei der LCD-Ansteuerung). Der Assembler wandelt Zeichen in einfachen Anführungsstrichen automatisch in den entsprechenden ASCII-Wert um. Nach dem Wort "Test!" werden noch die Codes 10 (New Line) und 13 (Carriage Return) gesendet, um dem Terminalprogramm mitzuteilen, dass eine neue Zeile beginnt. Eine Übersicht aller ASCII-Codes gibt es auf www.asciitable.com. Die Berechnung der Baudrate wird übrigens nicht im Controller während der Programmausführung durchgeführt, sondern schon beim Assemblieren, wie man beim Betrachten der Listingdatei feststellen kann. Zum Empfang muss auf dem PC ein Terminal-Programm wie z. B. HyperTerminal gestartet werden. Der folgende Screenshot zeigt, welche Einstellungen im Programm vorgenommen werden müssen: Linux-Benutzer können das entsprechende Device (z. B. /dev/ttyS0) mit stty konfigurieren und mit cat die empfangenen Daten anzeigen oder ein Terminalprogramm wie minicom nutzen. Alternativ kann unter Windows und Linux HTerm genutzt werden. (Freeware)
[Bearbeiten] Senden von ZeichenkettenEine bequemere Methode um längere Zeichenketten (Strings) zu übertragen ist hier zu sehen. Dabei werden die Zeichenketten im Flash gespeichert. Als Abschluss des Strings wird der Wert 0x00 genutzt, so wie auch in der Programmiersprache C.
[Bearbeiten] Empfangen von Zeichen per PollingDer AVR kann nicht nur Daten seriell senden, sondern auch empfangen. Dazu muss man, nachdem die Baudrate wie oben beschrieben eingestellt wurde, das Bit RXEN setzen. Sobald der UART ein Byte über die serielle Verbindung empfangen hat, wird das Bit RXC im Register UCSRA gesetzt, um anzuzeigen, dass ein Byte im Register UDR zur Weiterverarbeitung bereitsteht. Sobald es aus UDR gelesen wurde, wird RXC automatisch wieder gelöscht, bis das nächste Byte angekommen ist. Das erste einfache Testprogramm soll das empfangene Byte auf den an Port D angeschlossenen LEDs ausgeben. Dabei sollte man daran denken, dass PD0 (RXD) bereits für die Datenübertragung zuständig ist, so dass das entsprechende Bit im Register PORTD keine Funktion hat und damit auch nicht für die Datenanzeige verwendet werden kann. Nachdem der UART konfiguriert ist, wartet das Programm einfach in der Hauptschleife darauf, dass ein Byte über den UART ankommt (z. B. indem man im Terminalprogramm ein Zeichen eingibt), also RXC gesetzt wird. Sobald das passiert, wird das Register UDR, in dem die empfangenen Daten stehen, nach temp eingelesen und an den Port D ausgegeben.
[Bearbeiten] Empfangen von Zeichen per InterruptDieses Programm lässt sich allerdings noch verfeinern. Statt in der Hauptschleife auf die Daten zu warten, kann man auch veranlassen dass ein Interrupt ausgelöst wird, sobald ein Byte angekommen ist. Das sieht in der einfachsten Form so aus:
Diese Methode hat den großen Vorteil, dass das Hauptprogramm (hier nur eine leere Endlosschleife) andere Dinge erledigen kann, während der Controller Daten empfängt. Auf diese Weise kann man mehrere Aktionen quasi gleichzeitig ausführen, da das Hauptprogramm nur kurz unterbrochen wird, um die empfangenen Daten zu verarbeiten. Probleme können allerdings auftreten, wenn in der Interruptroutine die gleichen Register verwendet werden wie im Hauptprogramm, da dieses ja an beliebigen Stellen durch den Interrupt unterbrochen werden kann. Damit sich aus der Sicht der Hauptschleife durch den Interruptaufruf nichts ändert, müssen alle in der Interruptroutine geänderten Register am Anfang der Routine gesichert und am Ende wiederhergestellt werden. Das gilt vor allem für das CPU-Statusregister (SREG)! Sobald ein einziger Befehl im Interrupt ein einziges Bit im SREG beeinflusst, muss das SREG gesichert werden. Das ist praktisch fast immer der Fall, nur in dem ganz einfachen Beispiel oben ist es überflüssig, weil die verwendeten Befehle das SREG nicht beeinflussen. In diesem Zusammenhang wird der Stack wieder interessant. Um die Register zu sichern, kann man sie mit push oben auf den Stapel legen und am Ende wieder in der umgekehrten Reihenfolge(!) mit pop vom Stapel herunternehmen. Im folgenden Beispielprogramm werden die empfangenen Daten nun nicht mehr komplett angezeigt. Stattdessen kann man durch Eingabe einer 1 oder einer 0 im Terminalprogramm eine LED (an PB0) an- oder ausschalten. Dazu wird das empfangene Byte in der Interruptroutine mit den entsprechenden ASCII-Codes der Zeichen 1 und 0 (siehe www.asciitable.com) verglichen. Für den Vergleich eines Registers mit einer Konstanten gibt es den Befehl cpi register, konstante. Das Ergebnis dieses Vergleichs kann man mit den Befehlen breq label (springe zu label, wenn gleich) und brne label (springe zu label, wenn ungleich) auswerten.
[Bearbeiten] HandshakeWerden Daten schnell über eine serielle Leitung an ein langsames Gerät übertragen, dann kann es passieren, dass die Situation eintritt, dass das empfangende Gerät nicht mehr mitkommt. Das kann z.B. dadurch passieren, dass das empfangende Gerät selbst etwas Zeit für die Bearbeitung der Daten benötigt. Man denke z.B. an die Situation, dass an ein Modem Daten übertragen werden. Das Modem muss diese Daten bearbeiten und unter Umständen über eine langsame Telefonleitung absetzen. Überträgt der AVR seine Daten mit voller Geschwindigkeit an das Modem, so wird auch dem besten Modem irgendwann der interne Speicher ausgehen, in dem es die Daten zwischenspeichern kann. Was benötigt wird, ist also eine Möglichkeit, wie die Gegenstelle dem Sender signalisieren kann: "Bitte jetzt nichts senden, ich bin beschäftigt!". Die einfachste Form eines derartigen Protokolls, nennt sich Handshake. Es gibt bei RS232 2 Arten, wie dieses Handshake implementiert werden kann: Software-Handshake und Hardware-Handshake. [Bearbeiten] Hardware HandshakeHardware Handshake benutzt die beiden Steuerleitungen RTS - Request to Send und CTS - Clear to Send um die Flusskontrolle durchzuführen. Die etwas seltsam anmutenden Namen haben historische Ursache. Ursprünglich war RS232 dazu gedacht ein Modem (ein sog. Data Carrier Equipment oder DCE) an einen Endpunkt (DTE oder Data Terminal Equipment) anzuschliessen. Wenn das DTE Daten senden wollte, aktivierte es die Leitung RTS, es fragte praktisch beim DCE an: "Darf ich senden? (engl. Request sending)". Wenn das DCE bereit war, dann aktivierte es seinerseits die CTS Leitung und signalisierte damit "Alles ok. Daten marsch! (engl. Clear to send)". Solange das DCE nicht bereit war, Daten entgegenzunehmen, musste das DTE warten, bis es vom DCE die Freigabe zum Senden bekam.
Das war die ursprüngliche Idee. Heutzutage ist es aber normal, dass 2 DTE miteinander über eine RS232 Verbindung gekoppelt werden. Wird in so einem Fall Hardware Handshake benutzt, so muss jedes DTE seiner Gegenstelle eine korrekte Bedienung der RTS/CTS Leitung vortäuschen. Der Teil, dass CTS nur dann bedient wird, wenn über RTS die Anfrage nach der Sendefreigabe erfolgt entfällt dabei. Jeder Gesprächspartner überprüft ganz einfach vor dem Sendevorgang den Zustand der CTS Leitung der Gegenstelle, während der eigene RTS Ausgang zur Signalisierung der Empfangsbereitschaft für die Gegenstelle dient. Dies ist auch der Grund warum bei einem Null-Modem-Kabel nicht nur die RX/TX Leitungen, sondern auch die RTS/CTS Leitungen gekreuzt werden müssen. Möchte man obige Schaltung um eine Hardware-Flusskontrolle erweitern, so bietet es sich an, die beiden noch freien Kanäle des MAX232 dafür zu verwenden. Die Schaltung sieht dann wie folgt aus: Am Mega8 stehen dann die Signale RTS bzw. CTS an den Pins PD4 bzw. PD5 zur Verfügung. An PD5 kann abgefragt werden, ob die Gegenstelle zum Empfang von Daten bereit ist, während der Mega8 über PD4 signalisieren kann, dass er im Moment keine Daten über die serielle Schnittstelle empfangen kann. [Bearbeiten] Software HandshakeSoftware Handshake benutzt die Datenleitung selbst, um die Flußkontrolle von Sender/Empfänger zu erreichen. Dazu wurden im ASCII Code 2 spezielle 'Zeichen' vorgesehen: XON (mit dem Code 0x11) und XOFF (mit dem Code 0x13). Bemerkt ein Empfänger, dass er in Kürze keine Daten mehr vom Sender aufnehmen kann, dann sendet er seinerseits ein XOFF, woraufhin der Sender das Senden der Daten unterbricht. Ist der Empfänger wieder aufnahmebereit, so gibt er die Übertragung durch das Senden eines XON wieder frei. Der Nachteil des Software-Handshaking besteht also in mehreren Punkten
[Bearbeiten] Weblinks
|