Forum: Mikrocontroller und Digitale Elektronik _delay_us Funktion in ISR Routine bringt ISR aus dem Tackt.


von Felix N. (felix_n888)


Lesenswert?

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.

von Allwissender (Gast)


Lesenswert?

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.

von Allwissender (Gast)


Lesenswert?

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.

von Einer K. (Gast)


Lesenswert?

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.

von Hubert G. (hubertg)


Lesenswert?

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.

von Poster (Gast)


Lesenswert?

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.

von Allwissender (Gast)


Lesenswert?

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?

von Felix N. (felix_n888)


Angehängte Dateien:

Lesenswert?

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.

von Huh (Gast)


Lesenswert?

Allwissender schrieb:
> Ob das jemand versteht?

Die weitaus größere Hälfte versteht das. :-)

von Hubert G. (hubertg)


Lesenswert?

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.

von Felix N. (felix_n888)


Lesenswert?

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

von Peter R. (peterfido)


Lesenswert?

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.

von Rainer B. (katastrophenheinz)


Lesenswert?

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.

von Werner P. (Gast)


Lesenswert?

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!?

von A. S. (Gast)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@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"

von Stefan F. (Gast)


Lesenswert?

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
}

von Löthansel (Gast)


Lesenswert?

Stefan U. schrieb:
> Etwa so:
> .............

Jawoll. Dann kommt die ISR Routine auch nicht aus dem Tackt.
Und das LCD Display zeigt auch was an.

von Felix N. (felix_n888)


Lesenswert?

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
Noch kein Account? Hier anmelden.