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ß
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.
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
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.
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.
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:
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?
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
voidloop(){
2
for(inti=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
voidloop(){
2
for(inti=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
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.