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:8_temperatur [2020/10/20 23:00]
tfischer
microcontrollertechnik:8_temperatur [2024/04/23 01:23] (aktuell)
mexleadmin
Zeile 1: Zeile 1:
-====== 8Temperatur-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/web/analogdigital/analogdigital_inhalt.html|schnelle und seichte Einführung in AD-Wandler]]+  * Eine [[https://www.katharinengymnasium.de/wolf/web/analogdigital/analogdigital_inhalt.html|schnelle und seichte Einführung in AD-Wandler]]
  
-</WRAP> <WRAP column 65%+</WRAP> <WRAP> 
-==== Video ====+===== Video =====
    
 +{{youtube>IkPOZXeW_CM?size=700x400}}
 +
 +<WRAP hide>
 {{youtube>MaddgEBeMLE?size=700x400}} {{youtube>MaddgEBeMLE?size=700x400}}
 +</WRAP> 
  
 </WRAP> </WRAP> </WRAP> </WRAP>
  
-==== Messsignal-Digitalisierung und Auswertung ====+<WRAP > 
 +<imgcaption BildNr10 | Schaltung des AD-Wandlers im 328> 
 +</imgcaption> 
 +{{drawio>AdcWandler.svg}} 
 +</WRAP> 
 +===== Messsignal-Digitalisierung und Auswertung =====
  
 <WRAP right> <WRAP right>
 <imgcaption BildNr1 | Signalpfad von Sensorsignal zur Auswertung in C> <imgcaption BildNr1 | Signalpfad von Sensorsignal zur Auswertung in C>
 </imgcaption> </imgcaption>
-{{drawio>SignalpfadSensorBisC}}+{{drawio>SignalpfadSensorBisC.svg}}
 </WRAP> </WRAP>
  
 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: $R_{NTC} \rightarrow V_{NTC}$\\ Aufbereitung des Messsignal (z.B. Filter, Verstärkung oder Umwandlung in Spannungssignal) \\ \\ +  - Sensorschaltung: $R_{\rm NTC} \rightarrow V_{\rm NTC}$\\ Aufbereitung des Messsignal (z.B. Filter, Verstärkung oder Umwandlung in Spannungssignal) \\ \\ 
-  - ADC: $V_{NTC} \rightarrow ad\_wert$\\ Digitalisierung in ein Zahlenwert im Code \\ \\ +  - ADC: $V_{\rm NTC} \rightarrow adcValue$\\ Digitalisierung in ein Zahlenwert im Code \\ \\ 
-  - Aufbereitung im Code $ad\_wert \rightarrow t\_wert$ \\ um ein Abbild des Sensorwerts zu erhalten \\ \\+  - Aufbereitung im Code $adcValue \rightarrow tValue$ \\ um ein Abbild des Sensorwerts zu erhalten \\ \\
  
 ~~PAGEBREAK~~ ~~CLEARFIX~~ ~~PAGEBREAK~~ ~~CLEARFIX~~
Zeile 39: Zeile 47:
 <WRAP group><WRAP column 45% > <WRAP group><WRAP column 45% >
 <tabcaption TabNr1 | Beispiel für eine Vorgabe im Datenblatt> <tabcaption TabNr1 | Beispiel für eine Vorgabe im Datenblatt>
-^Temperatur\\ $T$ ^Widerstandswert $R_{NTC}$^  +^Temperatur\\ $T$ ^Widerstandswert $R_{\rm NTC}$^  
-|$5°C    |$11,9k\Omega$  |  +|$5° \rm C      |$11,9~\rm k\Omega$  |  
-|$10°C   |$9,34k\Omega$ +|$10°\rm C      |$9,34~\rm k\Omega$ 
-|$15°C   |$7,37k\Omega$ +|$15°\rm C      |$7,37~\rm k\Omega$ 
-| ...      |  ...          +| ...             |  ...               
 </tabcaption> </tabcaption>
 </WRAP><WRAP column 40% > </WRAP><WRAP column 40% >
  
 <tabcaption TabNr2 | Beispiel für die notwendige Relation im Code> <tabcaption TabNr2 | Beispiel für die notwendige Relation im Code>
-^ADC-Wert \\ $ad\_wert$ ^Temperatur \\ $T$   ^  +^ADC-Wert \\ $adcValue$ ^Temperatur \\ $T$   ^  
-|$256$     |$52,1°C$       |  +|$256$     |$52,1°\rm C$       |  
-|$272$     |$49,9°C$       | +|$272$     |$49,9°\rm C$       | 
-|$288$     |$47,9°C$       | +|$288$     |$47,9°\rm C$       | 
-| ...      |  ...          +| ...      |  ...              
 </tabcaption> </tabcaption>
 </WRAP></WRAP> </WRAP></WRAP>
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:
 </callout> </callout>
  
-=== 2. + 3. Relation von Widerstandswert zu ADC-Wert ===+==== 2. + 3. Relation von Widerstandswert zu ADC-Wert ====
  
 <WRAP right> <WRAP right>
 <imgcaption BildNr0 | Schaltung des Sensorwiderstands> <imgcaption BildNr0 | Schaltung des Sensorwiderstands>
 </imgcaption> </imgcaption>
-{{drawio>SchaltungSensorWiderstand}}+{{drawio>SchaltungSensorWiderstand.svg}}
 </WRAP> </WRAP>
  
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:herleitung_adcwerte.xlsx|diesem Ecxelfile}} umgesetzt. Einige der verwendeten Excel-Tricks sollen hier beschrieben werden:+Diese Relation muss nun im Analyse-Tool abgebildet werden und wurde in {{microcontrollertechnik:herleitung_adcwerte.xlsx|dieser Excel-Datei}} umgesetzt. Einige der verwendeten Excel-Tricks sollen hier beschrieben werden:
   * 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 ''Wissenschaftlich'' mit 2 Dezimalstellen gestellt werden, um die Auflösung zu erhöhen.   * 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 ''Wissenschaftlich'' mit 2 Dezimalstellen gestellt werden, um die Auflösung zu erhöhen.
  
-=== 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°C50°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 C50°\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 ad_wert zwischen zwei bekannten Werten liegt.+<WRAP group><WRAP column 60%> 
 +Die einfachste Variante wäre die vorhandenen Daten aus dem Datenblatt direkt zu nehmen und zu prüfen, ob der eingelesene tValue zwischen zwei bekannten Werten liegt.
  
 <sxh c; first-line: 42> <sxh c; first-line: 42>
  
-#define MAX_INDEX 17 // Anzahl der Stuetzstellen+#define MAX_INDEX 17 // Anzahl der Stuetzstellen
  
-const int TEMP[MAX_INDEX]    { -30, -25, -20, -15, -10,  -5,   0,   5,  10,  15,  20,  25,  30,  35,  40,  45,  50}; +const int TEMP[MAX_INDEX]    ={ -30, -25, -20, -15, -10,  -5,   0,   5,  10,  15,  20,  25,  30,  35,  40,  45,  50}; 
-const int ADC_VAL[MAX_INDEX]   { 968, 950, 927, 899, 866, 827, 783, 734, 681, 625, 568, 512, 456, 404, 356, 311, 271}; +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  
 + // entsprechenden ADC-Werte abgespeichert
 ... ...
  for(int index=0; index<MAX_INDEX; index++)  for(int index=0; index<MAX_INDEX; index++)
- { // suche den Index, bei dem der gemessene Wert zum ersten mal darueber liegt + {     // suche den Index, bei dem der gemessene  
- if (ad_wert >= ADC_VAL[index]) break;  // wenn gefunden, breche ab (dann zeigt index auf die untere Temperatur) +     // Wert zum ersten mal darueber liegt 
- }; + if (tValue >= ADC_VAL[index]) break; // wenn gefunden, breche ab  
- int t_wert = TEMP[index]; // Der Temperaturwert mit dem gefundenen index wird zugewiesen + };     // (index zeigt auf untere Temperatur) 
-...+ int tValue = TEMP[index];// Der Temperaturwert mit dem  
 +...      // gefundenen index wird zugewiesen
 </sxh> </sxh>
  
-Nachteil dabei ist, dass die Ausführung unterschiedlich lange benötigt und die Auflösung nur 5°C beträgt. 
  
-== direkte Verwendung der Datenblattwerte mit Interpolation ==+</WRAP> 
 +<WRAP column 35%> 
 +<imgcaption BildNr2 | Bildliche Erklärung für die "direkte Verwendung"> 
 +</imgcaption> 
 +{{drawio>BildDirekteVerwendungDS.svg}} 
 +</WRAP> 
 +</WRAP> 
 + 
 +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>
  
-#define MAX_INDEX 17 // Anzahl der Stuetzstellen 
  
-const int TEMP[MAX_INDEX]    { -30, -25, -20, -15, -10,  -5,   0,   5,  10,  15,  20,  25,  30,  35,  40,  45,  50}; +#define MAX_INDEX 17 // Anzahl der Stuetzstellen 
-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 +const int TEMP[MAX_INDEX]    ={ -30, -25, -20, -15, -10,  -5,   0,   5,  10,  15,  20,  25,  30,  35,  40,  45,  50}; 
- // 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  
 + // entsprechenden ADC-Werte abgespeichert
 ... ...
- for(int index=0; index<MAX_INDEX-1; index++) + for(int index=0; index<MAX_INDEX; index++) 
- { // suche den Index, bei dem der gemessene Wert zum ersten mal darueber liegt + {     // suche den Index, bei dem der gemessene  
- if (ad_wert >= ADC_VAL[index]) break;  // wenn gefunden, breche ab (dann zeigt index auf die untere Temperatur) +     // Wert zum ersten mal darueber liegt 
- }; + if (tValue >= ADC_VAL[index]) break; // wenn gefunden, breche ab  
- int t_uW = TEMP[index]; // Der Temperaturwert mit dem gefundenen index wird gespeichert (unterer Wert = uW) + };     // (index zeigt auf untere Temperatur) 
- int t_oW = TEMP[index+1]; // Der darauffolgende Temperaturwert wird gespeichert (oberer Wert = oW)+ int t_uW = TEMP[index]; // Der Temperaturwert mit dem gefundenen index  
 +     // wird gespeichert (unterer Wert = uW) 
 + int t_oW = TEMP[index+1];// Der darauffolgende Temperaturwert wird  
 +     // gespeichert (oberer Wert = oW)
  
- int ADC_uW = ADC_VAL[index]; // Der ADC-Wert mit dem gefundenen index wird zugewiesen (unterer Wert = uW) + int ADC_uW = ADC_VAL[index];// Der ADC-Wert mit dem gefundenen index  
- int ADC_oW = ADC_VAL[index+1]; // Der darauffolgende ADC-Wert wird zugewiesen (oberer Wert = oW)+     // wird zugewiesen (unterer Wert = uW) 
 + int ADC_oW = ADC_VAL[index+1];// Der darauffolgende ADC-Wert  
 +     // wird zugewiesen (oberer Wert = oW)
  
- float t_wert = TEMP[index] + 5 * (ad_wert -ADC_uW ) / (ADC_oW - ADC_uW ) ; // lineare Interpolation + float tValue = TEMP[index] +  
- // zum unteren Temperaturwert wird ein Bruchteil entsprechend der  + 5 * (tValue - ADC_uW ) / (ADC_oW - ADC_uW ) ;  
- // Differenz von ad_wert zum unteren ADC Wert ADC_uW hinzuaddiert+     // lineare Interpolation 
 +     // zum unteren Temperaturwert wird ein   
 +     // Bruchteil entsprechend der Differenz von  
 +     // tValue zum unteren ADC Wert ADC_uW hinzuaddiert
 ... ...
 </sxh> </sxh>
  
-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> 
 +<WRAP column 35%> 
 +<imgcaption BildNr3 | Bildliche Erklärung für die "direkte Verwendung mit Interpolation"> 
 +</imgcaption> 
 +{{drawio>BildDirekteVerwendungDSmitInterpol.svg}} 
 +</WRAP> 
 +</WRAP>
  
-Der float-Wert kann umgangen werden, wenn im gesamten Programm die Temperaturwerte in Zehntel-Grad geschrieben werden. Also 37,5°C $\rightarrow$ 375. Dann kann ein t_wert als signed integer geschrieben werden.+Die Auflösung ist nun sehr genau (vgl. <imgref BildNr3>). 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.  
 + 
 +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. 
 + 
 +\\ \\ 
 +=== Umrechnung der Datenblattwerte ===
  
-== Umrechnung der Datenblattwerte ==+<WRAP group> 
 +<WRAP column 60%>
  
-Eine geschicktere Option ist es, den Temperatur-Array ''TEMP[]'' bereits so vorauszuberechnen, dass diese über den ADC-Wert ''ad_wert'' adressiert werden kann. Dazu würde aber für alle Werte ein Array mit 1024 Werten benötigt (1kB Speicher). Eine Reduktion des Arrays ist also sinnvoll.+Eine geschicktere Option ist es, den Temperatur-Array ''TEMP[]'' bereits so vorauszuberechnen, dass diese über den ADC-Wert ''tValue'' adressiert werden kann. Dazu würde aber für alle Werte ein Array mit 1024 Werten benötigt (1kB Speicher). Eine Reduktion des Arrays ist also sinnvoll.
    * Betrachtet man die obige Tabelle ''ADC_VAL[]'', so ist jeder Wert größer als 256 und kleiner als 976. Diese Werte können also ignoriert werden.     * Betrachtet man die obige Tabelle ''ADC_VAL[]'', so ist jeder Wert größer als 256 und kleiner als 976. Diese Werte können also ignoriert werden. 
    * Statt die restlichen Werte in Einzelschritten abzuspeichern, könnte auch nur jeder 16. Wert gespeichert werden    * Statt die restlichen Werte in Einzelschritten abzuspeichern, könnte auch nur jeder 16. Wert gespeichert werden
  
-Der Index kann damit als ''index = (ad_wert-256)/16; '' ermittelt werden. Der Vorteil in den verwendeten Zahlen (Vielfache von 2, hier 16) ist, dass der Microcontroller mit diesen leichter rechnen kann. Dies ist in dem untenstehenden Code umgesetzt. Die Umrechnung des Temperatur-Arrays ''TEMP[]'' ist in obigen Excel-File beschrieben.+Der Index kann damit als ''index = (tValue - 256)/16; '' ermittelt werden. Der Vorteil in den verwendeten Zahlen (Vielfache von 2, hier 16) ist, dass der Microcontroller mit diesen leichter rechnen kann. Dies ist in dem untenstehenden Code umgesetzt. Die Umrechnung des Temperatur-Arrays ''TEMP[]'' ist in obigen Excel-File beschrieben. 
 + 
 + 
 +</WRAP> 
 +<WRAP column 35%> 
 +<imgcaption BildNr4 | Bildliche Erklärung für die "Umrechnung der Datenblattwerte"> 
 +</imgcaption> 
 +{{drawio>BildUmrechnungDSwerte.svg}} 
 +</WRAP> 
 +</WRAP> 
 + 
  
 ~~PAGEBREAK~~ ~~CLEARFIX~~ ~~PAGEBREAK~~ ~~CLEARFIX~~
  
-==== Übung ====+===== Übung =====
  
 --> I. Vorarbeiten # --> I. Vorarbeiten #
-  - Laden Sie folgende Datei herunter:  +  - Laden Sie folgenden Dateien herunter:  
-    - {{microcontrollertechnik:8_temperature_0.5.15.simu}}+    - {{microcontrollertechnik:8_temperature_1.0.0.sim1}} 
     - {{microcontrollertechnik:8_temperature.hex}}     - {{microcontrollertechnik:8_temperature.hex}}
     - {{microcontrollertechnik:lcd_lib_de.h}}     - {{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!
 +  * Das Display ist nunan einem anderen Port um den Analog-Digial-Wandler am Port C zu nutzen. \\ Deshalb muss der Treiber ''lcd_lib_de.h'' wiefolgt angepasst werden:
 +    * Zeile 26: ''#define F_CPU **12288000UL**''
 +    * Zeile 39: ''#define PIN_EN PD**4**''
 +    * Zeile 40: ''#define PIN_RS PD**7**''
 +    * Zeile 44: ''#define DDR_DATA DDR**D**''
 +    * Zeile 46: ''#define PORT_DATA PORT**D**''
 +
 +</callout>
 +\\ \\
 +
 +
  
 <-- <--
Zeile 207: Zeile 281:
 <-- <--
  
---> III. Eingabe in Atmel Studio #+--> III. Eingabe in Microchip Studio # 
 + 
 <WRAP group><WRAP column 40%><sxh c; first-line: 1> <WRAP group><WRAP column 40%><sxh c; first-line: 1>
 /* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------
- +  
- Experiment 8: Temperaturmessung mit MiniMEXLE + Experiment 8:  Temperaturmessung 
  =============    ===============================  =============    ===============================
 + 
  Dateiname: 8_Temperature.c  Dateiname: 8_Temperature.c
 +  
 + Autoren    : Peter Blinzinger
 +              Prof. G. Gruhler  (Hochschule Heilbronn, Fakultaet T1)
 +              D. Chilachava     (Georgische Technische Universitaet)
 +               
 + Datum      : 01.05.2020
 +  
 + Version    : 1.1
 +  
 + Hardware:  MEXLE2020 Ver. 1.0 oder hoeher
 +            AVR-USB-PROGI Ver. 2.0
    
- Autoren Peter Blinzinger + Software Entwicklungsumgebung: AtmelStudio 7.0 
-   ProfG. Gruhler (Hochschule Heilbronn, Fakultaet T1) +            C-CompilerAVR/GNU C Compiler 5.4.0
-   D. Chilachava (Georgische Technische Universitaet) +
-    +
- Datum    01.05.2020+
    
- Version : 1.1+ 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: s)
    
- 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  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. +      und Umrechnung und einen zusaetzlichen Takt von 1 s fuer die Anzeige. 
- +  
- Verwendung von Hardware-Timer 0 und T0 Overflow-Interrupt. +        Verwendung von Hardware-Timer 0 und T0 Overflow-Interrupt. 
- Frequenzen: Quarzfrequenz      12,288 MHz. +        Frequenzen: Quarzfrequenz                   12,288 MHz. 
- Timer-Vorteiler /  8 =>   1,536 MHz +        Timer-Vorteiler     /  8    =>   1,536 MHz 
- Hardware-Timer     /256 =>   6 kHz / 166 µs +        Hardware-Timer      /256    =>   6 kHz / 166 µs 
- Software-Vorteiler  / 60 => 100 Hz  /  10 ms +        Software-Vorteiler  / 60    => 100 Hz  /  10 ms 
- Hundertstel-Zaehler / 10 =>  10 Hz  / 100 ms +        Hundertstel-Zaehler / 10    =>  10 Hz  / 100 ms 
- Zehntel-Zaehler / 10 =>   1 Hz  /   1 s +        Zehntel-Zaehler     / 10    =>   1 Hz  /   1 s 
 + 
  2) Das Modul "AD-Wandlung" wird durch den Takt 100 ms aufgerufen.  2) Das Modul "AD-Wandlung" wird durch den Takt 100 ms aufgerufen.
     Der AD-Wandler wird mit einem internen Takt von 96 kHz betrieben.     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+      Im Modul wird eine einzelne AD-Wandlung des Kanals ADC0 mit 10 Bit
     Aufloesung gestartet. Dort ist der NTC des Boards mit Vorwiderstand     Aufloesung gestartet. Dort ist der NTC des Boards mit Vorwiderstand
     als temperaturabhaengiger Spannungsteiler bzw. Potentiometer angeschlossen.     als temperaturabhaengiger Spannungsteiler bzw. Potentiometer angeschlossen.
     Als Referenzspannung wird die 5V-Versorgung verwendet.     Als Referenzspannung wird die 5V-Versorgung verwendet.
-    Das Ergebnis wird in der globalen Variable ad_wert gespeichert. +    Das Ergebnis wird in der globalen Variable tValue gespeichert. 
 + 
  3) Das Modul "Umrechnung" wird nach der AD-Wandlung alle 100 ms gestartet.  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     Der Ergebniswert des Moduls "AD_Wandlung" wird mit Hilfe einer Tabelle in
     einen entsprechenden Temperaturwert umgerechnet. In der Tabelle sind      einen entsprechenden Temperaturwert umgerechnet. In der Tabelle sind 
     Temperaturwerte ueber aequidistante (Abstand = 16) AD-Werte aufgetragen.     Temperaturwerte ueber aequidistante (Abstand = 16) AD-Werte aufgetragen.
-   Die Werte dazwischen werden mit linearer Interpolation ermittelt.+      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
-   maximalen Temperatur verglichen und der Maximalwert optional angepasst. +      maximalen Temperatur verglichen und der Maximalwert optional angepasst. 
 + 
  4) Das Modul "Anzeigetreiber" ist an den 1 s-Takt gekoppelt. Damit wird ein  4) Das Modul "Anzeigetreiber" ist an den 1 s-Takt gekoppelt. Damit wird ein
     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 noch eine +      Zwischen AD-Wandlung / Umrechnung und Anzeige kann spaeter noch eine 
-    Mittelwertsbildung mit 10 Werten eingefuegt werden. +      Mittelwertsbildung mit 10 Werten eingefuegt werden. 
- +  
  Die Kopplung der Module wird ueber global definierte Variable realisiert:  Die Kopplung der Module wird ueber global definierte Variable realisiert:
- +  
-    1-Bit-Variable: Takt 100 ms: Taktgenerator => AD-Wandlung +      1-Bit-Variable:   Takt 100 ms:    Taktgenerator => AD-Wandlung 
-   => Umrechnung +                                                      => Umrechnung 
- Takt    s:    Taktgenerator => Anzeigetreiber +                        Takt    s:    Taktgenerator => Anzeigetreiber 
- +  
-   16-Bit-Variable: ad-wert AD-Wandlung => Umrechnung +      16-Bit-Variable:  ad-wert         AD-Wandlung => Umrechnung 
- t-wert Umrechnung => Anzeige +                        t-wert          Umrechnung => Anzeige 
- tmax-wert Umrechnung => Anzeige +                        tmax-wert       Umrechnung => Anzeige 
 + 
 // ----------------------------------------------------------------------------*/ // ----------------------------------------------------------------------------*/
 + 
 // Deklarationen ============================================================== // Deklarationen ==============================================================
 + 
 // Festlegung der Quarzfrequenz // Festlegung der Quarzfrequenz
-#ifndef F_CPU // optional definieren +#ifndef F_CPU                   // optional definieren 
-#define F_CPU 12288000UL // MiniMEXLE mit 12,288 MHz Quarz +#define F_CPU 12288000UL        // Atmega328 mit 12,288 MHz Quarz 
-#endif  +#endif                           
 + 
 // Include von Header-Dateien // Include von Header-Dateien
-#include <avr/io.h> // Header-Dateien zum ATmega88 +#include <avr/io.h>               // Header-Dateien zum ATmega328 
-#include <avr/interrupt.h> // Header-Datei fuer Interrupts +#include <avr/interrupt.h>        // Header-Datei fuer Interrupts 
-#include <util/delay.h> // Header-Datei fuer Wartezeit +#include <util/delay.h>           // Header-Datei fuer Wartezeit 
-#include "lcd_lib_de.h" // Header-Datei fuer LCD-Anzeige +#include "lcd_lib_de.h"         // Header-Datei fuer LCD-Anzeige 
 + 
 // Konstanten // Konstanten
-#define VORTEILER_WERT 60 +#define PRESCALER_VAL       60          // Faktor Vorteiler = 60 
-#define HUNDERTSTEL_WERT 10 +#define CYCLE10MS_MAX       10          // Faktor Hundertstel = 10 
-#define ZEHNTEL_WERT 10+#define CYCLE100MS_MAX      10          // Faktor Zehntel = 10 
  
 #define ASC_NULL            0x30        // Das Zeichen '0' in ASCII #define ASC_NULL            0x30        // Das Zeichen '0' in ASCII
 #define ASC_FULL_STOP       0x2E        // Das Zeichen ':' in ASCII #define ASC_FULL_STOP       0x2E        // Das Zeichen ':' in ASCII
- +  
-const int TEMP[45]  =  {521,499,479,459,440,422,404,388,371,354, +const int   TEMP[45]    =  {521,499,479,459,440,422,404,388,371,354, 
- 338,323,308,293,279,264,250,236,221,207, +                            338,323,308,293,279,264,250,236,221,207, 
- 193,179,165,151,137,122,108,93,78,63, +                            193,179,165,151,137,122,108,93,78,63, 
- 48,32,15,-1,-19,-38,-56,-77,-97,-121, +                            48,32,15,-1,-19,-38,-56,-77,-97,-121, 
- -145,-173,-202,-237,-278}; +                            -145,-173,-202,-237,-278}; 
- +  
- // Die Tabellenwerte sind in 1/10 °C angegeben +                            // Die Tabellenwerte sind in 1/10 °C angegeben 
- // Der erste Tabellenwert entspricht einem AD-Wert +                            // Der erste Tabellenwert entspricht einem AD-Wert 
- // von 256. Die Abstaende der AD-Werte sind 16 +                            // von 256. Die Abstaende der AD-Werte sind 16 
 + 
 // Variable // Variable
-unsigned char vorteiler VORTEILER_WERT; // Zaehlvariable Vorteiler +unsigned char softwarePrescaler PRESCALER_VAL   // Zaehlvariable Vorteiler 
-unsigned char  hundertstel HUNDERTSTEL_WERT+unsigned char cycle10msCount    CYCLE10MS_MAX   // Zaehlvariable Hundertstel 
-unsigned char zehntel    ZEHNTEL_WERT+unsigned char cycle100msCount   CYCLE100MS_MAX  // Zaehlvariable Zehntel 
- +  
-unsigned int adcValue = 0; // Variable fuer den AD-Wandlungswert +unsigned int  adcValue    = 0;    // Variable fuer den AD-Wandlungswert 
-int tValue = 0; // Variable fuer die Temperatur (in 1/10 °C) +int           tValue      = 0;    // Variable fuer die Temperatur (in 1/10 °C) 
-int tValueMax =-300; // Variable fuer maximale Temperatur (1/10 °C) +int           tValueMax   =-300;  // Variable fuer maximale Temperatur (1/10 °C) 
- +  
-bool  takt10ms; // Bit-Botschaft alle 10 ms +bool          cycle10msActive   // Bit-Botschaft alle 10 ms 
-bool  takt100ms; // Bit-Botschaft alle 100 ms +bool          cycle100msActive  // Bit-Botschaft alle 100 ms 
-bool takt1s; // Bit-Botschaft alle 1s +bool          cycle1sActive     // Bit-Botschaft alle 1s 
 + 
 //Funktionsprototypen //Funktionsprototypen
 void initTimer0 (void); void initTimer0 (void);
Zeile 351: Zeile 428:
 void refreshDisplayTemp(int tempValue, char line, char pos); void refreshDisplayTemp(int tempValue, char line, char pos);
 void refreshDisplay (void); void refreshDisplay (void);
 + 
 // Hauptprogramm ============================================================== // Hauptprogramm ==============================================================
 int main () int main ()
 { {
- initDisplay(); // Initialisierung LCD-Anzeige +    initDisplay();              // Initialisierung LCD-Anzeige 
- initTimer0(); // Initialisierung von Timer0 +    initTimer0();               // Initialisierung von Timer0 
- initAdc(); // Initialisierung des AD-Wandlers +    initAdc();                  // Initialisierung des AD-Wandlers 
- +  
- sei(); // generell Interrupts einschalten +    sei();                      // generell Interrupts einschalten 
-  +      
- // Hauptprogrammschleife __________________________________________________ +    // Hauptprogrammschleife __________________________________________________ 
- +  
- while(1) // unendliche Warteschleife mit Aufruf der +    while(1)                    // unendliche Warteschleife mit Aufruf der 
- // Funktionen abhaengig von Taktbotschaften +                                // Funktionen abhaengig von Taktbotschaften 
-+    
- if(takt100ms) // Durchfuehrung der Funktion einmal pro 100ms +        if(cycle100msActive   // Durchfuehrung der Funktion einmal pro 100ms 
- +        
- takt100ms = 0; // Taktbotschaft zuruecksetzen +            cycle100msActive = 0;// Taktbotschaft zuruecksetzen 
- doAdc(); // Ausfuehrung des Modules der A/D-Wandlung  +            doAdc();            // Ausfuehrung des Modules der A/D-Wandlung  
- calculateTemp(); // Ausfuehrung des Modules der Umrechnung +            calculateTemp();    // Ausfuehrung des Modules der Umrechnung 
- +        
- +  
- if(takt1s) // Durchfuehrung der Anzeige einmal pro 1s +        if(cycle1sActive      // Durchfuehrung der Anzeige einmal pro 1s 
- +        
- takt1s = 0; // Taktbotschaft zuruecksetzen +            cycle1sActive = 0;  // Taktbotschaft zuruecksetzen 
- refreshDisplay(); // Ausfuehrung des Modules der Anzeige +            refreshDisplay();   // Ausfuehrung des Modules der Anzeige 
- +        
- }+    }
 } }
- +  
 + 
 // Timer Initialisierung ============================================================== // Timer Initialisierung ==============================================================
 // //
Zeile 388: Zeile 465:
 void initTimer0() void initTimer0()
 { {
- TCCR0A |= (0<<WGM00)  +    TCCR0A  |= (0<<WGM00)  
- |  (0<<WGM01); // Timer 0 auf "Normal Mode" schalten +            |  (0<<WGM01);            // Timer 0 auf "Normal Mode" schalten 
- TCCR0B |= (0<<WGM02)  +    TCCR0B  |= (0<<WGM02)  
- |  (1<<CS01 ); // mit Prescaler /8 betreiben +            |  (1<<CS01 );            // mit Prescaler /8 betreiben 
- TIMSK0 |= (1<<TOIE0); // Overflow-Interrupt aktivieren+    TIMSK0  |= (1<<TOIE0);            // Overflow-Interrupt aktivieren
 } }
 + 
 // ADC-Initialisierung ============================================================== // ADC-Initialisierung ==============================================================
 //  // 
Zeile 401: Zeile 478:
 // Abfrage des ADC0 (NTC-Spannungsteiler) // Abfrage des ADC0 (NTC-Spannungsteiler)
 // Referenzspannung = analoge Versorgung Avcc // Referenzspannung = analoge Versorgung Avcc
-void initAdc ()+void initAdc()
 { {
- ADMUX   |= (1<<REFS0); // Vref =AVCC; ADC0 +    ADMUX   |= (1<<REFS0);        // Vref =AVCC; ADC0 
- +  
- ADCSRA |= (1<<ADPS0) +    ADCSRA  |= (1<<ADPS0) 
- |  (1<<ADPS1) +            |  (1<<ADPS1) 
- |  (1<<ADPS2) +            |  (1<<ADPS2) 
- |  (1<<ADEN); // Teiler 128; ADC ON+            |  (1<<ADEN); // Teiler 128; ADC ON 
 +    (void) ADCH;                // erster Wert ist "wegzuwerfen"
 } }
- +  
 + 
 // Timer-Service Routine ============================================================== // Timer-Service Routine ==============================================================
 //  // 
Zeile 418: Zeile 496:
 // werden von Timer 0 ausgeloest (Interrupt Nr. 1) // werden von Timer 0 ausgeloest (Interrupt Nr. 1)
 // //
-// Veraenderte Variable: vorteiler +// Veraenderte Variable:    vorteiler 
-// hunderstel +//                      hunderstel 
-// zehntel+//                      zehntel
 // //
-// Ausgangsvariable: takt10ms +// Ausgangsvariable:    takt10ms 
-// takt100ms +//                      takt100ms 
-// takt1s +//                      takt1s 
 + 
 ISR (TIMER0_OVF_vect) ISR (TIMER0_OVF_vect)
 { {
- --vorteiler; // Vorteiler dekrementieren +    --softwarePrescaler; // Vorteiler dekrementieren 
- if (vorteiler==0) // wenn 0 erreicht: 10ms abgelaufen +    if (softwarePrescaler==0) // wenn 0 erreicht: 10ms abgelaufen 
-+    
- vorteiler VORTEILER_WERT; // Vorteiler auf Startwert +        softwarePrescaler PRESCALER_VAL     // Vorteiler auf Startwert 
- takt10ms = true; // Botschaft 10ms senden +        cycle10msActive = true;                 // Botschaft 10ms senden 
- --hundertstel; // Hundertstelzaehler dekrementieren +        --cycle10msCount; // Hundertstelzaehler dekrementieren 
- +  
- if (hundertstel==0) // wenn 0 erreicht: 100ms abgelaufen +        if (cycle10msCount==0) // wenn 0 erreicht: 100ms abgelaufen 
- +        
- hundertstel HUNDERTSTEL_WERT; // Teiler auf Startwert +            cycle10msCount CYCLE10MS_MAX; // Teiler auf Startwert 
- takt100ms = true; // Botschaft 100ms senden +            cycle100msActive = true; // Botschaft 100ms senden 
- --zehntel+            --cycle100msCount
- +  
- if (zehntel==0) // Zehntelzaehler dekrementieren +            if (cycle100msCount==0)             // Zehntelzaehler dekrementieren 
- +            
- zehntel ZEHNTEL_WERT; // Teiler auf Startwert +                cycle100msCount CYCLE100MS_MAX;// Teiler auf Startwert 
- takt1s = true; // Botschaft 1s senden +                cycle1sActive = true; // Botschaft 1s senden 
- +            
- +        
- }+    }
 } }
 + 
 // ADWandlung ============================================================== // ADWandlung ==============================================================
 //  // 
Zeile 456: Zeile 534:
 void doAdc() void doAdc()
 { {
- ADCSRA |= (1<<ADSC); // Wandlung starten +    ADCSRA |= (1<<ADSC); // Wandlung starten 
- while (ADCSRA & (1<<ADSC)); // Ende der Wandlung abwarten +    while (ADCSRA & (1<<ADSC)); // Ende der Wandlung abwarten 
- +  
- adcValue = ADCL + (ADCH<<8); // 10-Bit-Wert berechnen+    adcValue = ADCL + (ADCH<<8); // 10-Bit-Wert berechnen
  // ADCL muss vor ADCH stehen!!  // ADCL muss vor ADCH stehen!!
  // siehe Datenblatt des ATmega 328  // siehe Datenblatt des ATmega 328
 } }
 + 
 // Umrechnung ============================================================== // Umrechnung ==============================================================
 //  // 
Zeile 469: Zeile 547:
 void calculateTemp () void calculateTemp ()
 { {
- unsigned char index; // Tabellenindex fuer Temperaturtabelle +    unsigned char index;                // Tabellenindex fuer Temperaturtabelle 
- unsigned char steps; // Abstand zum naechstkleineren Wert +    unsigned char steps;                // Abstand zum naechstkleineren Wert 
- // der AD-Werte der Temperaturtabelle +                                        // der AD-Werte der Temperaturtabelle 
-  +      
- index = (adcValue-256)/16; // Indexberechnung (Zeiger in Tabelle)  +    index   = (adcValue-256)/16;        // Indexberechnung (Zeiger in Tabelle)   
- steps = (adcValue-256)%16; // Rest fuer Tabellen-Interpolation  +    steps   = (adcValue-256)%16;        // Rest fuer Tabellen-Interpolation      
-  +      
- tValue = steps * (TEMP[index+1] - TEMP[index])/16 + TEMP[index];  +    tValue = steps * (TEMP[index+1] - TEMP[index])/16 + TEMP[index];     
- // Temperaturwert berechnen +                                        // Temperaturwert berechnen 
- +  
- if(tValue>=tValueMax) // aktueller Wert mit Maximalwert +    if(tValue>=tValueMax)               // aktueller Wert mit Maximalwert 
-+    
- tValueMax = tValue; // vergleichen und ggf. ersetzen +        tValueMax = tValue;             // vergleichen und ggf. ersetzen 
- }+    }
 } }
 +  
 +// Anzeigefunktion ============================================================== 
 +// 
 +// Der aktuelle Temperatur und die maximale Temperatur werden ausgegeben 
 + void refreshDisplay() 
 +
 +    refreshDisplayTemp(tValue,      0, 9);      // aktuelle Temperatur ab Position 0,9 
 +    refreshDisplayTemp(tValueMax,   1, 9);      // maximale Temperatur ab Position 1,9 
 +
 + 
 // Anzeigetreiber fuer Temperaturanzeige ============================================================== // Anzeigetreiber fuer Temperaturanzeige ==============================================================
 // //
Zeile 493: 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.+//         dann nochmals Integer-Teilung (/10) dieses Restes.
 // Einer:  Modulo-Ermittlung (%10), d.h. Rest bei der Teilung durch 10. // Einer:  Modulo-Ermittlung (%10), d.h. Rest bei der Teilung durch 10.
 // //
Zeile 499: Zeile 586:
 void refreshDisplayTemp(int tempValue, char line, char pos) void refreshDisplayTemp(int tempValue, char line, char pos)
 { {
- lcd_gotoxy(line, pos); // Startposition fuer Temperatur-Wert +    lcd_gotoxy(line, pos);                  // Startposition fuer Temperatur-Wert 
- if (tempValue>=0)  // zuerst Vorzeichen: ' ' oder '-' +    if (tempValue>=0)                        // zuerst Vorzeichen: ' ' oder '-' 
-+    
- lcd_putc(' '); +        lcd_putc(' '); 
-+    
- else +    else 
-+    
- lcd_putc('-'); +        lcd_putc('-'); 
- tempValue = -tempValue; // Vorzeichenumkehr bei negativer Zahl +        tempValue = -tempValue;             // Vorzeichenumkehr bei negativer Zahl 
-+    
- lcd_putc   (tempValue/100 + ASC_NULL); // Hunderter ausgeben (°C Zehner) +    lcd_putc   (tempValue/100 + ASC_NULL);  // Hunderter ausgeben (°C Zehner) 
- tempValue = tempValue%100; +    tempValue = tempValue%100; 
- lcd_putc   (tempValue/10  + ASC_NULL); // Zehner ausgeben (°C Einer) +    lcd_putc   (tempValue/10  + ASC_NULL);  // Zehner ausgeben (°C Einer) 
- lcd_putc   (ASC_FULL_STOP); // Punkt ausgeben +    lcd_putc   (ASC_FULL_STOP);             // Punkt ausgeben 
- lcd_putc   (tempValue%10  + ASC_NULL); // Einer ausgeben (°C Zehntel)+    lcd_putc   (tempValue%10  + ASC_NULL);  // Einer ausgeben (°C Zehntel)
 } }
- + 
-// Anzeigefunktion ============================================================== +
-// +
-// Der aktuelle Temperatur und die maximale Temperatur werden ausgegeben +
- void refreshDisplay() +
-+
- refreshDisplayTemp(tValue, 0, 9); // aktuelle Temperatur ab Position 0,9 +
- refreshDisplayTemp(tValueMax, 1, 9); // maximale Temperatur ab Position 1,9 +
-+
 // Initialisierung Display-Anzeige ============================================================== // Initialisierung Display-Anzeige ==============================================================
 // //
-void initDisplay() // Start der Funktion+void initDisplay()                  // Start der Funktion
 { {
- lcd_init(); // Initialisierungsroutine aus der lcd_lib +    lcd_init();                     // Initialisierungsroutine aus der lcd_lib 
-  +                      
- lcd_displayMessage("- Experiment 8 -",0,0); // Ausgabe in erster Zeile +    lcd_displayMessage("- Experiment 8 -",0,0); // Ausgabe in erster Zeile 
- lcd_displayMessage("  Temperature   ",1,0); // Ausgabe in zweiter Zeile +    lcd_displayMessage("  Temperature   ",1,0); // Ausgabe in zweiter Zeile 
- +  
- _delay_ms(2000); // Wartezeit nach Initialisierung +    _delay_ms(2000);                // Wartezeit nach Initialisierung 
- +  
- lcd_displayMessage("Temp.         ßC",0,0); // Ausgabe in erster Zeile +    lcd_displayMessage("Temp.         ßC",0,0); // Ausgabe in erster Zeile 
- lcd_displayMessage("Maximum       ßC",1,0); // Ausgabe in zweiter Zeile +    lcd_displayMessage("Maximum       ßC",1,0); // Ausgabe in zweiter Zeile 
- // "ßC" wird als °C dargestellt  +                                                // "ßC" wird als °C dargestellt  
-} // Ende der Funktion +                                
 </sxh> </sxh>
 </WRAP><WRAP column 55%> </WRAP><WRAP column 55%>
Zeile 551: 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 ''TEMP[]'' angelegt, in denen die Temperaturen in 1/10 °C abgespeichert sind. Beispielsweise entspricht der erste Eintrag''521'' einer Temperatur von $52,1°C$. Der erste Eintrag wird bei einem ADC-Wert von 256 benötigt. Der zweite Eintrag entspricht $49,9°C$ und wird bei einem ADC-Wert von 256+16 = 272 benötigt. Die weiteren entsprechend. Die Herleitung erfolgt wie unter [[8._temperatur#Messsignal-Digitalisierung und Auswertung]] vorgegeben.  \\ \\ \\  +  - Es wird eine zusätzliches Array ''TEMP[]'' angelegt, in denen die Temperaturen in 1/10 °C abgespeichert sind. Beispielsweise entspricht der erste Eintrag''521'' einer Temperatur von $52,1°\rm C$. Der erste Eintrag wird bei einem ADC-Wert von 256 benötigt. Der zweite Eintrag entspricht $49,9° \rm C$ und wird bei einem ADC-Wert von 256+16 = 272 benötigt. Die weiteren entsprechend. Die Herleitung erfolgt wie unter [[8_temperatur#Messsignal-Digitalisierung und Auswertung]] vorgegeben.  \\ \\ \\  
-  - Bei den Variablen entsprechen einige denen der letzten Programme. \\ \\ \\ \\ \\ \\+  - Bei den Variablen entsprechen einige denen der letzten Programme. \\ \\ \\ \\
   - Die Variable ''adcValue'' wird mit dem digitalisierten ADC-Wert befüllt.   - Die Variable ''adcValue'' wird mit dem digitalisierten ADC-Wert befüllt.
   - Die Variable ''tValue'' beinhaltet die aktuelle Temperatur in 1/10°C und mit Vorzeichen.   - Die Variable ''tValue'' beinhaltet die aktuelle Temperatur in 1/10°C und mit Vorzeichen.
   - Die Variable ''tValueMax'' beinhaltet die höchsten seit Start gemessene Temperatur in 1/10°C und mit Vorzeichen (entspricht einem Schleppzeiger). \\ \\ \\ \\   - Die Variable ''tValueMax'' beinhaltet die höchsten seit Start gemessene Temperatur in 1/10°C und mit Vorzeichen (entspricht einem Schleppzeiger). \\ \\ \\ \\
-  - Bei den Funktionsprototypen sind einige bekannte Unterprogramme vorhanden. Details werden weiter unten erklärt. \\ \\ \\ \\ +  - Bei den Funktionsprototypen sind einige bekannte Unterprogramme vorhanden. Details werden weiter unten erklärt. \\ \\ \\ \\ \\
  
 ''Hauptprogramm ========================='' ''Hauptprogramm =========================''
  
-  - Das Hauptprogramm ähnelt sehr stark dem [[4._up_down_counter|Up/Down Counter]], wobei die Initialisierung des Timers in eine Unterfunktion ''initTimer0()'' ausgegliedert wurde. Die Unterfunktion ''initAdc'' initialisiert den ADC. \\ \\ \\ \\ \\  +  - Das Hauptprogramm ähnelt sehr stark dem [[4_up_down_counter|Up/Down Counter]], wobei die Initialisierung des Timers in eine Unterfunktion ''initTimer0()'' ausgegliedert wurde. Die Unterfunktion ''initAdc'' initialisiert den ADC. \\ \\ \\ \\ \\  
-  - In der Endlosschleife sind auf der ersten Ebene wieder nur If-Abfragen zu den Flags ''takt10ms'' und ''takt100ms'' zu finden. \\ \\ +  - In der Endlosschleife sind auf der ersten Ebene wieder nur If-Abfragen zu den Flags ''cycle100msActive'' und ''cycle1sActive'' zu finden. \\ \\ 
-    - Alle $100ms$ (bzw. wenn das entsprechende Flag gesetzt wird) wird das Flag zurückgesetzt und das Unterprogramm ''doAdc()'' sowie ''calculateTemp()'' aufgerufen \\ \\ \\  +    - Alle $100~\rm ms$ (bzw. wenn das entsprechende Flag gesetzt wird) wird das Flag zurückgesetzt und das Unterprogramm ''doAdc()'' sowie ''calculateTemp()'' aufgerufen \\ \\ \\  
-    - Alle $1s$ (bzw. wenn das entsprechende Flag gesetzt wird) wird das Flag zurückgesetzt und das Unterprogramm ''refreshDisplay()'' aufgerufen \\ \\ \\ \\ \\ +    - Alle $1~\rm s$ (bzw. wenn das entsprechende Flag gesetzt wird) wird das Flag zurückgesetzt und das Unterprogramm ''refreshDisplay()'' aufgerufen \\ \\ \\ \\ \\ \\ \\ \\ 
-''Timer Initialisierung ========================='' +''Timer Initialisierung ============'' 
-  - Die Timer Initialisierung ist dem Programm [[4._up_down_counter|Up/Down Counter]] entlehnt und wird hier nicht weiter erklärt.  \\ \\ \\ \\ \\ \\ \\  +  - Die Timer Initialisierung ist dem Programm [[4_up_down_counter|Up/Down Counter]] entlehnt und wird hier nicht weiter erklärt.  \\ \\ \\ \\ \\ \\ \\ \\
  
-''ADC Initialisierung ========================='' \\ \\ \\ \\ \\  +''ADC Initialisierung =============='' 
-  - Über den Multiplexer ''ADMUX'' wird die Referenzspannung für die AD-Wandlung vorgegeben. Hier wird auf die interne Referenz, gegeben durch Avcc, geschalten +
-  - ''ADCSRA'' ist das "ADC Control and Status Register A". \\ Die Bits ''ADPS0'',''ADPS1'',''ADPS2'' geben den Teiler für die Wandlerfrequenz vor (hier 1/128 von F_CPU) \\ ''ADEN'' aktiviert den Wandler \\ \\ \\ +
  
-''Timer-Service Routine ========================='' \\ \\ \\ \\ \\ \\  +  - Über den Multiplexer ''<fc #008000>ADMUX</fc>'' wird die Referenzspannung für die AD-Wandlung vorgegeben. Hier wird auf die interne Referenz, gegeben durch Avcc, geschalten 
-  - DieTimer-Service Routine ist dem Programm [[4._up_down_counter|Up/Down Counter]] entlehnt und wird hier nicht weiter erklärt \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ +  - ''<fc #3333FF>ADCSRA</fc>'' ist das "ADC Control and Status Register A"\\ Die Bits ''ADPS0'',''ADPS1'',''ADPS2'' geben den Teiler für die Wandlerfrequenz vor (hier 1/128 von F_CPU) \\ ''ADEN'' aktiviert den Wandler. Die eigentliche Wandlung muss aber immer wieder gestartet werden.   
 +  - WICHTIG: der erste digitalisierte Wert nach Initialisierung und auch nach Kanalwechsel muss ignoriert werdenBei Änderungen der ADC-Konfiguration entspricht diese bei AVR-Microcontrollern noch keinem validen Wert!
  
-'' ADWandlung =============================================================='' +<WRAP 470px> 
-\\ \\ +<imgcaption BildNr10 | Schaltung des AD-Wandlers im 328> 
-  - In dieser Funktion werden zunächst die Stellungen aller Taster eingelesen (vgl. ''counterCounting(void)'' bei [[4._up_down_counter|Up/down Counter]]). \\ \\ \\ +</imgcaption> 
-  - 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.  \\ \\ \\ \\ \\ \\ \\+{{drawio>AdcWandler.svg}} 
 +</WRAP> 
 +\\  
 +''Timer-Service Routine =========================''   
 +  - Die Timer-Service Routine ist dem Programm [[4_up_down_counter|Up/Down Counter]] entlehnt und wird hier nicht weiter erklärt. \\ \\  \\ \\
  
-'' Umrechnung ==========================='' +'' ADWandlung =============================================================='' \\ \\ 
-  - Hierüber wird die Uhrzeit in der ersten Zeile im Format hh:mm:ss ausgegeben+  - Das Bit ''ADSC'' startet die AD-Wandlung 
-  - Ähnlich zum Counter werden die zweistelligen Werte mit Division durch 10 und dessen Rest in zwei einzelne Ziffern gewandelt +  - Während die AD-Wandlung läuft bleibt das Bit ''ADSC=1''. Ist die Wandlung beendet schaltet das Bit auf 0
-\\ \\ \\ \\ \\ \\ \\ \\ \\+  - Das Register ''ADCL'' enthält die untersten (8) Bits. Das Register ''ADCH'' enthält die oberen (2) Bits, welche hier um 8 Bitt nach links verschoben werden (also mit 256 multipliziert wird). \\ __Wichtig__:  
 +    - immer erst ADCL auslesen, dann ADCH.   
 +    - immer auch ADCH auslesen, selbst wenn es nicht benötigt wird. \\ \\
  
-''Anzeigetreiber fuer Temperaturanzeige ==============================='' +'' Umrechnung ==========================='' \\ \\ \\ 
-  - Die Zähl-Funktion ''uhrZaehlen()'' ist ganz ähnlich aufgebaut zur Interrupt-Service-Routine +  - Die Variable ''index'' gibt die Position in dem Array ''TEMP[]'' an. In ''TEMP[]'' sind nur Stützpunkte für jeden 16. Wert angegeben. 
-  - Zunächst wird ein Schaltwechsel am Ausgang mit dem Lautsprecher ausgegeben, um einen Knackton zu erzeugen +  - Die Variable ''steps'' gibt den Zwischenschritt zwischen zwei Stützpunkte in Sechzehntel an 
-  - Dann werden die Sekunden hochgezählt +  - ''tValue'' ermittelt die Lineare Interpolation zwischen zwei Schritten  
-  - ist das Maximum erreicht, so wird der Sekunden-Wert zurückgesetzt und der Minuten-Wert um eins hochgezählt. +  - In ''tValue'' wird der Schleppzähler gespeichert 
-  - 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 +
-\\ \\ \\ \\ \\ \\ \\ \\ \\+
  
 ''Anzeigefunktion ==============================='' ''Anzeigefunktion ===============================''
-  - Die Zähl-Funktion ''uhrZaehlen()'' ist ganz ähnlich aufgebaut zur Interrupt-Service-Routine +  - Da zwei Temperaturen ausgegeben werden müssen, ruft die Anzeigefunktion eine weitere Unterfunktion aufwelche mit unterschiedlichen Werten gespeist wird 
-  - 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 erreichtso wird der Sekunden-Wert zurückgesetzt und der Minuten-Wert um eins hochgezählt. +''Anzeigetreiber fuer Temperaturanzeige ==============================='' \\ \\ \\ \\ \\  
-  - ebenso wird beim Maximum des Minuten-Serts dieser zurückgesetzt und der Stundenwert hochgezählt. +  - 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 \\ \\ \\ \\  
-  - beim Maximum des Stunden-Werts wird dieser wieder auf Null gesetzt +  - 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. \\ \\
-\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\+
  
 ''Initialisierung Display-Anzeige ========================='' ''Initialisierung Display-Anzeige =========================''
   - Die Funktion ''initDisplay()'' wird zu Beginn des Programms aufgerufen und führt zunächst die Initialisierung des Displays aus.   - Die Funktion ''initDisplay()'' wird zu Beginn des Programms aufgerufen und führt zunächst die Initialisierung des Displays aus.
   - 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 angezeigt.+  - Nach zwei Sekunden wird die Display-Vorlage für die Temperatur angezeigt.
  
 </WRAP></WRAP> </WRAP></WRAP>
Zeile 624: 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:grundlagen_und_grundbegriffe#Temperaturabhängigkeit von Widerständen]]   - 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:grundlagen_und_grundbegriffe#Temperaturabhängigkeit von Widerständen]]
 +  - Entwickeln Sie ein Programm, welches mit einem Poti über den ADC des Atmega328 den Cursor bzw. ein Zeichen auf dem Display bewegt.
  
 <-- <--