C++ Arduino IF mit mehreren Bedingungen

spcqike

Lt. Commander
Registriert
Dez. 2008
Beiträge
1.548
Hallo zusammen,

Vorweg: der Titel ist bestimmt unpassend gewählt, leider weiß ich nicht, wie ich mein Problem kurz und knapp benenne und wahrscheinlich hab ich daher auch bei Google nichts gefunden :(

Ich möchte ein 5V Signal mit dem Arduino messen und, je nach anliegender Spannung, einen oder zwei (von drei möglichen) Ausgängen schalten.
Soweit läuft der Code auch, nur habe ich einen Knoten im Kopf und bekomme mein Problem nicht behoben...

Code:
C++:
//Start Definition der Konstanten
const int sensorPin = A0;   // Analogpin Eingang
const int Low_1 = 434;      //unterer Grenzwert 1 Ausgang1+2
const int High_1 = 475;     //oberer Grenzwert 1
const int Low_2 = 502;      //unterer Grenzwert 2 Ausgang 1+3
const int High_2 = 543;     //oberer Grenzwert 2
const int Low_3 = 549;      //unterer Grenzwert 3 Ausgang 1
const int High_3 = 590;     //oberer Grenzwert 3
const int Low_4 = 659;      //unterer Grenzwert 3 Ausgang 2+3
const int High_4 = 700;     //oberer Grenzwert 3
const int Low_5 = 737;      //unterer Grenzwert 3 Ausgang 2
const int High_5 = 778;     //oberer Grenzwert 3
const int Low_6 = 948;      //unterer Grenzwert 3 Ausgang 3
const int High_6 = 989;     //oberer Grenzwert 3

int sensorValue = 0;        // Variable für Messwert
byte out = 0;

void setup() {
  DDRD = B00111100;         //setze Pin D2,D3,D4,D5 auf Ausgang
}

void loop() {
while(1){
      PORTD ^= B00100000;
      sensorValue = analogRead(sensorPin);
      if(sensorValue>=Low_1 && sensorValue<=High_1) { PORTD |= B00001100; goto ende; } else { PORTD &= B11110011; }
      if(sensorValue>=Low_2 && sensorValue<=High_2) { PORTD |= B00010100; goto ende; } else { PORTD &= B11101011; }
      if(sensorValue>=Low_3 && sensorValue<=High_3) { PORTD |= B00000100; goto ende; } else { PORTD &= B11111011; }
      if(sensorValue>=Low_4 && sensorValue<=High_4) { PORTD |= B00011000; goto ende; } else { PORTD &= B11100111; }
      if(sensorValue>=Low_5 && sensorValue<=High_5) { PORTD |= B00001000; goto ende; } else { PORTD &= B11110111; }
      if(sensorValue>=Low_6 && sensorValue<=High_6) { PORTD |= B00010000; goto ende; } else { PORTD &= B11101111; }
      ende:;
    }
}

Problem:
Durch die 6 IF Abfragen, werden die gesetzten Ausgänge auch gelöscht, obwohl sie es nicht sollten.
Bspw. bei einer Spannung von 4,7V (ADC Wert von 962, also zwischen Low_6 und High_6). Da der Wert nicht zwischen Low2 und High2 liegt, greift die dortige else Bedingung und setzt den Ausgang auf low. Anschließend wird bei If Nr. 6 wieder auf High gesetzt. Dies führt zu einem ~3ns langem Low-Signal auf der Leitung, was in der Endanwendung zu einem Brummen oder einer anderen Störung führen kann.

Ich möchte gern, dass die aktiven Ausgänge aktiv bleiben, ohne unnötige Rechenleistung zu verbraten. Ein verschachteltes IF(IF(IF(IF))) würde zwar das falsche Deaktivieren verhindern, im Zweifelsfalls aber 12 Abfragen beinhalten... Das Programm läuft aktuell mit ~8,94kHz und soll auch möglichst schnell bleiben.
Das XOR in Zeile 25 wird noch entfernt, das dient aktuell nur zum Nachmessen der Durchlauffrequenz.

Leider stehe ich wie gesagt auf dem Schlauch und bekomme diesen wahrscheinlich dummen und hoffentlichen einfachen Fehler nicht behoben :(

Hat jemand eine Idee für mich? :)

Grüße
 
Hi,

fang es logisch an: momentan macht dein Programm genau was es soll! Du prüfst 6 Fälle, einer davon ist korrekt, daher sind alle anderen nicht korrekt.

Was muss denn für eine Bedingung herrschen, so dass das "else" eben NICHT aufgerufen wird? Bzw. wann genau DARF das else aufgerufen werden?

Um bei deinem Beispiel zu bleiben: 4,7V (ADC Wert von 962, also zwischen Low_6 und High_6).

Das wäre also "Fall 6". Wenn wäre denn der else Fall von Fall 6? Du hast keine Definition, wann der else Fall eintreten soll bzw. darf. Wie soll diese aussehen?

VG,
Mad
 
Über die Ausführgeschwindigkeit des Codes musst du dir erstmal keine Sorgen machen.
AnalogRead läuft mit max 10kHz laut Spec, der Arduino aber selbst hat doch irgendwas um die 16Mhz oder?

Mein Tipp wäre, um Glitches zu vermeiden, speicher den Ausgangswert in einer Variablen ab und setze den Ausgang nur einmal pro Schleife. (am Ende) Es macht doch keinen Sinn den Ausgang so oft zu ändern, (du schreibst ja direkt raus) wenn alle Zwischenwerte unwichtig sind, oder?
 
  • Gefällt mir
Reaktionen: werner_q und Revan1710
So wie es jetzt gelöst ist, gibt es im schlimmsten Fall (Fall 6) auch 12 Abfragen. Von daher könntest Du die Ifs auch verschachteln.
 
Zuletzt bearbeitet:
Braucht man im loop() eigentlich nochmal ein while(1)?

Ich würde die Lows und Highs sowie die Bitmasken in Arrays (ggf. als structs) packen und eine rekursive Funktion aufrufen, mit inkrementiertem Index/Zeiger.
 
lege 3x Arrays an und dann durch eine schöne FOR Schleife alle Arrays durchgehen und bei EQUAL abbrechen.
und bitte keine gotos verwenden :(
 
Also ich würde die Ports definiv erst schalten, wenn auch klar ist, was das Ziel ist - so schaltest du die Ausgänge ja immer erst falsch, bevor du sie richtig setzt. Außerdem arbeitest du auch unnötig viele if-Abfragen ab.

Ich würde es in etwa so machen:

Code:
void loop() {
    uint16 mask;
  
    while(1){
      sensorValue = analogRead(sensorPin);
     
      if (sensorValue>=Low_1 && sensorValue<=High_1)
          mask = B00001100;
      else if (sensorValue>=Low_2 && sensorValue<=High_2)
          mask = B00010100;
      else if (sensorValue>=Low_3 && sensorValue<=High_3)
          mask = B00000100;
      else if (sensorValue>=Low_4 && sensorValue<=High_4)
          mask = B00011000;
      else if (sensorValue>=Low_5 && sensorValue<=High_5)
          mask = B00001000;
      else if (sensorValue>=Low_6 && sensorValue<=High_6)
          mask = B00010000;
      else
          mask = B00000000;
     
       // falsche Ausgänge deaktivieren und richtige aktivieren
       PORTD &= mask;
       PORTD |= mask;
    }
}
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: Raijin und werner_q
Danke für eure Inputs.

@Madman1209,
die Else sind nur drin, damit die Ausgänge wieder auf low gesetzt werden, wenn ich nicht im Bereich liege.

ich denke, der Vorschlag von @KingLz und dessen Umsetzung von @Revan1710 sind genau das, was mit gefehlt hat :) werde ich mal testen.

@DaZpoon,
das While(1) ist drin, weil ich gelesen hab, dass dies schneller is als das permamente Durchlaufen von loop(). Zugegeben, ich hab es gar nicht ohne While(1) getestet und mich da nur auf 2-3 Jahre alte Foreneinträge verlassen :)

Gruß
 
Könntest Du den gemessenen Wert nicht eigentlich auf 0..7 normalisieren und dann direkt ausgeben? Also 7*Messwert/1000 2x shift damit du auf die Ausgänge kommst wo du hinwillst und fertig? Ich verstehe nämlich den Hintergrund nicht ganz, weil zwischen den Lows und Highs auch so Lücken sind, es gibt ja dann Messwerte denen kein Bitmuster zugeordnet ist?
Ok, gerade gelesen, das ist Absicht.
 
Korrekt, das ist Absicht :)

@Revan1710,
leider funktioniert dein Code nicht wie gedacht/gewünscht.
1. schimpft die Arduino IDE bei der Deklaration (uint16_t)
2. setzt PORTD &= ~mask; die Ports nicht zurück. ein einmal aktivierter Port bleibt aktiv :)

Da ich den D5 nur zu Analysezwecke wechseln lasse, und ihn im eigentlichen Einsatz nicht benötige, kann ich die Register auch direkt beschreiben, ohne auf die Zustände der restlichen Bits Rücksicht nehmen zu müssen :) (im ursprünglichen Code Zeile 25)

C++:
void loop() {
    uint16_t mask;

    while(1){
      sensorValue = analogRead(sensorPin);
      
      if (sensorValue>=Low_1 && sensorValue<=High_1)
          mask = B00001100;
      else if (sensorValue>=Low_2 && sensorValue<=High_2)
          mask = B00010100;
      else if (sensorValue>=Low_3 && sensorValue<=High_3)
          mask = B00000100;
      else if (sensorValue>=Low_4 && sensorValue<=High_4)
          mask = B00011000;
      else if (sensorValue>=Low_5 && sensorValue<=High_5)
          mask = B00001000;
      else if (sensorValue>=Low_6 && sensorValue<=High_6)
          mask = B00010000;
      else
          mask = B00000000;
      
       // falsche Ausgänge deaktivieren und richtige aktivieren
       PORTD = mask;
    }
}

So funktioniert das Programm, wie es soll. Die Ausgänge bleiben aktiv, oder werden inaktiv, je nach anliegender Spannung, bei 8,94kHz.

Vielen Dank :)
 
Stimmt - blind programmieren ist immer so ne Sache :p

Also beim Zurücksetzen braucht man die Maske überhaupt nicht zu invertieren. Dann wäre das aber sowieso wie eine einfach Zuweisung und man müsste explizit nochmal sicherstellen, dass man keine anderen Bits damit noch wegbügelt.
 
Hallo,

kurze Rückmeldung nochmal.
Folgender Code deaktiviert die nicht benötigten Ausgänge und setzt/behält die gewünschten.

C++:
      PORTD &= ( B11100011 | mask );
      PORTD |= mask;

Plus: bestimmt nicht kriegsentscheidend, aber ich habs bei mir dennoch geändert. Eine 8 Bit-Maske kann natürlich in einem uint8_t definiert werden :)
 
Ich würde dir eine einfache Zuweisung empfehlen (deinen Code komprimiert):

C:
PORTD = (PORTD & ( B11100011 | mask )) | mask;

Weil wenn du 2x den Port änderst, hast du immernoch min. 1 Takt einen ungewollten Zustand. ( | mask noch nicht angewendet)
 
Zurück
Oben