Verwendung von UART und USART zur Kommunikation mit dem AVR-Mikrocontroller
Teilen
Verwenden von UART und USART zur Kommunikation mit dem AVR-Mikrocontroller
Hier ist die leicht verständliche Erklärung des USART (Universal Synchronous Asynchronous Transmitter/Receiver). Die hier enthaltenen Informationen sind eine Interpretation des Datenblatts, um sie leicht verständlich zu machen. Das USART-Merkmal des AVR-Mikrocontrollers kann mit einem anderen Mikrocontroller (nicht unbedingt einem anderen AVR-Mikrocontroller), mehreren Mikrocontrollern oder einem Computer unter Verwendung eines Spannungswandlers oder -umsetzers kommunizieren. Der USART kann Daten über einen Puffer und ein Schieberegister (mehr dazu später) senden, Daten über ein Schieberegister und einen Puffer empfangen, einen Datenrahmen erstellen, der sowohl am Empfangs- als auch am Sendeende erkannt wird. All dies funktioniert gemäß einer vereinbarten Geschwindigkeit von beiden Seiten, oder im synchronen Modus, bei dem die Taktleitung (ein Draht) direkt verbunden ist.
Das Senden und Empfangen von Informationen in der UART- und USART-Kommunikation ist wie eine Zugfahrt. Der Zug bringt Sie zu einem Ziel, so wie er Daten an einen anderen Computer oder Mikrocontroller sendet. Der Bahnhof ist wie der interne Sender und Empfänger im Mikrocontroller. Es muss einen Ort geben, an dem die Zugreisenden Schlange stehen, um in den Zug einzusteigen. Zuerst holt sich ein Passagier das Ticket und setzt sich, um auf den nächsten Zug zu warten. Der Passagier muss sich dann anstellen, um in den Zug einzusteigen. Dann steigt der Passagier in den Zug. Genau so funktioniert ein USART-Sender. Die Daten werden an einen Puffer gesendet, der wie der Warteraum für den Zug ist. Das Schieberegister ist wie die Warteschlange, um in den Zug einzusteigen. Die Daten bewegen sich vom Puffer in die Schieberegister. Dies geschieht, nachdem die vorherigen Daten die Schieberegister verlassen haben. Von den Schieberegistern bewegen sich die Daten entlang des Sendedrahtes, genau wie ein Zug auf einer Strecke fährt.
Das Empfangen von Informationen ist dasselbe, aber umgekehrt. In der menschlichen Analogie kommt der Zug am Bahnhof an. Die Person steigt aus dem Zug und muss in einer Schlange warten, damit die Personen vor ihr Platz machen. Beim Datenempfang des Mikrocontrollers verlassen die Daten den Draht und gehen direkt in die Schieberegister. Mit dem Atmega32 gibt es zwei Puffer, die die Daten verwenden können, um auf die Verwendung durch das Programm im Mikrocontroller zu warten. Mit dem Schieberegister bietet dieser Empfangsbereich insgesamt drei Puffer, in denen die Daten sitzen und auf die Verwendung warten können. Dies wird durch den Chip so eingerichtet, dass Datenüberläufe (DOR) weniger wahrscheinlich sind. Der DOR ist ein Flag, das Sie betrachten können.
Der Zug ist eigentlich ähnlich der Baudrate. Die Baudrate ist der Takt, der die Daten über die Leitung schiebt. Es gibt verschiedene Arten von Takten, die Sie mit UART und USART anwenden können. Der UART ermöglicht es Ihnen nur, eine vereinbarte Baudrate von beiden Parteien anzuwenden (jeder Mikrocontroller muss mit dieser spezifischen Baudrate eingestellt werden). In USART muss ein Taktdraht zwischen jedem Mikrocontroller verbunden sein. Dieser Draht wird wie ein Herzschlag pulsieren. Im asynchronen Fall hat jeder Mikrocontroller seinen eigenen Takt, und da die Daten mit dem sendenden Mikrocontroller unter Verwendung dieses Taktes (Baudrate) gesendet werden, muss der empfangende Mikrocontroller diese Daten im gleichen Tempo empfangen, sodass sein Takt (Baudrate) derselbe sein muss.
Taktmodi:
Wählen Sie zwischen synchron oder asynchron
UCSRC &= ~(1 << UMSEL); //UMSEL-Bit für den asynchronen Modus auf 0 setzen
Asynchron:
Asynchron bedeutet, dass der Takt des Mikrocontrollers nicht durch einen Draht mit dem anderen Mikrocontroller verbunden ist, aber sie benötigen denselben Takt, um die Daten auf der Datenleitung zu verarbeiten.
Beispiel: UBBR = ( 1.000.000 / 16 * 2400 ) - 1 = ( 1.000.000 / 38.400 ) - 1 = 26,0416667 - 1 = 25,0416667 = 25
Es ist 25, weil der UBBR keinen Dezimalwert akzeptiert, aber das führt zu einem Fehler von 0,2%, was akzeptabel ist. Eine Tabelle mit häufig verwendeten UBBR-Nummern finden Sie unter „Beispiele für Baudrateneinstellungen“ im USART-Abschnitt des Datenblatts.
Die Baudrate aus dem UBBR zu finden ist optional:
Beispiel: Baud = 1.000.000 / (16 * (25 + 1)) = 1.000.000 / (16 * 26) = 1.000.000 / 416 = 2403,84615 = 2400
Ziemlich nah an der Standard-Baudrate von 2400 mit 0,2 % Fehler, was akzeptabel ist. Überprüfen Sie Ihr Datenblatt auf akzeptable Fehlerprozentsätze.
Einstellung der Baudrate:
UBBRH = (unsigned char) (# aus Tabelle >> 8);
UBBRL = (unsigned char) # aus Tabelle;
Das URSEL ist auf 0 gesetzt, da UBBRH dieselbe E/A wie UCSRC teilt. Das URSEL muss auf 0 gesetzt werden, um in UBBRH zu schreiben. Das UBBRH setzt den oberen Teil der Baudrate – von Bit 8 bis Bit 11. Das UBBRL legt die restlichen Bits 0 bis 7 in das niedrige UBBR-Register.
Einstellung des Asynchronmodus:
Doppelgeschwindigkeits-Asynchron- U2X-Bit in UCSRA steuert Doppelgeschwindigkeits-Asynchron
UCSRA &= ~(1 << U2X); //U2X-Bit auf 0 setzen für normales Asynchron
Synchron
Wo der Takt mit einem Draht zwischen den beiden Mikrocontrollern verbunden ist - DDR_XCK Data Direction Register steuert, welcher Mikrocontroller der Master und welcher der Slave ist. Wenn der DDR_XCK auf Ausgang eingestellt ist, dann ist der XCK-Pin an diesem Mikrocontroller der Master, da er den Taktausgang an diesem XCK-Pin erzeugt.
Slave Synchron
Datenrahmen:
Dies sind die tatsächlich übertragenen Daten. Es ist der Zug mit den Daten an Bord. Dies ist mit 9 Bits für die Datenbits, einem Startbit, zwei Stoppbits, einem Paritätsbit, was die maximale Rahmengröße ist.
Bit 01: Die Lokomotive - Bit 1: Startbit - immer niedrig (0)
Bit 02: Person #1 im Zug - Datenbit #0 - hoch oder niedrig je nach Daten
Bit 03: Person #2 im Zug - Datenbit #1 - hoch oder niedrig je nach Daten
Bit 04: Person #3 im Zug - Datenbit #2 - hoch oder niedrig je nach Daten
Bit 05: Person #4 im Zug - Datenbit #3 - hoch oder niedrig je nach Daten
Bit 06: Person #5 im Zug - Datenbit #4 - hoch oder niedrig je nach Daten
Bit 07: Person #6 im Zug - Datenbit #5 - hoch oder niedrig je nach Daten
Bit 08: Person #7 im Zug - Datenbit #6 - hoch oder niedrig je nach Daten
Bit 09: Person #8 im Zug - Datenbit #7 - hoch oder niedrig je nach Daten
Bit 10: Person #9 im Zug - Datenbit #8 - hoch oder niedrig je nach Daten
Bit 11: Zugwagen kurz vor dem Schlusswagen, der alles überwacht - Paritätsbit - hoch oder niedrig je nach den 1en der Daten
Bit 12: Der Schlusswagen - Stoppbit - immer hoch
Bit 13: Der zusätzliche Schlusswagen - Stoppbit - immer hoch und immer vom Empfänger ignoriert
zurück in den hohen Ruhezustand - oder zu einem neuen Startbit.
Es gibt maximal 13 Bits im größten Datenrahmen.
Einstellung der Datenbitgröße - Anzahl der gewünschten Datenbits im Rahmen. Verwenden Sie die UCSZ2:0 (UCSZ0, UCSZ1, UCSZ2) Bits im UCSRC-Register. Beachten Sie, dass, wenn alle Bits in UCSZ2:0 nicht gesetzt sind, eine 5-Bit-Datenlänge festgelegt wird.
UCSZ - Zeichengröße
UCSRC |= (1 << UCSZ1); //7-Bit-Datenlänge, oder
UCSRC |= (2 << UCSZ0); //Alternativer Code für 7-Bit-Datenlänge
UCSRC |= (1 << UCSZ1) | (1 << UCSZ0); //8-Bit-Datenlänge, oder
UCSRC |= (3 << UCSZ0); //Alternativer Code für 8-Bit-Datenlänge
UCSRC |= (1 << UCSZ2) | (1 << UCSZ1) | (1 << UCSZ0); //8-Bit-Datenlänge, oder
UCSRC |= (7 << UCSZ0); //Alternativer Code für 8-Bit-Datenlänge
Huch, was sind diese „2, 3 und 7“?!? Nun, es ist höchste Zeit, dass Sie eine Kurzschreibweise zum Setzen von Bits lernen. Das UCSZ0 ist das Äquivalent zur Einerstelle im Binärsystem. Wenn Sie eine andere Zahl, wie 7, eingeben, setzen Sie tatsächlich das binäre Äquivalent der Zahl 7 an dieser Stelle, was die nächsten Binärziffern ausfüllt, da die Zahl 7 im Binärsystem 111 ist.
Hinweis: Wenn Sie etwas an den unten gezeigten Rahmendaten ändern möchten, werfen Sie einen Blick auf die TXC- und RXC-Flags und stellen Sie sicher, dass diese nicht gesetzt sind. Wenn sie gesetzt sind, laufen noch Übertragungen. Für die meisten Mikrocontroller-Projekte ist dies möglicherweise nie erforderlich, es sei denn, Sie möchten die Kontrolle über die Port-E/A-Pins des Chips, wo sich TX und RX befinden, haben.
Ein weiterer Hinweis: Aus irgendeinem seltsamen Grund teilen sich UBBRH und UCSRC denselben E/A-Speicherort, weshalb Sie sicherstellen müssen:
URSEL-Bit in UCSRC ist gesetzt, wenn Sie Daten in dieses Register schreiben möchten.
Einstellung des Paritätsmodus - Hier stellen Sie die Fehlerkorrekturfunktion der UART- und USART-Kommunikation ein. Die Parität wird nur vom Sender eingestellt.
Wenn die Parität auf „gerade“ gesetzt ist und eine ungerade Anzahl von Einsen in den Datenbits vorhanden ist, setzt der Sender das Paritätsbit auf „1“, sodass eine gerade Anzahl von Einsen einschließlich des Paritätsbits vorhanden ist.
Wenn die Parität auf „ungerade“ gesetzt ist und eine gerade Anzahl von Einsen in den Datenbits vorhanden ist, setzt der Sender das Paritätsbit auf „1“, sodass eine ungerade Anzahl von Einsen einschließlich des Paritätsbits vorhanden ist.
UPM - Paritätsmodus
UCSRC |= (1 << UPM1) | (1 << UPM0); //Setzt Parität auf ODD, oder
UCSRC |= (3 << UPM0); //Alternative Methode, Parität auf ODD zu setzen
Einstellung der Anzahl der zu verwendenden Stoppbits - Möchten Sie einen oder zwei Schlusswagen am Ende Ihres Zuges? Denken Sie daran, dass der Empfänger das zweite Stoppbit ignoriert, warum wurde es also überhaupt eingefügt? Wer weiß, aber ich denke, dass es dem nächsten Datenrahmen ein „bisschen“ Atempause verschafft. Wie gefällt Ihnen dieses Wortspiel? Huh!
USBS - Stoppbit-Auswahl
UCSRC &= ~(1 << USBS); //Löscht das USBS für 1 Stoppbit, nur notwendig, wenn das Bit bereits gesetzt war
Hinweis: Wenn der Mikrocontroller, der den Datenrahmen (den Choo Choo Zug) empfängt, ein Stoppbit sieht, das niedrig ist (es sollte hoch sein), wird das Frame Error Flag Bit (FE) gesetzt.
Beispielinitialisierung für USART oder UART:
In diesem Beispiel wird die Baudrate eingestellt, der Sender (TXEN - Transmitter Enable) und Empfänger (RXEN - Receiver Enable) aktiviert, und im UCSRC wird das URSEL gesetzt, damit wir UCSRC ändern können, 2 Stoppbits verwendet und die Länge der Datenbits 8-Bit beträgt.
Sie werden feststellen, dass dies erstens eine Funktion ist und zweitens ein unsigned int an diese Funktion übergeben wird. Das void am Anfang bedeutet, dass diese Funktion nach Abschluss nichts zurückgibt. Dies ist dasselbe Beispiel, das im Datenblatt des Atmega324 gezeigt wird. Beachten Sie, dass die Parität in diesem Beispiel nicht eingestellt ist.
{
UBBRH = (unsigned char) (Baud >> 8);
//Den restlichen Teil der Baudzahl hier einfügen
UBBRL = (unsigned char) Baud;
//Empfänger und Sender aktivieren
UCSRB = (1 << RXEN) | (1 << TXEN);
//2 Stoppbits und 8-Bit-Datenlänge einstellen
UCSRC = (1 << URSEL) | (1 << USBS) | (3 << UCSZ0);
Hinweis: Wenn TXEN gesetzt ist (Transmitter Enabled), ist die allgemeine Funktion dieses Pins nicht verfügbar, bis TXEN deaktiviert wird. Mit anderen Worten, Sie können den TX-Pin nicht verwenden, um LEDs zu beleuchten oder Tastendrücke und ähnliches zu empfangen. Dasselbe gilt für RXEN und den RX-Pin. Noch etwas: TXEN kann nicht deaktiviert werden, während eine Übertragung aussteht. Wenn Sie den XCK-Pin für den synchronen Betrieb verwenden, ist die andere allgemeine Funktion am XCK-Pin deaktiviert.
Etwas senden!!
In diesem nächsten Beispiel werden wir tatsächlich etwas übertragen. Wir werden unsere Daten in den UDR (USART I/O Data Register) Bahnhof werfen. Ich werde Ihnen hier ein Geheimnis verraten, denn Sie könnten später etwas verwirrt sein: Sie werden die Daten, die Sie senden möchten, in das UDR-Register legen, und Sie werden auch empfangene Daten aus dem UDR erhalten. Das scheint völlig daneben zu sein, nicht wahr? Wenn das stimmt, wie könnten wir dann gleichzeitig senden und empfangen, wenn wir nur ein Datenregister zu verwenden haben… Ich meine, wenn Vollduplex verfügbar ist, warum gibt es dann nur ein Register, um Daten zu senden und zu empfangen? Scheint seltsam.
Nun, hier ist das Geheimnis! Pssst, verraten Sie es niemandem! Das UDR hat tatsächlich zwei Stellen. Die geheimen Speicherorte heißen TXB und RXB (dies sind 8-Bit-Speicherorte). Der Mikrocontroller ist so intelligent, dass, wenn Sie Informationen in das UDR-Register legen, die Dinge, die Sie einfügen, tatsächlich in den TXB-Speicherort gehen. Wenn Sie die Daten aus dem UDR-Register nehmen, dann nehmen Sie tatsächlich die Dinge aus dem RXB-Speicherort. Sie könnten fragen, was passiert, wenn wir unsere Datenbitlänge auf 9 Bits einstellen, da TXB und RXB nur 8 Bits haben? Diese Bits befinden sich im UCSRB-Register und heißen, Sie haben es erraten, TXB8 und RXB8. Oh, noch etwas, dies ist ein Polling-Beispiel, was bedeutet, dass wir warten werden (mit den Zehen tippen), bis das UDRE (USART Data Register Empty) Signal sagt, dass es in Ordnung ist zu gehen. Es ist wie eine Ampel. Sie können nichts in das UDR einfügen, wenn das System nicht bereit für Sie ist. Genug geredet, kommen wir zum Beispiel:
{
while (! (UCSRA & (1 << UDRE)) );
//Das 9. Bit vorerst auf 0 setzen
UCSRB &=~(1 << TXB8);
//Wenn das 9. Bit der Daten eine 1 ist
if (data & 0x0100)
USCRB |= (1 << TXB8);
UDR = data;
Die Bedingung in der while-Anweisung mag Ihnen fremd erscheinen. Sie ist scheinbar kompliziert, aber es ist dieselbe Art von (und) und (nicht) bitweiser Operation, die wir in einem früheren Video gelernt haben. Das ! ist ein (nicht) und & ist ein (und). Erinnern Sie sich, wenn wir sagen register &=~(1 << bit)? Wir führen eine & (und)-Berechnung durch und kehren die Bits mit dem ! (nicht) um. Wir können dies auch in eine Bedingung wie diese setzen: Wenn Sie überprüfen möchten, ob ein Bit (nicht) 0 ist, dann verwenden wir (! (register & (1 << bit)) ).
Ok, hier ist eine Erklärung der Flags, die uns über den Sendeprozess informieren:
Polling-Ressourcen:
TXC = Transmit complete - Diese Ressource können Sie verwenden, wenn Sie Halbduplex betreiben. Dieses Flag wird gesetzt, wenn die Daten das Schieberegister verlassen. Erinnern Sie sich an diese Warteschlange, von der wir vorhin gesprochen haben.
Interrupt-Ressourcen: Denken Sie daran, Ihre globale Interrupt-Variable zu setzen, bevor Sie diese coolen Ressourcen nutzen!
TXCIE = Transmit Complete Interrupt Enable - Verwenden Sie dies, wenn Sie Halbduplex betreiben und möchten, dass der Mikrocontroller Sie unterbricht, damit Sie Ihre Daten in den UDR-Bahnhof legen können.
Etwas empfangen!!
Der Prozess, wie der Mikrocontroller Daten empfängt, die auf einer Leitung vom anderen Mikrocontroller existieren, läuft wie folgt ab:
- Der Mikrocontroller erkennt ein Startbit
- Die Datenbits folgen und werden zusammen mit der Baudrate (oder XCK für den synchronen Herzschlag) aufgenommen
- Die Daten gehen in das Schieberegister (wie Menschen, die aus dem Zug steigen und in der Schlange warten, um den Bahnsteig zu verlassen)
- Dies geschieht, bis das erste Stoppbit empfangen wird (denken Sie daran, das zweite Stoppbit wird ignoriert)
- Der Inhalt der Daten geht dann direkt in das UDR (nun ja, eigentlich in das RXB, aber wir erhalten es immer noch aus dem UDR)
Hier ist ein Beispiel, wie man Daten empfängt:
{
return UDR; //Daten rausholen und zurück ins Hauptprogramm!
Beachten Sie das unsigned char am Anfang unserer Funktionsdeklaration? Das ist der Rückgabetyp, den wir bereitstellen werden. Das UDR ist ein 8-Bit-Wert ohne Vorzeichen (char). Wir verwenden die return-Anweisung, um die Daten aus dem UDR freizugeben und sie an das Hauptprogramm zurückzugeben.
Lassen Sie mich wissen, wenn Sie ein 9-Bit-Beispiel sehen möchten.
Ok, hier ist eine Erklärung der Flags, die uns über den Empfangsprozess informieren:
Polling-Ressource:
Interrupt-Ressource: Denken Sie daran, Ihre globale Interrupt-Variable zu setzen, wenn Sie diese verwenden!