Bei steigender Flanke in nächste Funktion springen

Hallo zusammen,

Ich habe folgendes vor:
Immer wenn ein wechsel von LOW nach HIGH (steigende Flanke) erfolgt, soll die nächste Funktion aufgerufen werden. Befindet man sich in der letzten Funktion, so soll bei steigender Flanke wieder an den Anfang gesprungen werden.

Nun bin ich soweit, das er bei HIGH an Pin 13 led_1 abarbeitet und bei LOW led_2. Ich möchte dies noch erweitern bis led_10, jedoch weis ich nicht wie das oben beschriebene zu Programmieren ist.

const int inputPin = 2;
const int outputPin = 13;


void setup() {                
  pinMode(outputPin, OUTPUT);
  pinMode(inputPin, INPUT);  
}

void loop(){
if (digitalRead (inputPin) == HIGH) {led_1();}
if (digitalRead (inputPin) == LOW) {led_2();}
}

void led_1()
{
  digitalWrite(outputPin, HIGH);   // set the LED on
  delay(1000);              // wait for a second
  digitalWrite(outputPin, LOW);    // set the LED off
  delay(1000);              // wait for a second
}
  

void led_2()
{
  digitalWrite(outputPin, HIGH);   // set the LED on
  delay(100);              // wait for a second
  digitalWrite(outputPin, LOW);    // set the LED off
  delay(100); 
}

Hoffe ihr könnt mir helfen :slight_smile:

Viele Grüße
Michael

Du liest den inputPin aus und merkst Dir den Zustand in einer Variablen.
Vor dem merken vergleichst Du den aktuellen Zustand mit dem Wert in der Variablen, wenn der inputPin HIGH ist und der Wert in der Variblen LOW, dann hast Du eine steigende Flanke, dann zählst Du eine Zählervariable um eins hoch. Und der Zählerwert ist die Nummer der Funktion, die Du anspringen sollst. :wink:

Ganz richtig, wie es Joghurt sagt. Und wenn deine Zählervariable 10 übersteigt, setzt du sie einfach auf 1 zurück.

Irgendwie passt Dein Sketch aber nicht ganz zu dem, was Du machen willst. Du schreibst, das bei einer steigenden Flanke "reagiert" werden soll, in Deinem Sketch reagierst Du aber nur auf HIGH oder LOW Deines inputPin. Du musst Dir, wie bereits geschrieben aber den alten Zustand merken und dann mit dem aktuellen vergleichen, um einen Flankenwechsel zu erkennen. Interupts würden auch gehen, sind aber etwas komplizierter und unübersichtlicher. Für den Anfang reicht sicher erstmal:

const int inputPin = 2;
const int outputPin = 13;

int count = 1;
int state = LOW;
int laststate = LOW;


void setup() {                
  pinMode(outputPin, OUTPUT);
  pinMode(inputPin, INPUT);  
}

void loop(){
 state = digitalRead (inputPin);
 
 //Flankenwechsel erkennen
 if(laststate == LOW && state == HIGH) {
    //abhängig vom counter passende funktion aufrufen
    switch(count) {
     case 1:     led_1();
                 break;
     case 2:     led_2();
                 break;
     case 3:     led_3();
                 break;
     //hier weitere "case" fälle ergänzen
    }   
    //zähler erhöhen
    count++;
    //zähler zurücksetzen
    if(count == 10) count = 1;
 }
  
 //letzten zustand merken 
 laststate = state; 
}

void led_1()
{
  digitalWrite(outputPin, HIGH);   // set the LED on
  delay(1000);              // wait for a second
  digitalWrite(outputPin, LOW);    // set the LED off
  delay(1000);              // wait for a second
}
  

void led_2()
{
  digitalWrite(outputPin, HIGH);   // set the LED on
  delay(100);              // wait for a second
  digitalWrite(outputPin, LOW);    // set the LED off
  delay(100); 
}

void led_3()
{
  digitalWrite(outputPin, HIGH);   // set the LED on
  delay(100);              // wait for a second
  digitalWrite(outputPin, LOW);    // set the LED off
  delay(100); 
}

Danke für eure Antworten.
Ja, mein Sketch passte nicht zu dem was ich vor habe...da ich nicht weiter kam.
(Hab erst vor kurzem damit angefangen ;))

@mkl0815:
Im Prinzip funktioniert der Sketch den du gepostet hast, jedoch wird jede Funktion bei Tastendruck immer nur einmal abgearbeitet. Hab oben vergessen zu erwähnen, dass er so lange in der vorherigen Funktion bleiben soll bis die Flanke kommt. Also blinken mit 1000ms, Flanke, Blinken mit 100ms, Flanke, etc...

Hallo,

wenn man die Sache mit dem nur-einmal-abarbeiten korrigiert, hat dein Sketch ein Problem: Das Programm hängt ständig in langen delay() fest und wird deshalb sicher viele Tastenklicks verpassen.

Das Zauberwort zur Lösung dieses Problems heißt Interrupt. Zum Glück hast du deinen Taster an Pin 2 angeschlossen (einer der beiden interruptfähigen Pins des Uno), so dass es ungefähr so funktionieren müsste:

volatile int state=1;  //Variablen, die von Iterruptroutinen angefasst werden, sollten "volatile" deklariert werden
volatile unsigned long lastTime=0;  //Zeitpunkt der letzten steigenden Flanke, nötig zum Entprellen

void setup(){
...
  attachInterrupt(0, keyPressed, RISING); // 0 ist der Interrupt für Pin 2, bei jeder steigenden Flanke wird keyPressed() aufgerufen
}

void keyPressed(){                   //state hochzählen, falls mindestens 50ms seit der letzten Flanke vergangen sind
  unsigned long now = millis();
  if (now - lastTime > 50){        //entprellen
    state++;
    if (state > 3){state=1;}
  }
  lastTime=now;
}

void loop(){
  switch (state){  //die passende Ausgaberoutine aufrufen
    ....
  }
}

So funktioniert es erstmal, danke flohzirkus!

Eins ist mir noch aufgefallen, manchmal reagiert er nicht auf den Tastendruck (Das könnte aber auch daran liegen das ich momentan ein Kabel benutze welches den Schalter/Taster simuliert, ohne Widerstand).

Oder ist doch im Programm ein Fehler? (Habs nochmal angehängt)

const int inputPin = 2;
const int outputPin = 13;

volatile int state=1;                   // Variablen, die von Interruptroutinen angefasst werden, sollten "volatile" deklariert werden
volatile unsigned long lastTime=0;      // Zeitpunkt der letzten steigenden Flanke, nötig zum Entprellen


void setup() {                
  pinMode(outputPin, OUTPUT);
  pinMode(inputPin, INPUT);  

attachInterrupt(0, keyPressed, RISING);       // 0 ist der Interrupt für Pin 2, bei jeder steigenden Flanke wird keyPressed() aufgerufen
}

void keyPressed(){                   // state hochzählen, falls mindestens 50ms seit der letzten Flanke vergangen sind
  unsigned long now = millis();
  if (now - lastTime > 50){          // entprellen
    state++;
    if (state > 2){state=0;}
  }
  lastTime=now;
}


void loop(){
    switch(state) {
     case 0:      
          digitalWrite(outputPin, HIGH);  
          delay(1000);                      
          digitalWrite(outputPin, LOW);  
          delay(1000);
          break;
     case 1:     
          digitalWrite(outputPin, HIGH);  
          delay(500);                    
          digitalWrite(outputPin, LOW); 
          delay(500);
          break;
     case 2:     
          digitalWrite(outputPin, HIGH); 
          delay(100);                 
          digitalWrite(outputPin, LOW); 
          delay(100); 
          break;
     //hier weitere "case" fälle ergänzen
    }   
 }

Was mir spontan auffällt ist zum einen, das "state" bei 1 anfängt zu zählen, der switch() - case Block aber bei 0. Der erste Fall "case 0:" wird also nie erreicht.
Außerdem fehlt das "Zurücksetzen" der state Variable. state wird immer weiter erhöht und irgendwann greift keiner der "case" fälle mehr. Du kannst das recht einfach in den "default:" Zweig des "switch - case" Block packen. (siehe auch http://arduino.cc/en/Reference/SwitchCase)

...
     case 2:     
          digitalWrite(outputPin, HIGH); 
          delay(100);                 
          digitalWrite(outputPin, LOW); 
          delay(100); 
          break;
     //hier weitere "case" fälle ergänzen
     ...
     default:
          state = 0;
          break;
}

Ich habs mal geändert, hatte aber irgendwie keine Auswirkungen.
Nachdem ich jedoch eine Pulldown-Widerstand von 10k in die Schaltung eingefügt habe, funktioniert es nun :slight_smile:

Durch eine Erweiterung funktioniert nun auch vor- und zurück schalten:

const int inputPin_0 = 2;
const int inputPin_1 = 3;
const int outputPin = 13;

volatile int state=1;                   // Variablen, die von Interruptroutinen angefasst werden, sollten "volatile" deklariert werden
volatile unsigned long lastTime=0;      // Zeitpunkt der letzten steigenden Flanke, nötig zum Entprellen


void setup() {                
  pinMode(outputPin, OUTPUT);
  pinMode(inputPin_0, INPUT);
  pinMode(inputPin_1, INPUT);    

attachInterrupt(0, keyPressed_0, RISING);       // 0 ist der Interrupt für Pin 2, bei jeder steigenden Flanke wird keyPressed_0() aufgerufen
attachInterrupt(1, keyPressed_1, RISING);       // 1 ist der Interrupt für Pin 2, bei jeder steigenden Flanke wird keyPressed_1() aufgerufen
}

void keyPressed_0(){                   // state hochzählen, falls mindestens 50ms seit der letzten Flanke vergangen sind
  unsigned long now = millis();
  if (now - lastTime > 100){           // entprellen
    state++;
    if (state > 4){state=1;}
  }
  lastTime=now;
}

void keyPressed_1(){                   // state hochzählen, falls mindestens 50ms seit der letzten Flanke vergangen sind
  unsigned long now = millis();
  if (now - lastTime > 100){           // entprellen
    state--;
    if (state < 1){state=4;}
  }
  lastTime=now;
}

void loop(){
    switch(state) {
     case 1:     led_1();
                 break;
     case 2:     led_2();
                 break;
     case 3:     led_3();
                 break;
     case 4:     led_4();
                 break;
     //hier weitere "case" fälle ergänzen
     default:
          state = 1;
          break;
    }   
 }


void led_1()
{
  digitalWrite(outputPin, HIGH);
  delay(1000);
  digitalWrite(outputPin, LOW);
  delay(1000);
}


void led_2()
{
  digitalWrite(outputPin, HIGH);
  delay(500);
  digitalWrite(outputPin, LOW);
  delay(500);
}


void led_3()
{
  digitalWrite(outputPin, HIGH);
  delay(100);
  digitalWrite(outputPin, LOW);
  delay(100);
}


void led_4()
{
  digitalWrite(outputPin, HIGH);
  delay(50);
  digitalWrite(outputPin, LOW);
  delay(50);
  digitalWrite(outputPin, HIGH);
  delay(500);
  digitalWrite(outputPin, LOW);
  delay(500);
}

Nun habe ich aber ein weiteres Problem, die Schaltung soll in ein RC-Modell mit Servo-Ausgängen. Dachte mir ich könnte die 5V des Servo-Ausgang schalten, jedoch ändert sich dann nur die Pulsbreite des PWM-Signals (Hätte mal vorher darüber nachdenken sollen ;))
Wie kann ich das PWM-Signal auswerten?
Folgendes soll passieren:
Ich habe einen 3-Stufen-Schalter (Bei Mittelstellung keine Aktion ausführen, Impulslänge zwischen 1,3 und 1,7ms).
Beim Impulslängen kleiner 1,3ms wird zurückgeschaltet (Schalterstellung unten), bei Impulslängen größer 1,7ms wird weitergeschaltet (Schalterstellung oben).

Werden hierfür beide Interrupt-Eingänge benötigt, oder kann das auch anderst realisiert werden?

Wenn ich dich richtig verstehe, hast du jetzt einen Schalter, der in seinem Zustand bleibt - und keinen Taster, bei dem sich der Arduino merken muss, ob und wie oft er schon gedrückt wurde?

Dann sind Interrupt-Kopfstände und Flankensuche überflüssig, und dein loop() sieht etwa so aus:

void loop(){
  liesSchalterstellung();
  berechneServoWinkel();
  setzeServoWinkel();
}

Zum Kontrollieren von Servos gibt es eine Library, die die ganze Pulsweiten-Ansteuerung im Hintergrund Timer-gesteuert erledigt, schau' da mal rein.

Schönen Sonntag noch!

Hab mich wohl etwas falsch ausgedrückt, momentan ist es ein 3-Stufen-Schalter der aber eigentlich ein (Ein)-Aus-(Ein)-Taster sein soll. Also Ein-Oben state++, Ein-Unten state--.
Dieser Schalter ist in einem Sender für ein RC-Modell verbaut, am Empfänger kommen aber nur Impulse für Servos raus. Diesen Impuls müsste man nun irgendwie am Arduino einlesen.
Bei Schaltermittelstellung ergeben sich Impulslängen von ca. 1.5ms, jedoch wären hier eine Überwachung von +/-0.2ms von Vorteil.

Hoffe nun ist es eher zu verstehen :wink:

Die Servo-Libary ist aber nur zum ansteuern von Servos, oder hab ich da was falsch verstanden?

Dir auch einen schönen Sonntag.

Also hoffe ich, dass ich dich jetzt richtig verstehe: Der Arduino soll Impulse von einem RC-Empfänger auswerten, die eigentlich für einen Servo gedacht sind und damit z. B. verschiedene LED-Blinkprogramme ablaufen lassen. Es ist aber nur ein Servo, der gesteuert wurde, also auch nur eine Leitung als Eingang zum Arduino. Richtig so?

Das bedeutet: du musst die Länge jedes Eingangsimpulses messen und prüfen, ob sie von der Mittelstellung abweicht.
Wenn sie das macht und beim letztenmal noch nicht gemacht hat, dann ändere den Status.

Dazu könnte man z.B. eine Interruptroutine bei jedem Zustandwechsel aufrufen lassen. Bei steigender Flanke merkt man sich die Startzeit und bei fallender berechnet man die Impulslänge und daraus dann die Schalterstellung. Die muss dann in der Interruptroutine mit der letzten Schalterstellung verglichen werden. Abhängig von Ergebnis wird eine Statusvariable gesetzt, die innerhalb von loop() abgefragt wird, um zu entscheiden, was denn nun getan werden muss.

Warum denn einen Interrupt verwenden? Das wäre doch mit Kanonen auf Spatzen geschossen. Ich würde das einfach über die pulseIn-Funktion erfassen.

Warum denn einen Interrupt verwenden?

Weil adawolfs Hauptprogramm längere Zeit in irgendwelchen delays hängt und deshalb immer wieder Impulse verpassen würde - also nicht jeden Tastendruck zählen würde.