Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
microcontrollertechnik:8_temperatur [2020/10/20 16:35] tfischer |
microcontrollertechnik:8_temperatur [2024/04/23 01:23] (aktuell) mexleadmin |
||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
- | ====== 8. Temperatur-Messung und Analog-Digital-Wandler ====== | + | ====== 8 Temperatur-Messung und Analog-Digital-Wandler ====== |
- | ===== ADC - wie kommt mehr als nur ein Bit über einen Pin? ===== | ||
<WRAP group> | <WRAP group> | ||
<WRAP column 30%> | <WRAP column 30%> | ||
- | ==== Ziele ==== | + | ===== Ziele ===== |
Nach dieser Lektion sollten Sie: | Nach dieser Lektion sollten Sie: | ||
Zeile 11: | Zeile 10: | ||
- wissen, wie ein ADC genutzt wird | - wissen, wie ein ADC genutzt wird | ||
- | ==== weiterführende Links ==== | + | ===== weiterführende Links ===== |
- | * Eine [[http://katgym.by.lo-net2.de/c.wolfseher/ | + | * Eine [[https://www.katharinengymnasium.de/wolf/ |
- | </ | + | </ |
- | ==== Video ==== | + | ===== Video ===== |
+ | {{youtube> | ||
+ | |||
+ | <WRAP hide> | ||
{{youtube> | {{youtube> | ||
+ | </ | ||
</ | </ | ||
- | ==== Messsignal-Digitalisierung und Auswertung ==== | + | <WRAP > |
+ | < | ||
+ | </ | ||
+ | {{drawio> | ||
+ | </ | ||
+ | ===== Messsignal-Digitalisierung und Auswertung | ||
<WRAP right> | <WRAP right> | ||
< | < | ||
</ | </ | ||
- | {{drawio> | + | {{drawio> |
</ | </ | ||
Um Sensorwerte in C sinnvoll nutzen zu können sind einige Schritte zwischen dem passiven Sensor über den ADC bis in den Code umzusetzen (siehe <imgref BildNr1> | Um Sensorwerte in C sinnvoll nutzen zu können sind einige Schritte zwischen dem passiven Sensor über den ADC bis in den Code umzusetzen (siehe <imgref BildNr1> | ||
- | - Sensor: $T \rightarrow R_{NTC}$\\ Umwandlung der physikalischen Größe in ein Messsignal \\ \\ | + | - Sensor: $T \rightarrow R_{\rm NTC}$\\ Umwandlung der physikalischen Größe in ein Messsignal \\ \\ |
- | - Sensorschaltung: | + | - Sensorschaltung: |
- | - ADC: $V_{NTC} \rightarrow | + | - ADC: $V_{\rm NTC} \rightarrow |
- | - Aufbereitung im Code $ad\_wert | + | - Aufbereitung im Code $adcValue |
~~PAGEBREAK~~ ~~CLEARFIX~~ | ~~PAGEBREAK~~ ~~CLEARFIX~~ | ||
Zeile 39: | Zeile 47: | ||
<WRAP group>< | <WRAP group>< | ||
< | < | ||
- | ^Temperatur\\ $T$ ^Widerstandswert $R_{NTC}$^ | + | ^Temperatur\\ $T$ ^Widerstandswert $R_{\rm NTC}$^ |
- | |$5°C$ | + | |$5° \rm C$ |
- | |$10°C$ |$9,34k\Omega$ | + | |$10°\rm C$ |
- | |$15°C$ |$7,37k\Omega$ | + | |$15°\rm C$ |
- | | ... | ... | | + | | ... |
</ | </ | ||
</ | </ | ||
< | < | ||
- | ^ADC-Wert \\ $ad\_wert$ ^Temperatur \\ $T$ ^ | + | ^ADC-Wert \\ $adcValue$ ^Temperatur \\ $T$ ^ |
- | |$256$ | + | |$256$ |
- | |$272$ | + | |$272$ |
- | |$288$ | + | |$288$ |
- | | ... | ... | | + | | ... | ... | |
</ | </ | ||
</ | </ | ||
Zeile 65: | Zeile 73: | ||
~~PAGEBREAK~~ ~~CLEARFIX~~ | ~~PAGEBREAK~~ ~~CLEARFIX~~ | ||
- | === 1.a Umwandlung der physikalischen Größe in ein Messsignal (Sensorwert zu Widerstandswert) === | + | ==== 1.a Umwandlung der physikalischen Größe in ein Messsignal (Sensorwert zu Widerstandswert) |
* Bei vielen passiven Sensoren wird der Sensorwert in eine Widerstandsänderung umgewandelt. Die Relation von Sensorwert zu Widerstandswert ist im Datenblatt des Sensors angegeben. | * Bei vielen passiven Sensoren wird der Sensorwert in eine Widerstandsänderung umgewandelt. Die Relation von Sensorwert zu Widerstandswert ist im Datenblatt des Sensors angegeben. | ||
Zeile 88: | Zeile 96: | ||
</ | </ | ||
- | === 2. + 3. Relation von Widerstandswert zu ADC-Wert === | + | ==== 2. + 3. Relation von Widerstandswert zu ADC-Wert |
<WRAP right> | <WRAP right> | ||
< | < | ||
</ | </ | ||
- | {{drawio> | + | {{drawio> |
</ | </ | ||
Zeile 102: | Zeile 110: | ||
Die Spannung am ADC lässt sich leicht über den Widerstandswert berechnen: | Die Spannung am ADC lässt sich leicht über den Widerstandswert berechnen: | ||
- | \begin{align} {{V_{NTC}} \over {V_{CC}}} = {{R_{NTC}}\over{R_{NTC} + R_{pullup}}} \tag{8.1} \end{align} | + | \begin{align} {{V_{\rm NTC}} \over {V_{\rm CC}}} = {{R_{\rm NTC}}\over{R_{\rm NTC} + R_{\rm pullup}}} \tag{8.1} \end{align} |
Daraus ergibt sich der digitalisierte Wert zu | Daraus ergibt sich der digitalisierte Wert zu | ||
- | \begin{align} ADCwert = round \left({{V_{NTC}}\over{V_{CC}}} \cdot 1024 \right) \tag{8.2} \end{align} | + | \begin{align} ADCwert = {\rm round} \left({{V_{\rm NTC}}\over{V_{\rm CC}}} \cdot 1024 \right) \tag{8.2} \end{align} |
aus $(8.1)$ und $(8.2)$ ergibt sich | aus $(8.1)$ und $(8.2)$ ergibt sich | ||
- | \begin{align} ADCwert = round \left({{R_{NTC}}\over{R_{NTC} + R_{pullup}}} \cdot 1024 \right) \tag{8.3} \end{align} | + | \begin{align} ADCwert = {\rm round} \left({{R_{\rm NTC}}\over{R_{\rm NTC} + R_{\rm pullup}}} \cdot 1024 \right) \tag{8.3} \end{align} |
- | Diese Relation muss nun im Analyse-Tool abgebildet werden und wurde in {{microcontrollertechnik: | + | Diese Relation muss nun im Analyse-Tool abgebildet werden und wurde in {{microcontrollertechnik: |
* Benamung von Zellen und Bereichen: Für viele Zellen wurden Namen vergeben, z.B. Zelle J2 in Tabelle 1 erhielt die Benamung $V\_CC$ oder der Bereich E7:E45 die Benamung $R\_NTC$. | * Benamung von Zellen und Bereichen: Für viele Zellen wurden Namen vergeben, z.B. Zelle J2 in Tabelle 1 erhielt die Benamung $V\_CC$ oder der Bereich E7:E45 die Benamung $R\_NTC$. | ||
* Rechnen mit Bereichen: In den Zellen F7 und G7 in Tabelle 1 wurden mit Angabe der Bereichsnamen Formeln eingefügt. Damit entfällt das Auffüllen der weiteren Zeilen | * Rechnen mit Bereichen: In den Zellen F7 und G7 in Tabelle 1 wurden mit Angabe der Bereichsnamen Formeln eingefügt. Damit entfällt das Auffüllen der weiteren Zeilen | ||
* Einfügen einer Trendlinie: Für das Diagramm wurde ADCval als x-Werte und $R\_NTC$ als y-Werte verwendet. Für die Trendlinie wurde ein Polynom 4. Grades genutzt. Mit Anzeige der Trendlinien-Formel wird die Relation von Widerstandswert zu ADC-Wert sichtbar. Die Zahl-Eigenschaft der Trendlinienbeschriftung sollte dabei auf '' | * Einfügen einer Trendlinie: Für das Diagramm wurde ADCval als x-Werte und $R\_NTC$ als y-Werte verwendet. Für die Trendlinie wurde ein Polynom 4. Grades genutzt. Mit Anzeige der Trendlinien-Formel wird die Relation von Widerstandswert zu ADC-Wert sichtbar. Die Zahl-Eigenschaft der Trendlinienbeschriftung sollte dabei auf '' | ||
- | === 4. Aufbereitung der des ADC-Werts im Code === | + | ==== 4. Aufbereitung der des ADC-Werts im Code ==== |
- | Mit den bisherigen Betrachtungen ist zwar klar, wie die Relation von Widerstandswert zu ADC-Wert aussieht, aber diese ist noch nicht im Code umgesetzt. Zunächst sollte der notwendige Wertebereich geklärt werden: In welchem Bereich werden Messwerte erwartet? In diesem Konkreten Fall geht es um die Innenraumtemperatur. Damit wird die Temperatur im Bereich $[-30°C, 50°C]$ sein. | + | Mit den bisherigen Betrachtungen ist zwar klar, wie die Relation von Widerstandswert zu ADC-Wert aussieht, aber diese ist noch nicht im Code umgesetzt. Zunächst sollte der notwendige Wertebereich geklärt werden: In welchem Bereich werden Messwerte erwartet? In diesem Konkreten Fall geht es um die Innenraumtemperatur. Damit wird die Temperatur im Bereich $[-30°\rm C, 50°\rm C]$ sein. |
+ | \\ \\ | ||
== direkte Verwendung der Datenblattwerte == | == direkte Verwendung der Datenblattwerte == | ||
- | Die einfachste Variante wäre die vorhandenen Daten aus dem Datenblatt direkt zu nehmen und zu prüfen, ob der eingelesene | + | <WRAP group>< |
+ | Die einfachste Variante wäre die vorhandenen Daten aus dem Datenblatt direkt zu nehmen und zu prüfen, ob der eingelesene | ||
<sxh c; first-line: 42> | <sxh c; first-line: 42> | ||
- | # | + | # |
- | const int TEMP[MAX_INDEX] = { -30, -25, -20, -15, -10, -5, | + | const int TEMP[MAX_INDEX] |
- | const int ADC_VAL[MAX_INDEX] | + | const int ADC_VAL[MAX_INDEX] ={ 968, 950, 927, 899, 866, 827, 783, 734, 681, 625, 568, 512, 456, 404, 356, 311, 271}; |
- | // Datenwerte aus dem Datenblatt | + | // Datenwerte aus dem Datenblatt |
- | // TEMP[] enthaelt die Temperaturen | + | // TEMP[] enthaelt die Temperaturen |
- | // In ADC_VAL[] sind die den Temperaturen entsprechenden ADC-Werte abgespeichert | + | // In ADC_VAL[] sind die den Temperaturen |
+ | // | ||
... | ... | ||
for(int index=0; index< | for(int index=0; index< | ||
- | { // suche den Index, bei dem der gemessene Wert zum ersten mal darueber liegt | + | { |
- | if (ad_wert | + | |
- | }; | + | if (tValue |
- | int t_wert | + | }; |
- | ... | + | int tValue |
+ | ... // gefundenen index wird zugewiesen | ||
</ | </ | ||
- | Nachteil dabei ist, dass die Ausführung unterschiedlich lange benötigt und die Auflösung nur 5°C beträgt. | ||
- | == direkte Verwendung | + | </ |
+ | <WRAP column 35%> | ||
+ | < | ||
+ | </ | ||
+ | {{drawio> | ||
+ | </ | ||
+ | </ | ||
+ | Nachteil dabei ist, dass die Ausführung unterschiedlich lange benötigt und die Auflösung nur 5°C beträgt (vgl. <imgref BildNr2> | ||
+ | |||
+ | \\ \\ | ||
+ | === direkte Verwendung der Datenblattwerte mit Interpolation === | ||
+ | |||
+ | <WRAP group> | ||
+ | <WRAP column 60%> | ||
In einem weiteren Schritt könnte eine Interpolation zwischen den Werten umgesetzt werden | In einem weiteren Schritt könnte eine Interpolation zwischen den Werten umgesetzt werden | ||
<sxh c; first-line: 42> | <sxh c; first-line: 42> | ||
- | # | ||
- | const int TEMP[MAX_INDEX] = { -30, -25, -20, -15, -10, -5, | + | # |
- | const int ADC_VAL[MAX_INDEX] | + | |
- | // Datenwerte aus dem Datenblatt | + | const int TEMP[MAX_INDEX] |
- | // TEMP[] enthaelt die Temperaturen | + | const int ADC_VAL[MAX_INDEX] ={ 968, 950, 927, 899, 866, 827, 783, 734, 681, 625, 568, 512, 456, 404, 356, 311, 271}; |
- | // In ADC_VAL[] sind die den Temperaturen entsprechenden ADC-Werte abgespeichert | + | // Datenwerte aus dem Datenblatt |
+ | // TEMP[] enthaelt die Temperaturen | ||
+ | // In ADC_VAL[] sind die den Temperaturen | ||
+ | // | ||
... | ... | ||
- | for(int index=0; index< | + | for(int index=0; index< |
- | { // suche den Index, bei dem der gemessene Wert zum ersten mal darueber liegt | + | { |
- | if (ad_wert | + | |
- | }; | + | if (tValue |
- | int t_uW = TEMP[index]; | + | }; |
- | int t_oW = TEMP[index+1]; | + | int t_uW = TEMP[index]; |
+ | | ||
+ | int t_oW = TEMP[index+1];// | ||
+ | | ||
- | int ADC_uW = ADC_VAL[index]; | + | int ADC_uW = ADC_VAL[index];// |
- | int ADC_oW = ADC_VAL[index+1]; | + | |
+ | int ADC_oW = ADC_VAL[index+1];// | ||
+ | | ||
- | float | + | float |
- | // zum unteren Temperaturwert wird ein Bruchteil entsprechend der | + | 5 * (tValue |
- | // Differenz von ad_wert | + | |
+ | | ||
+ | | ||
+ | | ||
... | ... | ||
</ | </ | ||
- | Die Auflösung ist nun sehr genau. Nachteile sind aber, dass die Ausführung immernoch unterschiedlich lange benötigt und die ein float-Wert relativ viel Speicher und CPU-Zeit bei weiteren Berechnungen benötigt. | + | </ |
+ | <WRAP column 35%> | ||
+ | < | ||
+ | </ | ||
+ | {{drawio> | ||
+ | </ | ||
+ | </ | ||
- | Der float-Wert | + | Die Auflösung ist nun sehr genau (vgl. <imgref BildNr3> |
- | == Umrechnung der Datenblattwerte == | + | Der float-Wert kann umgangen werden, wenn im gesamten Programm die Temperaturwerte in Zehntel-Grad geschrieben werden. Also $37,5°\rm C \rightarrow$ 375. Dann kann ein tValue als signed integer geschrieben werden. |
- | Eine geschicktere Option ist es, den Temperatur-Array '' | + | \\ \\ |
+ | === Umrechnung der Datenblattwerte === | ||
+ | |||
+ | <WRAP group> | ||
+ | <WRAP column 60%> | ||
+ | |||
+ | Eine geschicktere Option ist es, den Temperatur-Array '' | ||
* Betrachtet man die obige Tabelle '' | * Betrachtet man die obige Tabelle '' | ||
* Statt die restlichen Werte in Einzelschritten abzuspeichern, | * Statt die restlichen Werte in Einzelschritten abzuspeichern, | ||
- | Der Index kann damit als '' | + | Der Index kann damit als '' |
+ | |||
+ | |||
+ | </ | ||
+ | <WRAP column 35%> | ||
+ | < | ||
+ | </ | ||
+ | {{drawio> | ||
+ | </ | ||
+ | </ | ||
+ | |||
~~PAGEBREAK~~ ~~CLEARFIX~~ | ~~PAGEBREAK~~ ~~CLEARFIX~~ | ||
- | ==== Übung ==== | + | ===== Übung |
--> I. Vorarbeiten # | --> I. Vorarbeiten # | ||
- | - Laden Sie folgende Datei herunter: | + | - Laden Sie folgenden Dateien |
- | - {{microcontrollertechnik: | + | - {{microcontrollertechnik: |
- {{microcontrollertechnik: | - {{microcontrollertechnik: | ||
- {{microcontrollertechnik: | - {{microcontrollertechnik: | ||
+ | |||
+ | \\ \\ | ||
+ | <callout type=" | ||
+ | |||
+ | **Beachten Sie, folgendes** | ||
+ | * Es wird nun ein ATmega328 genutzt, d.h. das Programm ist nicht mehr kompatibel mit dem MiniMEXLE! | ||
+ | * Das Display ist nunan einem anderen Port um den Analog-Digial-Wandler am Port C zu nutzen. \\ Deshalb muss der Treiber '' | ||
+ | * Zeile 26: ''# | ||
+ | * Zeile 39: ''# | ||
+ | * Zeile 40: ''# | ||
+ | * Zeile 44: ''# | ||
+ | * Zeile 46: ''# | ||
+ | |||
+ | </ | ||
+ | \\ \\ | ||
+ | |||
+ | |||
<-- | <-- | ||
Zeile 207: | Zeile 281: | ||
<-- | <-- | ||
- | --> III. Eingabe in Atmel Studio # | + | --> III. Eingabe in Microchip |
+ | |||
<WRAP group>< | <WRAP group>< | ||
/* ---------------------------------------------------------------------------- | /* ---------------------------------------------------------------------------- | ||
- | + | ||
- | | + | |
| | ||
+ | |||
| | ||
+ | | ||
+ | | ||
+ | Prof. G. Gruhler | ||
+ | D. Chilachava | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | AVR-USB-PROGI Ver. 2.0 | ||
- | Autoren : Peter Blinzinger | + | Software: |
- | Prof. G. Gruhler (Hochschule Heilbronn, Fakultät T1) | + | |
- | D. Chilachava (Georgische Technische Universität) | + | |
- | | + | |
- | Datum : 01.05.2020 | + | |
- | | ||
- | |||
- | | ||
- | AVR-USB-PROGI Ver. 2.0 | ||
- | |||
- | | ||
- | C-Compiler: | ||
- | |||
| | ||
- | Maximaltemperatur im Betriebszeitraum in °C mit 1/10 Grad. | + | |
- | Keine Tastenbedienung | + | Keine Tastenbedienung |
- | + | ||
- | | + | |
- | +----------------+ +----------------+ | + | |
- | |- Experiment 8 -| |Temp. | + | |
- | | Temperature | + | |
- | +----------------+ +----------------+ | + | |
- | + | ||
- | | + | |
- | + | ||
- | | + | |
- | + | ||
- | Fuses im uC: CKDIV8: Aus (keine generelle Vorteilung des Takts) | + | Fuses im uC: |
- | + | ||
- | | + | |
- | + | ||
- | | + | |
- | 2) AD-Wandlung (Takt: 100 ms) | + | 2) AD-Wandlung (Takt: 100 ms) |
- | 3) Umrechnung | + | 3) Umrechnung |
- | 4) Anzeigetreiber (Takt: 1 s) | + | 4) Anzeigetreiber (Takt: 1 s) |
- | + | ||
- | 1) Das Modul " | + | 1) Das Modul " |
- | | + | und Umrechnung und einen zusaetzlichen |
- | + | ||
- | Verwendung von Hardware-Timer 0 und T0 Overflow-Interrupt. | + | Verwendung von Hardware-Timer 0 und T0 Overflow-Interrupt. |
- | Frequenzen: Quarzfrequenz | + | Frequenzen: Quarzfrequenz |
- | Timer-Vorteiler / 8 => | + | Timer-Vorteiler |
- | Hardware-Timer | + | Hardware-Timer |
- | Software-Vorteiler | + | Software-Vorteiler |
- | Hundertstel-Zaehler / | + | Hundertstel-Zaehler / 10 => 10 Hz / 100 ms |
- | Zehntel-Zaehler / 10 => | + | Zehntel-Zaehler |
+ | |||
2) Das Modul " | 2) Das Modul " | ||
Der AD-Wandler wird mit einem internen Takt von 96 kHz betrieben. | Der AD-Wandler wird mit einem internen Takt von 96 kHz betrieben. | ||
- | | + | |
- | | + | |
- | als temperaturabhängiger | + | als temperaturabhaengiger |
Als Referenzspannung wird die 5V-Versorgung verwendet. | Als Referenzspannung wird die 5V-Versorgung verwendet. | ||
- | Das Ergebnis wird in der globalen Variable | + | Das Ergebnis wird in der globalen Variable |
+ | |||
3) Das Modul " | 3) Das Modul " | ||
Der Ergebniswert des Moduls " | Der Ergebniswert des Moduls " | ||
einen entsprechenden Temperaturwert umgerechnet. In der Tabelle sind | einen entsprechenden Temperaturwert umgerechnet. In der Tabelle sind | ||
- | Temperaturwerte | + | Temperaturwerte |
- | | + | Die Werte dazwischen werden mit linearer Interpolation ermittelt. |
Weiterhin wird im Modul jede aktuelle Temperatur mit der gespeicherten | Weiterhin wird im Modul jede aktuelle Temperatur mit der gespeicherten | ||
- | | + | |
+ | |||
4) Das Modul " | 4) Das Modul " | ||
zu schnelles Umschalten der Anzeigewerte vermieden. Das Modul gibt die | zu schnelles Umschalten der Anzeigewerte vermieden. Das Modul gibt die | ||
Werte der aktuellen und der maximalen Temperatur in 1/10 °C aus. | Werte der aktuellen und der maximalen Temperatur in 1/10 °C aus. | ||
- | | + | |
- | | + | Zwischen AD-Wandlung / Umrechnung und Anzeige kann spaeter |
- | | + | Mittelwertsbildung mit 10 Werten |
+ | |||
+ | Die Kopplung der Module wird ueber global definierte Variable realisiert: | ||
+ | |||
+ | 1-Bit-Variable: | ||
+ | => Umrechnung | ||
+ | Takt | ||
+ | |||
+ | 16-Bit-Variable: | ||
+ | t-wert | ||
+ | tmax-wert | ||
- | Die Kopplung der Module wird über global definierte Variable realisiert: | ||
- | |||
- | | ||
- | | ||
- | Takt | ||
- | |||
- | 16-Bit-Variable: | ||
- | t-wert Umrechnung => Anzeige | ||
- | tmax-wert Umrechnung => Anzeige | ||
- | |||
// ----------------------------------------------------------------------------*/ | // ----------------------------------------------------------------------------*/ | ||
+ | |||
// Deklarationen ============================================================== | // Deklarationen ============================================================== | ||
+ | |||
// Festlegung der Quarzfrequenz | // Festlegung der Quarzfrequenz | ||
- | #ifndef F_CPU // optional definieren | + | #ifndef F_CPU |
- | #define F_CPU 12288000UL // MiniMEXLE | + | #define F_CPU 12288000UL |
- | #endif | + | # |
+ | |||
// Include von Header-Dateien | // Include von Header-Dateien | ||
- | #include < | + | #include < |
- | #include < | + | #include < |
- | #include < | + | #include < |
- | #include < | + | #include " |
- | #include " | + | |
// Konstanten | // Konstanten | ||
- | # | + | # |
- | # | + | # |
- | # | + | # |
- | const int TEMP[45] = {521, | ||
- | 338, | ||
- | 193, | ||
- | 48, | ||
- | -145, | ||
- | |||
- | // Die Tabellenwerte sind in 1/10 °C angegeben | ||
- | // Der erste Tabellenwert entspricht einem AD-Wert | ||
- | // von 256. Die Abstände der AD-Werte sind 16 | ||
+ | #define ASC_NULL | ||
+ | #define ASC_FULL_STOP | ||
+ | |||
+ | const int | ||
+ | 338, | ||
+ | 193, | ||
+ | 48, | ||
+ | -145, | ||
+ | |||
+ | // 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 | // Variable | ||
- | unsigned char vorteiler = VORTEILER_WERT; // Zählvariable | + | unsigned char softwarePrescaler |
- | unsigned char hundertstel = HUNDERTSTEL_WERT; | + | unsigned char cycle10msCount |
- | unsigned char zehntel | + | unsigned char cycle100msCount |
- | + | ||
- | unsigned int ad_wert | + | unsigned int |
- | int t_wert=0; // Variable | + | int tValue |
- | int tmax_wert=-300; // Variable | + | int tValueMax |
- | + | ||
- | bool takt10ms; // Bit-Botschaft alle 10 ms | + | bool |
- | bool takt100ms; // Bit-Botschaft alle 100 ms | + | bool |
- | bool takt1s; // Bit-Botschaft alle 1s | + | bool |
+ | |||
// | // | ||
void initTimer0 (void); | void initTimer0 (void); | ||
- | void adWandlerInit (void); | + | void initAdc |
- | void adWandlung (void); | + | |
- | void umrechnung (void); | + | |
- | void anzeigeTreiber | + | |
void initDisplay (void); | void initDisplay (void); | ||
- | + | void doAdc (void); | |
- | // Initialisierung und Hauptprogramm | + | void calculateTemp (void); |
- | // ============================================================================ | + | void refreshDisplayTemp(int tempValue, char line, char pos); |
+ | void refreshDisplay (void); | ||
+ | |||
+ | // Hauptprogramm | ||
int main () | int main () | ||
{ | { | ||
- | initDisplay(); | + | |
- | initTimer0(); | + | initTimer0(); |
- | adWandlerInit(); // Initialisierung des AD-Wandlers | + | |
- | + | ||
- | sei(); // generell Interrupts einschalten | + | sei(); |
- | + | | |
- | + | // Hauptprogrammschleife __________________________________________________ | |
- | // Hauptprogrammschleife __________________________________________________ | + | |
- | + | while(1) | |
- | while(1) // unendliche Warteschleife mit Aufruf der | + | // Funktionen |
- | // Funktionen | + | { |
- | { | + | if(cycle100msActive) // Durchfuehrung |
- | if(takt100ms == true) // Durchführung | + | { |
- | { | + | |
- | takt100ms | + | doAdc(); // Ausfuehrung |
- | adWandlung(); // Ausführung | + | |
- | umrechnung(); // Ausführung | + | } |
- | } | + | |
- | + | if(cycle1sActive) | |
- | if(takt1s == true) // Durchführung | + | { |
- | { | + | |
- | takt1s | + | |
- | anzeigeTreiber(); | + | } |
- | } | + | } |
- | } | + | |
} | } | ||
- | + | ||
- | // Funktionen | + | |
- | + | // Timer Initialisierung | |
- | // Timer-Initialisierung: | + | |
- | // --------------------- | + | |
// | // | ||
// Initialisierung des Timer0 zur Erzeugung eines getakteten Interrupts. | // Initialisierung des Timer0 zur Erzeugung eines getakteten Interrupts. | ||
- | // Er dient dazu, die benötigten | + | // Er dient dazu, die benoetigten |
void initTimer0() | void initTimer0() | ||
{ | { | ||
- | TCCR0A |= (0<< | + | |
- | TCCR0B |= (0<< | + | |
- | TIMSK0 |= (1<< | + | TCCR0B |
+ | | ||
+ | TIMSK0 | ||
} | } | ||
- | + | ||
- | // A/D-Wandler-Initialisierung: | + | // ADC-Initialisierung |
- | // --------------------------- | + | // |
// Initialisierung des A/ | // Initialisierung des A/ | ||
// Vorteiler = 128 => interner Takt = 96 kHz | // Vorteiler = 128 => interner Takt = 96 kHz | ||
// Abfrage des ADC0 (NTC-Spannungsteiler) | // Abfrage des ADC0 (NTC-Spannungsteiler) | ||
// Referenzspannung = analoge Versorgung Avcc | // Referenzspannung = analoge Versorgung Avcc | ||
- | + | void initAdc() | |
- | void adWandlerInit | + | |
{ | { | ||
- | ADMUX |= (1<< | + | |
- | + | ||
- | ADCSRA |= (1<< | + | ADCSRA |
+ | | ||
+ | | ||
+ | | ||
+ | (void) ADCH; // erster Wert ist " | ||
} | } | ||
- | + | ||
- | + | ||
- | // Interrupt-Routine | + | // Timer-Service |
- | // ============================================================================ | + | // |
- | ISR (TIMER0_OVF_vect) | + | |
// In der Interrupt-Routine sind die Softwareteiler realisiert, die die Takt- | // In der Interrupt-Routine sind die Softwareteiler realisiert, die die Takt- | ||
- | // botschaften (10ms, 100ms, 1s) für die Module erzeugen. Die Interrupts | + | // botschaften (10ms, 100ms, 1s) fuer die Module erzeugen. Die Interrupts |
- | // werden von Timer 0 ausgelöst | + | // werden von Timer 0 ausgeloest |
// | // | ||
- | // Veränderte | + | // Veraenderte |
- | // hunderstel | + | // hunderstel |
- | // zehntel | + | // zehntel |
// | // | ||
- | // Ausgangsvariable: | + | // Ausgangsvariable: |
- | // takt100ms | + | // takt100ms |
- | // takt1s | + | // takt1s |
+ | |||
+ | ISR (TIMER0_OVF_vect) | ||
{ | { | ||
- | --vorteiler; // Vorteiler dekrementieren | + | |
- | if (vorteiler==0) // wenn 0 erreicht: 10ms abgelaufen | + | if (softwarePrescaler==0) // wenn 0 erreicht: 10ms abgelaufen |
- | { | + | { |
- | vorteiler | + | |
- | takt10ms | + | |
- | --hundertstel; // Hundertstelzähler | + | --cycle10msCount; // Hundertstelzaehler |
- | + | ||
- | if (hundertstel==0) // wenn 0 erreicht: 100ms abgelaufen | + | if (cycle10msCount==0) // wenn 0 erreicht: 100ms abgelaufen |
- | { | + | { |
- | hundertstel | + | |
- | takt100ms | + | |
- | --zehntel; | + | --cycle100msCount; |
- | + | ||
- | if (zehntel==0) // Zehntelzähler | + | if (cycle100msCount==0) |
- | { | + | { |
- | zehntel | + | |
- | takt1s | + | |
- | } | + | } |
- | } | + | } |
- | } | + | } |
} | } | ||
- | + | ||
- | // Modul ADWandlung: | + | // ADWandlung |
- | // ---------------- | + | // |
- | // Durchführung | + | // Durchfuehrung |
// Spannung in einen digitalen 10-bit-Wert (einmal pro 100 ms). | // Spannung in einen digitalen 10-bit-Wert (einmal pro 100 ms). | ||
- | void adWandlung() | + | void doAdc() |
{ | { | ||
- | ADCSRA |= (1<< | + | |
- | while (ADCSRA & (1<< | + | while (ADCSRA & (1<< |
- | + | ||
- | ad_wert | + | |
// | // | ||
// | // | ||
} | } | ||
- | + | ||
- | // Modul Umrechnung: | + | // Umrechnung |
- | // -------------------- | + | // |
// (wird alle 100 ms aufgerufen) | // (wird alle 100 ms aufgerufen) | ||
- | void umrechnung | + | void calculateTemp |
{ | { | ||
- | unsigned char index; // Tabellenindex | + | |
- | unsigned char abschnitt; // Abstand zum nächstkleineren | + | unsigned char steps; // Abstand zum naechstkleineren |
- | // der AD-Werte der Temperaturtabelle | + | // der AD-Werte der Temperaturtabelle |
- | + | | |
- | index = (ad_wert-256)/16; // Indexberechnung (Zeiger in Tabelle) | + | index |
- | abschnitt | + | |
- | + | | |
- | t_wert | + | |
- | // Temperaturwert berechnen | + | // Temperaturwert berechnen |
- | + | ||
- | if(t_wert>=tmax_wert) // aktueller Wert mit Maximalwert | + | if(tValue>=tValueMax) |
- | { | + | { |
- | tmax_wert | + | |
- | } | + | } |
} | } | ||
- | + | ||
- | // Modul Anzeigetreiber: | + | // Anzeigefunktion ============================================================== |
- | // -------------------- | + | // |
+ | // Der aktuelle Temperatur und die maximale Temperatur werden ausgegeben | ||
+ | void refreshDisplay() | ||
+ | { | ||
+ | refreshDisplayTemp(tValue, | ||
+ | refreshDisplayTemp(tValueMax, | ||
+ | } | ||
+ | |||
+ | // Anzeigetreiber fuer Temperaturanzeige ============================================================== | ||
// | // | ||
// Beschreiben der Anzeige mit dem erstellten Temperaturwert | // Beschreiben der Anzeige mit dem erstellten Temperaturwert | ||
Zeile 490: | Zeile 580: | ||
// Hunderter: einfache Integer-Teilung (/100). | // Hunderter: einfache Integer-Teilung (/100). | ||
// Zehner: Modulo-Ermittlung (%100), d.h. Rest bei der Teilung durch 100 | // Zehner: Modulo-Ermittlung (%100), d.h. Rest bei der Teilung durch 100 | ||
- | // dann nochmals Integer-Teilung (/10) dieses Restes. | + | // |
// Einer: | // Einer: | ||
// | // | ||
- | // Umrechnung in ASCII-Werte | + | // Umrechnung in ASCII-Werte |
- | void tempAnzeige(int ausgabewert, char zeile, char spalte) | + | void refreshDisplayTemp(int tempValue, char line, char pos) |
{ | { | ||
- | int i; | + | |
- | + | if (tempValue> | |
- | i = ausgabewert; | + | { |
- | lcd_gotoxy(zeile, spalte); // Startposition | + | lcd_putc(' |
- | if (i> | + | } |
- | { | + | else |
- | lcd_putc(' | + | { |
- | } | + | lcd_putc(' |
- | else | + | |
- | { | + | } |
- | lcd_putc(' | + | lcd_putc |
- | i = -i; // Vorzeichenumkehr bei negativer Zahl | + | |
- | } | + | lcd_putc |
- | lcd_putc(i/100 + 0x30); // Hunderter ausgeben (°C Zehner) | + | lcd_putc |
- | i = i%100; | + | lcd_putc |
- | lcd_putc(i/10 + 0x30); // Zehner ausgeben (°C Einer) | + | |
- | lcd_putc(0x2E); // Punkt ausgeben | + | |
- | lcd_putc(i%10 + 0x30); // Einer ausgeben (°C Zehntel) | + | |
} | } | ||
- | + | ||
- | // Anzeigefunktion | + | // Initialisierung Display-Anzeige ============================================================== |
- | // --------------- | + | |
// | // | ||
- | // Der aktuelle Temperatur und die maximale Temperatur werden ausgegeben | + | void initDisplay() // Start der Funktion |
- | void anzeigeTreiber() | + | |
{ | { | ||
- | tempAnzeige(t_wert, | + | |
- | tempAnzeige(tmax_wert, | + | |
- | } | + | |
- | + | | |
- | // Initialisierung Display-Anzeige | + | |
- | void initDisplay() // | + | _delay_ms(2000); |
- | { | + | |
- | lcd_init(); | + | |
- | + | | |
- | lcd_gotoxy(0, | + | // " |
- | lcd_putstr("- Experiment 8 -"); // Ausgabe Festtext: 16 Zeichen | + | } |
- | + | ||
- | lcd_gotoxy(1,0); // Cursor auf 2. Zeile, 1. Zeichen | + | |
- | lcd_putstr(" | + | |
- | + | ||
- | _delay_ms(2000); | + | |
- | + | ||
- | lcd_gotoxy(0, | + | |
- | lcd_putstr(" | + | |
- | // " | + | |
- | + | ||
- | lcd_gotoxy(1,0); // Cursor auf 2. Zeile, 1. Zeichen | + | |
- | lcd_putstr(" | + | |
- | // " | + | |
- | } // Ende der Funktion | + | |
</ | </ | ||
</ | </ | ||
Zeile 558: | Zeile 628: | ||
- Hier wird wieder geprüft ob die Frequenz des Quarz bereits eingestellt wurde und - falls nicht - dessen Frequenz eingestellt. \\ \\ | - Hier wird wieder geprüft ob die Frequenz des Quarz bereits eingestellt wurde und - falls nicht - dessen Frequenz eingestellt. \\ \\ | ||
- Die Header-Dateien entsprechen denen der letzten Programme. \\ \\ \\ \\ \\ | - Die Header-Dateien entsprechen denen der letzten Programme. \\ \\ \\ \\ \\ | ||
- | - Die Konstanten entsprechen denen der letzten Programme. \\ \\ \\ \\ | + | - Die Konstanten entsprechen denen der letzten Programme. |
- | - Es wird eine zusätzliches Array angelegt, in denen die Temperaturen in 1/10 °C abgespeichert sind. Beispielsweise entspricht der erste Eintrag'' | + | - Es wird eine zusätzliches Array '' |
- | - Bei den Variablen entsprechen einige denen der letzten Programme. | + | - Bei den Variablen entsprechen einige denen der letzten Programme. \\ \\ \\ \\ |
- | - Für die Uhr werden Stunden, Minuten, Sekunden und Zehntelsekunden mit Anfangswerten deklariert. \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ | + | - Die Variable '' |
- | - Bei den Funktionsprototypen sind einige bekannte Unterprogramme vorhanden. Details werden weiter unten erklärt. \\ \\ \\ | + | - Die Variable '' |
+ | - Die Variable '' | ||
+ | - Bei den Funktionsprototypen sind einige bekannte Unterprogramme vorhanden. Details werden weiter unten erklärt. | ||
'' | '' | ||
- | - Das Hauptprogramm ähnelt sehr stark dem [[4._up_down_counter|Up/Down Counter]]. Entsprechend werden | + | - Das Hauptprogramm ähnelt sehr stark dem [[4_up_down_counter|Up/Down Counter]], wobei die Initialisierung des Timers in eine Unterfunktion '' |
- | - In der Endlosschleife sind auf der ersten Ebene wieder nur If-Abfragen zu den Flags '' | + | - In der Endlosschleife sind auf der ersten Ebene wieder nur If-Abfragen zu den Flags '' |
- | - Alle $10ms$ (bzw. wenn das entsprechende Flag gesetzt wird) wird das Flag zurückgesetzt und das Unterprogramm '' | + | - Alle $100~\rm ms$ (bzw. wenn das entsprechende Flag gesetzt wird) wird das Flag zurückgesetzt und das Unterprogramm '' |
- | - Alle $100ms$ (bzw. wenn das entsprechende Flag gesetzt wird) wird das Flag zurückgesetzt und das Unterprogramm | + | - Alle $1~\rm s$ (bzw. wenn das entsprechende Flag gesetzt wird) wird das Flag zurückgesetzt und das Unterprogramm '' |
- | - Alle $1s$ (bzw. wenn das entsprechende Flag gesetzt wird) wird das Flag zurückgesetzt und das Unterprogramm '' | + | '' |
- | '' | + | - Die Timer Initialisierung |
- | - Auch die Interrupt Routine | + | |
- | '' | + | '' |
- | - Auch das Einstellen des Data Direction Registers und der Pullups wurde bereits in vorherigen Programmen erklärt. \\ \\ \\ \\ | + | |
- | '' | + | |
- | \\ \\ | + | |
- | - In dieser Funktion werden zunächst die Stellungen aller Taster eingelesen (vgl. '' | + | |
- | - Neu hier ist, dass die Bedienung der Schalter die Variablen für Stunden, Minuten um eins hochsetzen, bzw. bei Überlauf wider zurück auf 0 setzen. Zusätzlich wird bei eine Änderung des Minuten-Werts der Sekunden-Wert auf 0 gesetzt. | + | |
- | '' | + | - Über den Multiplexer |
- | - Hierüber | + | - ''< |
- | - Ähnlich zum Counter werden | + | - WICHTIG: der erste digitalisierte Wert nach Initialisierung und auch nach Kanalwechsel muss ignoriert werden. Bei Änderungen der ADC-Konfiguration entspricht diese bei AVR-Microcontrollern noch keinem validen Wert! |
- | \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ | + | |
+ | <WRAP 470px> | ||
+ | < | ||
+ | </ | ||
+ | {{drawio> | ||
+ | </ | ||
+ | \\ | ||
+ | '' | ||
+ | - Die Timer-Service Routine ist dem Programm [[4_up_down_counter|Up/ | ||
+ | |||
+ | '' | ||
+ | - Das Bit '' | ||
+ | - Während die AD-Wandlung läuft bleibt das Bit '' | ||
+ | - Das Register '' | ||
+ | - immer erst ADCL auslesen, dann ADCH. | ||
+ | - immer auch ADCH auslesen, selbst wenn es nicht benötigt wird. \\ \\ | ||
+ | |||
+ | '' | ||
+ | - Die Variable '' | ||
+ | - Die Variable '' | ||
+ | - '' | ||
+ | - In '' | ||
+ | \\ \\ \\ \\ \\ \\ \\ | ||
+ | |||
+ | '' | ||
+ | - Da zwei Temperaturen ausgegeben werden müssen, ruft die Anzeigefunktion eine weitere Unterfunktion auf, welche mit unterschiedlichen Werten gespeist wird | ||
+ | \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ | ||
+ | |||
+ | '' | ||
+ | - Auf der ersten Position wird gegebenenfalls ein Vorzeichen angezeigt. Falls die Temperatur negativ ist, muss für den nächsten Schritt der Betrag gebildet werden \\ \\ \\ \\ | ||
+ | - Für die Temperatur wird zunächst die Zehner und die Einer ermittelt und ausgegeben. Im nächsten Schritt folgt das Dezimaltrennzeichen und die Zehntel Grad. \\ \\ | ||
'' | '' | ||
- Die Funktion '' | - Die Funktion '' | ||
- Danach wird der erste Text auf den Bildschirm geschrieben und damit der Programmname dargestellt. | - Danach wird der erste Text auf den Bildschirm geschrieben und damit der Programmname dargestellt. | ||
- | - Nach zwei Sekunden wird der Auswahlbildschirm | + | - Nach zwei Sekunden wird die Display-Vorlage für die Temperatur |
- | + | ||
- | \\ \\ \\ \\ \\ \\ \\ \\ \\ | + | |
- | '' | ||
- | - Die Zähl-Funktion '' | ||
- | - Zunächst wird ein Schaltwechsel am Ausgang mit dem Lautsprecher ausgegeben, um einen Knackton zu erzeugen | ||
- | - Dann werden die Sekunden hochgezählt | ||
- | - ist das Maximum erreicht, so wird der Sekunden-Wert zurückgesetzt und der Minuten-Wert um eins hochgezählt. | ||
- | - ebenso wird beim Maximum des Minuten-Serts dieser zurückgesetzt und der Stundenwert hochgezählt. | ||
- | - beim Maximum des Stunden-Werts wird dieser wieder auf Null gesetzt | ||
</ | </ | ||
Zeile 616: | Zeile 703: | ||
- Die Anzeige am Display scheint nicht immer genau zum eingestellten Temperaturwert zu passen. Um dies zu verstehen, sollten Sie betrachten, welche Parameter in Simulide beim NTC eingestellt werden können. Weiterhin gibt es dazu Tipps unter dem Elektrotechnik 1-Kapitel: [[elektrotechnik_1: | - Die Anzeige am Display scheint nicht immer genau zum eingestellten Temperaturwert zu passen. Um dies zu verstehen, sollten Sie betrachten, welche Parameter in Simulide beim NTC eingestellt werden können. Weiterhin gibt es dazu Tipps unter dem Elektrotechnik 1-Kapitel: [[elektrotechnik_1: | ||
+ | - Entwickeln Sie ein Programm, welches mit einem Poti über den ADC des Atmega328 den Cursor bzw. ein Zeichen auf dem Display bewegt. | ||
<-- | <-- | ||