Frequenzen messen mit Arduino Mega 2560

Hallo

ich bin relativ neu hier im Forum.

Mein Ziel ist es mit dem Arduino Mega 2560 eine Frequenz im Bereich von 0 - 12 KHz zu messen.

Die Messgenauigkeit soll bei +/- 1% liegen. Das Problem hierbei ist das der Arduino noch andere Aufgaben hat wie den Analog Input zu lesen. Meine Idee wäre dies über Interrupts zu messen, leider ist mein Know-How nicht groß genug

Nun ist meine Frage ob dies möglich ist?

LG Nitzi43.

ob dies möglich ist?

ja.
Wenn du das Signal auf TTL Level (also abwechselnd <0.7V und >3.3V) kriegst.
Und wenn du nicht die exakte Dauer jeder einzelnen Schwingung aufzeichnen willst.

Zeiten kann der Arduino auf ca 1 Promille genau messen.

Guckst du attachInterrupt und zählst du am besten nur die Ereignisse in der ISR.

Wenn du in loop() Zeit findest, holst du den Zähler, setzt ihn zurück, und ermittelst die Zeit seit du das das letzte Mal gemacht hast. f = Zähler / Zeit

Für ausreichende Genauigkeit (1%) sollte Zähler > 100 sein.

Das eigentliche Problem ist also 0 kHz :wink:

Danke für die schnelle antwort :slight_smile:

Das signal wechseld zwischen 0 und 5 V.
Falls nun eine frequenz von 0Hz anliegt wie könnte ich dieses Problem lösn?

Außerdem hättest du einen kleinen Sketch für die veranschaulichung deiner Idee?
Lg Nitzi

Falls nun eine frequenz von 0Hz anliegt wie könnte ich dieses Problem lösn?

Beachte das Smiley :wink: und überlege selbst:

Wenn deine Genauigkeitsanforderung 1% von 12 kHz ist, wäre es einfach: unter 60 Hz (= 0.5% von 12000 Hz ) , kannst du 0 anzeigen. Oder wenn die Zeit zwischen zwei Impulsen größer als 16 ms ist, falls du so schnell sein willst.

Wenn du nur jede Sekunde einen Mittelwert der Frequenz anzeigst, kannst du Frequenzen bis 1Hz anzeigen. Kein Puls in der vorigen Sekunde wäre dann 0 Hz.
Ansonsten ist die Anzahl der Pulse genau die Frequenz. ( Wenn du es schaffst, genau jede Sekunde den Zähler auszuwerten. )

Außerdem hättest du einen kleinen Sketch für die veranschaulichung deiner Idee?

Sketche hat man nicht, die macht man sich. Viel Spass dabei. Bei Problemen und unerklärlichem Verhalten kannst du deinen gerne zeigen.

Bei 16MHz kann der Arduino Zeiten auf ca. 63ns genau messen, mit micros() auf etwa 1µs genau. Fragt man micros() am Anfang und Ende einer Periode ab, reicht das für Meßzeiten bis 4s, also Frequenzen runter bis 0,25Hz. Für noch kleinere Frequenzen kann man millis() statt micros() verwenden, dann kommt es nur auf die Geduld an, wie lange man auf ein Ergebnis warten möchte :wink:

Wobei man die Stabilität des Systemtakts berücksichtigen muß, die beim keramischen Resonator kaum an 1% rankommen dürfte. Für die geforderte Genauigkeit dürfte man um einen externen temperaturstabilisierten Taktgeber für den Controller nicht herumkommen.

Wenn ich nun mit Interrupts arbeite beeinflusst dies nicht mein restliches Programm??

DrDiettrich:
mit micros() auf etwa 1µs genau.

Nein. micros() bewegt sich in 4µs Schritten.

Nitzi43:
Wenn ich nun mit Interrupts arbeite beeinflusst dies nicht mein restliches Programm??

Jain.
Nein, wenn Du die ISR so kurz wie möglich machst.
Ja, weil die ISR natürlich trotzdem Rechenzeit benötigt und damit Dein laufendes Programm unterbricht. Abhängig davon wie oft das passiert (also abhängig von der Frequenz die Du gerade misst) sind die Verzögerungen unterschiedlich lang über eine feste Zeit.

Wenn Du nur Frequenzen messen willst, kannst Du einen der Timer im CTC Mode betreiben. Dann zählt der einen 16-bit Wert bei jedem Pegelwechsel an einem bestimmten Pin bis zu einem maximalen Wert von 65535. Beim Überlauf kann auch wieder ein Interrupt ausgelöst werden. Bei Frequenzen bis 12kHz kannst Du aber locker bis 5 Sekunden zählen bis Du den Wert abfragen musst ohne einen Überlauf zu riskieren.
Schau mal hier: AVR Timers - CTC Mode » maxEmbedded
Oder such einfach nach Atmega 2560 CTC mode

Der Vorteil ist, Du brauchst keinen Interrupt zum Zählen, weil der Prozessor das in Hardware macht. Du musst also nur alle 2 oder 3 Sekunden dafür sorgen, dass der Zähler ausgelesen wird und über die vergangene Zeit die Frequenz berechnen. Danach den Zähler und die Zeitmessung zurücksetzen und von vorn anfangen.
Das dürfte damit auch genauer werden, denn soweit ich mich erinnere werden beim Ausführen einer ISR weitere Interrupts verhindert. Da das Zählen von millis() und micros() auch über einen Timer-Interrupt passiert, kann es passieren das "Ticks" verloren gehen, was für eine gewisse Ungenauigkeit sorgt. Gemeinerweise wächst die Wahrscheinlichkeit das das passiert, je höher die Frequenz und damit die Anzahl der ausgelösten Interrupts ist.

Mario.

Nitzi43:
Wenn ich nun mit Interrupts arbeite beeinflusst dies nicht mein restliches Programm??

Natürlich beeinflußt es Dein restliches Programm, da es dies ja unterbricht. Interrupts sind nicht gerade anfängerfreundlich, Alternativen sehe ich aber auch nicht.

mkl0815:
Da das Zählen von millis() und micros() auch über einen Timer-Interrupt passiert, kann es passieren das "Ticks" verloren gehen, was für eine gewisse Ungenauigkeit sorgt.

Na ja, millis kommt nur aus dem Tritt, wenn eine ISR länger als 2ms dauert.
Ansonsten werden während der ISR auftretende andere Interrupts direkt nach der ISR ausgeführt.

Whandall:
Na ja, millis kommt nur aus dem Tritt, wenn eine ISR länger als 2ms dauert.
Ansonsten werden während der ISR auftretende andere Interrupts direkt nach der ISR ausgeführt.

Das stimmt nicht so ganz glaube ich. Schaut man sich den Code in "hardware/arduino/avr/cores/arduino/wiring.c" an, so würde ich sagen, das sowohl millis() als auch micors() auf Variablen zugreifen, die von der gleiche ISR verarbeitet werden. Damit dürfte es ab 8 µs Laufzeit der zusätzlichen ISR zu Ungenauigkeiten kommen.

mkl0815:
Das stimmt nicht so ganz glaube ich.

Ein Irrglaube.

Bei Gelegenheit suche ich dir einen Artikel dazu raus.

Alternativ kannst du natürlich auch deine Ansicht belegen.

Bei Gelegenheit suche ich dir einen Artikel dazu raus.

Ich sach mal dazu:
Der Code liegt auf jedem seinen Rechner.
Einfach mal reinschauen, kostet nix.

Hab ich schon mal, ist aber etwas her.

Sollte ich also besser sagen: wenn du es nicht glaubst schau selbst nach?

Achso...
Nöö, was du sagen solltest, das möchte ich gar nicht beeinflussen.

Aber ich sage:
Ich weiß nicht was delay() tut? Dann schaue ich in den Code!
Tuts SPI in einer ISR? Dann schaue ich in den Code!
usw.

Mein Tipp:
Einfach mal in den Code schauen....
Ist völlig kostenlos!
Die einzige Gefahr: Das Unwissen schwindet.

Whandall:
Na ja, millis kommt nur aus dem Tritt, wenn eine ISR länger als 2ms dauert.
Ansonsten werden während der ISR auftretende andere Interrupts direkt nach der ISR ausgeführt.

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	unsigned long m = timer0_millis;
	unsigned char f = timer0_fract;

	m += MILLIS_INC;
	f += FRACT_INC;
	if (f >= FRACT_MAX) {
		f -= FRACT_MAX;
		m += 1;
	}

	timer0_fract = f;
	timer0_millis = m;
	timer0_overflow_count++;
}

Diese Routine wird regelmäßig aufgerufen um millis() zu erzeugen.
Das funktioniert solange niemand die Interrupts länger als 2ms abschaltet.

QED

mkl0815:
Wenn Du nur Frequenzen messen willst, kannst Du einen der Timer im CTC Mode betreiben. Dann zählt der einen 16-bit Wert bei jedem Pegelwechsel an einem bestimmten Pin bis zu einem maximalen Wert von 65535. Beim Überlauf kann auch wieder ein Interrupt ausgelöst werden. Bei Frequenzen bis 12kHz kannst Du aber locker bis 5 Sekunden zählen bis Du den Wert abfragen musst ohne einen Überlauf zu riskieren.
Schau mal hier: AVR Timers - CTC Mode » maxEmbedded
Oder such einfach nach Atmega 2560 CTC mode

Der Vorteil ist, Du brauchst keinen Interrupt zum Zählen, weil der Prozessor das in Hardware macht. Du musst also nur alle 2 oder 3 Sekunden dafür sorgen, dass der Zähler ausgelesen wird und über die vergangene Zeit die Frequenz berechnen. Danach den Zähler und die Zeitmessung zurücksetzen und von vorn anfangen.
Das dürfte damit auch genauer werden, denn soweit ich mich erinnere werden beim Ausführen einer ISR weitere Interrupts verhindert. Da das Zählen von millis() und micros() auch über einen Timer-Interrupt passiert, kann es passieren das "Ticks" verloren gehen, was für eine gewisse Ungenauigkeit sorgt. Gemeinerweise wächst die Wahrscheinlichkeit das das passiert, je höher die Frequenz und damit die Anzahl der ausgelösten Interrupts ist.

Mario.

Wenn ich dies so lösen würde sollte ich dann Pin 47(T5) benutzen? zählt der Timer dann mit der Frequenz des Signals an diesem Pin angelegt?

Sorry wenn dies dumme Fragen sind, bin echt ein Newbie ^^^

LG Nitzi

Ok, entscheidend ist aber der Teil vor der ISR Definition:

// the prescaler is set so that timer0 ticks every 64 clock cycles, and the
// the overflow handler is called every 256 ticks.
#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))

// the whole number of milliseconds per timer0 overflow
#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)

dazu noch

hardware/arduino/avr/cores/arduino/Arduino.h:#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
hardware/arduino/avr/cores/arduino/Arduino.h:#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )

die ISR wird alle 16384 Taktcyclen (64*256) aufgerufen. Bei 16 MHz ist das 976 Mal pro Sekunde und damit etwas mehr als einmal pro ms.
Ich bin davon ausgegangen, das die ISR häufiger aufgerufen wird, damit eine präzisere Messung per micros() möglich ist, das scheint aber nicht der Fall zu sein.
Du hast also Recht.
Aber um es ganz genau zu machen, reicht in ungünstigsten Fall schon eine eigene ISR die etwas länger als 1ms braucht.
Werden während der eignen ISR per cli() weitere Interrupts verhindert und tritt unmittelbar danach ein timer Interrupt auf, so wird der nächste der nach einer weiteren Millisekunde kommt ignoriert, weil es die gleiche Quelle ist. Aber auch hier lasse ich mich gern eines besseren belehren.
Mario.

Warum sollte die ISR öfter aufgerufen werden als notwendig? micros() liest den Zählerstand aus, und ergänzt mit dem Überlauf-Zähler auf 32 Bit.

Interrupts sind innerhalb einer ISR abgeschaltet, muß man nicht extra machen. Und wer dort länger als ein paar µs verbrät, wird fristlos entlassen, wegen erwiesener Unfähigkeit :-]

Aber um es ganz genau zu machen,
reicht in ungünstigsten Fall schon eine eigene ISR die etwas länger als 1ms braucht.

Meine 2ms sind die Zeit in der es garantiert passiert, da hast du Recht.

Aber auch die eine Millisekunde ist schon extrem für eine ISR, sind ja ca 16000 Instruktionen.