Forum: Mikrocontroller und Digitale Elektronik Problem mit Temperaturmesssystem


von Robin F. (gehacktes)


Lesenswert?

Schönen guten Morgen,

unzwar habe ich vor einiger Zeit einen Temperaturmesssystem gebaut, wo 
per Tastendruck ein Messinterval eingestellt wird und nach ablauf dieser 
eingestellten Zeit die Temperatur, das Datum und die Uhrzeit auf eine 
SD-Karte gespeichert wird. Jetzt ist mir nach einigen Messungen 
aufgefallen, dass nach dem Starten des Gerätes die erste Messung immer 
nach der hälfte der eingestellten Zeit gespeichert wird und jede weitere 
korrekt nach der eingestellten Zeit. Das darf auf keinen fall geschehen.

Ich weiß nicht woran das liegt, ich habe das Programm rauf und runter 
studiert und geguckt ob ich irgendwo einen Fehler gemacht habe aber ich 
kann keinen finden.

Es wäre nett wenn Ihr nochmal über das Programm gucken könnt und evtl. 
einen Fehler finden könnt wodurch dieses phänomen zu stande kommt.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#include <Wire.h>
5
#include <OneWire.h>
6
#include <LiquidCrystal.h>
7
#include <SD.h>
8
#include "RTClib.h"
9
10
LiquidCrystal lcd ( A2, A1, 6, 5, 2, 3 ); 
11
OneWire  ds ( 9 ); 
12
const int chipSelect = 4;
13
RTC_DS1307 RTC;
14
15
double Wert  = 1;
16
byte secound = 0;                                    // Timer auf 0 setzen
17
byte timer = 0;                                      // Timer auf 0 setzen
18
int l = 0;
19
const int hoch = 7;                                  // Taster für Hochzählen defenieren
20
const int runter = 8;                                // Taster für Runterzählen defenierern
21
22
void setup () {
23
  
24
  lcd.begin(16,2);                                   // lcd initialisieren
25
  lcd.clear ();                                      // lcd löschen
26
  pinMode ( hoch, INPUT );                           // Port als input
27
  pinMode ( runter, INPUT );                         // Port als input
28
    digitalWrite ( hoch, HIGH );                     // Pull up
29
    digitalWrite ( runter, HIGH );                   // pull up
30
    
31
    Wire.begin();
32
    RTC.begin();
33
                                                     // lcd intro   
34
 lcd.setCursor ( 0,0 );
35
 lcd.print ( "Temp Messgeraet" ); 
36
 lcd.setCursor ( 0,1 );
37
 lcd.print ( " PTB   Juni 2012" );
38
      delay (2000);
39
 lcd.clear ();
40
 
41
 lcd.print ( "Init SD card..." );
42
        delay ( 1000 );
43
                                                    // SD-Karte eingesteckt und initialisiert?
44
  if (!SD.begin(chipSelect)) {
45
      lcd.clear();
46
      lcd.print("Card failed");
47
        delay ( 2000 );
48
                                                     
49
    return;
50
  }
51
     lcd.clear();
52
     lcd.print("card init.");
53
        delay ( 2000 );
54
     lcd.clear();
55
                                                   // Interrupt
56
  cli();
57
  TCCR1A = 0;
58
  TCCR1B = 0;
59
60
    OCR1A = 15624;
61
                                                   // turn on CTC mode:
62
    TCCR1B |= (1 << WGM12);
63
                                                   // Set CS10 and CS12 bits for 1024 prescaler:
64
    TCCR1B |= (1 << CS10);
65
    TCCR1B |= (1 << CS12);
66
                                                   // enable timer compare interrupt:
67
    TIMSK1 |= (1 << OCIE1A);
68
  sei();
69
  
70
     lcd.setCursor ( 0,0 );
71
     lcd.print ( "Log:  " );
72
     lcd.print ( Wert );
73
     lcd.print ( " min." );
74
}
75
76
void loop () {
77
          
78
  if ( digitalRead ( hoch ) == LOW ) {
79
    l = 0;
80
81
    Wert += 1.0f;                                  // zählt in 1,0'ner schritten hoch
82
      lcd.clear();
83
      lcd.setCursor ( 0,0 );
84
      lcd.print ( "Log:  " );
85
      lcd.print ( Wert );
86
      lcd.print ( " min." );
87
      secound = 0;  
88
      timer = 0;             
89
                                                  // Entprellung
90
    while ( digitalRead ( hoch ) == LOW ) {}
91
    }
92
                                                  // Taster für Runterzählen abfragen
93
   if ( digitalRead ( runter ) == LOW ) {
94
    l = 0;
95
    
96
    Wert -= 1.0f;                                 // zählt in 1,0'ner schritten runter
97
      lcd.clear();
98
      lcd.setCursor ( 0,0 );
99
      lcd.print ( "Log:  " );
100
      lcd.print ( Wert ); 
101
      lcd.print ( " min." ); 
102
      secound = 0;  
103
      timer = 0; 
104
                                                  // Entprellung
105
    while ( digitalRead ( runter ) == LOW ) {}
106
    }
107
}
108
109
ISR ( TIMER1_COMPA_vect) {
110
111
  secound ++;
112
  if ( secound == 30 ) {
113
     secound = 0;
114
     timer ++;
115
  }
116
  if ( timer == Wert ) {
117
  timer = 0;
118
  
119
  byte i;
120
  byte present = 0;
121
  byte type_s;
122
  byte data[12];
123
  byte addr[8];
124
  float celsius;
125
126
  if ( !ds.search(addr)) {
127
    ds.reset_search();
128
      delay(250);
129
    return;
130
  }
131
132
  if (OneWire::crc8(addr, 7) != addr[7]) {
133
      return;
134
  }  
135
                                                     // the first ROM byte indicates which chip
136
  switch (addr[0]) {
137
    case 0x10:
138
      type_s = 1;
139
      break;
140
    case 0x28:
141
      type_s = 0;
142
      break;
143
    default:
144
      return;
145
  } 
146
  ds.reset();
147
  ds.select(addr);
148
  ds.write(0x44,1);         
149
  
150
    delay(750);     
151
    
152
  present = ds.reset();
153
  ds.select(addr);    
154
  ds.write(0xBE);                                    // Read Scratchpad
155
156
  for ( i = 0; i < 9; i++) {                         // we need 9 bytes
157
    data[i] = ds.read();
158
159
  }
160
    // convert the data to actual temperature
161
  unsigned int raw = (data[1] << 8) | data[0];
162
  if (type_s) {
163
    raw = raw << 3;                                  // 9 bit resolution default
164
    if (data[7] == 0x10) {
165
                                                     // count remain gives full 12 bit resolution
166
      raw = (raw & 0xFFF0) + 12 - data[6];
167
    }
168
  } else {
169
    byte cfg = (data[4] & 0x60);
170
    if (cfg == 0x00) raw = raw << 3;                 // 9 bit resolution, 93.75 ms
171
      else if (cfg == 0x20) raw = raw << 2;          // 10 bit res, 187.5 ms
172
      else if (cfg == 0x40) raw = raw << 1;          // 11 bit res, 375 ms
173
                                                     // default is 12 bit resolution, 750 ms conversion time
174
  }
175
176
  celsius = (float)raw / 16.0;
177
  
178
  if ( celsius >= 100 ) {
179
     lcd.setCursor ( 0,1 );
180
     lcd.print ( "Temp: ");
181
     lcd.print ( "-");
182
     lcd.print ( 4095 - celsius );                     // Wenn Temperatur ins Negative geht, dann diese Berechnung
183
     lcd.print(0xDF,BYTE);                             // 0xDF, BYTE geht nur für LCD ( °C )
184
     lcd.print("C");
185
  } else {
186
    lcd.setCursor ( 0,1 );
187
    lcd.print("Temp.: ");
188
    lcd.print(celsius );                               // Temperatur im positiven ausgeben
189
    lcd.print(0xDF,BYTE);                              // 0xDF, BYTE geht nur für LCD ( °C )
190
    lcd.print("C");
191
  }
192
  
193
  DateTime now = RTC.now();
194
  File dataFile = SD.open("datalog.txt", FILE_WRITE);  // Temperatur in .txt schreiben
195
196
  if (dataFile) {
197
    if ( celsius >= 100 ) {
198
     dataFile.print ( "-");
199
     dataFile.print ( 4095 - celsius );
200
   
201
     dataFile.print ( " ; " );
202
     
203
     dataFile.print(now.hour(),DEC);
204
     dataFile.print(":");
205
     dataFile.print(now.minute(),DEC);
206
        
207
     dataFile.print ( " ; " );
208
     
209
     dataFile.print(now.day(),DEC);
210
     dataFile.print(".");
211
     dataFile.print(now.month(),DEC);
212
     dataFile.print(".");
213
     dataFile.println(now.year(),DEC);
214
     dataFile.close();
215
  } else {
216
      dataFile.print(celsius);
217
      
218
      dataFile.print ( " ; " );
219
     
220
      dataFile.print(now.hour(),DEC);
221
      dataFile.print(":");
222
      dataFile.print(now.minute(),DEC);
223
          
224
      dataFile.print ( " ; " );
225
 
226
      dataFile.print(now.day(),DEC);
227
      dataFile.print(".");
228
      dataFile.print(now.month(),DEC);
229
      dataFile.print(".");
230
      dataFile.println(now.year(),DEC);
231
      dataFile.close();
232
  }}
233
  }
234
}

Danke im voraus

Gruß

von Karl H. (kbuchegg)


Lesenswert?

>   if ( secound == 30 ) {

Seit wann hat eine Minute 30 Sekunden?

von Robin F. (gehacktes)


Lesenswert?

Hat sie nicht!

Das musste ich so machen, weil in der gesamten Schleife im Interrupt 1 
sekunde pro durchlauf versiebt und der Interrupt auch alle sekunde 
abgefragt wird. So ergibt sich 2sec * 30 = 60sec / 1min

von Helmut L. (helmi1)


Lesenswert?

In einer Interrupt Service Routine auf die Flashkarte per Filesystem 
schreiben macht man nicht.

Besser Flag setzen und das im Hauptprogramm bearbeiten.

von Robin F. (gehacktes)


Lesenswert?

Okey werde ich umschreiben aber meinst du das löst mein Problem?

von Pako (Gast)


Lesenswert?

Helmut Lenzen schrieb:
> In einer Interrupt Service Routine auf die Flashkarte per Filesystem
> schreiben macht man nicht.

Das beeinflußt vermutlich Dein Timing negativ.
Ggf. hat Deine ISR unterschiedliche Laufzeiten und "verschluckt" ein 
Interruptereignis.

Tip:
Benutze eine zeitsynchronisierte Hauptschleife, in der Du alle 
laufzeitintensiven Sachen machst, und prüfe in dieser, daß sie immer 
rechtzeitig aufgerufen wird. Damit kannst Du Aussetzer durch zu lange 
Laufzeiten aufspüren.

von Karl H. (kbuchegg)


Lesenswert?

Robin F. schrieb:
> Hat sie nicht!
>
> Das musste ich so machen, weil in der gesamten Schleife im Interrupt 1
> sekunde pro durchlauf versiebt

Aber nicht beim ersten mal. Vom Einschalten bis zum ersten mal der 
Messprozess angestossen wird, gibt es keinen Vorgängerprozess, der 'zu 
lange gebraucht hätte', so dass dein Programm Interrupts vertrödelt 
hätte.

> und der Interrupt auch alle sekunde
> abgefragt wird. So ergibt sich 2sec * 30 = 60sec / 1min

Du hast eine Scheinlösung gebaut.

Deine ISR muss jede Sekunden aufgerufen werden. Nur dann stimmt deine 
Zeitrechnung. Das du aufgrund der Zeitrechnung etwas tun musst, was dann 
länger als 1 Sekunde dauert ist eine andere Geschichte. Allerdings eine 
Geschichte, die nicht in der ISR abgehandelt werden kann, denn die ist 
darauf angewiesen, dass sie jede Sekunde aufgerufen wird. Ist diese 
Vorausstzung nicht erfüllt, dann stimmt deine ganze Zeitrechnung nicht 
mehr.

Und das ist genau das, was du siehst.
(wenn du programmtechnisch zu derartigen 'Tricks' greifen musst, ist das 
immer ein sehr sicheres Zeichen, dass dein Programmaufbau nicht stimmt)

-> In der ISR die Zeitbehandlung machen. Wenn es an der Zeit ist eine 
neue Messung anzustossen, dann wird ein Jobflag gesetzt, welches in 
loop() ausgewertet wird. Damit hast du die Messung von der Erfassung und 
Zählung der Zeitintervalle entkoppelt. Jetzt muss nur noch das Intervall 
der Messungen länger sein, als eine Messung dauert und dann kann schon 
nichts mehr passieren.

von Robin F. (gehacktes)


Lesenswert?

Ah jetzt macht es klick!

Danke an alle Antworten. Werde mich gleich dran setzten und die 
Änderungen vor nehmen. Wenn ich das jetzt richtig verstehe muss ich die 
Auswertung des Temperatursensors aus der ISR verbannen und extra 
schreiben?.

Ich melde mich wenn ich das soweit habe oder noch ein Problem auftreten 
sollte noch einmal.

Gruß

von Robin F. (gehacktes)


Lesenswert?

Ich habe gerade nochmal mit meinem Arbeitskolegen geredet.
Müsste das dann nicht bei allen Abfragen der fall sein, das die Zeit nur 
halb so groß ist wie der eingeegbene Wert?

von Karl H. (kbuchegg)


Lesenswert?

Robin F. schrieb:
> Ich habe gerade nochmal mit meinem Arbeitskolegen geredet.
> Müsste das dann nicht bei allen Abfragen der fall sein, das die Zeit nur
> halb so groß ist wie der eingeegbene Wert?

Nein. Denn deine Bearbeitung, das Messen und das Schreiben auf die 
SD-Karte, brauchen ja auch ihre Zeit.


ABer vielleicht hab ich mich ja auch verrechnet.
Aus deinem OCR Wert hab ich zurückgerechnet, dass dein Arduino mit 16Mhz 
läuft. Stimmt das?
Denn wenn der nur mit 8Mhz läuft, dann kommt die ISR tatsächlich nur 
alle 2 Sekunden, und dann wäre 30 korrekt. Aber warum sollte man dann 
eine Variable 'Sekunde' nennen (auch wenn man das im englischen anders 
schreibt ist doch erkennbar, dass du mit 'secound' eigentlich 'second' 
meintest.)

von Robin F. (gehacktes)


Lesenswert?

Okey. Ja das Arduino läuft mit 16 Mhz!
Oh stimmt danke für den Hinweis werde ich gleich ändern.
nagut ich versuch das jetzt einfach mal mit der Änderung

von Karl H. (kbuchegg)


Lesenswert?

Du kannst ja auch mal Folgendes machen:

Häng dir einfach mal eine LED an einen Pin.
Am Anfang der ISR schaltest du sie ein, am Ende schaltest du sie aus.
Im abgedunkelten Raum dürfte man die im Sekundentakt aufblitzende LED 
mit etwas glück gerade noch sehen können. Und alle 30 Sekunden brennt 
die LED dann mal länger. Und genau dieses länger brennen ist der 
Zeitbedarf für dein Messen, Abspeichern und aufs LCD ausgeben. Würde 
mich tatsächlich interessieren, wie lang das ungefähr dauert. 2 
Sekunden, 5 Sekunden oder gar noch länger?


(wenn du das Aufblitzen im Sekundentakt nicht sehen kannst, dann mach 
mal eine kurzes delay probehalber vor dem Ausschalten rein. Ein paar 
Millisekunden sieht man auf jeden Fall schon ganz gut)

von Robin F. (gehacktes)


Lesenswert?

okey mach ich...hoffe kann was erkennen weil ich es nicht dunkel genug 
kriegen werde.

von Karl H. (kbuchegg)


Lesenswert?

Anhand der Zeitintervalle zwischen den langen Brennphasen kannst du dann
auch sagen, welche Zeitabstände du wirklich hast. Denn aufgrund deiner
Timestamps, die du nur mit Minuten erstellst, ist das ja sekundenmässig
sowieso eher mehr ein Schätzeisen, als das man sagen könnte: Yep - die 
Abstände in Minuten stimmen.

Beispiel.
Du willst eigentlich alle 1 Minute messen, aber aus irgendeinem Grund 
misst du alle 55 Sekunden.
Den ersten Timestamp kriegst du bei 00:00:55, Im File hast du keine 
Sekunden, da steht dann 00:00
Der nächste Timestamp kommt bei 00:01:50. Im File fallen wieder die 
Sekunden weg und du liest 00:01   Sieht für dich in Ordnung aus. Denn 
zwischen den Timestamps liegt ja laut Timestamp 1 Minute. Wie gehts 
weiter?

  reale Zeit     Timestamp
  ------------------------
  00:00:55       00:00
  00:01:50       00:01
  00:02:45       00:02
  00:03:40       00:03
  00:04:35       00:04
  00:05:30       00:05
  00:06:25       00:06
  00:07:20       00:07
  00:08:15       00:08
  00:09:10       00:09
  00:10:05       00:10
  00:11:00       00:11
  00:11:55       00:11

Hoppla. Da haben jetzt 2 Timestamps hintereinander plötzlich keine 
Differenz mehr! Im Sekundenbereich haben sie die noch, aber reduziert 
nur auf die Minuten dann eben nicht mehr.
Wie ist das zustande gekommen: In der Spalte mit den Sekunden sieht man 
es deutlich: du misst nicht alle 1 Minuten, sondern alle 55 Sekunden. 
Reduziert auf die Minuten sieht man das aber nicht mehr. Bis eben auf 
den kleinen Glitsch ab und an.

von Robin F. (gehacktes)


Lesenswert?

So ich habe da jetzt mal eine LED angeschlossen und einfach ma laufen 
lassen.
Alle 30 Sekunden leuchtet die LED für 2 Sekunden auf. Das Aufblitzten 
nach jeder Sekunde kann ich so mit bloßem Auge nicht erkennen. Darum 
habe ich zusätzlich nochmal ein Delay von 500ms eingebaut. Jetzt kann 
ich jede Sekunden ein Blitzen erkennen und alle 30 Sekunden ein 2 
Sekunden langes leuchten wahr nehmen.

von Karl H. (kbuchegg)


Lesenswert?

Robin F. schrieb:

> ich jede Sekunden ein Blitzen erkennen und alle 30 Sekunden ein 2
> Sekunden langes leuchten wahr nehmen.

Na also. Deckt sich mit den 30 Sekunden in deinem Programm. (mit einer 
Uhr gestoppt werden es dann eventuell schon 31 Sekunden von einem 
Messvorgang zum nächsten sein. Je nachdem, ob du noch knapp unter 2 
Sekunden bist oder schon drüber)

D.h. das war wohl nichts, dass man als Benutzer die Anzahl der Minuten 
von einem Messvorgang zum nächsten einstellt. Tatsächlich ist bei dir 
eine (Benutzer-) Einstellung von 2 Minuten als genau die Hälfte zu 
werten. Stellt der Benutzer 10 Minuten ein, finden die Messungen 
tatsächlich alle 5 Minuten statt.

-> in der Programmierung: testen, kontrollieren, testen, messen!
Alles andere ist sinnlos, man macht als Programmierer immer wieder mal 
Fehler.


-> größer 2 Sekunden ist zu lang für eine ISR, die jede 1 Sekunde 
aufgerufen werden müsste.


PS: gibt es eigentlich einen Grund, warum die Variable 'timer' den 
Datentyp byte hat, während 'Wert' (schlechter Name, das ist das 
gewünschte Messíntervall in Minuten und nicht einfach irgendein Wert) 
den Datentyp double hat?

von Route_66 H. (route_66)


Lesenswert?

Hallo!
Und von mir noch ein Tip: Wenn Du wieder einmal diese 
LED-Aufblitz-Methode verwendest, und das Aufblitzen ist zu kurz, und Du 
willst kein Delay benutzen, dann kannst Du auch Toggeln.

von Robin F. (gehacktes)


Lesenswert?

Karl Heinz Buchegger schrieb:

> -> in der Programmierung: testen, kontrollieren, testen, messen!
> Alles andere ist sinnlos, man macht als Programmierer immer wieder mal
> Fehler.

Ja das stimmt. Das Problem war das ich keinen Wert beim einschalten 
speichern lasse und es so anfangs nicht gemerkt habe. Da wir jetzt auf 
Arbeit einen Zeitintervall von 1h gewählt hatten und dazu ein 2. Gerät 
hatten das Zeitgleich gestartet wurde und dann plötzlich nach 30min der 
erste Log durchgeführt wurde ist uns der Fehler später beim auswerten 
aufgefallen.

Jetzt weiß ich aber erlich gesagt nicht wie ich das zu lösen habe. Muss 
ich das Auslesen des Temperatursensors aus der ISR raus löschen und 
dafür einen externen Programmabschnitt anlegen, welches ich dann immer 
abfragen lasse wenn der timer im ISR gleich den Wert ist?

von Karl H. (kbuchegg)


Lesenswert?

Robin F. schrieb:

> Jetzt weiß ich aber erlich gesagt nicht wie ich das zu lösen habe.

Erst mal muss deine Minute wieder 60 Sekunden kriegen.

> Muss
> ich das Auslesen des Temperatursensors aus der ISR raus löschen und
> dafür einen externen Programmabschnitt anlegen, welches ich dann immer
> abfragen lasse wenn der timer im ISR gleich den Wert ist?

Das beste wärs, wenn du den Teil ebenfalls in die Hauptschleife 
verlagerst.
1
volatile byte performMeasure;
2
3
void loop()
4
{
5
  ....
6
  der Tastenteil
7
  ...
8
9
  if( performMeasure )
10
  {
11
    performMeasure = 0;
12
13
    ... hier dann der Mess, Anzeigeteil und SD-Kartenteil
14
  }
15
}
16
17
18
ISR ( TIMER1_COMPA_vect)
19
{
20
  secound ++;
21
  if ( secound == 60 ) {
22
    secound = 0;
23
    timer ++;
24
25
    if( timer == Wert ) {
26
      timer = 0;
27
      performMeasure = 1;
28
    }
29
  }
30
}

und räum ein bischen in deinen Datentypen auf. Es macht keinen Sinn, für 
alles und jedes einfach double zu benutzen.
Überleg dir auch, wie sinnvoll deine Variablennamen sind und ob du den 
Code nicht noch vereinfachen kannst. Da geht noch einiges um ihn kürzer 
zu kriegen, ohne das die Übersicht leidet.

von Robin F. (gehacktes)


Lesenswert?

Ja okey werde ich machen :)

Ich werde das Programm jetzt versuchen ab zu wandeln und zu vereinfachen 
und dann werde ich das neue Programm hochladen.

Vielen Dank für die großartige Hilfe

von Robin F. (gehacktes)


Lesenswert?

Hallo,

Karl Heinz das klappt alles nicht so ganz. Ich habe alles so gemacht wie 
du gesagt hast und jetzt hat ist genau das gegenteil der Fall. Beim 
Einschalten ist die Zeit richtig und bei jeder weiteren Messung 
verdoppelt sich die Zeit.

Ich habe noch keine Lösung werde aber erneut Asukunft geben wenn ich was 
neues weiß

von Karl H. (kbuchegg)


Lesenswert?

Robin F. schrieb:
> Hallo,
>
> Karl Heinz das klappt alles nicht so ganz. Ich habe alles so gemacht wie
> du gesagt hast und jetzt hat ist genau das gegenteil der Fall. Beim
> Einschalten ist die Zeit richtig und bei jeder weiteren Messung
> verdoppelt sich die Zeit.

Tja.
Dann hast du einen Fehler im Programm. Soll vorkommen :-)

von Robin F. (gehacktes)


Lesenswert?

Ja das habe ich dann wohl... Naja ma gucken was ich noch raus bekomme 
...dann werde ich mir mal weiter den Kopf zerbrechen müssen

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.