Probleme bei Pulsemessung mit pulseIn()

Hallo Forum,

ich werte das Signal meines Anemometers mit einem Hallsensor TLE4905 aus. Bei jeder vollen Umdrehung fährt einmal ein Magnet über den Hallsensor welcher dann über einen open Collector Ausgang das HIGH Signal ( via 10k Pull UP ) auf Masse zieht.

http://img96.imageshack.us/img96/8223/unbenanntwpk.jpg

Dieses Rechtecksignal möchte ich mit PulseIn auswerten, sodass ich die Pulselänge vom HIGH Impuls bzw LOW Impuls messen kann und daraus Rückschlüsse auf die Windgeschwindigkeit zu ziehen.

Was ich aber bemerke ist, dass ich zwar "schnelle Signale" damit verarbeiten kann, jedoch größer als zB 180µS HIGH Impulslänge schafft er nicht mehr zu messen. Im Default ist ja bei pulseIn 1 Sekunde als Timeout eingestellt. Lt Referenz müsst ich bis zu 3min lange Impulse messen können. Um auch sehr geringe Windgeschwindigkeiten ( ab 1km/h ) messen zu können, müsste ich Signale mit bis zu 2sek Periodendauer verarbeiten können.

Doch wieso bekomme ich dass mit pulseIn nicht hin? Gibt es da einen BUG ( entweder bei mir im Code oder in den Arduino Libs? )

/*
Testprogramm für Windgeschwindigkeitsmessung
*/

float g_wind;

void setup()
{
    Serial.begin(19200);
    pinMode(10,INPUT);
    pinMode(2,INPUT);

}

void loop()
{
    g_wind = windSpeed();
    Serial.print("\t Windgeschwindigkeit [m/s] = ");Serial.print(g_wind);Serial.print("\t Windgeschwindigkeit [km/h] = ");Serial.println(g_wind/3.6);
}

float windSpeed()
 {
  // Umfang Kreisbahn für Windmessschale => U = 2.pi.R = 2.pi.0,08m =>0,5m==>500mm==500000µm (notwendig damit gleich durch µS dividiert werden kann )
  const unsigned long Umfang = 500000;
  // unsigned long timeoutH =  2000000; // Wert in µS
  // unsigned long timeoutL =  2000000; // Wert in µS
  unsigned long timeHigh = pulseIn(10,HIGH);  //time in µs für HIGH Impulsdauer
  unsigned long timeLow  = pulseIn(2,LOW); //time in µS für LOW  Impulsdauer
  unsigned long time = timeHigh+timeLow; // Gesamtdauer für eine Umdrehung in µS
  Serial.print("   Debug Time HIGH:");Serial.print(timeHigh);Serial.print("\t Debug Time LOW: ");Serial.print(timeLow);
  float wind = float(Umfang) / float(time); //Ergebnis in km/h ==> div durch 10 muss entfernt werden hier nur für Testzwecke
  return wind;

 }

Bevor ihr euch fragts, wieso ich den LOW Impuls mit einem andern PIN messe.... also mit dem gleichen PIN wie bei PulseIn (.. High) bekomme ich es nicht hin. Da misst er mir dann den HIGH Impuls nicht, sondern nur den LOW Impuls. :cold_sweat:

Wäre für Antworten dankbar.

Gäbe es nicht eine elegantere Lösung? Ich hätte mir sowas in der Art vorgestellt. Ich starte einen Hardwaretimer. Von der Main Loop springe ich zyklisch ( polling ) alle 5 Sekunden in meine Funktion float WindSpeed(), dort warte ich eine gewisse Zeit ( max 1-2Sekunden) bis ich einen Zustandswechsel am Signal feststellen kann ( pos oder neg Flanke ) , dann warte ich wieder bis der nächste Zustandswechsel kommt ( natürlich mit timeOut abgesichert ). Nachdem der Zustandswechsel abgeschlossen ist, vergleiche ich den Start und End Timerwert und errechne mir daraus die Persiodendauer. Ich weiß dass man sowas auch mit der Function micros() machen kann, nur meine Frage: ist diese auch genau genug für meine Signalmessung oder wäre ein laufenden Hardwaretimer besser? Ich will aber keinen interupt haben, da ich meine Programmabläufe einhalten muss. Die Messwerterfassung darf nur via Funktionsaufruf ( durch polling ) erfolgen.

danke

Bitte in Prio 1 auf das PulseIn Problem antworten!

lg Dieter

Was ist "Prio 1"? Grüße Uwe

Hallo,

sollte eigentlich heißen, dass die Antworten auf mein Post in erster Linie auf das pulseIn Problem gehen soll ( priorität 1 ). Ich hoffe somit sämtliche Verwirrungen beseitigt zu haben :)

lg Dieter

Ich will aber keinen interupt haben, da ich meine Programmabläufe einhalten muss.

Verstehe ich nicht ganz: Eine Interruptroutine liefert dir z.B. im Hintergrund direkt die Zeit und Anzahl der letzten Impulse, seit der letzten Abfrage, und deinen Programmablauf kannst du einhalten, ohne auf delay und pulseIn Zeiten achten zu müssen.

d.h mit einer interrupt routine hat man einen Art Parallel Thread? Ich werde somit niemals aus einem Programmteil bei interupt hinausgerissen?

mit einer interrupt routine hat man einen Art Parallel Thread? -- >Ja Ich werde somit niemals aus einem Programmteil bei interupt hinausgerissen? Doch, Interrupt hat eine höhere Priorität.

Dein Programm - loop() - muss Interrupte zu machen, wenn es mehr als 1 byte liest/schreibt, die mit der ISR zusammenhängen und konsistent sein müssen. Diese Variablen müssen als "volatile" deklariert sein, um dem Compiler zu sagen, dass Zugriffe nicht wegoptimiert werden dürfen.

Setze ich dann nach der ISR an der selben Programmstelle wieder fort, wo der Interrupt aufgerufen worden ist? Wie siehts da mit lokalen Variablen aus?behalten diese ihren wert?lg

ids2001: Setze ich dann nach der ISR an der selben Programmstelle wieder fort, wo der Interrupt aufgerufen worden ist? Wie siehts da mit lokalen Variablen aus?behalten diese ihren wert?lg

Ja, gut, ja. Die Interrupt-Routine muss kurz sein ( auch wenn es weh tut zum Testen: kein Serial.print() ) In der Haupt-Schleife merkt man nichts davon, ausser bei globalen Variablen, die von der ISR geschrieben werden.

Soll ich dann die Pulslänge mittels der Function micros() messen oder gleich "richtig" mittels TIMER?

micros() macht es “gleich richtig mittels TIMER”.

Du kannst natürlich den Code von micros() anschauen, sehen, ob was in deinem Spezialfall überflüssig ist und weglassen, oder was anderes (z.B. definierter Startzeitpunkt) in deine spezialMicros() zufügen, und du lernst sicher eine Menge dabei, das Rad neu zu erfinden.

Die Arduino Funktionen zu Interrupts und Timern erlauben längst nicht alles, was direkt an Hardware-Unterstüzung möglich ist, aber wenn die gebotenen Funktionen das machen was man braucht…
( Ich nehme micros() )

Ok danke fuer die gute Unterstuetzung zu Interrupts.

Aber zurueck zum Thema: wieso geht bei mir das pulsein bei langsamen signalen nicht?

unsigned long timeHigh = pulseIn(10,HIGH);  //time in µs für HIGH Impulsdauer
unsigned long timeLow  = pulseIn(2,LOW); //time in µS für LOW  Impulsdauer

Wofür du 2 Pins brauchst, hab ich übrigens nicht verstanden …
Welcher von beiden hängt denn, oder was passiert genau ?
Dein Verfahren lässt in jedem Fall jeden 2. Wechsel aus und braucht Pulse > 10 µs …
http://arduino.cc/forum/index.php/topic,86039.0.html Dieser Thread meint sogar, von Version 0.22 nach 1.0 habe sich etwas verschlechtert …

The timing of this function has been determined empirically and will probably show errors in longer pulses. Works on pulses from 10 microseconds to 3 minutes in length.

Das Wort “empirically” macht mich stutzig …

Die beiden roten Leitungen sind im Kreuzungspunkt verbunden, hoffe ich ?

Du kannst übrigens den internen Pullup Widerstand verwenden.
Und wenn dich nicht unbedingt das Puls/Pause Verhältnis interessiert, und du auf das zeitfressende pulseIn() verzichten kannst, bis du einfacher dran mit

volatile unsigned long pSpeed;  // Globale Variable

void myTimer () {
 static unsigned long lastmicro;
 unsigned long now = micros();
 pSpeed =  now - lastmicro; // zur Demo ohne jedes Filtern ...
 lastmicro = now;
}

void setup() {
  pinMode(2, INPUT);
  digitalWrite(2, HIGH); // pullup aktiv für OpenCollector  
  attachInterrupt(0, myTimer, RISING); // Pin 2 am UNO 
 // ...
}

void loop() {
unsigned long ps;
noInterrupts();
 ps = pSpeed;
interrupts();

//...
float speed = windspeed(ps); // nur noch Umrechnen ...
// ...
}

Nur am Rande: Ist die Windgeschwindigkeit die gleiche wie die Drehgeschwindigkeit des Rädchens ???

Edit: fix code damit der Compiler nicht meckert …

Danke fuer die Infos.

2 Eingaenge habe ich gebraucht damit pulsein ueberhaupt was misst. Denke da is pulsein etwas bugy.....werde es daher nicht mehr verwenden.

Bzgl deiner frage am rande.... Nein die Geschwindigkeit ist nicht gleich der von mir errechneten.Diese muss noch mit einem Faktor x multipliziert werden. Diesen muss ich aber noch ermitteln mit einem gekauften Windmesser als Vergleichsmessung....

Lg dieter

Hi,

ich habe auch sonderbare Unterschiede zwischen den über pulseIn und Interrupt + micros() ermittelten Dauern. Ansonsten konnte ich problemlos sowohl via pulseIn als auch via Interrupt über eine einzelne Leitung (Pin 2, Interrupt 0) Signalflanken ermitteln. Via Interrupt ist es relativ einfach, daher würde ich dass auch empfehlen. Ich kann gern mal Beispielcode posten.

Hey danke für deine Unterstützung mit BspCodes. Jedoch bin ich schon am Ziel angelangt :))) lg Dieter

PS: das Problem mit pulseIn konnte ich nicht lösen..... ( aber egal, werde es zukünftig nicht mehr verwenden... )