3.6 🚧 UART / RS232
Synopsis: [de.wikipedia.org/wiki/Universal_Asynchronous_Receiver_Transmitter 🔗] [www.arduino.cc/reference/en/language/functions/communication/serial/ 🔗]
Nochmal anschauen: 2.6 Schieberegister
Ist die älteste und schwierigste einfache Serielle Schnittstelle. Die Datenübertragung erfolgt asynchron (eine Baud-Rate muss eingestellt werden) und hat Start- und Stopp-Bits ausserdem kann ein Paritätsbit übertragen werden.
Es ist die elementare serielle Schnittstelle für Arduino, darüber werden viele der Boards programmiert und darüber wird mit dem Seriellen Monitor kommuniziert.
Datenübertragung analysieren
Nun wollte ich mit dem Oszilloskop die Kommunikation mitschneiden und steckte den Tastkopf in D1/TX auf dem L152RE-Board und fand nur Rauschen.
In der Doku fand ich raus, dass die Pins D1/PA2/TX und D0/PA3/RX einfach abgeklemmt sind und sich erst mit Lötbrücken wieder anklemmen lassen: [Doku UM1724] Seite 25 6.8 USART Communication. Auf der ST-LINK-Platine können die Signale aber abgegriffen werden, zur Kommunikation wird USART2 verwendet.
void setup() {
Serial.begin(9600); // USART2 PA2, PA3 mit ST-Link verbunden
}
void loop() {
Serial.println("ab");
delay(1000);
}
Übertragung mit Logic-Analyzer beobachten
Aufgaben
- Zeichnen Sie ein Blockdiagramm mit 2 Kommunikationsteilnehmern die mittels UARTs verbunden sind
- Erklären Sie die serielle Kommunikation
- Parity
- Ergänzen Sie jeweils zu gerader Parity (even)
- 01010101___
- 11001000___
- 00000001___
- Ergänzen Sie jeweils zu Ungerader Parity (odd)
- 01101010___
- 11100011___
- 00000000___
- Richtig oder fehlerhaft
- even 101011011
- odd 01011101
- even 111001111
- Erläutern sie, wie die Erkennung von Fehlern bei der Datenübertragung funktioniert
- Ergänzen Sie jeweils zu gerader Parity (even)
- Wie ist mit UARTs eine Vollduplex-Datenübertragung möglich?
- Wie kann bei der Datenübertragung mit UART auf eine Taktleitung verzichtet werden?
- Was versteht man unter einer asynchronen Datenübertragung?
Lösung
- Siehe Forsa S. 17
- Die Datenbits werden seriell, d.h. nacheinander übertragen. Damit das funktioniert müssen Sender und Empfänger mit der gleichen Geschwindigkeit arbeiten.
- Parity
- Ergänzen Sie jeweils zu gerader Parity (even)
- 01010101__0_
- 11001000__1_
- 00000001__1_
- Ergänzen Sie jeweils zu Ungerader Parity (odd)
- 01101010__1_
- 11100011__0_
- 00000000__1_
- Richtig oder fehler
- even 101011011 ✅
- odd 01011101 ✅
- even 111001111 ❌
- Wenn bei der Übertragung ein Bit umkippt kann dies durch die Parität erkannt werden.
- Ergänzen Sie jeweils zu gerader Parity (even)
- Für Senden und Empfang ist jeweils eine Leitung vorhanden.
- Sender und Empfänger haben einen Taktgenerator, durch das Startbit kann der Empfänger sich auf den Sender synchronisieren.
- Sender und Empfänger arbeiten nicht mit dem selben Takt sondern der Empfänger hat eine eigene Taktbasis.
Vom Seriellen-Monitor Zeichen einlesen
Synopsis: [arduino.cc/reference/en/language/functions/communication/serial/available/] [arduino.cc/reference/en/language/functions/communication/stream/]
Die serielle Schnittstelle, mit der der µC programmiert wird kann auch zum Übertragen von Daten, Zeichen vom und zum µC verwendet werden. Dazu muss in setup() die Schnittstelle mit der gewünschten Baud-Rate (Übertragungsgeschwindigkeit) initialisiert werden.
Beim Empfangen von Zeichen (Bytes) werden in einen Puffer (64 Bytes) gespeichert. Die Operation Serial.available() gibt die Anzahl der empfangen Bytes zurück.
Mit der Operation Serial.read() kann ein Zeichen aus dem Puffer gelesen werden, das Zeichen wird dabei aus dem Puffer entfernt und die Anzahl der Zeichen im Puffer um 1 erniedrigt. Beispielcode:
Serial.available() gibt Anzahl der Bytes im Empfangspuffer zurück.
Serial.read() gibt ein Zeichen aus dem Empfangspuffer zurück und erniedrigt die Anzahl um eins.
void setup(){
Serial.begin(9600); // Serielle Schnittstelle mit 9600 Baud starten
}
void loop(){
char zeichen;
if(Serial.available()){ // wenn Zeichen gesendet wurden
zeichen = Serial.read(); // lese Zeichen
Serial.printf("Zeichen: %2c Hex: %#x\n",zeichen,zeichen); // gib Zeichen und Code aus
}
}
Die eingegebenen Zeichen werden aus der Eingabezeile des seriellen Monitors erst nach einem “Return” zum µC gesendet, es kann eingestellt werden, ob am Ende noch weitere Bytes, Symbole gesendet werden sollen:
- Kein Zeilenende, es wird kein weiters Byte mehr gesendet.
- Neue Zeile, es wird das Symbol New Line, (NL, “\n”, 0xa, bzw. Line Feed, LF) gesendet, das eine neue Zeile bewirken soll, denke an eine Schreibmaschine bei der in eine neue Zeile gegangen wird. Ist der Normalfall.
- Zeilenumbruch, es wird das Symbol Carriage Return (CR, 0xd) gesendet, das einen Wagenrücklauf bewirken soll, also wieder zum Anfang der Zeile.
- Sowohl NL als auch CR, es werden beide Symbole gesendet.
Mehrere Zeichen in einen String einlesen
Es gibt auch Operationen, mit denen nicht nur einzelne Zeichen sondern das Empfangene auch gleich z.B. in einen String eingelesen wird.
void setup(){
Serial.begin(9600); // Serielle Schnittstelle mit 9600 Baud starten
}
void loop(){
String s;
if(Serial.available()){ // wenn Zeichen gesendet wurden
s = Serial.readString(); // lese Daten in String ein
Serial.print(s); // gib String aus
}
}
Beim Testen des Codes fällt auf, dass es 1 Sekunde dauert bis im Seriellen Monitor die Eingabe erscheint. Die readString()-Operation hat erst fertig wenn nach einem Timeout von 1 Sekunde kein weiteres Zeichen mehr empfangen wird, siehe [Stream.readString() 🔗].
Mit readStringUntil(‘\n’) kann das Einlesen z.B. mit dem Terminal-Symbol ‘\n’, New Line gestoppt werden, die Eingabe erscheint sofort, siehe [Stream.readStringUntil() 🔗].
Aufgabe: Von Seriell-Monitor einlesen und Töne ausgeben
Die Tasten a..l auf der Tastatur sollen die Töne ab c’ ausgeben, dieser Code ist vorgegeben:
void setup(){
Serial.begin(9600); // Serielle Schnittstelle mit 9600 Baud starten
analogWriteResolution(16); // 16 Bit PWM-Auflösung
}
unsigned char buchstabe[]={'a','s','d','f','g','h','j','k','l'};
unsigned int frequenz[]={262,277,293,311,330,349,370,392,415,440,466,494}; // Frequenzen
void loop(){
String s;
if(Serial.available()){ // wenn Zeichen gesendet wurden
s = Serial.readStringUntil('\n'); // lese Daten in String ein
Serial.print(s); // gib String aus
for (int i=0; _____ ;i++){ // String durchgehen
//Serial.println(s[i]);
for (int k=0; _____ ;k++){ // finde Buchstaben sizeof(buchstabe) gibt die Länge des Arrays zurück
if( ____ ){ // wenn Buchstabe passt
analogWriteFrequency(frequenz[k]);
analogWrite(D11,32000); // PWM mit ca. 50%
delay(200); // Ton halten
analogWrite(D11,0); //Ton aus
delay(20); // bisschen Abstand
______ // etwas effizienter
}
}
}
}
}
Lösungsvorschlag
void setup(){
Serial.begin(9600); // Serielle Schnittstelle mit 9600 Baud starten
analogWriteResolution(16); // 16 Bit PWM-Auflösung
}
unsigned char buchstabe[]={'a','s','d','f','g','h','j','k','l'};
unsigned int frequenz[]={262,277,293,311,330,349,370,392,415,440,466,494}; // Frequenzen
void loop(){
String s;
if(Serial.available()){ // wenn Zeichen gesendet wurden
s = Serial.readStringUntil('\n'); // lese Daten in String ein
Serial.print(s); // gib String aus
for (int i=0;i<s.length();i++){
//Serial.println(s[i]);
for (int k=0;k<sizeof(buchstabe);k++){ // finde Buchstaben sizeof(buchstabe) gibt die Länge des Arrays zurück
if(s[i]==buchstabe[k]){ // wenn Buchstabe passt
analogWriteFrequency(frequenz[k]); // stelle PWM-Frequenz ein
analogWrite(D11,32000); // PWM mit ca. 50%
delay(200); // Ton halten
analogWrite(D11,0); //Ton aus
delay(20); // bisschen Abstand
break; // etwas effizienter
}
}
}
}
}
Verbindung zu weiterer Seriellen Schnittstelle
Die Standart-UART-Schnittstelle dient der Programmierung und Kommunikation mit dem seriellen Monitor. Wenn weitere UART-Module (z.B. Bluetooth) angeschlossen werden sollen wird eine zusätzliche Schnittstelle benötigt. Der µC hat dafür bereits die Hardware, die an bestimmten Ports betrieben werden kann siehe mezmedia.de/etc/hardware/stm32-nucleo-l152re/#Pinout. Beispiel zum Anschluss eines BT-Moduls am Sturm-Board:
HardwareSerial
Synopsis: [https://github.com/stm32duino/Arduino_Core_STM32/wiki/API#hardwareserial]
#define BTRX PB11 // USART3-RX
#define BTTX PB10 // USART3-TX
HardwareSerial SerialBT(BTRX,BTTX); //https://github.com/stm32duino/Arduino_Core_STM32/wiki/API#hardwareserial
SoftwareSerial
Synopsis: [SoftwareSerial Library]
Falls an den gewünschten Pins keine Hardware helfen kann, gibt es auch die Möglichkeit per Software die Schnittstelle an beliebigen Pins zu simulieren, Beispielcode:
#include <SoftwareSerial.h> // Wir verwenden Software Serial
#define BTRX PB11
#define BTTX PB10
SoftwareSerial SerialBT(BTRX, BTTX);
Exit-Room-Telefon
Synopsis: [mezdata.de/mez-entwicklung/090_exitroom-telefon/index.php] [wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299]
Info: Kommando 0x12 spielt aus MP3-Ordner ab…