Go Down

Topic: Arduino Periodendauer messen (Read 2377 times) previous topic - next topic

Selachii

Nov 24, 2012, 04:38 pm Last Edit: Nov 24, 2012, 10:38 pm by Selachii Reason: 1
Hallo,

ich möchte mit folgendem Code die Periodendauer (in µs) einer Rechteckschwingung messen.
Es klappt soweit auch ganz gut, jedoch kommen in bestimmten Abständen Ausreißer die ich mir nicht ganz erklären kann.

Code: [Select]

volatile unsigned long T=0,last=0;
void setup () {
  Serial.begin(9600);
  pinMode(5,INPUT);
  attachInterrupt(5,TMessen,RISING);
}

void loop ()
{
  Serial.println(T);
}


void TMessen ()
{
  detachInterrupt(5);
  T=micros()-last;
  last=micros();
  attachInterrupt(5,TMessen,RISING);
}



Ausgabe (Rechtecksignal mit FG erzeugt,ca. 8000Hz)  :

Code: [Select]
...
124
124
4294966420
124
124
124
1124
124
123
123
4294966420
124
124
124
124
124
124
124
124
124
124
124
124
123
...




Addi

/ \    _|  _| o
 /--\ (_| (_| |

sth77

So wie es aussieht, findet hier ein Variablenüberlauf statt: http://arduino.cc/en/Reference/UnsignedLong
Mein Arduino-Blog: http://www.sth77.de/ - letzte Einträge: Teensy 3.0 - Teensyduino unter Window 7 - Teensyduino unter Windows 8

Selachii

#3
Nov 24, 2012, 05:23 pm Last Edit: Nov 24, 2012, 05:40 pm by Selachii Reason: 1
Danke für die schnelle Antwort.
Gibt es eine Möglichkeit sich nicht immer auf die andauernd anwachsende micros-Zeit  zu beziehen.
Z.B. einen Timer zu starten der wieder auf 0 gesetzt werden kann?

michael_x

#4
Nov 24, 2012, 05:44 pm Last Edit: Nov 24, 2012, 05:55 pm by michael_x Reason: 1
Wenn du das detachInterrupts / attachInterrupts weglässt, sollte micros() sich innerhalb deiner interrupt-Routine nicht ändern. Dann funktioniert die Differenz-Bildung auch bei Überlauf.

Oder du machst in TMessen:

Code: [Select]
unsigned long current = micros(); // nur einmal lesen
T=current-last;
last=current;


Edit: Aber eigentlich sollten mit deiner Methode nur ein paar micros fehlen ... Differenzbildung geht auch über den Überlauf hinweg richtig

Selachii

Ok, habe jetzt den Code wie folgt verändert um den Einfluss des Überlaufs zu reduzieren:

Code: [Select]
volatile unsigned long T=0,last=0;

void setup () {
Serial.begin(9600);
pinMode(5,INPUT);
attachInterrupt(5,TMessen,RISING);
}

void loop ()
{
Serial.println(T);
}

 
void TMessen ()
{
unsigned long current = micros();
if (current>last)
{T=current-last;}
last=current;
}








Allerdings treten noch immer einige "Störungen" auf:
Code: [Select]

...
125
125
125
9
125
125
125
125
125
...
125
1125
125
125
124
125
...

sth77

#6
Nov 24, 2012, 07:39 pm Last Edit: Nov 24, 2012, 07:52 pm by sth77 Reason: 1
Kurze Zwischenfrage: Du sagst, dass das zu messende Signal 8000 kHz hat.
Mit t=125 µS=0,125ms=0,000125s ergibt sich f=1/t=8000Hz. Oder habe ich einen Denkfehler?

[Edit] Und mit welchem Arduino-Board experimentierst du? Pin 5 deutet auf einen Arduino Due hin, oder?
Mein Arduino-Blog: http://www.sth77.de/ - letzte Einträge: Teensy 3.0 - Teensyduino unter Window 7 - Teensyduino unter Windows 8

Selachii

Ja, stimmt.
Woran könnten die Abweichungen liegen bzw. wie kann ich diese minimieren?

jurs

#8
Nov 25, 2012, 03:45 pm Last Edit: Nov 25, 2012, 08:08 pm by jurs Reason: 1

Ja, stimmt.
Woran könnten die Abweichungen liegen bzw. wie kann ich diese minimieren?


 detachInterrupt(5);
...
 attachInterrupt(5,TMessen,RISING);

Was sollten denn diese Aufrufe innerhalb der Interrupt-Routine bewirken, ausser das Interrupt-System völlig aus dem Takt zu bringen?

Und im übrigen, rechne bitte mal nach, wie oft Deine loop-Funktion läuft, wie viele Zeichen da wohl pro Sekunde ausgegeben werden sollen, wieviele Zeichen pro Sekunde bei der gesetzten Baudrate tatsächlich über die Schnittstelle rausgehen und wie oft pro Sekunde der serielle Sendepuffer wohl schätzungsweise überläuft!

Ändere Deine loop-Funktion mal auf:

void loop ()
{
Serial.println(T);
Serial.flush();
}

Und wie sieht es dann aus?

Selachii

Erst einmal danke  für die Antworten.
Leider besteht das Problem trotz „Serial.flush()" weiterhin.

Code: [Select]
volatile unsigned long T=0,last=0;

void setup () {
Serial.begin(9600);
pinMode(5,INPUT);
attachInterrupt(5,TMessen,RISING);
}

void loop ()
{
Serial.println(T);
Serial.flush();
}

 
void TMessen ()
{
unsigned long current = micros();
if (current>last)
{T=current-last;}
last=current;
}

jurs


Leider besteht das Problem trotz „Serial.flush()" weiterhin.


Leider stören sich auch regelmäßig die Timer-Interrupts beim Senden über die serielle Schnittstelle und die Rising-Interrupts Deiner Frequenz, da es ziemlich kleine gemeinsame Vielfache zwischen der Baudrate (9600) und der Frequenz (8000) gibt.

6*8000 = 48000
5*9600 = 48000

D.h. ein Konflikt tritt rein rechnerisch alle 48000 Schwingungen = ca. 6 Sekunden auf.

Kommt das ungefähr hin, ein Fehler alle ca. 6 Sekunden?
Oder mit welcher Häufigkeit gibt es die Ausreißer (circa) bei den Messwerten?

mkl0815

Blöde Frage, aber warum verwendest Du nicht
pulseIn()?
http://arduino.cc/en/Reference/PulseIn

michael_x

Quote
Blöde Frage, aber warum verwendest Du nicht pulseIn()?

Weil das ein ganz anderer Ansatz ist ... ohne Interrupts ist zu einfach  ;)

Aber, was mir auffällt:
volatile unsigned long T; 
darf eigentlich in loop() nicht "einfach so" verwendet werden, da der Zugriff auf die 4 byte von T von der Interrupt-Routine unterbrochen werden kann.

Rein theoretisch müsste es so aussehen:
Code: [Select]
void loop()
{
noInterrupts(); // oder cli();
unsigned long tmp = T;
interrupts();    // oder sei();
Serial.println(tmp);
Serial.flush();
}


Da aber der "richtige" Wert nur ca. 125 ist, spielt das hier keine Rolle und dein Effekt rührt wohl daher, dass Serial oder gar Timer-Interrupts manchmal deinen Interrupt 5 blockieren.

Da du ja sowieso asynchron zur Messung deine Ausgaben machst: Wie wäre es mit einer "statistischen" Auswertung ( max/min/avg ) oder sammeln eines Histogramms ( Anzahl Werte <123, 123, 124, 125, 126, 127 , >127 ) und Ausgabe erst nach der Messung ? Da brauchst du auch nicht so auf den Schirm zu starren, um Ausreisser zu sehen, bevor sie durchgerauscht sind.


In >100 µs hast du eigentlich Zeit genug ...

Udo Klein

Benutze doch den Input Capture Interrupt. Schau Dir mal mein Beispiel hier an: http://blog.blinkenlight.net/experiments/measurements/power-grid-monitor-2/.
Check out my experiments http://blog.blinkenlight.net

Selachii

#14
Dec 11, 2012, 06:41 pm Last Edit: Dec 11, 2012, 06:45 pm by Selachii Reason: 1
Das mit dem Input Capture Interrupt scheint auf jedenfall sehr nützlich zu sein, da es hardwareseitig abläuft und dadurch nicht so schnell durch andere Interrupts gestört werden kann (... wenn ich das richtig verstanden habe).
Ist das Ganze in dieser Form auch auf den ARM basierenden DUE übertragbar oder sollte dazu das Datenblatt zu Rate gezogen werden?

Leider fange ich gerade erst an mich in solche µc-Themen einzulesen...

Deswegen auch die Frage:
Gibt es innerhalb der "Arduino-Sprache" die Möglichkeit hardwareseitig die ankommenden Impulse zu zählen und den Zähler dann im "Hauptprogramm" auszulesen und im Anschluss einen Reset durchzuführen (dazu die Zeit-Diff. mittels micros() ermitteln)?


Go Up