Bei der Programmierung wünscht man sich häufig die Möglichkeit Daten des Mikrocontrollers irgendwo darzustellen.
Nach dieser Lektion sollten Sie:
Dieses Unterkapitel ist z.Zt. in einem provisorischem Zustand.
Variante mit Polling: 9_uartpolling_temperature.c
/* ----------------------------------------------------------------------------
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 1 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 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
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;
};
}
Variante mit Interrupt: 9_uart_temperature.c
/* ----------------------------------------------------------------------------
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.
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 1 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
}
}
Unvollständige Falstad Simulation
Bitte arbeiten Sie folgende Aufgaben durch: