Forum: Mikrocontroller und Digitale Elektronik Arduino delay(ms) in ISR?


von Daniel B. (yzdani)


Lesenswert?

Servus,

wie kann ich ein Delay in der ISR des Arduinos verwenden?

Meine  ISR sieht so aus:

-Kofig
1
attachInterrupt(digitalPinToInterrupt(2),fire, RISING);
-ISR
1
void fire(){
2
  for(int i =3;i<7;i++){
3
      digitalWrite(i,HIGH);
4
      delay(1000);
5
      digitalWrite(i,LOW);
6
      delay(1000);
7
    
8
      
9
    }
10
 }

Danke im Voraus!

Gruß
Dani

von Stefan S. (sschultewolter)


Lesenswert?

Zum Glück garnicht, soweit ich weiß. Schreibe deinen Code nicht 
blockierend, dann musst du auch nicht die ISRoutine missbrauchen!

Einfach ein flag setzen, und in der loop auswerten.

von Daniel B. (yzdani)


Lesenswert?

Stefan S. schrieb:
> Einfach ein flag setzen, und in der loop auswerten.

Danke für deine Antwort!
Bin Neuling bei den Timern... hast du mir  einen Link  zu einer Quelle?
Oder eine Erklärung  wie ich das am besten mit einem Flag und einer 
Schleife löse?

Gruss

Daniel

von Karl H. (kbuchegg)


Lesenswert?

1
volatile uint8_t firePulse;
2
3
4
void fire(){
5
  firePulse = 1;
6
}
7
8
9
10
....
11
12
13
void loop()
14
{
15
  if( firePulse == 1 ) {
16
      firePulse = 0;
17
18
      digitalWrite(i,HIGH);
19
      delay(1000);
20
      digitalWrite(i,LOW);
21
      delay(1000);
22
  }
23
24
  ....
25
}

aber eigentlich sind auch hier die delay nicht besonders toll.

http://playground.arduino.cc/Learning/BlinkWithoutDelayDe

: Bearbeitet durch User
von Daniel B. (yzdani)


Lesenswert?

Danke für deine Antwort!

versuche es gerade mit dem flexitimer...werde berichten
Blinken ist ja leicht aber eine LED 100ms leuchten lassen dann 200ms 
pause und dann wieder von vorne ist nicht so leicht...

von Amateur (Gast)


Lesenswert?

Ich bin der Überzeugung: Wer delay() verwendet hat offensichtlich nicht 
besseres zu tun.

Wird diese Aussage wörtlich genommen, so ergeben sich zwei sehr einfache 
Schlussfolgerungen:
1. Trifft diese Aussage ohne Einschränkung zu, so kann man problemlos
   auch mal 12 Sekunden in einer Interrupt-Routine rumlungern.
2. Trifft dies aber nicht zu, so hilft nur noch "Üben".

Ob die Patentantwort - "dann verlagere das Problem doch in die 
Hauptschleife" - wirklich hilft, ist eine andere Sache.

von Dieter S. (Gast)


Lesenswert?

Daniel R. schrieb:
> Danke für deine Antwort!
>
> versuche es gerade mit dem flexitimer...werde berichten
> Blinken ist ja leicht aber eine LED 100ms leuchten lassen dann 200ms
> pause und dann wieder von vorne ist nicht so leicht...

Auch dass ist lösbar über eine Library:
https://github.com/robertgallup/BobaBlox

von Karl H. (kbuchegg)


Lesenswert?

Daniel R. schrieb:

> versuche es gerade mit dem flexitimer...werde berichten
> Blinken ist ja leicht aber eine LED 100ms leuchten lassen dann 200ms
> pause und dann wieder von vorne ist nicht so leicht...

doch ist es.
selbes Prinzip. Du hast eine Uhr in Form von millis(). Du bestimmst, 
wann ein Vorgang beendet sein soll, in dem du zum Zeitpunkt an dem der 
Vorgang beginnt die Zeit nimmtst, die geplante Dauer dazuaddierst und 
laufend (bei jedem durchlauf durch loop) überprüfst, ob dieser zeitpunkt 
schon vorbei ist.

Aus praktischen Gründen macht man es technisch ein klein wenig anders. 
Man nimmt die Zeitdifferenz zwischen 'jetzt' (repräsentiert durch 
millis()) und dem Beginn der Aktion und sieht nach ob diese 
zeitdifferenz schon größer ist als die geplante Zeitdifferenz.
Jede Hausfrau kann das. Stellt sie das Gulasch auf den Ofen, schaut sie 
auf die Küchenuhr. ZUr abgelesenen Zeit kommen 2 Stunden dazu und ab zu 
wird auf die Uhr geschaut, ob dieser Zeitpunkt erreicht ist. Wenn sie 
wissen will, wie lange das Gulasch schon kocht, dann liest sie die 
aktuelle Zeit von der Küchenuhr ab, zieht davon die Startzeit ab und 
findet so raus, dass das Gulasch schon seit anderthalb Stunden kocht. 
Das ist weniger als die geplanten 2 Stunden, daher darf es noch weiter 
kochen.


Ganz simpel.
1
unsigned long blinkStart;
2
3
void setup()
4
{
5
  blinkStart = millis();
6
}
7
8
void loop()
9
{
10
  unsigned long now = millis();
11
12
  if( now - blinkStart < 100 )
13
    led einschalten
14
15
  else if( now - blinkStart < 300 )  // 100 + 200; ein kompletter Blinkzyklus dauert daher 300 ms
16
                                     // in der ersten Hälfte des Zyklus war die LED ein
17
                                     // jetzt geht es darum, das sie bis zum Ende
18
                                     // des Blinkzyklus aus sein muss
19
    led ausschalten
20
21
  else                               // ein kompletter Blinkzyklus ist abgelaufen
22
                                     // neuen starten indem man sich merkt
23
                                     // wann das der Fall war.
24
    blinkStart = now;
25
}

und damit gelingt es dann auch ganz problemlos, 2 LED mit völlig 
unterschiedlichen Zeiten blinken zu lassen. Zb die eine mit 100+200 ms 
und die andere mit 400+700ms
1
unsigned long blinkStart1;
2
unsigned long blinkStart2;
3
4
void setup()
5
{
6
  blinkStart1 = millis();
7
  blinkStart2 = millis();
8
}
9
10
void loop()
11
{
12
  unsigned long now = millis();
13
14
  if( now - blinkStart1 < 100 )
15
    led1 einschalten
16
17
  else if( now - blinkStart1 < 100 + 200 )
18
    led1 ausschalten
19
20
  else
21
    blinkStart1 = now;
22
23
  // uuuuuund die 2.te LED
24
25
  if( now - blinkStart2 < 400 )
26
    led2 einschalten
27
28
  else if( now - blinkStart2 < 400 + 700 )
29
    led2 ausschalten
30
31
  else
32
    blinkStart2 = now;
33
}

: Bearbeitet durch User
von Thomas H. (Firma: CIA) (apostel13)


Lesenswert?

Wer delay's benutzt kann nicht programmieren. Und das kommt von dem 
absolut untauglichen Einsteigerbeispiel eine LED mit einem delay blinken 
zu lassen. Ein vernünftiges Einsteigerbeispiel wäre es einmal zwei oder 
drei LED's gleichzeitig blinken zu lassen und alle mit einer anderen 
Frequenz und ohne delay's. Dann wäre eine ganze Menge für die Zukunft 
geschient. Und zwar richtig.

von Bastler (Gast)


Lesenswert?

Was bisher bei delay() im Interrupt (Arduino Sketch) übersehen wurde:
Funktioniert wohl sowieso nicht, Zitat aus Arduino Reference:

"Since delay() requires interrupts to work, it will not work if called 
inside an ISR"

Daher ist der letzte Ansatz von Karl Heinz schon passend.

von Ulrich F. (Gast)


Lesenswert?

Bastler schrieb:
> "Since delay() requires interrupts to work, it will not work if called
> inside an ISR"
Das ist der wesentliche Punkt!

von Guest (Gast)


Lesenswert?

Thomas H. schrieb:
> Und das kommt von dem
> absolut untauglichen Einsteigerbeispiel eine LED mit einem delay blinken
> zu lassen. Ein vernünftiges Einsteigerbeispiel wäre es einmal zwei oder
> drei LED's gleichzeitig blinken zu lassen und alle mit einer anderen
> Frequenz und ohne delay's.

Genau und wer als Baby anfängt zu Krabbeln hat sich schon das ganze 
Leben versaut, man hat sofort aufzustehen und über Kopfsteinpflaster zu 
laufen.
Ist das jetzt die Aroganz der Ingenieure oder das Kastendenken der Elite 
die untersich bleiben will und allen den Einstieg möglichst schwer 
machen will?
Natürlich ist das einfachste Blinken mit Delay der beste Anfang um nicht 
gleich gleich abgeschreckt oder als Anfänger mit seinen Fragen in diesem 
Forum vor den Kopf gestossen zu werden.

von Ulrich F. (Gast)


Lesenswert?

Irgendwann, kommt man an den Punkt, wo einem delay() nicht mehr weiter 
hilft. Dann ist weiterlernen angesagt.
Was ist daran schlimm?

von Marcel P. (souko)


Lesenswert?

Geht auch viel einfacher, ganz ohne Variablen und ganz unabhängig 
voneinander ohne blockierendes delay():
1
#define LED_PIN_1 12
2
#define LED_PIN_2 13
3
4
void setup() {
5
6
  pinMode(LED_PIN_1, OUTPUT);
7
  pinMode(LED_PIN_2, OUTPUT);
8
}
9
10
11
void loop() {
12
  if (millis() % 1000 < 500) digitalWrite(LED_PIN_1, HIGH);   // 1000 ist die Periodenlänge (Frequenz), 500 is ON-Zeit
13
  else digitalWrite(LED_PIN_1, LOW);
14
  
15
  if (millis() % 750 < 125) digitalWrite(LED_PIN_2, HIGH);    // zweite LED, völlig unabhängige Zeit
16
  else digitalWrite(LED_PIN_2, LOW);
17
}

von Ulrich F. (Gast)


Lesenswert?

Marcel P. schrieb:
> Geht auch viel einfacher
1
const byte LED_PIN_1 = 12;
2
const byte LED_PIN_2 = 13;
3
4
void setup() 
5
{
6
  pinMode(LED_PIN_1, OUTPUT);
7
  pinMode(LED_PIN_2, OUTPUT);
8
}
9
10
11
void loop() 
12
{
13
  digitalWrite(LED_PIN_1,millis() % 1000 < 500);
14
  digitalWrite(LED_PIN_2,millis() % 750  < 125);
15
}

von xXx (Gast)


Lesenswert?

Thomas H. schrieb:
> Wer delay's benutzt kann nicht programmieren.

Sagt Jemand, der mit Deppenapostrophen um sich wirft...

von Daniel B. (yzdani)


Lesenswert?

vielen Dank für die Antworten!

Frage:
1
int valuemillis = millis() % 1000;
2
    
3
      Serial.println(valuemillis);

bekomme ich am Terminal nicht 0 1 2 3 4,.... sondern, 0 0 0 1 1 1 2 2 3 
3 3

möchte jetzt nach ende einer Periode den Pin wechseln.
zuerst pin 3 dann 4 dann 5 usw...

so funktioniert es ja nicht:
1
  
2
int a =3;
3
int ende = 40;
4
5
if(valuemillis ==0)
6
a++;
7
8
if(valuemillis ==ende)
9
a=3
10
11
//das a setze ich dann beim timer für die Einzelne LED wie oben genannt ein

so würde er ja bei einer Periode gleich 4 5 6 zählen.

Gruss
Daniel

von Marcel P. (souko)


Lesenswert?

Ulrich F. schrieb:
> Marcel P. schrieb:
>> Geht auch viel einfacher
>
>
1
>   digitalWrite(LED_PIN_1,millis() % 1000 < 500);
2
>   digitalWrite(LED_PIN_2,millis() % 750  < 125);
3
4
>

@Ulrich Du Fuchs ! Daran hab ich noch gar nicht gedacht ^^

@Daniel

Du hast glaube ich nicht verstanden was millis() sowie % bedeutet, oder?

Modulo (%) gibt den Rest einer Division zurück. D.h. deine Variable 
valuemillis zählt immer von 0 ms bis 999 ms hoch, je nachdem wie 
schnell/oft du deine Berechnung aufrufst.

: Bearbeitet durch User
von Daniel B. (yzdani)


Lesenswert?

millis() % 1000 =Periodendauer von 1000ms richtig?

wenn ich die Periodendauer abrufe bekomme ich nicht 0 1 2 3 4 5 6,. 
sondern
0 0 0 1 1 2 2 2 3 3 3 4 4,... hätte ich nur 1 2 3,...  wär das Problem 
mit nem einfachen IF lösbar.

von Ulrich F. (Gast)


Lesenswert?

> ... sondern, 0 0 0 1 1 1 2 2 3 ...

>  0 0 0
3 Durchläufe in 1 ms

> 1 1 1
3 Durchläufe in 1 ms

>  2 2
2 Durchläufe in 1 ms

von Daniel B. (yzdani)


Lesenswert?

Sooo  habs jetzt anderst gelöst:

So praxistauglich?
1
#define LED_PIN_1 4
2
#define LED_PIN_2 5
3
#include <FlexiTimer2.h>
4
5
int i =3;
6
int a =0;
7
int ms = 0;
8
bool starten = false;
9
10
int pulse =  99;
11
int periode= 100;
12
13
void setup() {
14
15
  pinMode(3, OUTPUT);
16
  pinMode(4, OUTPUT);
17
   pinMode(5, OUTPUT);
18
  pinMode(6, OUTPUT);
19
  Serial.begin(115200);
20
21
  FlexiTimer2::set(1, givems); 
22
  FlexiTimer2::start();
23
}
24
25
26
void loop() {
27
28
 }
29
 
30
 void givems(){
31
 ms++;
32
 Serial.println(ms);
33
34
 
35
  if(ms<periode){
36
    digitalWrite(i, LOW);
37
    if(ms<pulse){
38
    digitalWrite(i, HIGH);}
39
  
40
  }else
41
    {
42
      ms=0;
43
      i++;
44
   if(i>6)
45
    i=3;
46
    }
47
 }
48
49
 void  led(){ 
50
  ms=0;
51
 }

Gruß
Dani

: Bearbeitet durch User
von Thomas H. (Firma: CIA) (apostel13)


Lesenswert?

Guest schrieb:
> Natürlich ist das einfachste Blinken mit Delay der beste Anfang um nicht
> gleich gleich abgeschreckt oder als Anfänger mit seinen Fragen in diesem
> Forum vor den Kopf gestossen zu werden.

Das Einfachste ist noch lange nicht das didaktisch sinnvollste.

Guest schrieb:
> Genau und wer als Baby anfängt zu Krabbeln hat sich schon das ganze
> Leben versaut, man hat sofort aufzustehen und über Kopfsteinpflaster zu
> laufen.

Die biologische Entwicklung des ZNS, sowie die psychomotorische 
Entwicklung von Lebewesen haben nichts mit der Didaktik in der 
Erwachsenenbildung zu tun!

von M. K. (sylaina)


Lesenswert?

Marcel P. schrieb:
> Geht auch viel einfacher, ganz ohne Variablen und ganz unabhängig
> voneinander ohne blockierendes delay():
>
>
1
>
2
>   if (millis() % 1000 < 500) digitalWrite(LED_PIN_1, HIGH);   // 1000 
3
>
4
>

Tja, ich sehe da zwei Variablen: 1000 und 500. Nur weil du nicht z.B. 
int myVar = 1000; geschrieben hast heißt das nicht, dass die 1000 nicht 
doch eine Variable sind. ;)

von Ziegenpeter (Gast)


Lesenswert?

Michael K. schrieb:
> Tja, ich sehe da zwei Variablen: 1000 und 500

Ich weiß ja nicht welche Sprache du sprichst, aber in meiner nennt sich 
sowas Konstante, nicht Variable.

von M. K. (sylaina)


Lesenswert?

Ziegenpeter schrieb:
> Ich weiß ja nicht welche Sprache du sprichst, aber in meiner nennt sich
> sowas Konstante, nicht Variable.

Ja, aber ob das eine (veränderbare Variable) ist oder Konstante ist dem 
Compiler und dem AVR relativ egal, beides wird SRAM benötigen. Es ist 
also raltiv wurscht ob man schreibt myVar1 = myVar2 + myVar3 oder myVar 
= 100 + 400. Der "Vorteil" bei letzterem ist nur, sofern der Compiler 
klug genug ist, dass es ggf. weniger SRAM belegt als ersteres (je 
nachdem wie man myVar2 und myVar3 definiert hat).

von Wolfgang (Gast)


Lesenswert?

Bastler schrieb:
> Was bisher bei delay() im Interrupt (Arduino Sketch) übersehen wurde:
> Funktioniert wohl sowieso nicht, Zitat aus Arduino Reference:
>
> "Since delay() requires interrupts to work, it will not work if called
> inside an ISR"

Diese Schlussfolgerung ist doch wohl Unfug. Warum soll in einer ISR kein 
weiterer Interrupt abgearbeitet werden können?
Was hindert einen nach anfänglichen Aktionen innerhalb der ISR, die 
Abarbeitung von ISR wieder frei zu geben.

von Ulrich F. (Gast)


Lesenswert?

Michael K. schrieb:
> Ja
In c/C++ ist relativ klar definiert, was eine Konstante, und was eine 
Variable ist.
Und wenn du einer Sprachverwirrung unterliegst, dann ist das dein 
Problem.
Daran kannst du arbeiten, oder es bleiben lassen...

von M. K. (sylaina)


Lesenswert?

Wolfgang schrieb:
> Diese Schlussfolgerung ist doch wohl Unfug. Warum soll in einer ISR kein
> weiterer Interrupt abgearbeitet werden können?
> Was hindert einen nach anfänglichen Aktionen innerhalb der ISR, die
> Abarbeitung von ISR wieder frei zu geben.

Einfach mal nachschaun wie delay() innerhalb der Arduino-Umgebung 
definiert ist. Ganz offenbar so, dass es innerhalb einer ISR nicht 
benutzt werden kann/darf ;)

von Ulrich F. (Gast)


Lesenswert?

Wolfgang schrieb:
> Was hindert einen nach anfänglichen Aktionen innerhalb der ISR, die
> Abarbeitung von ISR wieder frei zu geben.
Einen klugen Menschen wird nichts daran hindern....
Aber er wird sich dann über die Eintrittsinvarianz seines eigenen 
Interrupts ebenfalls Gedanken machen dürfen...

Nein!
Delay() in Interrupts ist keine gute Idee.

von M. K. (sylaina)


Lesenswert?

Ulrich F. schrieb:
> Michael K. schrieb:
>> Ja
> In c/C++ ist relativ klar definiert, was eine Konstante, und was eine
> Variable ist.
> Und wenn du einer Sprachverwirrung unterliegst, dann ist das dein
> Problem.
> Daran kannst du arbeiten, oder es bleiben lassen...

Ja, deshalb heißt es in C/C++ ja auch "Konstante Variable", also eine 
Variable die nach ihrer Initialisierung nicht mehr verändert werden 
kann. Das ändert aber auch nichts daran, dass sie genauso viel RAM 
benötig wie eine Variable, die nach ihrer Initialisierung verändert 
werden kann.

Oben war nur die Rede davon, dass der Code ganz ohne Variable gehen 
würde und dass nur, weil man der "Variablen" keinen Namen verpasst hat. 
Wer das aber glaubt weiß schlicht nicht wie der Compiler arbeitet. Man 
muss sich immer bewusst machen wenn man z.B. irgendwo im Code eine 100 
hin schreibt der Compiler daraus sehr wohl eine "Variable" macht. Und 
die liegt dann im RAM.
Am besten sieht man das IMO bei Strings. Es gibt bei den AVRs ja das 
Makro PSTR(string). Hinter PSTR verbirgt sich aber nur eine Anweisung 
für den Compiler den String nicht ins RAM zu kopieren, was er sonst 
machen würde, sondern ihn im Flash zu lassen.

von Ulrich F. (Gast)


Lesenswert?

Michael K. schrieb:
> Wer das aber glaubt weiß schlicht nicht wie der Compiler arbeitet.

Auf die Gefahr hin, dass mich mein Kompiler belügt:
int test = 3 + 5;
Beansprucht 2 Byte Ram auf meinem Arduino getriebenen AVR.
Wenn 3 und 5 konstante Variablen im Ram wären, würde ich 6 Byte 
erwarten.

Vielleicht liebt mich mein Kompiler?
Vielleicht möchte er dir auch einen Stock zwischen die Beine werfen?
Wer kann es wissen...

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.