Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
microcontrollertechnik:9_uart_und_terminal [2020/10/29 11:32]
tfischer
microcontrollertechnik:9_uart_und_terminal [2024/03/11 00:04] (aktuell)
mexleadmin
Zeile 1: Zeile 1:
-====== 9UART und Terminal ======+====== 9 UART und Terminal ======
 <WRAP> <WRAP>
  
Zeile 6: Zeile 6:
   * In der Simulation SimulIDE kann ein Terminalfenster direkt im unteren Bereich angezeigt werden.   * In der Simulation SimulIDE kann ein Terminalfenster direkt im unteren Bereich angezeigt werden.
  
-  - [[https://www.mikrocontroller.net/articles/AVR-Tutorial:_UART|Tutorial zu UART auf mikrocontroller.net]]+  - [[https://www.mikrocontroller.net/articles/AVR-Tutorial:_UART|Tutorial zu UART auf mikrocontroller.net]], weiteres [[https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Der_UART|Tutorial zu UART auf mikrocontroller.net]]
   - [[https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#Data_framing|Datenpaket für RX/TX]]   - [[https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#Data_framing|Datenpaket für RX/TX]]
   - [[https://de.wikipedia.org/wiki/RS-232#Leitungsl%C3%A4nge_und_%C3%9Cbertragungsrate|Leitungslänge vs. Übertragungsrate]]   - [[https://de.wikipedia.org/wiki/RS-232#Leitungsl%C3%A4nge_und_%C3%9Cbertragungsrate|Leitungslänge vs. Übertragungsrate]]
Zeile 24: Zeile 24:
 </WRAP> <WRAP column 65%> </WRAP> <WRAP column 65%>
 ==== Video ==== ==== Video ====
- + 
 +{{youtube>-tFVvbUUco4?size=700x400}} 
 + 
 +<WRAP hide>
 {{youtube>r_5iVt3rcwA?size=700x400}} {{youtube>r_5iVt3rcwA?size=700x400}}
  
 {{youtube>hCCGMMyTtK4?size=700x400}} {{youtube>hCCGMMyTtK4?size=700x400}}
 +</WRAP>
  
 </WRAP> </WRAP> </WRAP> </WRAP>
  
-==== Code ====+ 
 + 
 +<WRAP> 
 +<imgcaption BildNr09 | Zusammenspiel der UART-Register> 
 +</imgcaption> 
 +{{drawio>UARTRegister.svg}} 
 +</WRAP> 
 + 
 +--> I. Vorarbeiten # 
 +  - Laden Sie folgende Datei herunter:  
 +    - {{microcontrollertechnik:9_temperatureuart_1.0.0.sim1}}  
 +    - {{microcontrollertechnik:9_uart.hex}} 
 +    - {{microcontrollertechnik:lcd_lib_de.h}} 
 + 
 +<callout type="warning" icon="true">  
 + 
 +**Beachten Sie folgendes** 
 +  * Es wird nun ein ATmega328 genutzt, d.h. das Programm ist nicht mehr kompatibel mit dem MiniMEXLE! 
 +  * Überprüfen Sie die Pins und Ports des Displays. 
 +  * Überprüfen Sie die Taktfrequenz. 
 + 
 +</callout> 
 + 
 +<-- 
 + 
 +--> Code mit Polling # 
 +==== Code mit Polling ====
  
 Dieses Unterkapitel ist z.Zt. in einem provisorischem Zustand. Dieses Unterkapitel ist z.Zt. in einem provisorischem Zustand.
  
-  - {{microcontrollertechnik:9_temperatureuart_0.5.15.simu}} +Variante mit Polling: {{microcontrollertechnik:9_uartpolling_temperature.c}} 
-  - Variante mit Polling: {{microcontrollertechnik:9_uartpolling_temperature.c}} + 
-  - Variante mit Interrupt: {{microcontrollertechnik:9_uart_temperature.c}}+<sxh c; first-line: 1> 
 +/* ---------------------------------------------------------------------------- 
 + 
 + Experiment 9: Temperaturmessung mit UART (Polling)  
 + =============    =============================== 
 + 
 + Dateiname: 9_Temperature.c 
 +  
 + Autoren : Tim Fischer 
 +    
 + Datum    : 29.10.2020 
 +  
 + Version : 0.1 
 +  
 + Hardware: MEXLE2020 Ver. 1.0 oder hoeher 
 + AVR-USB-PROGI Ver. 2.0 
 + 
 + Software: Entwicklungsumgebung: AtmelStudio 7.0 
 + C-Compiler: AVR/GNU C Compiler 5.4.0 
 + 
 + Funktion : Thermometer mit Anzeige der aktuellen Temperatur und der 
 + Maximaltemperatur im Betriebszeitraum in °C mit 1/10 Grad. 
 + Keine Tastenbedienung 
 + 
 + Displayanzeige: Start (fuer 2s): Betrieb: 
 +      +----------------+ +----------------+ 
 +    |- Experiment 8 -| |Temp.     18.5°C| 
 +     Temperature   | |Maximum   21.6°C| 
 +    +----------------+ +----------------+ 
 + 
 + Tastenfunktion: keine 
 + 
 + Jumperstellung: keine 
 + 
 + Fuses im uC: CKDIV8: Aus (keine generelle Vorteilung des Takts) 
 + 
 + Header-Files: lcd_lib_de.h (Library zur Ansteuerung LCD-Display Ver.1.3) 
 + 
 + Module 1) Taktgenerator 
 + 2) AD-Wandlung (Takt: 100 ms) 
 + 3) Umrechnung fuer Temperatur  (Takt: 100 ms) 
 + 4) Anzeigetreiber (Takt: 1 s) 
 + 
 + 1) Das Modul "Taktgenerator" erzeugt den Takt von 100 ms fuer die AD-Wandlung 
 +   und Umrechnung und einen zusaetzlichen Takt von 1 s fuer die Anzeige. 
 + 
 + Verwendung von Hardware-Timer 0 und T0 Overflow-Interrupt. 
 + Frequenzen: Quarzfrequenz     12,288 MHz. 
 + Timer-Vorteiler /  8 =>   1,536 MHz 
 + Hardware-Timer      /256 =>   6 kHz / 166 µs 
 + Software-Vorteiler / 60 => 100 Hz  /  10 ms  
 + Hundertstel-Zaehler / 10 =>  10 Hz  / 100 ms 
 + Zehntel-Zaehler / 10 =>   1 Hz  /   1 s 
 + 
 + 2) Das Modul "AD-Wandlung" wird durch den Takt 100 ms aufgerufen. 
 +    Der AD-Wandler wird mit einem internen Takt von 96 kHz betrieben. 
 +   Im Modul wird eine einzelne AD-Wandlung des Kanals ADC0 mit 10 Bit 
 +    Aufloesung gestartet. Dort ist der NTC des Boards mit Vorwiderstand 
 +    als temperaturabhaengiger Spannungsteiler bzw. Potentiometer angeschlossen. 
 +    Als Referenzspannung wird die 5V-Versorgung verwendet. 
 +    Das Ergebnis wird in der globalen Variable ad_wert gespeichert. 
 + 
 + 3) Das Modul "Umrechnung" wird nach der AD-Wandlung alle 100 ms gestartet. 
 +    Der Ergebniswert des Moduls "AD_Wandlung" wird mit Hilfe einer Tabelle in 
 +    einen entsprechenden Temperaturwert umgerechnet. In der Tabelle sind  
 +    Temperaturwerte ueber aequidistante (Abstand = 16) AD-Werte aufgetragen. 
 +   Die Werte dazwischen werden mit linearer Interpolation ermittelt. 
 +    Weiterhin wird im Modul jede aktuelle Temperatur mit der gespeicherten 
 +   maximalen Temperatur verglichen und der Maximalwert optional angepasst. 
 + 
 + 4) Das Modul "Anzeigetreiber" ist an den 1 s-Takt gekoppelt. Damit wird ein 
 +    zu schnelles Umschalten der Anzeigewerte vermieden. Das Modul gibt die 
 +    Werte der aktuellen und der maximalen Temperatur in 1/10 °C aus. 
 +    
 +   Zwischen AD-Wandlung / Umrechnung und Anzeige kann spaeter noch eine 
 +    Mittelwertsbildung mit 10 Werten eingefuegt werden. 
 +  
 + Die Kopplung der Module wird ueber global definierte Variable realisiert: 
 + 
 +    1-Bit-Variable: Takt 100 ms: Taktgenerator => AD-Wandlung 
 +   => Umrechnung 
 + Takt    s:    Taktgenerator => Anzeigetreiber 
 + 
 +   16-Bit-Variable: ad-wert AD-Wandlung => Umrechnung 
 + t-wert Umrechnung => Anzeige 
 + tmax-wert Umrechnung => Anzeige 
 + 
 +// ----------------------------------------------------------------------------*/ 
 + 
 +// Deklarationen ============================================================== 
 + 
 +// Festlegung der Quarzfrequenz 
 +#ifndef F_CPU // optional definieren 
 +#define F_CPU 8000000L // Simulation mit 8 MHz Quarz 
 +#endif  
 + 
 +// Include von Header-Dateien 
 +#include <avr/io.h> // Header-Dateien zum ATmega88 
 +#include <avr/interrupt.h> // Header-Datei fuer Interrupts 
 +#include <util/delay.h> // Header-Datei fuer Wartezeit 
 +#include "lcd_lib_de.h" // Header-Datei fuer LCD-Anzeige 
 +#include <string.h> 
 + 
 +// Konstanten 
 +#define VORTEILER_WERT 60 
 +#define HUNDERTSTEL_WERT 10 
 +#define ZEHNTEL_WERT 10 
 + 
 +#define ASC_NULL            0x30        // Das Zeichen '0' in ASCII 
 +#define ASC_FULL_STOP       0x2E        // Das Zeichen ':' in ASCII 
 + 
 +#define UART_BUFFER_SIZE 10 // Buffersize für übertrgene Daten 
 +#define BAUD_RATE 9600L 
 + 
 +// Berechnungen für die Baudrate 
 +#define UBRR_VAL  ((F_CPU+BAUD_RATE*8)/(BAUD_RATE*16)-1) // clever runden 
 +#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1))) // Reale Baudrate 
 +#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD_RATE-1000) // Fehler in Promille 
 + 
 +// Überprüfung der Baudrate und ob Fehler zu groß ist 
 +#if ((BAUD_ERROR>10) || (BAUD_ERROR<-10)) 
 +#error Systematischer Fehler der Baudrate zu hoch! 
 +#endif 
 + 
 + 
 +const int TEMP[45] =  {521,499,479,459,440,422,404,388,371,354, 
 + 338,323,308,293,279,264,250,236,221,207, 
 + 193,179,165,151,137,122,108,93,78,63, 
 + 48,32,15,-1,-19,-38,-56,-77,-97,-121, 
 + -145,-173,-202,-237,-278}; 
 + 
 + // Die Tabellenwerte sind in 1/10 °C angegeben 
 + // Der erste Tabellenwert entspricht einem AD-Wert 
 + // von 256Die Abstaende der AD-Werte sind 16 
 + 
 +// Variable 
 +unsigned char vorteiler = VORTEILER_WERT; // Zaehlvariable Vorteiler 
 +unsigned char hundertstel = HUNDERTSTEL_WERT; 
 +unsigned char zehntel =   ZEHNTEL_WERT; 
 + 
 +unsigned int adcValue = 0; // Variable fuer den AD-Wandlungswert 
 +int tValue = 0; // Variable fuer die Temperatur (in 1/10 °C) 
 +int tValueMax =-300; // Variable fuer maximale Temperatur (1/10 °C) 
 + 
 +bool takt10ms; // Bit-Botschaft alle 10 ms 
 +bool takt100ms; // Bit-Botschaft alle 100 ms 
 +bool takt1s; // Bit-Botschaft alle 1s 
 + 
 +char wort[UART_BUFFER_SIZE]; // Variable für die Ein- und Ausgabe von Wörtern und Zahlen 
 + 
 + 
 +//Funktionsprototypen 
 +void initTimer0 (void); 
 +void initAdc (void); 
 +void initDisplay (void); 
 +void doAdc (void); 
 +void calculateTemp (void); 
 +void refreshDisplayTemp(int tempValue, char line, char pos); 
 +void refreshDisplay (void); 
 + 
 +void initUart(void); // Initialisierung der UART-Register 
 +void putData(char *daten); // Daten senden 
 +uint8_t getData(void); // Daten empfangen 
 + 
 +// Hauptprogramm ============================================================== 
 +int main () 
 +
 + uint8_t bufferFilled = 0; // Flag für vollen Puffer 
 + 
 + initDisplay(); // Initialisierung LCD-Anzeige 
 + initTimer0(); // Initialisierung von Timer0 
 + initAdc(); // Initialisierung des AD-Wandlers 
 + 
 + initUart(); // UART initialisieren 
 + sei(); // generell Interrupts einschalten 
 +  
 + // Hauptprogrammschleife __________________________________________________ 
 + 
 + while(1) // unendliche Warteschleife mit Aufruf der 
 + // Funktionen abhaengig von Taktbotschaften 
 +
 + if(takt10ms) 
 +
 + takt10ms = 0; // Taktbotschaft zuruecksetzen  
 + 
 + if(getData()=='r') tValueMax = tValue; // wenn r eingegeben, dann ValueMax zurücksetzen 
 + 
 + if (bufferFilled==1) // Bei verfügbarer Zeichenkette 
 +
 + putData(wort); // Wort zurück senden 
 + bufferFilled=0; // Puffer leeren 
 + } 
 + 
 +
 + if(takt100ms) // Durchfuehrung der Funktion einmal pro 100ms 
 +
 + takt100ms = 0; // Taktbotschaft zuruecksetzen 
 + doAdc(); // Ausfuehrung des Modules der A/D-Wandlung  
 + calculateTemp(); // Ausfuehrung des Modules der Umrechnung 
 +
 + 
 + if(takt1s) // Durchfuehrung der Anzeige einmal pro 1s 
 +
 + takt1s = 0; // Taktbotschaft zuruecksetzen 
 + 
 + refreshDisplay(); // Ausfuehrung des Modules der Anzeige 
 + wort[5] = '°'; 
 + wort[6] = 'C'; 
 + wort[7] = '\n'; // ...New Line und... 
 + wort[8] = '\r'; // ...Carriage Return. 
 + wort[9] = 0; // Endzeichen 
 + bufferFilled=1; 
 +
 +
 +
 + 
 + 
 +// Timer-Initialisierung ============================================================== 
 +// 
 +// Initialisierung des Timer0 zur Erzeugung eines getakteten Interrupts. 
 +// Er dient dazu, die benoetigten Taktbotschaften zu erzeugen. 
 +void initTimer0() 
 +
 + TCCR0A |= (0<<WGM00)  
 + (0<<WGM01); // Timer 0 auf "Normal Mode" schalten 
 + TCCR0B |= (0<<WGM02)  
 + |  (1<<CS01 ); // mit Prescaler /8 betreiben 
 + TIMSK0 |= (1<<TOIE0); // Overflow-Interrupt aktivieren 
 +
 + 
 +// Timer-Initialisierung ============================================================== 
 +//  
 +// Initialisierung des A/D-Wandlers:  
 +// Vorteiler = 128 => interner Takt = 96 kHz 
 +// Abfrage des ADC0 (NTC-Spannungsteiler) 
 +// Referenzspannung = analoge Versorgung Avcc 
 +void initAdc () 
 +
 + ADMUX   |= (1<<REFS0) 
 + |  (1<<MUX0) 
 + |  (1<<MUX2); // Vref =AVCC; ADC5 
 + 
 + ADCSRA |= (1<<ADPS0) 
 + |  (1<<ADPS1) 
 + |  (1<<ADPS2) 
 + |  (1<<ADEN); // Teiler 128; ADC ON 
 +
 + 
 + 
 +// Timer-Initialisierung ============================================================== 
 +//  
 +// In der Interrupt-Routine sind die Softwareteiler realisiert, die die Takt- 
 +// botschaften (10ms, 100ms, 1s) fuer die Module erzeugen. Die Interrupts 
 +// werden von Timer 0 ausgeloest (Interrupt Nr. 1) 
 +// 
 +// Veraenderte Variable: vorteiler 
 +// hunderstel 
 +// zehntel 
 +// 
 +// Ausgangsvariable: takt10ms 
 +// takt100ms 
 +// takt1s 
 + 
 +ISR (TIMER0_OVF_vect) 
 +
 + --vorteiler; // Vorteiler dekrementieren 
 + if (vorteiler==0) // wenn 0 erreicht: 10ms abgelaufen 
 +
 + vorteiler = VORTEILER_WERT; // Vorteiler auf Startwert 
 + takt10ms = true; // Botschaft 10ms senden 
 + --hundertstel; // Hundertstelzaehler dekrementieren 
 + 
 + if (hundertstel==0) // wenn 0 erreicht: 100ms abgelaufen 
 +
 + hundertstel = HUNDERTSTEL_WERT; // Teiler auf Startwert 
 + takt100ms = true; // Botschaft 100ms senden 
 + --zehntel; 
 + 
 + if (zehntel==0) // Zehntelzaehler dekrementieren 
 +
 + zehntel = ZEHNTEL_WERT; // Teiler auf Startwert 
 + takt1s = true; // Botschaft 1s senden 
 +
 +
 +
 +
 + 
 +// ADWandlung ============================================================== 
 +//  
 +// Durchfuehrung einer Einzelwandlung der am NTC-Spannungsteiler anstehenden  
 +// Spannung in einen digitalen 10-bit-Wert (einmal pro 100 ms). 
 +void doAdc() 
 +
 + ADCSRA |= (1<<ADSC); // Wandlung starten 
 + while (ADCSRA & (1<<ADSC)); // Ende der Wandlung abwarten 
 + 
 + adcValue = ADCL + (ADCH<<8); // 10-Bit-Wert berechnen 
 + // ADCL muss vor ADCH stehen!! 
 + // siehe Datenblatt des ATmega 328 
 +
 + 
 +// Umrechnung ============================================================== 
 +//  
 +// (wird alle 100 ms aufgerufen) 
 +void calculateTemp () 
 +
 + unsigned char index; // Tabellenindex fuer Temperaturtabelle 
 + unsigned char steps; // Abstand zum naechstkleineren Wert 
 + // der AD-Werte der Temperaturtabelle 
 +  
 + index = (adcValue-256)/16; // Indexberechnung (Zeiger in Tabelle)  
 + steps = (adcValue-256)%16; // Rest fuer Tabellen-Interpolation  
 +  
 + tValue = steps * (TEMP[index+1] - TEMP[index])/16 + TEMP[index];  
 + // Temperaturwert berechnen 
 + 
 + if(tValue>=tValueMax) // aktueller Wert mit Maximalwert 
 +
 + tValueMax = tValue; // vergleichen und ggf. ersetzen 
 +
 +
 + 
 +// Anzeigetreiber fuer Temperaturanzeige ============================================================== 
 +// 
 +// Beschreiben der Anzeige mit dem erstellten Temperaturwert  
 +// und mit dem maximalen Wert (wird alle 1 s aufgerufen). 
 +// 
 +// Umrechnung der Zahlenwerte (1/10 °C) in Anzeigewerte wie folgt:  
 +// Hunderter: einfache Integer-Teilung (/100). 
 +// Zehner: Modulo-Ermittlung (%100), d.h. Rest bei der Teilung durch 100 
 +//    dann nochmals Integer-Teilung (/10) dieses Restes. 
 +// Einer:  Modulo-Ermittlung (%10), d.h. Rest bei der Teilung durch 10. 
 +// 
 +// Umrechnung in ASCII-Werte fuer die Anzeige durch Addition von 0x30. 
 +void refreshDisplayTemp(int tempValue, char line, char pos) 
 +
 + lcd_gotoxy(line, pos); // Startposition fuer Temperatur-Wert 
 + if (tempValue>=0) // zuerst Vorzeichen: ' ' oder '-' 
 +
 + wort[0] = ' ';  
 +
 + else 
 +
 + wort[0] = '-'; 
 + tempValue = -tempValue; // Vorzeichenumkehr bei negativer Zahl 
 +
 + wort[1] = tempValue/100 + ASC_NULL; 
 + tempValue = tempValue%100; 
 + wort[2] = tempValue/10 + ASC_NULL; 
 + wort[3] = ASC_FULL_STOP; 
 + wort[4] = tempValue%10 + ASC_NULL; 
 + wort[5] = 0; 
 + lcd_putstr   (wort); // Hunderter ausgeben (°C Zehner) 
 +
 + 
 +// Anzeigefunktion ============================================================== 
 +// 
 +// Der aktuelle Temperatur und die maximale Temperatur werden ausgegeben 
 + void refreshDisplay() 
 +
 + refreshDisplayTemp(tValueMax, 1, 9); // maximale Temperatur ab Position 1,9 
 + refreshDisplayTemp(tValue, 0, 9); // aktuelle Temperatur ab Position 0,9 
 +
 + 
 +// Initialisierung Display-Anzeige ============================================================== 
 +// 
 +void initDisplay() // Start der Funktion 
 +
 + lcd_init(); // Initialisierungsroutine aus der lcd_lib 
 +  
 + lcd_displayMessage("- Experiment 9b-",0,0); // Ausgabe in erster Zeile 
 + lcd_displayMessage("UART-Temperature",1,0); // Ausgabe in zweiter Zeile 
 + 
 + _delay_ms(2000); // Wartezeit nach Initialisierung 
 + 
 + lcd_displayMessage("Temp.         ßC",0,0); // Ausgabe in erster Zeile 
 + lcd_displayMessage("Maximum       ßC",1,0); // Ausgabe in zweiter Zeile 
 + // "ßC" wird als °C dargestellt  
 +} // Ende der Funktion 
 + 
 +void initUart (void) // Initialisierung der UART-Register 
 +
 + UBRR0H = UBRR_VAL >> 8; // High-Baudraten-Register beschreiben 
 + UBRR0L = UBRR_VAL & 0xFF; // Low-Baudraten-Register beschreiben 
 + UCSR0B = (1<<RXEN0 ) | (1<<TXEN0 ); // Senden und Empfangen ermoeglichen 
 + UCSR0C = (1<<UCSZ00) | (1<<UCSZ01); // Setzen der Datenbits auf 8-Bit 
 +
 + 
 + 
 +/* puts ist unabhaengig vom Controllertyp */ 
 +void putData (char *data) 
 +
 + while (*data) 
 + {   /* so lange *s != '\0' also ungleich dem "String-Endezeichen(Terminator)" */ 
 + while (!(UCSR0A & (1<<UDRE0))){}  /* warten bis Senden moeglich */ 
 + UDR0 = *data;                      /* sende Zeichen */ 
 + data++; 
 +
 +
 + 
 +uint8_t getData(void) // Daten empfangen 
 +
 +    if  (UCSR0A & (1<<RXC0))   // wenn Zeichen verfuegbar 
 +
 + return UDR0;           // Zeichen aus UDR an Aufrufer zurueckgeben 
 + }else{ 
 + return 0; 
 + }; 
 +
 + 
 +</sxh> 
 +<-- 
 + 
 +--> Code mit Interrupt # 
 +==== Code mit Interrupt==== 
 + 
 +Variante mit Interrupt: {{microcontrollertechnik:9_uart_temperature.c}} 
 +<sxh c; first-line: 1> 
 +/* ---------------------------------------------------------------------------- 
 + 
 + Experiment 9:    Temperaturmessung und UART 
 + =============    =============================== 
 + 
 + Dateiname  : 9_UART_Temperature.c 
 +  
 + Autoren : Tim Fischer 
 +    
 + Datum    : 01.05.2020 
 +  
 + Version : 0.1 
 +  
 + Hardware: MEXLE2020 Ver. 1.0 oder hoeher 
 + AVR-USB-PROGI Ver. 2.0 
 + 
 + Software: Entwicklungsumgebung: AtmelStudio 7.0 
 + C-Compiler: AVR/GNU C Compiler 5.4.0 
 + 
 + Funktion : Thermometer mit Anzeige der aktuellen Temperatur und der 
 + Maximaltemperatur im Betriebszeitraum in °C mit 1/10 Grad. 
 + Keine Tastenbedienung 
 + 
 + Displayanzeige: Start (fuer 2s): Betrieb: 
 +      +----------------+ +----------------+ 
 +    |- Experiment 8 -| |Temp.     18.5°C| 
 +     Temperature   | |Maximum   21.6°C| 
 +    +----------------+ +----------------+ 
 + 
 + Tastenfunktion: keine 
 + 
 + Jumperstellung: keine 
 + 
 + Fuses im uC: CKDIV8: Aus (keine generelle Vorteilung des Takts) 
 + 
 + Header-Files: lcd_lib_de.h (Library zur Ansteuerung LCD-Display Ver.1.3) 
 + 
 + Module 1) Taktgenerator 
 + 2) AD-Wandlung (Takt: 100 ms) 
 + 3) Umrechnung fuer Temperatur  (Takt: 100 ms) 
 + 4) Anzeigetreiber (Takt: 1 s) 
 + 
 + 1) Das Modul "Taktgenerator" erzeugt den Takt von 100 ms fuer die AD-Wandlung 
 +   und Umrechnung und einen zusaetzlichen Takt von 1 s fuer die Anzeige. 
 + 
 + Verwendung von Hardware-Timer 0 und T0 Overflow-Interrupt
 + FrequenzenQuarzfrequenz     12,288 MHz. 
 + Timer-Vorteiler /  8 =>   1,536 MHz 
 + Hardware-Timer      /256 =>   6 kHz / 166 µs 
 + Software-Vorteiler / 60 => 100 Hz  /  10 ms  
 + Hundertstel-Zaehler / 10 =>  10 Hz  / 100 ms 
 + Zehntel-Zaehler / 10 =>   1 Hz  /   1 s 
 + 
 + 2) Das Modul "AD-Wandlung" wird durch den Takt 100 ms aufgerufen. 
 +    Der AD-Wandler wird mit einem internen Takt von 96 kHz betrieben. 
 +   Im Modul wird eine einzelne AD-Wandlung des Kanals ADC0 mit 10 Bit 
 +    Aufloesung gestartet. Dort ist der NTC des Boards mit Vorwiderstand 
 +    als temperaturabhaengiger Spannungsteiler bzw. Potentiometer angeschlossen. 
 +    Als Referenzspannung wird die 5V-Versorgung verwendet. 
 +    Das Ergebnis wird in der globalen Variable ad_wert gespeichert. 
 + 
 + 3) Das Modul "Umrechnung" wird nach der AD-Wandlung alle 100 ms gestartet. 
 +    Der Ergebniswert des Moduls "AD_Wandlung" wird mit Hilfe einer Tabelle in 
 +    einen entsprechenden Temperaturwert umgerechnet. In der Tabelle sind  
 +    Temperaturwerte ueber aequidistante (Abstand = 16) AD-Werte aufgetragen. 
 +   Die Werte dazwischen werden mit linearer Interpolation ermittelt. 
 +    Weiterhin wird im Modul jede aktuelle Temperatur mit der gespeicherten 
 +   maximalen Temperatur verglichen und der Maximalwert optional angepasst. 
 + 
 + 4) Das Modul "Anzeigetreiber" ist an den 1 s-Takt gekoppelt. Damit wird ein 
 +    zu schnelles Umschalten der Anzeigewerte vermieden. Das Modul gibt die 
 +    Werte der aktuellen und der maximalen Temperatur in 1/10 °C aus. 
 +    
 +   Zwischen AD-Wandlung / Umrechnung und Anzeige kann spaeter noch eine 
 +    Mittelwertsbildung mit 10 Werten eingefuegt werden. 
 +  
 + Die Kopplung der Module wird ueber global definierte Variable realisiert: 
 + 
 +    1-Bit-Variable: Takt 100 ms: Taktgenerator => AD-Wandlung 
 +   => Umrechnung 
 + Takt    s:    Taktgenerator => Anzeigetreiber 
 + 
 +   16-Bit-Variable: ad-wert AD-Wandlung => Umrechnung 
 + t-wert Umrechnung => Anzeige 
 + tmax-wert Umrechnung => Anzeige 
 + 
 +// ----------------------------------------------------------------------------*/ 
 + 
 +// Deklarationen ============================================================== 
 + 
 +// Festlegung der Quarzfrequenz 
 +#ifndef F_CPU // optional definieren 
 +#define F_CPU 8000000L //12288000UL // MiniMEXLE mit 12,288 MHz Quarz 
 +#endif  
 + 
 +// Include von Header-Dateien 
 +#include <avr/io.h> // Header-Dateien zum ATmega88 
 +#include <avr/interrupt.h> // Header-Datei fuer Interrupts 
 +#include <util/delay.h> // Header-Datei fuer Wartezeit 
 +#include "lcd_lib_de.h" // Header-Datei fuer LCD-Anzeige 
 +#include <string.h> 
 + 
 +// Konstanten 
 +#define VORTEILER_WERT 60 
 +#define HUNDERTSTEL_WERT 10 
 +#define ZEHNTEL_WERT 10 
 + 
 +#define ASC_NULL            0x30        // Das Zeichen '0' in ASCII 
 +#define ASC_FULL_STOP       0x2E        // Das Zeichen ':' in ASCII 
 + 
 +#define UART_BUFFER_SIZE 10 // Buffersize fuer uebertragene Daten 
 +#define BAUD_RATE 9600L 
 + 
 +// Berechnungen für die Baudrate 
 +#define UBRR_VAL  ((F_CPU+BAUD_RATE*8)/(BAUD_RATE*16)-1) // clever runden 
 +#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1))) // Reale Baudrate 
 +#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD_RATE-1000) // Fehler in Promille 
 + 
 +// Überprüfung der Baudrate und ob Fehler zu groß ist 
 +#if ((BAUD_ERROR>10) || (BAUD_ERROR<-10)) 
 +#error Systematischer Fehler der Baudrate zu hoch! 
 +#endif 
 + 
 + 
 +const int TEMP[45] =  {521,499,479,459,440,422,404,388,371,354, 
 + 338,323,308,293,279,264,250,236,221,207, 
 + 193,179,165,151,137,122,108,93,78,63, 
 + 48,32,15,-1,-19,-38,-56,-77,-97,-121, 
 + -145,-173,-202,-237,-278}; 
 + 
 + // Die Tabellenwerte sind in 1/10 °C angegeben 
 + // Der erste Tabellenwert entspricht einem AD-Wert 
 + // von 256. Die Abstaende der AD-Werte sind 16 
 + 
 +// Variable 
 +unsigned char vorteiler = VORTEILER_WERT; // Zaehlvariable Vorteiler 
 +unsigned char hundertstel = HUNDERTSTEL_WERT; 
 +unsigned char zehntel =   ZEHNTEL_WERT; 
 + 
 +unsigned int adcValue = 0; // Variable fuer den AD-Wandlungswert 
 +int tValue = 0; // Variable fuer die Temperatur (in 1/10 °C) 
 +int tValueMax =-300; // Variable fuer maximale Temperatur (1/10 °C) 
 + 
 +bool takt10ms; // Bit-Botschaft alle 10 ms 
 +bool takt100ms; // Bit-Botschaft alle 100 ms 
 +bool takt1s; // Bit-Botschaft alle 1s 
 + 
 +volatile uint8_t uart_rx_flag = 0;          // Flag für vollständig empfangene Daten 
 +volatile uint8_t DataTransferFinished = 1;          // Flag für vollständig gesendete Daten 
 +char uart_rx_buffer[UART_BUFFER_SIZE];      // Empfangspuffer 
 +char uart_tx_buffer[UART_BUFFER_SIZE];      // Sendepuffer 
 +char wort[UART_BUFFER_SIZE]; // Variable für die Ein- und Ausgabe von Wörtern und Zahlen 
 + 
 + 
 +//Funktionsprototypen 
 +void initTimer0 (void); 
 +void initAdc (void); 
 +void initDisplay (void); 
 +void doAdc (void); 
 +void calculateTemp (void); 
 +void refreshDisplayTemp(int tempValue, char line, char pos); 
 +void refreshDisplay (void); 
 + 
 +void initUart (void); // Initialisierung der UART-Register 
 +void putData(char *data); // Daten senden 
 + 
 +// Hauptprogramm ============================================================== 
 +int main () 
 +
 + uint8_t bufferFilled = 0; // Flag für vollen Puffer 
 + 
 + initDisplay(); // Initialisierung LCD-Anzeige 
 + initTimer0(); // Initialisierung von Timer0 
 + initAdc(); // Initialisierung des AD-Wandlers 
 + 
 + initUart(); // UART initialisieren 
 + sei(); // generell Interrupts einschalten 
 + UDR0 = 0; // Work around für Probleme bei Simulide <= R941 : ohne diese Zeile is kein Senden möglich 
 +  
 + // Hauptprogrammschleife __________________________________________________ 
 + 
 + while(1) // unendliche Warteschleife mit Aufruf der 
 + // Funktionen abhaengig von Taktbotschaften 
 +
 + if(takt10ms) 
 + {  
 + takt10ms = 0; 
 + if (DataTransferFinished==1 && bufferFilled==1) // Bei abgeschlossener Sendung und verfügbarer Zeichenkette 
 +
 + bufferFilled=0; // Puffer leeren 
 + putData(wort); // Wort zurück senden 
 +
 + 
 +
 + if(takt100ms) // Durchfuehrung der Funktion einmal pro 100ms 
 +
 + takt100ms = 0; // Taktbotschaft zuruecksetzen 
 + doAdc(); // Ausfuehrung des Modules der A/D-Wandlung  
 + calculateTemp(); // Ausfuehrung des Modules der Umrechnung 
 +
 + 
 + if(takt1s) // Durchfuehrung der Anzeige einmal pro 1s 
 +
 + takt1s = 0; // Taktbotschaft zuruecksetzen 
 + refreshDisplay(); // Ausfuehrung des Modules der Anzeige 
 + wort[5] = '°'; 
 + wort[6] = 'C'; 
 + wort[7] = '\n'; // ...New Line und... 
 + wort[8] = '\r'; // ...Carriage Return. 
 + wort[9] = 0; // Endzeichen 
 + bufferFilled=1; 
 +
 +
 +
 + 
 + 
 +// Timer-Initialisierung ============================================================== 
 +// 
 +// Initialisierung des Timer0 zur Erzeugung eines getakteten Interrupts. 
 +// Er dient dazu, die benoetigten Taktbotschaften zu erzeugen. 
 +void initTimer0() 
 +
 + TCCR0A |= (0<<WGM00)  
 + |  (0<<WGM01); // Timer 0 auf "Normal Mode" schalten 
 + TCCR0B |= (0<<WGM02)  
 + |  (1<<CS01 ); // mit Prescaler /8 betreiben 
 + TIMSK0 |= (1<<TOIE0); // Overflow-Interrupt aktivieren 
 +
 + 
 +// Timer-Initialisierung ============================================================== 
 +//  
 +// Initialisierung des A/D-Wandlers:  
 +// Vorteiler = 128 => interner Takt = 96 kHz 
 +// Abfrage des ADC0 (NTC-Spannungsteiler) 
 +// Referenzspannung = analoge Versorgung Avcc 
 +void initAdc () 
 +
 + ADMUX   |= (1<<REFS0) 
 + |  (1<<MUX0) 
 + |  (1<<MUX2); // Vref =AVCC; ADC5 
 + 
 + ADCSRA |= (1<<ADPS0) 
 + |  (1<<ADPS1) 
 + |  (1<<ADPS2) 
 + |  (1<<ADEN); // Teiler 128; ADC ON 
 +
 + 
 + 
 +// Timer-Initialisierung ============================================================== 
 +//  
 +// In der Interrupt-Routine sind die Softwareteiler realisiert, die die Takt- 
 +// botschaften (10ms, 100ms, 1s) fuer die Module erzeugen. Die Interrupts 
 +// werden von Timer 0 ausgeloest (Interrupt Nr. 1) 
 +// 
 +// Veraenderte Variable: vorteiler 
 +// hunderstel 
 +// zehntel 
 +// 
 +// Ausgangsvariable: takt10ms 
 +// takt100ms 
 +// takt1s 
 + 
 +ISR (TIMER0_OVF_vect) 
 +
 + --vorteiler; // Vorteiler dekrementieren 
 + if (vorteiler==0) // wenn 0 erreicht: 10ms abgelaufen 
 +
 + vorteiler = VORTEILER_WERT; // Vorteiler auf Startwert 
 + takt10ms = true; // Botschaft 10ms senden 
 + --hundertstel; // Hundertstelzaehler dekrementieren 
 + 
 + if (hundertstel==0) // wenn 0 erreicht: 100ms abgelaufen 
 +
 + hundertstel = HUNDERTSTEL_WERT; // Teiler auf Startwert 
 + takt100ms = true; // Botschaft 100ms senden 
 + --zehntel; 
 + 
 + if (zehntel==0) // Zehntelzaehler dekrementieren 
 +
 + zehntel = ZEHNTEL_WERT; // Teiler auf Startwert 
 + takt1s = true; // Botschaft 1s senden 
 +
 +
 +
 +
 + 
 +// ADWandlung ============================================================== 
 +//  
 +// Durchfuehrung einer Einzelwandlung der am NTC-Spannungsteiler anstehenden  
 +// Spannung in einen digitalen 10-bit-Wert (einmal pro 100 ms). 
 +void doAdc() 
 +
 + ADCSRA |= (1<<ADSC); // Wandlung starten 
 + while (ADCSRA & (1<<ADSC)); // Ende der Wandlung abwarten 
 + 
 + adcValue = ADCL + (ADCH<<8); // 10-Bit-Wert berechnen 
 + // ADCL muss vor ADCH stehen!! 
 + // siehe Datenblatt des ATmega 328 
 +
 + 
 +// Umrechnung ============================================================== 
 +//  
 +// (wird alle 100 ms aufgerufen) 
 +void calculateTemp () 
 +
 + unsigned char index; // Tabellenindex fuer Temperaturtabelle 
 + unsigned char steps; // Abstand zum naechstkleineren Wert 
 + // der AD-Werte der Temperaturtabelle 
 +  
 + index = (adcValue-256)/16; // Indexberechnung (Zeiger in Tabelle)  
 + steps = (adcValue-256)%16; // Rest fuer Tabellen-Interpolation  
 +  
 + tValue = steps * (TEMP[index+1] - TEMP[index])/16 + TEMP[index];  
 + // Temperaturwert berechnen 
 + 
 + if(tValue>=tValueMax) // aktueller Wert mit Maximalwert 
 +
 + tValueMax = tValue; // vergleichen und ggf. ersetzen 
 +
 +
 + 
 +// Anzeigetreiber fuer Temperaturanzeige ============================================================== 
 +// 
 +// Beschreiben der Anzeige mit dem erstellten Temperaturwert  
 +// und mit dem maximalen Wert (wird alle 1 s aufgerufen). 
 +// 
 +// Umrechnung der Zahlenwerte (1/10 °C) in Anzeigewerte wie folgt:  
 +// Hunderter: einfache Integer-Teilung (/100). 
 +// Zehner: Modulo-Ermittlung (%100), d.h. Rest bei der Teilung durch 100 
 +//    dann nochmals Integer-Teilung (/10) dieses Restes. 
 +// Einer:  Modulo-Ermittlung (%10), d.h. Rest bei der Teilung durch 10. 
 +// 
 +// Umrechnung in ASCII-Werte fuer die Anzeige durch Addition von 0x30. 
 +void refreshDisplayTemp(int tempValue, char line, char pos) 
 +
 + lcd_gotoxy(line, pos); // Startposition fuer Temperatur-Wert 
 + if (tempValue>=0) // zuerst Vorzeichen: ' ' oder '-' 
 +
 + wort[0] = ' ';  
 +
 + else 
 +
 + wort[0] = '-'; 
 + tempValue = -tempValue; // Vorzeichenumkehr bei negativer Zahl 
 +
 + wort[1] = tempValue/100 + ASC_NULL; 
 + tempValue = tempValue%100; 
 + wort[2] = tempValue/10 + ASC_NULL; 
 + wort[3] = ASC_FULL_STOP; 
 + wort[4] = tempValue%10 + ASC_NULL; 
 + wort[5] = 0; 
 + lcd_putstr   (wort); // Hunderter ausgeben (°C Zehner) 
 +
 + 
 +// Anzeigefunktion ============================================================== 
 +// 
 +// Der aktuelle Temperatur und die maximale Temperatur werden ausgegeben 
 + void refreshDisplay() 
 +
 + refreshDisplayTemp(tValueMax, 1, 9); // maximale Temperatur ab Position 1,9 
 + refreshDisplayTemp(tValue, 0, 9); // aktuelle Temperatur ab Position 0,9 
 +
 + 
 +// Initialisierung Display-Anzeige ============================================================== 
 +// 
 +void initDisplay() // Start der Funktion 
 +
 + lcd_init(); // Initialisierungsroutine aus der lcd_lib 
 +  
 + lcd_displayMessage("- Experiment 9 -",0,0); // Ausgabe in erster Zeile 
 + lcd_displayMessage("UART-Temperature",1,0); // Ausgabe in zweiter Zeile 
 + 
 + _delay_ms(2000); // Wartezeit nach Initialisierung 
 + 
 + lcd_displayMessage("Temp.         ßC",0,0); // Ausgabe in erster Zeile 
 + lcd_displayMessage("Maximum       ßC",1,0); // Ausgabe in zweiter Zeile 
 + // "ßC" wird als °C dargestellt  
 +} // Ende der Funktion 
 + 
 +void initUart (void) // Initialisierung der UART-Register 
 +
 + UBRR0H = UBRR_VAL >> 8; // High-Baudraten-Register beschreiben 
 + UBRR0L = UBRR_VAL & 0xFF; // Low-Baudraten-Register beschreiben 
 +  
 + UCSR0B = (1<<TXEN0) | (1<<RXEN0) | (1<<RXCIE0); // Aktivierung des UART Senders, Empfaengers und der Interrupts 
 + UCSR0C = (1<<UCSZ00) | (1<<UCSZ01); // Setzen der Datenbits auf 8-Bit 
 +
 + 
 + 
 +ISR(USART_RX_vect) // Interrupt Routine für das Empfangen 
 +
 + char data = UDR0; // Daten auslesen, um Interruptflag zu loeschen 
 + if (data=='r') tValueMax = tValue; 
 +
 +  
 +ISR(USART_UDRE_vect) // Interrupt Routine für das Senden 
 +
 + static char* uart_tx_p = uart_tx_buffer; // Zeiger auf Sendepuffer 
 + char data = *uart_tx_p++; // Daten einlesen und Pointer erhöhen 
 +  
 + if (data==0 ) // Am Ende der Eingabe 
 +
 + UCSR0B &= ~(1<<UDRIE0); // UDRE Interrupt ausschalten 
 + uart_tx_p = uart_tx_buffer; // Pointer zurücksetzen 
 + DataTransferFinished = 1; // Flag für Senden setzen 
 +
 + else UDR0 = data; // Daten senden 
 +
 + 
 +void putData(char *data) // Daten senden 
 +
 + if (DataTransferFinished==1) // Wenn Daten vollstaendig gesendet 
 +
 + DataTransferFinished = 0; // Flag für Senden löschen 
 + strcpy(uart_tx_buffer, data); // Daten in Sendepuffer kopieren 
 + UCSR0B |= (1<<UDRIE0); // UDRE Interrupt einschalten 
 +
 +}</sxh> 
 + 
 +<-- 
 + 
 + 
 +<WRAP onlyprint> 
 +Unvollständige [[http://www.falstad.com/circuit/circuitjs.html?ctz=CQAgzCCMCmC0AcICcA6A7PMSnwCwFYwA2eAJkgnwAYR8RdK5JIAoAJRFlKsVn31KdS+Ip36DIICVRmyapFHQV0aVRexBFcnIjKhVtsXapA1cNFadPruaTYdyltkbp0fbtAGwCGAI2ieLI6SXDycYMSckLiGEaLcuIguViaWFrRWdFmmLAAyUcmwcZxogkWREiAAZt6eAM7QGVQsAJJuNFylVp2Cqoq0rUKGTjSQolzmKf34gxNRaKJj86J9WbOToYgd3FvWawDu3aRdSz05h3MTHRsj50dhpzt3l5AL7cvPTm68G4l3HfBRuNAZ9DMwyhtwf8ojEoONotpmm1YBsQQCTGo1siviikuM-qsBtiwclTlDCTNkZDYR0EVMDkMQCCUTQ0SxDmY8fQtuzGadJktmhyoot4St1pyrAS9kTGSClmiZZS+aK4fTZSyRd1BUqWJB8CENmBYQhwrDJMkGZrmTckOKLkaDO8CbzNfyOmBkkL3kVXJrYILXb8fh7SDyHR7Iv62RGChCPQJnkbdD66d7-a98R6SEmPW9-U90zdfh68LmhF1-VhenqiKqjHoccYpAYkFBSK7eMbDCHw5xe3H2n2A4UwE6R-Hh1CzgWuunp7cC5N54VM6m3iuyk8C2FN-3rujntO-v6XRdp8faV7XReTdTETfkifJmfBzbWapHxIs2q9wGf-+zzVi2XZIBINKumAbzblBiyJumxBAtm8i7pBsIweaOYIWGUD5mAOGFpByQwaSZbpqQdogUIlGQGBnwgtuYbbHOeThARYS+ogpD4VIVg1PUjQqCwthsVxHHdlISzaAAOnUYDCVQdhFMR4kEVJICyR2+TKYIJG6TxlT8Q0TSsVwDEcUxUgGXxtTGUJIlmShvAUfE6myc0DkuVROmSaIMl1Kw2leYxlHcbpNkCSZ2mId5MVhbxNBGYJOQOXF4nQW5dQzKl6HiSm5B+RpdS4KZEkYdo8WGbZyXNNF0HpfE1mJdVJmpfVoG6ZlRAsAAHuEaBtqQdZCPAShEG2FXaAAqgAQmwbAAHa5ApSnAdu5hcZlaClXRMG7U11QtUJfV8NxUjDSiwjnRNUjTXNi0ABKmTETluJMlURXZKWKW9r0oiIvn0FAK1uF863veptWgxVHHuFZ4XNZFQnaQQ8SwwDH2I19Hk-f9aM-F0BVA1pbiE7DhMHUlJkOmDsNfBQ9q-d5L3tsOqPMwDLifg6JyFLgZPDl5tFbvKdEIaRwzymRkE4Wujm4Yz8tjF2mGC-KKZFCmYzcyNowmsBaaupZ+plLBUDwUb8pRnDDP0aMOEogKOHkfKfrs1zdtQHzvM1hcxt0fLVye8yxvXn7IInsbrgu0Cuus57gFK5EMffHHhvh4eSsWxng5KymKcBiaxtYZbtL5sbG6l0IFlSw+QXiVGpA4sUVVIzk9dduOTexE6rfY0bOLHFuFksX7g83BZy5GxZFmD6hY9guXHFruRy9Fzi6dCBvhTbuS0+8CHOIxlvDjObDgvL+M3dauR9NXw8itPJHXyvlwXwh2EbLYsyjyQisurYi0GqN+zh-6Yg1ENbQh8oEYmmLMSykcXy7HATMP2f8T43yNkAx46tQSShAdyT4QCQ4gi-nHQuwx7zqmVFwIBE4MH0IpPAuhz9T7UPgcyEOqJYFWkXHSAhFCiEkhFleX2QxM7XwQDrWhbDA6v0gR8QOK8jYT2cnPQWdDFyWSDuRG4lZJFDxyN-bYk8THILgYAxeV8N4bgAUITRNxx48IgSHfRejei6jGM4KMEllZmmcEDDEGQqbphVt48YElmgAFkoBlk4vDUSvFlAsBiZAOJZUeJFHQlIdQqT0nEUyZ6cKyS8nDFCpk4KOSZhpIqsyMqlEskBI7J5Di4gohxLadoAAtt4AAlgtAAwrkAA0qwFpSRVx0ShN0vpgyRljJ+hJRp4A8Kwm0AADRGXqeAbYiA4i6EQAGsAya4DbJAHWpxmyPFHsA-AZZf4XPvlyPgJdDinDaWSSubz4SYTEMnXk7zXBkjDsA3BfAnTen5PAZ5r4yT1n5mI-knNVSQr8jCqe3zoZqjZCJM2mowCJk1NNKJABlAAorkKgrBDiJEQF0Wl4R-mHDxUgkMdxgJzAZXMHGdgOVfAJVuDwIApqkopTIEGcNEACqZEDKaAAFKJ4qaXv25IgPsG1+yqs1Ty+gHEpWEsQNNBVVLeQItoGixAANvTs1Zbqu4LMX7Qtut9OwDrtDSpfsKklM0SU6ptTQaVL5hUDJJQALRNSJBlXRpX0uDXKgA8pSiVXQAbSoBtNEN4bQk8RPJgCQ0cQYID+Jku0UrBAVQAFxTUzeKhyebCGwFLYQyt1aE1Jrrdg8YTb1KkCrTWhZSloUmEbVhYmVbvW+s7IqBAPFpHwE7f2HigYLh4DLYuwQs7C1IFHGUJtvox3ysVdSzg27TZlHrfu0JDTTgXsDHW69XacyARbcanVjaHYnj3X8FtorKXpjAj8A+mTX5IBNHQi9QD-3mj9KBwwkHXQAfbB0RDuJninrYie0kBaV00T9DssUzwh3dCIx7UJ2C8MLtCX8D2Ra13ZvLUB02+dTIkbw-KbJWMaqFtYwCXDhqiqyUE3UWS8kO3ODY-bfjQnpN1BJggcjAJsHZMkFTZG66kPqbpB41TLrNN4eozhfyMn3LcYM4p8TUnhNWaE4FfsZm7NJA44dNuUNG1YeQ6SJzOm33oZo4hlwlnjNZW47hjz+bAvWasyVbS+GNMIBosp5z-dtL+Zg7LLzR1dPvqSDB6DEWZPdROkgT0uEgN2DSZISawr7pLS3WBwwe6gE-vJZS2zjboPIfNBllzN4xwOBPBQJ0Vreu9zBH1tw8HzzjdxLSabU5puQgW5Rec02DRlEG4YG5AZpvnLG+OAQ839smg21EJlp3xx+O2-t5jU39v5hO6EaRD34pXcMPgMWI24PL2mwolb449l7bKeKBySA8JPtg+EHMGyRnNNxibNVvB9TJCdTM-pQzhmw6UvDsQ93qBvbeKjuZGPtlggMNmRHVBm45kkB2GJI4fF+nOU6308gqkpIKD4qMTOuwVDZ3T5gPin3c8h1xPnHOImEuF0URMul1BTYdsQRHNSRc5C8QUdJQuTYq8q5kXXITZja22NekQRuwEWKiHc4YdD9RF0g3YgMlvq60nwEXPQTDkSG6EJk-UGt4pMPPCb6i8IXdA6PI7mRIo9AR-nIHrg3uQ9e7EQ7uJ3BaTNke2HuJlZPfHKTz72kF2E8BghTecPtt-xR9tjHjWl2bdghLh79Pu2Lc14hfb-UWf1tF7nO3svXOi9V-b03oXse-H+4t2WI5nvhsB-ymCBPXwY9xMR+nqcsfIRF6no3xs8+5-sI947qfCerW9+Xxbk05isSR4PC3m-4+HewiPzhGfFuCLz-Qg+APF-X8HzDx+53d4T23eXeDsW22Oxe7+pOn+5+Y2weDsVepe8BI+x2ryMBTuP+6BMeDsL2deieYetu8+2Bk2aBXAxu2By2pk4B+EiORe1BvEKmmWrmVBNetB+UUASWXG546eOBVyN21+6BFeow2c-B-4K+egm8ghp2Yo4hZ2khEB-BAWR46e2eVy2GIhf84hIKchYhowJeXBO+-BFB+hFg0hrIU4vBOh9i0BkhzOrwdETq84zY16aAbwRhChF2Cwuh1hnuF4aA5oWhPhSBfGSh4hjOaA7mIRowXOJAURisPhKBSm3hWgSQkuVywh-4S6NevBT2RASwteFhR4A0ggthRRsS3hLhiwcSrwrhcRnhZRywXhhR5oVRfhzg142kdhEgLRu0YQ2mjBha1RlRi8UydERmVmcmgx9RAYFRVEfRPWHRrRUxrwuUounGrUcOixyu0xWsoxAmVmb6mYowLRbwwsQMMmbWhxSxdRTwcx-cJ0NSEATg62pQSgBAxMLaNWT0CxEs8wKkqxHB6xWO4RXRi8ssuxQW209x40g0jgE+rxdAVWvas080tWDkYwNELRpI4JkWsk3UHR40QxIooUvR7B3mlBdYzgVRFJsxpJ-RaJ1JWxysow2JMmJU9JtcRJiwLJkWMw+JHJohr0txXG+JeRVJSwNxn0XG9Jop9eS63JNmAxVyYpXJ5aexQmTBSp9ejYJJaxamGRIJIoBkJJDBPW9JS6VJ1G8p4xip1GVJ2CVpIm5Jtp9efwEpJp-cxhSx3Bbh-4MpIoeRqB+pXpmROR1GqRzpSh2CdpzgEEXBVsYoHJjhGJ9ewR84wJVxER84DA8YUhd+N42Zmq22GsU4BZU+MUL+hcBksB8g3hBZohuZIoJZ3uXOMURZR4dZ2erZYBdZzeRZBetZFSzZvum654dZqeDZGeWZFS1urZv2+ZFS16s5Pp0QhS8BdZBZK2H63u65I5p2CuQudBKIm6Yw8I02AOUQXkRgtMpy7YvWvui5GsuAy6E5CifZQ4R4rZ5gs2w5ees5lerZwEK2GsYAVRrZY+d535E5ZsQFyEE5zeMFJQ62AF6RDMHox2n5T2rZiOAFshqFiSb5i+EF5qUFBaSOUFB5+5SQ7YGQNCeFGY+Ed+7eSy2F44l+GoFAj+p5Q2ZuV+BFY2rgi+uo8uN+r27wK23+b5pom5NZ52VW4l2gZZKYw2HuYFFFaebFtFSy5eD2g+KCBuSynZ44PeelKlre-F-Z7CwljZsl1l8lUhcFuFDFUQF2O2ehp2JonZGsYBSy45kl450leBolcev5BBL5xB22fol5z2FBpl2wzZkVs6TFrus2XcbuSVVugOVh++L5i5kVy2uoHRdZYFHKcUtJPWwQGBmstIuAdCaUBmKQuuqQTQNFgJF5RyMQiOvlHVQMPg-gngHYHAAYwg4w55Q1lyglZg6QPCGgDuDs+AuO+Y81iw9BcgsgVS61qw2yp5ssLZpIFQVABoMqTB8UjJlUxpAJepLgfpY1+M9BF17c6ulJJIZYbp91x14hVR45EpupD1Q1UZYICi31b1lBTgSQn1rp51ZJHRxsn15kou7pXG9xYYjxsJI4OAt0CJzqSJAAIotCDcmRecSf8WSQAPb0H-w9TwDnKWjWByCg6HJ7KWC9AoAHWYa6rmJUBYBUCM05BAA|Falstad Simulation]] 
 + 
 +</WRAP> 
 + 
 +Bitte arbeiten Sie folgende Aufgaben durch: 
 + 
 +--> Aufgabe# 
 + 
 +Versuchen Sie die folgenden Zeitverlauf des UART Signals zu entschlüsseln - es kam vom vorliegenden Programm. \\ Welche neun Zeichen wurden gesendet?  \\ {{drawio>microcontrollertechnik:UARTSignal.svg}} 
 + 
 +<-- 
  
-Zu Beachten: 
-  * Display an einem anderen Port --> muss in lcd_lib geändert werden