Hallo, es geht um eine vermeintlich simple Angelegenheit, jedoch klappt es bei mir nach vielen Versuchen immer noch nicht. Aufgabe: Vier Lichtschranken (eine pro Bahn), detektieren fallende Teile als positive Flanke und geben bei jeder Auslösung ein digitales Signal aus. Durch die Geometrie der Teile ist eine doppelt Auslösung der Lichtschranke möglich, muss aber für eine einstellbare Zeit, z.B 1s deaktiviert sein. --> Alles über eine SoftwareAuswertung, aber meine Versuche mit millis() scheitern bisher Klingt einfach, ich bekomm es aber leider nicht gebacken. Ich freue mich über jede Hilfe! Vielen Dank. Gruß
:
Verschoben durch User
Code hier hochladen. Sperre würde ich bei Erkennung setzten und per timer runterzählen. Nachtrag wenn du eh schon eine Zeitbasis (millis() klingt danach) hast, dann merk dir einfach die letzte Zeit und vergleich die.
:
Bearbeitet durch User
T1waz T. schrieb: > Durch die Geometrie der Teile ist eine doppelt Auslösung der > Lichtschranke möglich, muss aber für eine einstellbare Zeit, z.B 1s > deaktiviert sein. Der Lichtschranke wirst du nur schwer verbieten können, ein zweites Mal auszulösen, sofern du nicht nach der ersten Auslösung den Sender abschaltest. Am einfachsten wird es sein, wenn du das zweite Signal in deiner Software ignorierst, z.B. indem du einen neuen Unterbrechungspuls erst nach einer gewissen Mindestzeit akzeptierst.
Wolfgang schrieb: > T1waz T. schrieb: >> Durch die Geometrie der Teile ist eine doppelt Auslösung der >> Lichtschranke möglich, muss aber für eine einstellbare Zeit, z.B 1s >> deaktiviert sein. > > Der Lichtschranke wirst du nur schwer verbieten können, ein zweites Mal > auszulösen, sofern du nicht nach der ersten Auslösung den Sender > abschaltest. > > Am einfachsten wird es sein, wenn du das zweite Signal in deiner > Software ignorierst, z.B. indem du einen neuen Unterbrechungspuls erst > nach einer gewissen Mindestzeit akzeptierst. Das war wohl schlecht ausgedrückt, ich will nicht die Lichtschranke pausieren, sondern wie du sagst, erst nach einer Mindest Zeit das nächste Signal akzeptieren. Genau daran scheitere ich. Ich poste später mal den Code
Hier ist mein Code.
1 | const int counterPin = D2; |
2 | const int sensorPin[4] = {D3,D4,D5,D6}; |
3 | |
4 | const long interval = 2000; |
5 | unsigned long previousMillis[4] = {0,0,0,0}; |
6 | unsigned long currentMillis[4]; |
7 | int sensorPinBlocked[4] = {0,0,0,0}; |
8 | int buttonState[4]; |
9 | int lastButtonState[4]; |
10 | unsigned long myCounter = 0; |
11 | unsigned long myCounterOld = 0; |
12 | |
13 | void setup() { |
14 | // put your setup code here, to run once:
|
15 | |
16 | Serial.println("Hau rein...."); |
17 | pinMode(counterPin, OUTPUT); |
18 | |
19 | for (int i = 0; i < 4; i++) { |
20 | pinMode(sensorPin[i], INPUT); |
21 | }
|
22 | Serial.begin(9600); |
23 | }
|
24 | |
25 | void loop() { |
26 | for (int i = 0; i < 4; i++) { |
27 | currentMillis[i] = millis() + interval; |
28 | buttonState[i] = digitalRead(sensorPin[i]); |
29 | |
30 | if (myCounter == HIGH) { |
31 | if (currentMillis[i] - previousMillis[i] >= interval){ |
32 | previousMillis[i] = currentMillis[i]; |
33 | sensorPinBlocked[i] = HIGH; |
34 | } else { |
35 | sensorPinBlocked[i] = LOW; |
36 | }
|
37 | }
|
38 | |
39 | if (sensorPinBlocked[i] == LOW) { |
40 | if (buttonState[i] != lastButtonState[i]) { |
41 | myCounter = HIGH; |
42 | }else { |
43 | myCounter = LOW; |
44 | }
|
45 | }
|
46 | |
47 | delay(299); |
48 | lastButtonState[i] = buttonState[i]; |
49 | }
|
50 | }
|
soweit meine Idee. Wo die Flankenauswertung zurückgesetzt wird, ist mir nicht ganz klar. Zusätzliche Frage: Gibt es eine gute Möglihckeit für Debuggen oder Simulieren, um das Programm zeilenweise mit Werten zu sehen?
T1waz T. schrieb: > soweit meine Idee. > Wo die Flankenauswertung zurückgesetzt wird, ist mir nicht ganz klar. Wenn das Deine Idee ist, solltest Du eigentlich wissen, wo Du was zurück setzt. Ich finde es recht kompliziert und schwer zu verstehen. Schreib das Programm einfach erstmal nur für eine Lichtschranke. Und erst, wenn es funktioniert, erweitere es.
Die Logik ist verkehrt, sensorPinBlocked ist nur für eine Schleifeniteration HIGH, sonst immer LOW. Daher wird wohl dann immer myCounter=HIGH. Wobei ich den Code für etwas unübersichtlich halte.
1 | currentMillis[i] = millis() + interval; |
2 | buttonState[i] = digitalRead(sensorPin[i]); |
3 | |
4 | if (myCounter == HIGH) { |
5 | if (currentMillis[i] - previousMillis[i] >= interval){ |
6 | previousMillis[i] = currentMillis[i]; |
7 | sensorPinBlocked[i] = LOW; |
8 | } else { |
9 | sensorPinBlocked[i] = HIGH; |
10 | }
|
11 | }
|
12 | |
13 | if (sensorPinBlocked[i] == LOW) { |
14 | if (buttonState[i] != lastButtonState[i]) { |
15 | myCounter = HIGH; |
16 | lastButtonState[i] = buttonState[i]; |
17 | }else { |
18 | myCounter = LOW; |
19 | }
|
20 | }
|
21 | |
22 | delay(299); |
23 | //lastButtonState[i] = buttonState[i];
|
:
Bearbeitet durch User
T1waz T. schrieb: > Genau daran scheitere ich. > > Ich poste später mal den Code Nee, laß das mal mit dem Posten von nicht funktionierendem Code. Aber warum scheiterst du an so etwas einfachem? Ich komme mir schon vor wie eine tibetanische Gebetsmühle, aber anstatt mal zuzuhören und zu lernen, halten sich die Leute ihre Augen fest zu. Also lerne aus der Lernbetty oder irgend einem anderen Zeugs, was ich hier mal gepostet hatte und arbeite mit Events. Das löst dein Problem im Handumdrehen. Wie geht's? Anstatt auf irgendwas zu warten, wird das Eintreffen irgendeines Ereignisses in eine Event-Warteschlange geschrieben und dann im Grundprogramm ausgewertet. Und mit einer Systemuhr (so alle 1..10 ms oder so) kannst du dann auch verzögerte Events haben. Das sind welche, die erst nach einer von dir wählbaren Zeit von der Systemuhr in die Event-Warteschlange geschrieben werden. mit sowas kannst du "Entprell"-Totzeiten machen, ohne deinen µC irgendwie mit Warten auf Entprellung zu blockieren. W.S.
1 | if input1 { |
2 | if age1 > x { |
3 | count1; |
4 | age1 = millis(); |
5 | } |
6 | } |
Das ganze einfach 4mal untereinander in die Loop, fertich. Vermutlich. Ist natürlich pseudo und schnell mal hingelegt.
W.S. schrieb: > Aber warum scheiterst du an so etwas einfachem? Gute Frage, darum habe ich hier gepostet. Was Event Warteschlangen sind, bzw. wie ich sowas implementiere in meine Anforderung, weiß ich leider nicht. Könnte daran liegen, dass ich mich noch nicht ewig damit beschäftige Zu meiner Frage mit Simulation /Debugging, gibts da irgendwas? # Gruß
W.S. schrieb: > arbeite mit Events seid wann hat Arduino events? > mit sowas kannst du "Entprell"-Totzeiten machen, ohne deinen µC > irgendwie mit Warten auf Entprellung zu blockieren. das ist doch garnicht das Problem hier. Thomas W. schrieb: > sensorPinBlocked ist nur für eine Schleifeniteration HIGH, sonst immer > LOW. stimmt nicht ganz. nach einer Flanke am Pin außerhalb der "Totzeit" wird myCounter high, dadurch wird sensorPinBLocked high bis Ende der "Totzeit" und myCounter bleibt gezwungenermaßen high. Am Ende der "Totzeit" wird sensorPinBlocked low, erst dann kann myCounter low werden, aber dann ist sensorPinBlocked auf low festgesetzt. Probleme bei deinem Code: Wenn der Pin sich innerhalb der Blockierzeit ändert, wird die Erkennung bis zum Ende der Blockierzeit aufgeschoben. myCounter ist sowohl nach einer steigenden als auch einer fallenden Flanke high, und zwar für die "Totzeit" *(stimmt so nicht ganz, siehe nächster Absatz). beide Flanken sind gleichwertig, wenn der Pin high wird wird genau gleich behandelt udn gibt intern das gleiche Signal als ob der Pin low wird. das Echte Problem ist, dass PreviousMillis nur am Ende der letzten Totzeit gesetzt wird. das heißt deine "Totzeit" beginnt nicht bei einer Flanke zu zählen, sondern bei Ende der letzten "Totzeit". daher ist die Dauer der echten Totzeit auch nicht 2000ms sondern (2000ms - Zeit seit Ende der letzten "Totzeit"). Der beginn der Totzeit muss beim Ändern des Pins gesetzt werden. Beispiel - wenn vor 10s die letzte Flanke kam, ist previousmillis die Zeit von vor 8s - bei der nächsten Flanke wird myCounter high - im nächsten durchlauf ist die Totzeit bereits >8s, daher bleibt sensorpinblocked low - nun wird sofort myCounter wieder low, da die "Totzeit" schon lange abgelaufen ist. -> das funktioniert bei steigender und fallender Flanke gleich das hier dürfte besser funktionieren:
1 | if (currentMillis[i] - previousMillis[i] >= interval){ |
2 | sensorPinBlocked[i] = LOW; |
3 | } else { |
4 | ...
|
5 | |
6 | if (buttonState[i] != lastButtonState[i]) { |
7 | previousMillis[i] = currentMillis[i]; |
8 | myCounter = HIGH; |
9 | lastButtonState[i] = buttonState[i]; |
10 | }else { |
:
Bearbeitet durch User
Ich hab noch einen Vorschlag (weil mir grad langweilig war):
1 | unsigned long lastEventTime[4] = {0,0,0,0}; |
2 | bool lastInput[4] = {digitalRead(sensorPin[0], |
3 | digitalRead(sensorPin[1], |
4 | digitalRead(sensorPin[2], |
5 | digitalRead(sensorPin[3]}; |
6 | |
7 | for (int i = 0; i < 4; i++) { |
8 | unsigned long now = millis(); |
9 | bool input = digitalRead(sensorPin[i]); |
10 | |
11 | if (input != lastInput[i]) { |
12 | lastInput[i] = input; |
13 | if (now < lastEventTime[i] + interval) { |
14 | // ignore
|
15 | }
|
16 | else { |
17 | lastEventTime[i] = now; |
18 | // do something here
|
19 | }
|
20 | }
|
21 | delay(299); |
22 | }
|
:
Bearbeitet durch User
Thomas W. schrieb:
1 | > if (now < lastEventTime[i] + interval) { |
2 | > // ignore |
3 | > } |
4 | > else { |
5 | > lastEventTime[i] = now; |
6 | > // do something here |
7 | > } |
8 | > } |
9 | > delay(299); |
10 | > } |
Dein Vorschlag hat aber ein inherentes Problem, das nach etwa 49 Tagen ununterbrochenen Betriebs zum Vorschein kommt. Spielt hier vielleicht keine Rolle, sollte aber von vornherein vermieden werden. Wenn "interval = 2000" ist, und wenn "lastEventTime[i] = now" bei einem millis() Stand von z.B. (2^32 - 1000) gesetzt wird, dann wird beim nächsten Schleifendurchlauf in der Abfrage "if (now < lastEventTime + interval)" die Summe von "lastEventTime[i] + interval" =1000 ergeben. Da aber millis() noch keinen Überlauf hatte, wird die Abfrage gleich wieder nach "do something here" reinlaufen, obwohl die Zeit noch nicht abgelaufen ist. Also Zeitspannen immer über eine Differenz abprüfen: if (now - lastEventTime[i] >= interval)
T1waz T. schrieb: > W.S. schrieb: >> Aber warum scheiterst du an so etwas einfachem? > > Gute Frage, darum habe ich hier gepostet. Also keine gute Frage von W.S. T1waz T. schrieb: > Was Event Warteschlangen sind, bzw. wie ich sowas implementiere in meine > Anforderung, weiß ich leider nicht. Brauchst Du auch nicht zu wissen. Man muß nicht alles unnötig verkomplizieren. Warteschlangen braucht man erst, wenn temporär mehr Ereignisse eintreffen, als gerade behandelt werden können. Z.B. ne GUI ist gerade damit ausgelastet, nervtötende Animationen auf das GLCD zu malen.
ich danke euch für die Mühen. Sobald ich Zeit habe, werde ich mich dem Problem weiter widmen, anschließend hoffe ich, ich kann meine Lösung präsentieren. Nochmals zu der Frage mit Simulation /Debugging, gibts da irgendwas?
Kommt auf die Hardware drauf an. Beim Arduino wohl nur printf...
Arduino ist für easy use, da gibt es sowas nicht. Unter gewissen Umständen kannst du einfach hier und da via print Werte ausgeben, aber das kann z.B. timings versauen und braucht RAM.
Hab jetzt eine Kombi aus dem von MinMax und Thomas W., vielen Dank, scheint genau zu machen, was ich will.
1 | if (now - lastEventTime[i] <= interval) |
hier nur in kleiner Zeichen umgedreht.
1 | void loop() { |
2 | for (int i=0; i<4; i++){ |
3 | currentMillis = millis() + interval; |
4 | input = digitalRead(sensorPin[i]); |
5 | |
6 | if (input != lastInput[i]) { |
7 | lastInput[i] = input; |
8 | |
9 | if (currentMillis - previousMillis[i] <= interval){ |
10 | Serial.println(" blocked:" ); |
11 | Serial.println(input); |
12 | digitalWrite(counterPin, LOW); |
13 | delay(1); |
14 | }
|
15 | else { |
16 | previousMillis[i] = currentMillis; |
17 | // do something here
|
18 | Serial.println("counting:" ); |
19 | Serial.println(input); |
20 | digitalWrite(counterPin, HIGH); |
21 | }
|
22 | }
|
23 | |
24 | }
|
25 | |
26 | }
|
das
1 | currentMillis = millis() + interval |
habe ich gesetzt, dass nach dem einschalten sofort einmal gezählt werden kann,somit ist das quasi als Startwert definiert. Bei irgendwelchen Anmerkungen oder Kritik gerne nochmals posten. Ansonsten bedanke ich mich für die fast durchgehende sinnvolle Kritik. Gruß Roman
war leider etwas vorschnell, da mein externer Zähler das nicht packt. Möglicherweise das Signal zu kurz oder die Zeit dazwischen zu klein. daher folgende Lösung
1 | void loop() { |
2 | for (int i = 0; i < 4; i++) { |
3 | currentMillis[i] = millis() + interval; |
4 | input[i] = digitalRead(sensorPin[i]); |
5 | pulseDuration = millis(); |
6 | |
7 | //Zeit zwischen zwei Signalen, bevor Pin auf High gesetzt werden kann
|
8 | currentMillisCounter = millis(); |
9 | if (counter != counterOld) { |
10 | if (currentMillisCounter - previousMillisCounter <= intervalCounter + pulseInterval) { |
11 | //do nothing
|
12 | } else { |
13 | previousMillisCounter = currentMillisCounter; |
14 | counterOld = counterOld + 1; |
15 | digitalWrite(counterPin, HIGH); |
16 | pulseDurationOld = pulseDuration; |
17 | }
|
18 | }
|
19 | |
20 | //Länge des Signals, danach der Pin auf Low gesetzt
|
21 | if (pulseDuration - pulseDurationOld <= pulseInterval) { |
22 | //do Nothing
|
23 | }else{ |
24 | digitalWrite(counterPin, LOW); |
25 | }
|
26 | |
27 | //Flankenauswertung der Lichtschranken, erhöht einen internen Zähler
|
28 | if (input[i] != lastInput[i]) { |
29 | lastInput[i] = input[i]; |
30 | |
31 | if ((currentMillis[i] - previousMillis[i] <= interval)) { |
32 | //do nonthing
|
33 | }
|
34 | else { |
35 | if (input[i] == HIGH){ |
36 | previousMillis[i] = currentMillis[i]; |
37 | // do something here
|
38 | counter = counter + 1; |
39 | }
|
40 | }
|
41 | }
|
42 | |
43 | }
|
44 | |
45 | }
|
:
Bearbeitet durch User
K. S. schrieb: > seid wann hat Arduino events? "seit". Und: Wenn man es will, hat es jeder µC. Das Prinzip ist ausgesprochen einfach: Es gibt ne Event-Warteschlange (ein simper FIFO bzw. Ringpuffer) und einige Funktionen zum Füllen, Leeren, Abfragen und für Sonstiges. Gefüllt wird diese Warteschlange von diversen Quellen: Interrupts und sonstigen Routinen. Geleert wird sie in der Grundschleife, die ja zumeist in main() angesiedelt ist. Und für verzögerte Events (eben das, was hier gebraucht wird) braucht man noch irgend ein Uhrprogramm im µC, also nen Interrupt, der so alle 1..10 ms eine ISR aktiviert. Siehe angehängtes Zeug. Ich hatte das wohl schon mal gepostet und entsinne mich, daß sich da Leute drüber aufgeregt hatten, daß ich beim SysTick auch eine simple Wartefunktion vorgehalten habe. Sowas ist nützlich für den System-Anlauf, wenn wesentliche Teile der Firmware noch nicht laufen, weil sie ja erstmal aufgesetzt werden müssen. Für später baucht man sowas dann nichr mehr. So, mal ein Beispiel:
1 | #define Totzeit 1000 // Millisekunden
|
2 | |
3 | #define Schranke_1 1
|
4 | #define Tot_1 101
|
5 | |
6 | // und für weitere Schranken:
|
7 | #define Schranke_2 2
|
8 | #define Tot_2 102
|
9 | |
10 | #define Schranke_3 3
|
11 | #define Tot_3 103
|
12 | // usw.
|
13 | |
14 | // Interrupt-Handler für Lichtschranke 1:
|
15 | if (!Exists_Delayed_Event(Tot_1)) |
16 | { Add_Event(Schranke_1); |
17 | Add_Delayed_Event(Totzeit,Tot_1); |
18 | }
|
So, das reicht. Ist doch ein recht kurzes Stück Quelle. Jedesmal, wenn die Lichtschranke anspricht, wird getestet, ob die Totzeit schon vorüber ist. Wenn ja, dann wird ein Event "Schranke_1" in die Queue geworfen. Der wird dann in main() gelesen und eine entsprechende Aktion ausgeführt (was auch immer). Zusätzlich wird auch ein Event "Tot_1" mit einer Verzögerung von 1000 ms erzeugt. Solange der noch in der zeitabhängigen Warteschlange ist, wird die schranke ignoriert. Auch diesen Event kriegt main() dann ab, wenn seine Zeit um ist und kann dann auch darauf reagieren. Ach ja, das angehängte Zeugs ist für 32 Bit µC's, weswegen die Events als 32 Bit Worte definiert sind. Für kleiner µC's kann das problemlos zusammengekürzt werden wenn man nur wenige Event-Typen braucht. Ebenso kann man das Zeitraster von Millisekunden und Zeiten in long auf ein gröberes Zeitraster von vielleicht 10 oder 20 ms und Zeiten in int zusammenkürzen, was auf 8 Bittern dann weniger Ressourcen braucht. W.S.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.