Forum: Mikrocontroller und Digitale Elektronik Codeunterbrechung bei Interrupt


von Matthias (Gast)


Lesenswert?

Hallo,

laut dem Artikel zu Interrupts hier wird der loop Code bei Interrupt 
unterbrochen, der ISR Code ausgeführt und dann fortgesetzt.
Wie verhält es sich, wenn der loop Code gerade mit delay pausiert ist 
und ein Interrupt auftritt? Wird der Code dann mit dem Befehl nach delay 
fortgesetzt oder wird die restliche Zeit nach Unterbrechung pausiert?

Danke für alle Antworten

Matthias

von Matthias (Gast)


Lesenswert?

Achso...Attiny85/Attiny84

von Maxim B. (max182)


Lesenswert?

Matthias schrieb:
> Wird der Code dann mit dem Befehl nach delay
> fortgesetzt oder wird die restliche Zeit nach Unterbrechung pausiert?
Hallo,
Gemeint ist Maschinencode. Maschinencode "delay" gibt es nicht. Programm 
wird ab nächster Maschinenoperation fortgesetzt. Mit welchen 
Maschinencoden wird "delay" bei dir realisiert, solltest du in 
Disassembler kucken.
http://www.avr-asm-tutorial.net/avr_de/beginner/commands.html

: Bearbeitet durch User
von just everything (Gast)


Lesenswert?

Matthias schrieb:
> laut dem Artikel zu Interrupts hier wird der loop Code bei Interrupt
> unterbrochen, der ISR Code ausgeführt und dann fortgesetzt.
> Wie verhält es sich, wenn der loop Code gerade mit delay pausiert ist
> und ein Interrupt auftritt? Wird der Code dann mit dem Befehl nach delay
> fortgesetzt oder wird die restliche Zeit nach Unterbrechung pausiert?


delay ist kein befehl.
delay kann auf verschiedene Weise implementiert sein, die dazu führen 
können das während des ISR  der delay-"timer"  weiterläuft ... oder auch 
nicht.
https://youtu.be/5ZLtcTZP2js?t=114

von Carl D. (jcw2)


Lesenswert?

Wenn das Delay aus der bei avrlibc-üblichen "Verbrauch n-Clock-Cycles" 
Warteschleife besteht, dann wird es um die Interrupt-Laufzeit länger.

Für exakte Verzögerungen könnte man z.B. einen Timer benutzen und in 
einer Schleife auf eines von dessen "Interrupt-Flags" warten. Ohne 
allerdings diese Interrupts freizuschalten. Läuft der μC mit 1MHz und 
Timer0 läuft mit Teiler 1, dann setzt er nach 256μs das 
Timer0-Overflow-Flag.

: Bearbeitet durch User
von Matthias (Gast)


Lesenswert?

Exakt ist nicht notwendig.
1
void loop() {
2
  digitalWrite(PIN, HIGH);
3
  delay(10000);
4
  digitalWrite(PIN, LOW);
5
}

Wenn der Interrupt z.B. nach einer Sekunde eintritt sollte die Pause 
noch ca. 9 Sekunden andauern.

Sind die ersten Gehversuche in dieser Materie ;-)

von Maxim B. (max182)


Lesenswert?

Matthias schrieb:
> void loop() {
>   digitalWrite(PIN, HIGH);
>   delay(10000);
>   digitalWrite(PIN, LOW);
> }

Hallo,
das ist C-Code. Sie wird vom Compiler in Maschinencode umgesetzt. Die 
Maschinencode ist, womit Mikrocontroller arbeitet. Er hat keine Ahnung 
von loop und von delay - das ist nur für deine Bequemlichkeit so 
geschrieben. Nach ISR wird nächste Maschinencode gemacht.

Willst du wirklich etwas außer "loop" machen, mit ISR usw, dann solltest 
du bessere Vorstellung von Mikrocontroller haben. Kenntnisse nur von 
Arduino-IDE reichen nicht aus, du solltest wissen, was C ist und was 
Assembler ist. Arduino ist für einfache Sachen konzipiert, für Menschen, 
die nichts außer "loop" machen wollen.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Bei Arduino wird der delay nach dem Abarbeiten des Interrupts 
fortgesetzt. Das heißt, deine gewünschten 10.000ms sind nur eine 
"mindestens" Angabe, das Delay kann beliebig länger dauern.

Außerdem wird der Pin auch ohne Interrupts nicht exakt im 10s Raster 
umgeschaltet, weil dir digitalWrite() Aufrufe und der Rücksprung zum 
Anfang der loop auch Zeit kosten.

In deinem Code-Beispiel fehlt vermutlich ein zweites Delay ganz unten.

Wenn du es genau haben willst, musst du einen Timer verwenden. Zum 
Beispiel:
1
unsigned long zeit;
2
3
void setup()
4
{
5
   zeit=millis();
6
}
7
8
void loop() 
9
{
10
  digitalWrite(PIN, HIGH);
11
12
  while (millis()-zeit < 10000) {};
13
  zeit+=10000;
14
  
15
  digitalWrite(PIN, LOW);
16
17
  while (millis()-zeit < 10000) {};
18
  zeit+=10000;
19
}

Selbst wenn die while Schleife tatsächlich länger als 10s dauert, 
beginnt das nächste Intervall dennoch exakt 10s später weil die Variable 
Zeit immer exakt auf 10s weiter gesetzt wird.

von Wolfgang (Gast)


Lesenswert?

Matthias schrieb:
> Wie verhält es sich, wenn der loop Code gerade mit delay pausiert ist
> und ein Interrupt auftritt?

Guck es dir einfach an.

Setzte vor dem Aufruf von delay() einen IO-Pin auf High und danach 
wieder auf Low. Das Gleiche machst du für einen anderen IO-Pin am Anfang 
und Ende der Interrupt-Routine. Den Signalverlauf an den beiden IO-Pins 
zeichnest du mit einem einfachen Logikanalysator(*) auf. Dann kannst du 
nach herzenzlust rummessen.
(*) z.B. https://www.ebay.de/itm/273246600838

von Peter D. (peda)


Lesenswert?

Kommt drauf an, wie das Delay implementiert ist. Wenn mit einem Timer, 
dann verlängert sich das Delay nicht. Wenn mit einer Zählschleife, dann 
addiert sich die Zeit des Interrupts. Da typisch Interrupts <1% der 
CPU-Zeit benötigen, ist das in der Regel kein Problem. Eine genaue Uhr 
kann man mit Delay eh nicht machen.

: Bearbeitet durch User
von Axel S. (a-za-z0-9)


Lesenswert?

Matthias schrieb:
1
> void loop() {
2
>   digitalWrite(PIN, HIGH);
3
>   delay(10000);
4
>   digitalWrite(PIN, LOW);
5
> }

> Sind die ersten Gehversuche in dieser Materie ;-)

Das sieht man. Dieser Code wird vermutlich nicht das tun, was du 
erwartest. Der Pin wird zwar am Ende der Schleife auf LOW gesetzt, aber 
unmittelbar darauf am Anfang der Schleife wieder auf HIGH. Weder mit dem 
Auge noch mit den meisten Meßmitteln [1] wirst du überhaupt sehen, daß 
der Pin alle 10 Sekunden mal für ein paar Mikrosekunden auf LOW geht.


[1] keine Change mit jeglichem Multimeter. Auch ein Oszilloskop hilft 
nur bedingt. Am besten ist noch ein Logikprüfstift mit 
Impulsverlängerung.

von Wolfgang (Gast)


Lesenswert?

Axel S. schrieb:
> Auch ein Oszilloskop hilft nur bedingt.

Quatsch, umgehen können muss man mit dem Oszilloskop allerdings schon.

von Dietrich L. (dietrichl)


Lesenswert?

Wolfgang schrieb:
> Axel S. schrieb:
>> Auch ein Oszilloskop hilft nur bedingt.
>
> Quatsch, umgehen können muss man mit dem Oszilloskop allerdings schon.

Axel hat vielleicht an einen µC mit GHz-Takt gedacht. Dann sind die 
Ansprüche an einen Oszi schon ziemlich groß und das "bedingt" passt ;-)

von Wolfgang (Gast)


Lesenswert?

Matthias schrieb:
> Achso...Attiny85/Attiny84

Dietrich L. schrieb:
> µC mit GHz-Takt

???

von Maxim B. (max182)


Lesenswert?

Matthias schrieb:
> Achso...Attiny85/Attiny84

Wie ist es dir gelungen, Arduino mit Attiny85 zu machen?

von Wolfgang (Gast)


Lesenswert?

Maxim B. schrieb:
> Wie ist es dir gelungen, Arduino mit Attiny85 zu machen?

Ich weiss nicht, was der TO empfiehlt.

Google sagt z.B.:
http://highlowtech.org/?p=1229
https://www.instructables.com/id/Program-an-ATtiny-with-Arduino/

von Georg (Gast)


Lesenswert?

Matthias schrieb:
> Wird der Code dann mit dem Befehl nach delay
> fortgesetzt

Das würde bedeuten, dass der Interrupt erst nach Abarbeitung des Delays 
auftreten kann und das wäre ein schwerer Softwarefehler. Der Interrupt 
könnte so ja sekundenlang verzögert werden. Nein, der Interrupt muss den 
Delay mittendrin unterbrechen (deswegen heisst er Interrupt, siehe auch 
Coitus Interruptus).

Ich würde aber nicht die Hand dafür irgendwohin legen dass das immer so 
ist, besonders wenn es sich um eine selbstgestrickte Delay-Routine 
handelt.

Nach dem Interrupt wird der Rest des Delay abgearbeitet. Damit der 
möglichst wenig verfälscht wird darf die Interrupt-Bearbeitung nur sehr 
kurz laufen.

Georg

von Michael D. (nospam2000)


Lesenswert?

Wolfgang schrieb:
> Guck es dir einfach an.

Genau, einfach mal in den SourceCode von delay() schauen. Wenn man nicht 
die Arduino IDE sondern Visual Studio Code mit PlatformIO verwendet geht 
das sehr einfach mit einem Mausclick, ansonsten muss man eben mit grep 
suchen und wird dann hier fündig: 
https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring.c#L106:
void delay(unsigned long ms)
{
  uint32_t start = micros();

  while (ms > 0) {
    yield();
    while ( ms > 0 && (micros() - start) >= 1000) {
      ms--;
      start += 1000;
    }
  }
}

von Maxim B. (max182)


Lesenswert?

Georg schrieb:
> Das würde bedeuten, dass der Interrupt erst nach Abarbeitung des Delays
> auftreten kann

Wenn das so sein muß - es gibt nichts einfacher.
Vor delay cli() schreiben und danach sei().

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Die Funktionen '_delay_ms() und '_delay_us()' der AVRLib sind, sofern 
sie nicht in der ISR selber stehen, immer unterbrechbar. ISR beim AVR 
sind ja ohne besondere Massnahmen 'blockierend', d.h., sie können selber 
nicht mehr unterbrochen werden. Ein '_delay_ms()' in einer ISR ist 
deswegen tabu.
AVRGCC und die AVRLib erlauben allerdings unterbrechbare Sonderformen 
der ISR.

: Bearbeitet durch User
von Michael D. (nospam2000)


Lesenswert?

Maxim B. schrieb:
> Wenn das so sein muß - es gibt nichts einfacher.
> Vor delay cli() schreiben und danach sei().

Das ist keine gute Idee:
Du bringst die Zeitverwaltung durcheinander, da die von millis() und 
micros() zurückgelieferte Zeit nicht mehr durch die Timer IRQs erhöht 
wird und quasi stehen bleibt. Möglicherweise funktioniert delay() dann 
überhaupt nicht mehr, da es selbst micros() verwendet.

IRQs dürfen immer nur so kurz wie möglich gesperrt werden.

  Michael

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

Michael D. schrieb:
> Das ist keine gute Idee:
> Du bringst die Zeitverwaltung durcheinander, da die von millis() und
> micros()

Ich bin der Meinung, daß IDE Arduino in sich keine gute Idee ist, da 
keine Debug-Mittel integriert. Weder JTAG-Emulator noch Simulator sind 
möglich. Wenn man so IDE benutzt, kann man kaum noch etwas schlechter 
machen.

von Martin V. (oldmax)


Lesenswert?

Hi
Nun, einige Antworten sind ja schon in die richtige Richtung, bei 
anderen bezweifle ich, das die Schreiber wissen, was ein Interrupt 
ist.(gut, es gibt verschiedene Arten, aber bei einem µC ist es so 
ziemlich immer gleich)
Ein Programm wird nach Maschinencode abgearbeitet. Maschinencode ist 
nicht Assembler oder gar eine andere Sprache, sondern ganz schlicht 
elektrische Zustände, gespeichert in einem (elektrisch) adressierten 
Speicher ausgedrückt für die Programmierer in "1" und "0". Diese sind 
dann 8, 16 oder auch mal 32 Bit breit. Diese Bitmuster werden im 
Controller in der Befehlsmatrix zu einem Befehl umgesetzt. 
(Oberflächlich erklärt) Nun zum Interrupt. Einige Hardwarekomponenten 
sind in der Lage, den Programmablauf zu stören, also einzugreifen und 
das Adressregister für die Befehlsspeicher einfach auf einen anderen 
Speicherbereich zu setzen. Dort steht dann entweder der Programmcode für 
die Interruptbearbeitung, oder einfach ein Rücksprung. Ablauf ist etwa 
so:
Interrupt löst aus:
Adresse vom Adressregister für den Programmbereich wird 
zwischengespeichert.
Adresse wird auf einen festen Wert passend zum Interrupt im 
Programmspeicher gesetzt. (Stichwort Interruptvektortabelle)
Dort steht entweder ein Sprungbefehl zur Bearbeitung 
(Interruptserviceroutine oder ISR) mit abschließendem "Return from 
Interrupt" oder nur ein "Return from Interrupt" (RETI) der die Adresse 
des letzten Befehles wieder in das Adressregister für das Programm 
schreibt. (Rücksprung)
Nun ist ein Delay ein Befehl, der entweder in einer Schleife eine Anzahl 
unnützer Befehle (NOP) ausführt, um Zeit totzuschlagen oder auch eine 
etwas aufwändigere Programmierung über Zeitregister (Timer). Ersteres 
ist eine berechnete Anzahl von Wiederholungen, da der Controller für 
jede Bearbeitung Zeit benötigt. Etwa so:
Befehl lesen und Adressrregister erhöhen (nächste Befehlsadresse)
 Adresse liefert Befehl Befehl: tu nix (NOP)
Dieser Schritt kann auch ausgelassen werden, entsprechend mehr 
Schleifendurchläufe braucht es dann.
Weiter:
Befehl lesen und Adressrregister erhöhen (nächste Befehlsadresse)
Adresse liefert Befehl: Erhöhe einen Zähler (INC was auch immer)
Befehl lesen und Adressrregister erhöhen (nächste Befehlsadresse)
Adresse liefert Befehl: Vergleiche Zähler mit Vorgabe (CP was auch 
immer)
Befehl lesen und Adressrregister erhöhen (nächste Befehlsadresse)
Adresse liefert Befehl: Sprung wenn Flag (Flag ist ein Bit und wird beim 
Vergleich je nach Ergebnis gesetzt)
Entweder wird also wieder mit dieser Befehlsfolge erneut gearbeitet und 
Zeit vertrödelt, wenn die vorgegebene Anzahl der Schleifendurchläufe 
noch nicht erreicht ist oder die "normale" Programmbearbeitung 
fortgesetzt. Ein Interrupt schiebt sich, egal wo, einfach dazwischen und 
ob in einer Schleife oder den laufenden Programmcode, (Auch letztlich 
eine Schleife) der Controller landet immer wieder beim nächsten Befehl 
vor der Interruptauslösung. (Wenn man nicht irgendeinen Blödsinn in der 
ISR programmiert....) Allerdings verlängert sich in diesem Fall die Zeit 
um die Befehlsverarbeitungszeit in der ISR. Es ist möglich, das ein 
Interrupt mehrfach in einer Schleife erzeugt wird und somit auch 
entsprechend große Abweichungen der Delay-Zeit entsteht. Das Delay über 
Timer erzeugt selbst einen Interrupt mit einer eigenen ISR und bremst 
den Programmzyklus nicht aus. In C-Programmen wird die Bearbeitung so 
nicht deutlich. Da ich mit C nix am Hut hab, kann ich noch nicht mal 
sagen, ob der Programmierer weiß, das ein Delay eine 
programmzyklusverlängernde Wirkung hat oder einen Interrupt auslöst. Das 
könnt ihr besser.
Gruß oldmaX

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.