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
LiquidCrystallcd(A2,A1,6,5,2,3);
11
OneWireds(9);
12
constintchipSelect=4;
13
RTC_DS1307RTC;
14
15
doubleWert=1;
16
bytesecound=0;// Timer auf 0 setzen
17
bytetimer=0;// Timer auf 0 setzen
18
intl=0;
19
constinthoch=7;// Taster für Hochzählen defenieren
20
constintrunter=8;// Taster für Runterzählen defenierern
21
22
voidsetup(){
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
voidloop(){
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
bytei;
120
bytepresent=0;
121
bytetype_s;
122
bytedata[12];
123
byteaddr[8];
124
floatcelsius;
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
case0x10:
138
type_s=1;
139
break;
140
case0x28:
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
unsignedintraw=(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
bytecfg=(data[4]&0x60);
170
if(cfg==0x00)raw=raw<<3;// 9 bit resolution, 93.75 ms
171
elseif(cfg==0x20)raw=raw<<2;// 10 bit res, 187.5 ms
172
elseif(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
DateTimenow=RTC.now();
194
FiledataFile=SD.open("datalog.txt",FILE_WRITE);// Temperatur in .txt schreiben
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
In einer Interrupt Service Routine auf die Flashkarte per Filesystem
schreiben macht man nicht.
Besser Flag setzen und das im Hauptprogramm bearbeiten.
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.
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.
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ß
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?
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.)
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
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)
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.
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.
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?
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.
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?
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
volatilebyteperformMeasure;
2
3
voidloop()
4
{
5
....
6
derTastenteil
7
...
8
9
if(performMeasure)
10
{
11
performMeasure=0;
12
13
...hierdannderMess,AnzeigeteilundSD-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.
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
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ß
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 :-)