Hallo Leute, Ich experimente zurzeit ein bisschen mit denn Digitalen Temperatur Sensoren(DS18B20) herum. Auf dem Computer habe ich eine kleine Library wiedergefunden die ich mal vor Monaten heruntergeladen habe. In dieser Library wird mit der Delay Funktion _delay_us() gearbeitet. Da ich die Temperatur Lesung nicht in mein Hauptprogramm einbauen kann, habe ich ein 8 Bit Timer des ATMega328P genommen und habe diesen mit den Prescaler von 64 versehen. Zähle nun ein Integer bis auf 974 hoch und lese dann die Temperatur aus. Das ist ca. jede Sekunde bei 16 MHz und 64 Prescaler. Nur ich habe vorher schon in diesem Timer eine Laufzeit Programmiert die jede Sekunde ein Integer höher Zählt, dann bei 60 die Minuten eines höher zählt und so weiter. Wenn ich die Temperatur Lesung dort raus lasse erhöht sich der Sekunden Integer jede Sekunde dann funktionier alles Problem los. Ist aber die Temperatur Lesung mit drin. Dann erhöht sich der Sekunden Integer ca. ~1,5 bis 2,5 Sekunden. Also die Delay Funktion scheint die ISR Routine des Timer Overflow total aus dem Takt zu bringen. Was kann ich tun um die Temperatur auszulesen aber gleichzeitig die ISR normal weiterlaufen lässt. So das sich der Sekunden Integer auch wirklich jede Sekunde um 1 erhöht? Mfg Felix.
Felix N. schrieb: > _delay_us Funktion in ISR Routine bringt ISR aus dem Tackt. Ja klar, wenn das Delay länger dauert als die Zykluszeit mit der die ISR aufgerufen wird.
Felix N. schrieb: > _delay_us Funktion in ISR Routine bringt ISR aus dem Tackt. Delayen tut man nicht in der ISR. Und Tackt schreibt man Takt.
Felix N. schrieb: > Also die Delay Funktion scheint die ISR Routine des Timer Overflow total > aus dem Takt zu bringen. Nein! Es ist nicht das Delay. Aber: Die Kommunikation mit dem DS18B20 erfordert ein recht exaktes Timing. Darum werden sicherlich, in der Zeit, die Interrupts deaktiviert. Das führt bei dir dann zu verlorenen Timer Interrupts. Und Zack, deine Uhr läuft hinterher.
Ersetze dein delay durch einen Timer. Innerhalb einer ISR ist allerdings eine Verzögerung, egal wodurch, ohnehin nicht sinnvoll. Die Auswirkungen siehst du ja selbst. Du könntest dein Programm zeigen, dann könnte man dir einen gezielten Rat geben.
Felix N. schrieb: > Da ich die > Temperatur Lesung nicht in mein Hauptprogramm einbauen kann Dann must du halt das Programm so abändern das es geht. Die Idee mit einem Timer ist ja schon nicht schlecht. Aber der sollte nur ein Flag setzen was dann in deinem Hauptprogramm die Messung auslöst. Es gibt eigentlich nur sehr wenige Anwendungen für eine Delay Funktion, meist handelt man sich damit nur Probleme ein. Wahrscheinlich verwendest du in deinem vorhandenen Hauptprogramm auch Delays und daher wohl deine Probleme mit dem Einbau der Temperatur Lesung. Das geht auch anders, die Delays müssen weg.
Felix N. schrieb: > _delay_us Funktion in ISR Routine bringt ISR aus dem Tackt. Eine ISR Routine ist so etwas ähnliches wie ein LCD Display. Oder ein weisses Schimmel-Pferd. Oder ein SDR-Radio. Oder .... Im übertragenen Sinne natürlich. Ob das jemand versteht?
Hubert G. schrieb: > Du könntest dein Programm zeigen, dann könnte man dir einen gezielten > Rat geben. Klar hier ist mal die Library die ich verwende habe leider keinen Namen für euch. Da ich diese schon seit längern auf meiner Platte habe.
Felix N. schrieb: > Klar hier ist mal die Library die ich verwende habe leider keinen Namen > für euch. Da ich diese schon seit längern auf meiner Platte habe. Und wie und wo wird diese Lib aufgerufen. Ich sehe da keine ISR.
Hubert G. schrieb: > Und wie und wo wird diese Lib aufgerufen. Ich sehe da keine ISR. Befindet sich in meiner Main poste ich nachher bin grade nicht mehr zuhause. Aber da könnt ihr das aber mit denn _delay_us Funktion sehen
Eine vorhandene Uhr ist doch ideal. So mache ich es: Bei Sekunde 16 sage ich dem DS18B20, dass er messen soll und in Sekunde 17 hole ich das Ergebnis ab. Der DS18B20 benötigt ~0,7 Sekunden zum Messen und Aufbereiten der Werte. Eine Pause Delay wie auch immer hat in einer ISR nichts zu suchen. Idealerweise setzt man ein Flag und fragt dieses außerhalb der ISR ab. Ausnahmen sind bei mir zeitkritische Sachen wie SoftUART, Infrarotempfang und so.
Das sagt das Datenblatt unter "Notes": Temperature conversion takes up to 750 ms. Ich vermute, der Killer ist das aktive Warten auf das Ende der Messung
1 | while(!therm_read_bit(pin)); |
Wenn du dann "therm_read_temperature" aus der ISR aufrufst, legst du dir die Karten. Teile mal das Anstarten der Messung und das Lesen des Ergebnisses in zwei Funktionen auf, die du nacheinander und nicht aus der ISR heraus aufrufst. Also in der ISR nur ein Flag setzen "Temp Messung starten", im MainLoop dieses Flag auswerten, Messung starten, mit Timer warten, bis Messung fertig ist, dann Ergebnis auslesen.
Das auslesen dauert einfach zu lange. 3 x reset sind schon ca. 3,0 ms 4 x writebyte ca. 1,6 ms 2 x readbyte ca. 2,0 ms sind schon mal um die 6,6 ms. Den Rest noch gar nicht mitgerechnet. Wenn sein Zähler in der ISR bei Stand 974 eine Sekunde darstellt dann läuft der Timer mit ca. 1 ms. Wie soll das gehen!?
Felix N. schrieb: > Da ich die Temperatur Lesung nicht in mein Hauptprogramm einbauen kann Mit großer Wahrscheinlichkeit gibt es da einen Weg. Zähl mal die Gründe dagegen auf. > Zähle nun ein Integer bis auf 974 hoch und lese dann die > Temperatur aus. Das ist ca. jede Sekunde Das heisst, Du hast jede ms einen Interrupt. Dabei inkrementiert man den SysTicker und macht Dinge, die wirklich jedes 1..10te Mal laufen müssen (z.B. Tastaturauswertung). Doch in so einer Interrupt-Routine kannst du nicht die Temp. lesen. Alternativen: a) wenn Du mehrere Interrupt-Prioritäten hast, dann Timer hochprior, auslesen Niederprior. (Das ist Quasi ein Multitasking light mit Background-Task und dem niederpriorem Interrupt als hochpriore Task) b) ein RTOS (ja, gibt es auch für mini-CPUs) c) ein RTOS-Light, indem das Hauptprogramm "häufiger" eine bestimmte Funktion aufruft. Wenn das Hauptprogramm z.B. spätestens alle 10ms die Funktion "toggleLED" aufruft, ruft man von dort die Funktion "highPrioTask()" auf, in der ein Timer "if((SystTicker-StartWert)<1000) {...} " unter anderem das Temperaturlesen steuert.
@Felix Neumann (felix_n888) >ds18b20.c (2,49 KB, 21 Downloads) | Codeansicht >ds18b20.h (1,05 KB, 9 Downloads) | Codeansicht Diese Funktionen sind NICHT interruptsicher, d.h. wenn dir ein Interrupt zufällig in dein laufendes _delay_us() reinspuckt, ist das Timing und damit der OneWire-Zugriff im Eimer! Man muss während der zeitkritischen Abläufe die Interrupts sperren, aber nur dort! Dann klappt das auch mit dem Timer und dem Auslesen ganz normal im Hauptprogramm. Beitrag "Re: Onewire + DS18x20 Library"
Eigentlich ist der Ansatz schon völlig falsch, in der Interruptroutine eine externe Kommunikation durchzuführen. Du solltest in der ISR besser einfach nur ein Flag setzen, dass den AUftrag zu Messung erteilt und dann in der Hauptschleife die Messung durchführen. Etwa so:
1 | uint8_t bitteMessen=0; |
2 | |
3 | ISR() { |
4 | if (genug takte gezählt) { |
5 | bitteMessen=1; |
6 | } |
7 | } |
8 | |
9 | main() { |
10 | while(1) { |
11 | if (bitteMessen) { |
12 | führedieMessungDurch(); |
13 | bitteMessen=0; |
14 | } |
15 | else { |
16 | machWasAnderes(); |
17 | } |
18 | } |
19 | } |
Stefan U. schrieb: > Etwa so: > ............. Jawoll. Dann kommt die ISR Routine auch nicht aus dem Tackt. Und das LCD Display zeigt auch was an.
Rainer B. schrieb: > Teile mal das Anstarten der Messung und das Lesen des Ergebnisses in > zwei Funktionen auf Stefan U. schrieb: > Eigentlich ist der Ansatz schon völlig falsch, in der Interruptroutine > eine externe Kommunikation durchzuführen. Hallo Leute, Das Punkt von Rainer B. wahr ein Start Signal für mich. Ich habe nun das Messen mit dem Sensor auf zwei Funktionen umgeschrieben. Eine Funktion nennt sich nun "start_conversion(uint8_t pin)" und benötigt denn Pin wo der DS18B20 angeschlossen ist. Eine andere Funktion nennt sich "read_temp(void)" dieses liest die letzten Werte aus. Ich habe es nun so gemacht das ich ein boolean auf true setzte damit er die nächste Conversion im Hauptprogramm startet. Dort wird dann das mit dem delay ausgeführt und in globale Variablen die Werte gespeichert. Dann ein Boolean setzten um zu sagen das die Conversion fertig ist. Und dann in der ISR abfragen ob er fertig ist wenn ja -> readTemp(). Und danach dann neue Conversion starten. Funktioniert bis jetzt ohne Probleme. Die Laufzeit läuft nun normal weiter und ich bekomme denn Wert vom Sensor. Danke euch. mfg Felix
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.