ich steh grad irgendwie auf der Leitung - finde den Fehler nicht:
Ich möchte mit untenstehendem Code einen Puls, der
zwischen ca. 16us - 30 us schwankt, vermessen.
Dazu möchte ich nicht die pulseIn() Funktion von Arduino verwenden (die hat nur 1us Auflösung), sondern den Input Capture Mode von Timer1 meines Arduino Nano Clones (hat einen Atmega328p).
Leider erhalte ich auf meinem OLED-Display "kippende" Werte angezeigt:
z.B. abwechselnd 14 und 65522,
oder 13 und 65523,
ab und an auch schon mal nur 0.
Neben den kippenden Werten wundert mich auch, dass ich nicht Werte um 16us/(1/16Hz)= 254 herum erhalte.
Bitte schaut Euch einmal den Code an - ich bin irgendwie zu blind, den Fehler zu finden.
Viele Grüße
Igel1
1
#include <Arduino.h>
2
#include <U8g2lib.h>
3
#include <SPI.h>
4
#include <Wire.h>
5
6
#include <avr/io.h>
7
#include <util/delay.h>
8
#include <avr/interrupt.h>
9
10
11
// Set pinout
12
int pin = 8; // pin for puls input from oscillator
13
14
// Constructor - for OLED display library - see u8g2 documentation
Neben den kippenden Werten wundert mich auch, dass ich nicht Werte um
16us/(1/16Hz)= 254 herum erhalte.
Bitte schaut Euch einmal den Code an - ich bin irgendwie zu blind, den
Fehler zu finden.
Dein Ansatz ist richtig, die Umsetzng in einigen wichtigen Details nicht.
Variabelen, welche in einer ISR und im Hauptprogramm gelesen oder geschrieben werden, müssen sowohl volatile deklariert werden (hast du) als auch im Hauptprogramm atomar gelesen und geschrieben werden. SIehe Interrupt. Wnn Variablen aber NUR in der ISR benutzt werden, müssen sie nicht volatile sein.
Die Initialisierung des Timers macht man EINMALIG in setup() und nicht dauernd in loop()! Bei einer Bool-Variablen nimmt man im Normalfall kein Switch, denn außer true und false gibt es da nix, ein if/else reicht.
Bei Input Capture läßt man den Zähler in den meisten Fällen immer laufen. Man muss ihn NICH auf Null setzen, um die Pulsbreite zu bestimmen! Die Differenz falling_capture-rising_capture stimmt auch beim Überlauf!
TCCR1B &= ~(1 << ICES1); // set input capture for falling edge
Das kann schief gehen. Du invertierst hier das Bit. Damit kann man aber Pech haben, wenn sie deine Logik desynchronisiert. Hier ist es besser, die Bits explizit zu setzen. Das Gleiche gilt für deine Variable rising_edge. Hier is es besser und einfacher, die aktuellen Bits des Steuerregisters zu nutzen. Am Ende kann man die Pulsbreite auch gleich in der ISR berechnen, so eine einfache Differenz ist sehr schnell.
1
#include<Arduino.h>
2
#include<U8g2lib.h>
3
#include<SPI.h>
4
#include<Wire.h>
5
#include<avr/io.h>
6
#include<util/delay.h>
7
#include<util/atomic.h>
8
#include<avr/interrupt.h>
9
10
// Set pinout
11
12
intpin=8;// pin for puls input from oscillator
13
14
// Constructor - for OLED display library - see u8g2 documentation
Da TCNT1 nicht auf 0 gesetzt wird (ist auskommentiert) tritt das
beschrieben Verhalten auf, wenn der Zähler während der Messung
überläuft. pulse_width = falling_capture - rising_capture;
Dann ist die Differenz negativ, das ist unsigned 655xx.
Nein, eben nicht. Im Falle des Zählerüberlauf stimmt die Differenz trotzdem!
Die -64000 erzeugen selber wieder einen Überlauf, wodurch daraus ein positives, richtiges Ergebnis wird! Einzige Randbedingung. Die Differenz zwischen den Werten darf nicht größer als der halbe Zählerumfang sein, als max. 32767!
Ich möchte mit untenstehendem Code einen Puls, der
zwischen ca. 16us - 30 us schwankt, vermessen.
Da kommt es aber auch noch auf das Tastverhältnis bzw. Wiederholfrequenz an.
Wenn nur 1 µs Abstand besteht, kann es nicht klappen, da die ISR nicht hinterherkommt.
Vielleicht läßt Du Dir die +PW und -PW anzeigen, um mehr Durchblick zu erhalten.
Bist Du Dir da sicher? Soweit ich es verfolgt habe, wird erst dann
'vorbereitet', wenn PWM auch verwendet wird.
Hallo,
die Timer werden teilweise mittels setup() vorbereitet. Nur das was mit dem Pin direkt zu tun hat noch nicht. Viele Worte ohne Sinn, schau dir den Testcode an was wann passiert.
Erst einmal 1000 Dank an Euch alle, die Ihr Euch so viele Mühen mit Euren Antworten gemacht habt - einfach toll.
Auf die Schnelle habe ich Arduino F.'s Hinweis ausprobiert und die Steuerregister genullt:
TCCR1A = 0;
TCCR1B = 0;
Das hat die Werte zumindest schon mal in die richtige Region gebracht - nämlich zwischen ca. 240 und 1200 (je nach Frequenz meines Oszillators, den ich an Pin 8 (nach Arduino-Zählweise) hängen habe - Tastverhältnis ist ca. 50%).
Trotzdem flippen die Werte noch irgendwie herum - beim aktuell eingestellten Oszillatorwert/Eingangssignal z.B. zwischen
1189,
0,
394,
784
Da das alles in der Region von Vielfachen von 394 liegt, habe ich die Vermutung, dass der Capture nicht alle Flanken richtig mitbekommt.
Leider habe ich aktuell Null Zeit und kann Eure weiteren Tipps erst heute Abend oder morgen in Ruhe lesen und ausprobieren.
Dann werde ich mir nochmals die Register genau anschauen, ob da ggf. noch etwas genullt werden muss und ich werde Falk's und Veit D.'s Hinweise nochmals genau lesen und umsetzen.
Daher: bitte habt noch etwas Geduld - ich werde hier in jedem Fall berichten, wie die Sache weitergeht.
Gut. Wiring.c soll intern sogar noch vor setup() aufgerufen werden. Das soll der 'normale' Benutzer aber garnicht wissen.
Vermeiden kann man all dies, indem man kein setup() aufruft und main() verwendet ;-)
Letztens hatte ich Arduino für den RP2040 am Wickel, wo die Initialisierungen wohl erst nach Bedarf zur Laufzeit erfolgen. Da die ISR-Vektoren im RAM liegen ist das kein Problem.
Hmm, obwohl es so funktioniert und ich so auch schon ein Projet gemacht habe, muss man nach der Doku von Atmel/Microchip das Flag ICF1 im Register TIFR1 löschen, wenn die Flanke vom ICP1 geändert wird.
Also sicherheitshalber noch ein
TIFR1 = (1<<ICF1);
Ans Ende der ISR.
Siehe Datenblatt ATmega 328
16.6.3 Using the Input Capture Unit
"Measurement of an external signal’s duty cycle requires that the trigger edge is changed after each capture. Changing the edge sensing must be done as early as possible after the ICR1 Register has been read. After a change of the edge, the Input Capture Flag (ICF1) must be cleared by software writing a logical one to the I/O bit location). For measuring frequency only, the clearing of the ICF1 Flag is not required (if an interrupt handler is used)."