PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C-Code für Windmesser



daniel031287
25.03.2014, 16:49
Hallo Forengemeinde,

vor ein paar Wochen habe ich endlich meinen Windmesser fertig stellen können :)

27853
27854
27856
27857

Leider ergibt sich in meinem C-Code ein Problem: Da ich die maximale Windspitze speichern möchte, habe ich eine einfach Abfrage per if-Verzweigung eingefügt. Diese funktioniert auch super, nur leider gibt es (scheinbar) bestimmte Zustände in meinem Code, der dann den max-Wert vom Wind mit "655.3" anzeigt (maximaler int-Wert, siehe Bild). Daraus folgt, dass meine ganze Windmessung manchmal fehlerhaft ist...

Meine Frage: Sieht irgendjemand von euch das Problem, welches diesen Zustand verursacht? Mein Kollege und ich sind mittlerweile "betriebsblind" und haben keine Idee mehr.

Tests mit einem Schmitt-Trigger und einer "1-Sekunden-Mittelwert-Bildung" (aktuelle Windgeschwindigkeit im Display) brachten auch keinen Erfolg... :(

Ich habe meinen Code als zip-Datei angefügt :)

Viele Grüße und danke bereits im Voraus,

Daniel

Besserwessi
26.03.2014, 19:41
Zum einen gibt es da ggf. ein Hardware-Problem: Reed Kontakte können prellen und damit sehr kurze falsche Pulse geben. Das müsste man noch in Hardware oder Sorftware berücksichtigen, etwa indem man nach einer Flanke erst einmal für die nächsten etwa 2-10 ms alle weiteren Pulse ignoriert.


In der Software gibt es vermutlich noch Probleme: Die Variablen, die in ISR und Hauptprgramm genutzt werden, müssen in aller Regel als Volatile markiert werden. Sonst gibt es Probleme mit Optimierung (ohne kann es noch laufen). Ohne Optimierung wird delay.h aber nicht richtig funktionieren.


Kleinere Probleme kann es geben wenn die Auskommentierten LCD Befehle in der Timer ISR genutzt werden - das kann eine vergelichsweise große Verzögerung geben, und damit ggf. einige Ausreißer (nach unten) bei der Windmessung.

daniel031287
26.03.2014, 21:25
Hallo Besserwessi,

danke für deine Antwort. Wie sieht denn genau die volatile-Deklaration aus? (sorry für diese vllt. dumme Frage, bin jedoch nur "Hobbdy-Programmierer"...).
Und du redest von der header-datei "delay". Dort muss ebenfalls etwas optimiert werden? Was denn genau?

Du redest von auskommentierten LCD-Befehlen in der Timer-ISR. Ist das auf meinen Quelltext bezogen? Ich sehe da so keine LCD-Befehle. Zumindest nicht in den/der Timer-ISR.

Das hardware-technische Entprellen habe ich bereits mit einem Schmitt-Trigger mittels OP realisiert. Hat jedoch leider keine Verbesserung gebracht...

Schönen Abend noch und gute Nacht :)

Daniel

Besserwessi
27.03.2014, 16:12
Damit der der Compiler den zugriff auf Variablen nicht wegoptimieren darf, kann man bei der Variablendeklaration ein volatile davor setzen. Als etwa
volatile long Wind ;

Damit optimiert der Compiler den Zugriff auf diese Variabel nicht so sehr, weil ihm gesagt wird das sich der Wert ggf. von extern verändern kann, etwa durch die ISR. Gebraucht wird dies für Variablen die Im Hauptprogramm und einer ISR benutzt werden. Wie viele der Zugriffe der Compiler ohne das Volatile wegoptimiert ist schwer zu sagen - es kann also teilweise auch ohne funktionieren, muss aber nicht - es macht da auch wenig Sinn zu spekulieren wie viel ggf. noch geht - einfach die in der ISR benutzten Variablen als volatile kennzeichnen.

Damit die Delay Funktionen aus delay.h richtig funktionieren muss man mit Optimierung übersetzten, also beim Compiler die Einstellungen -O1. -O2 oder -Os haben. Am Code selber muss man da nichts einstellen, sondern die Einstellung erfolgt in der IDE oder im Makefile.

Der zu große Wert beim Wind könnte gut vom Prellen kommen: ein Entprellen in Software ist da deutlich flexibler und auch zuverlässiger als die HW Lösung: also testen ob die Zeit zu klein ist, und dann den Wert verwerfen.

Peter(TOO)
27.03.2014, 16:52
Hallo,

Damit der der Compiler den zugriff auf Variablen nicht wegoptimieren darf, kann man bei der Variablendeklaration ein volatile davor setzen. Als etwa
volatile long Wind ;QUOTE]

Ein Beispiel:


while (Wind)
{
// mach was
}


Ein optimierender Compiler findet, dass die Schleife viel schneller ist, wenn Wind nur einmal in ein Register geladen wird und dann vergleicht man immer das Register ...

Wenn Wind jetzt aber ein Port ist oder von einer ISR verändert wird, geht das daneben und die Schleife wird nie beendet!

Mit volatile wird der Compiler gezwungen, bei jeder Verwendung von Wind auch den Code für einen Zugriff auf diese Speicherstelle zu erzeugen.

MfG Peter(TOO)

- - - Aktualisiert - - -

Hallo Daniel
[QUOTE=daniel031287;596894]Das hardware-technische Entprellen habe ich bereits mit einem Schmitt-Trigger mittels OP realisiert. Hat jedoch leider keine Verbesserung gebracht...


Ein Schmitt-Trigger kann nicht entprellen, der macht aus dem Rauschpegel nur eine schöne Rechteckspannung ;-)

Ein guter mechanischer Schnapp-Kontakt verhält sich beim Schliessen mehr wie ein Ball. Der hüpft auf seinem Gegenkontakt eine weile rum. Bei guten Kontakten dauert die Hüpfzeit so um die 2-10ms. Beim Öffnen sieht es meist etwas besser aus, aber auch da prellt es.
Schlimmer sind Schiebeschalter, bei welchen die Kontakte aufeinander gleiten. Durch die Unebenheiten der der Kontakte tritt dabei prellen währenden des ganzen Verschiebevorgangs auf.

Entprellen ist also ein zeitliches Problem.
Rein per Hardware entlädt man dazu einen Kondensator und zwar so, dass dieser beim ersten Kontaktschluss entladen wird. Die Zeitkonstante muss dann so gewählt werden, dass sich der Kondensator nicht wieder auf den anderen Logikpegel aufladen kann, bevor der nächste Prell-Impuls auftritt.

Nun bekommt man ein neues Problem:
Logik-Eingänge haben zwischen den definierten Pegelbereichen einen undefinierten Bereich. Hier kann es sogar vorkommen, dass bei bestimmten Pegeln der Eingang schwingt.

Hier hilft dann der Schmitt-Trigger, indem er dafür sorgt, dass immer definierte Pegel am Logik-Eingang anliegen.

MfG Peter(TOO)

daniel031287
28.03.2014, 09:36
Guten Morgen,

ich habe einige Variablen nun als volatile deklariert. Zustätzlich habe ich mich noch schlau gelesen... wieder was dazu gelernt, danke für den Tipp :)

Ich hatte noch die Idee, statt einen Schmitt-Trigger einen schnellen Komparator einzusetzen... nur ob das dann wirklich hilft, ist fraglich.

Der Nachteil beim Software-Entprellen ist doch aber der, dass ich Zeit verstreichen lasse, in der schon der nächste Impuls kommen kann? Wenn ich das Programm nun dort "Zwangsanhalte", laufe ich Gefahr, Impulse bei sehr viel Wind nicht mitzubekommen.
Oder sehe ich das nun falsch?

Viele Grüße und ein schönes Wochenende,
Daniel

Klebwax
28.03.2014, 11:10
Der Nachteil beim Software-Entprellen ist doch aber der, dass ich Zeit verstreichen lasse, in der schon der nächste Impuls kommen kann? Wenn ich das Programm nun dort "Zwangsanhalte", laufe ich Gefahr, Impulse bei sehr viel Wind nicht mitzubekommen.
Das ist prizipiell so. Wenn die "Prellpulse" ähnliche Zeiten haben, wie das Nutzsignal, ist der Sensor ungeeignet. Ob man die Auswertung in Software oder in Hardware

Rein per Hardware entlädt man dazu einen Kondensator und zwar so, dass dieser beim ersten Kontaktschluss entladen wird. Die Zeitkonstante muss dann so gewählt werden, dass sich der Kondensator nicht wieder auf den anderen Logikpegel aufladen kann, bevor der nächste Prell-Impuls auftritt.
macht, ist dabei egal. Der Vorteil der Software ist auf jeden Fall, das man Zeitkonstanten ohne Lötkolben ändern kann.

MfG Klebwax

Besserwessi
28.03.2014, 11:18
Die Zeit die der Schalter mechanisch Prellen kann, muss man so oder so Ausklammern. Ob man das analog oder digital macht,macht da keinen Unterschied. Die Softwarelösung ist da eher noch exakter einstellbar. Normal sollte die Zeit vom Prellen auch so kurz sein, das so kurze Pulse auch bei Sturm kaum vorkommen können. Die Vielleicht 2 ms die ein Reed Kontakt prellt, würden immer noch 250 Hz Signal erlauben, was wohl theoretisch 100 m/s entspräche. Da hat man dann andere Probleme, wenn es mehr wird. Zu sehr übertreiben darf an es mit der Zeit zum entprellen aber halt auch nicht.

Bei der analogen Lösung ist ein Schmidt Trigger schon passend - ein Komparator hilft da nicht wirklich weiter. Die Eingänge beim AVR sind bereits Schmidt-trigger Eingänge - im Prinzip würden da passend bemessene Widerstände / Kondensatoren schon reichen. Man darf auch beide Methoden zum Entprellen kombinieren, also trotz analogem Teil noch mal in Software unrealistisch kurze Zeiten zu verwerfen.

daniel031287
28.03.2014, 11:26
Okay, dann werde ich mich da die Tage mal "ranwagen" und software-technisch entprellen... werde da aber erst Mitte nächster Woche zu kommen, da ich ein paar Tage im Center Parc bin... also nicht wundern, dass bis dahin nichts von mir kommt.

Sobald ich das Entprellen software-technisch realisiert habe, werde ich den Code hier mal hochladen und eure Meinung abwarten... :cool:

Schönes We,

Daniel

daniel031287
03.04.2014, 15:58
Moin zusammen,

ich habe mir nun ein paar Gedanken gemacht und mich ein wenig eingelesen: Das Software-Entrprellen (scheint) ja am sinnvollsten zu sein, wenn man es über Timer realisiert, um unnötige Warteschleifen zu verhindern und das Programm kurze Zeit zu "blocken".
Jedoch habe ich bereits 2 Timer in Verwendung: Sprich, wenn ich nun so programmieren würde, dass der Reed-Kontakt einen externen Interrupt auslöst, würde meine Int-Routine ausgeführt.
Angenommen, ich würde dann eine Variable hochzählen. Meinetwegen bis 5 Zeiteinheiten. Wenn diese 5 Zeiteinheiten erreicht sind, soll nochmal geschaut werden, ob das Signal (in meinem Fall) immer noch "high" ist. Wenn das der Fall ist, soll die Geschwindigkeit errechnet werden.
ABER: Um diese Zeiteinheiten hochzuzählen, würde die Software aus der Int-Routine rausgehen, um zum Beispiel die Timer0-Overflow-Routine aufrufen, um die Variable hochzuzählen. Wenn dies erledigt ist, springt er wieder in die Int-Routine vom Reed-Kontakt zurück. Oder wird dieser Zähler hardwaremäßig hochgezählt, ohne dass die Int-Routine dafür verlassen werden muss?


ISR(INT0_vect){
volatile unsigned long int us = 0;
volatile unsigned long int millisekunde = 0;

//Entprellzeit
_delay_ms(10);

//Interrupt an INT0 -> PD2
//alter Wert 32
us = (volatile unsigned long int) TCNT1 * 32;
TCNT1 = 0;

millisekunde = us / 1000;

// v = s/t
// Durchmesser Schale Mitte = 92mm
//Umfang = 92mm * pi = 2890mm
//laut Datenblatt Windweg = 0,4m
//Wert 1445

wind = 2890 / millisekunde; //*10 für Kommastelle
GIFR |= (1 << INTF0);
}

Eine einfache Wartezeit von nun beispielsweise 10ms wäre doch auch eine Entprellung? 10ms sind aber sehr wahrscheinlich zu hoch gegriffen...

Ich habe gerad irgendwie einen "Knoten im Kopf"... :confused:
Hoffentlich ist das nun auch rübergekommen, wo gerad mein Problem ist.

Gruß

Besserwessi
03.04.2014, 17:19
Die ISR von der Flanke muss man schon zu ende kommen lassen, denn sonst geht es auch mit dem Hauptprogramm nicht weiter. Was man machen kann, ist etwa in der ISR vom Int0 erst einmal Int0 zu deaktivieren und dann beim Timer das Compare register so zu programmieren, dass nach etwa 5 ms ein Timer interrupts kommt, denn man dann auch Einschalter. Die ISR für den Start ist dann schon fertig. In der Timer ISR deaktiviert man dann den Timer Interrupt wieder und erlaubt dafür wieder den Int0. Die Flanke auf die er reagiert muss man dabei ggf. je nach Zustand des Eingangs noch anpassen. Das Ende der Messung macht dann wieder die ISR zu Int0. Dabei kann dann ggf. auch gleich die Nächste Messung gestartet werden.

Ein Problem bekommt man ggf. noch bei sehr niedriger Geschwindigkeit: da kann der eine Pulse schon länger als 5 ms sein, und man hat es schwer das Ende des Pulses (mit ggf. Prellen als Störung) vom Anfang des nächsten Pulse zu unterscheiden.

Peter(TOO)
03.04.2014, 17:48
Hallo,

Man kann eine ISR aufsetzen, welche z.B. alle 1ms den Eingang abfrägt.

Der aktuelle Wert wird dann in ein Schieberegister eingeschoben.

Wenn man 8 Bit verwendet (8ms) und das Schieberegister den Wert 0xFF hat, stand am Eingang für mindestens 8ms eine 1 an.

Bei 0xFF wird dann der Impuls ausgewertet und ein Flag gesetzt. Ist das Flag schon gesetzt wird nicht ausgewertet.
Das Flag setzt man bei != 0xFF zurück.


Bei Tasten habe ich meistens einfach nur alle 20ms den Eingang abgefragt und dann nur den aktuellen mit dem vorigen Zustand verglichen.
Wenn man die Tasten dann noch in ein Bit-Array packt, kann man alle Tasten gleichzeitig entprellen.

MfG Peter(TOO)

daniel031287
04.04.2014, 18:16
Sooo, ich habe nun mal eine Funktion key_scan programmiert. Diese wertet jede Millisekunde die Taste aus und zählt dabei eine Variable count um einen hoch. Wenn die Variable bei 8 ist, also für 8ms die Taste bzw. der Reedkontakt "1" war, wird request = 1 und in der ISR dann die Bedingung für die Auswertung wahr.

Kann sich das einer von euch bitte mal anschauen, ob das so überhaupt logisch erscheint? Ich verwende nun 3 Timer und 4 Interrupts... es stellt sich da auch gerade für mich die Frage, welche Variablen da als volatile deklariert werden sollten.
Ich hoffe, dass ich da keinen mit auf die Nerven gehe, aber da mir die Programmier-Routine fehlt, kommt da die ein oder andere vllt. "dumme" Frage auf...

PS: Eigentlich kann ich den Tastenport doch gar nicht so abfragen, da mein Reedkontakt auch mal bei keinem Wind so stehen bleiben kann, dass permament High-Signal anliegt und damit die Bedingung wahr wird, obwohl eigentlich gar kein Wind anliegt...? :confused:

Vielen Dank und Gruß
Daniel

daniel031287
18.04.2014, 16:44
Hallo zusammen,

ich habe das Problem lösen können... ich habe einfach den externen Interrupt deaktiviert, sobald er einmal aufgerufen worden ist. Nach 3ms anschließend wieder aktiviert...

Frohe Ostern,

Gruß
Daniel