Hallo,
"mit im Code angeben" meinst Du in einem Timer Register?
Ich lese gerade tausende Seiten dazu, dazu schweigen sich aber alle aus.
Hallo,
"mit im Code angeben" meinst Du in einem Timer Register?
Ich lese gerade tausende Seiten dazu, dazu schweigen sich aber alle aus.
Ja. z.B. das automatische Toggeln der Pins per Compare Match Interrupt. Das wird im TCCRnA Register über die COMnA/B Bits eingestellt. Siehe Atmega328 Datenblatt Seite 134. In den PWM Modi werden diese Bits auch verwendet um zu bestimmen wie die Ausgänge schalten.
Das ist doch bei anderen internen Baugruppen genauso. Die Serial Pins kannst du als I/Os verwenden wenn die serielle Schnittstelle deaktiviert ist (mal von der USB Verbindung auf einem Arduino Board abgesehen). Die analogen Eingängen kannst du als I/Os verwenden. Die Pins von der SPI Schnittstelle genauso. Manche Pins haben noch mehr Funktionen, z.B. I2C auf dem UNO. Auf den kleinen AtTinys haben Pins locker 7 verschiedene Funktionen.
Hallo,
das hilft mir schon weiter. Hatte mal wieder gedanklich eine Blockade.
Danke für die Erklärung.
Hallo,
hab mich mal hingesetzt und eine Excel zur Berechnung des Compare Match Registers erstellt.
Auf die vorhandenen Prescaler und max. Wert des CMR je nach verwendeten Timer muß man noch selbst aufpassen.
Es wird der größt möglichste CMR Wert rausgesucht, wegen geringster Abweichung.
Den dazugehörigen Prescaler wird jeder selbst ablesen können.
Daneben werden noch mögliche Abweichungen von der gewünschten Frequenz angezeigt.
Ich hoffe das es den einen oder anderen auch helfen wird.
Arduino Timer Berechnung Forum.zip (8.6 KB)
Hallo,
hier noch ein Bsp. Code der die LED mit gewünschter Frequenz takten lässt.
Die Wunschfrequenz wird in setup der Funktion übergeben.
Die Funktion berechnet dazu den bestmöglichen Prescaler und Compare Match Register Wert.
mit digital.Write sind maximal 36kHz möglich, mit direkten Port-Registerzugrff sind maximal 170kHz möglich.
Dabei beachten ob man einen Uno oder Mega hat und entsprechend ganz unten im Sketch auskommentieren oder aktivieren.
Viel Spass! ![]()
Timer1_Frequenzgenerator_002.ino (4.18 KB)
Hallo,
es ist schon krass was 23 Takte mehr durch den Timer ISR Aufruf ausmachen.
Mit einem quasi nackten Sketch und das toggeln in einer while(1) bringt 727kHz macht.
Im Vergleich mit Timer 1 ISR Aufruf "nur" noch 170kHz.
Dabei werden 1468ns benötigt, was 23 Takte sind
Nur ohne Timer ISR kann man keine Frequenz nach Wunsch einstellen. ![]()
Ist nur der Vollständigkeit halber erwähnt.
void loop() {
while(1) {
_toggle_T1 =! _toggle_T1;
PORTB = (_toggle_T1 << 7);
}
}
Eine ISR ist wie ein Funktionsaufruf. Die sind teuer, da zig Register auf den Stack gesichert werden und wieder zurückgeschrieben werden.
Nur ohne Timer ISR kann man keine Frequenz nach Wunsch einstellen.
Doch. Auf den dazugehörigen Pins kannst du das automatisch Toggeln. Komplett in Hardware. Ohne dass Code ausgeführt wird. Du bist dann halt auf bestimmte Pins festgelegt.
Hallo,
ja okay, dass hatte ich in der Überlegung vorhin vergessen. Stimmt natürlich. Parallel dazu habe ich damit schon rumgespielt. Wenn man die direkten Pins der Timer-Register nimmt, sind max. 4MHz möglich. Kein Hertz mehr. ![]()
Dabei kommt mein Logic Analyzer auch so langsam aber sicher an seine Grenze. ![]()
Manchmal gibts leichte Fehlmessungen im Tastverhältnis der Frequenz.
Oszi wollte ich noch nicht rausholen.
Die ganze Sache ist übelst interessant wenn man damit so rumfummelt. Man verliert die Angst zu den Registern.
Ein Frequenzgenerator zubauen scheint in greifbarer Nähe. Wobei die max. mögliche Frequenz bei Sinusform rapide sinkt.
Ich häng das noch ran, wem das auch interessiert.
Edit:
weil die meisten hier doch einen Uno haben werden, habe ich das gleich dafür extra angepaßt, weils doch sehr hardware nah ist.
Edit 2:
hab den Code nochmal geändert und die Kommentare angepaßt
Edit 3:
siehe #34
Wenn du Kanal B Toggeln willst musst du glaube ich auch das Compare Match B Register beschreiben. Die Pins schalten jeweils wenn ihr zugehöriges Register einen bestimmten Wert erreicht
Außerdem hast du auf dem Mega auf den 16 Bit Timern auch einen Kanal C. Also bis zu 3 Pins pro Timer.
Und das ist Blödsinn:
(0 << COM1A1)
Denke mal nach was du machst!
Ein Bit löschen geht so:
register &= ~(1 << COM1A1)
Und mit dem Inversen
Da du vorher das ganze Register auf 0 gesetzt hast, brauchst du das aber nicht
Hallo,
Kanal B wird mit getoggelt.
Der Rest ist in dem Sinne kein Blödsinn, dass sind noch nicht gelöschte Reste vom Code während des programmierens. Am Anfang schreibe ich mehr als notwendig für den Überblick was passieren soll.
Vorallendingen wenn man mehrere Register nach dem Schema setzt.
Das macht aber auch kein Schaden.
(0 << COM1A1)
Das schiebt eine 0 x mal links. Das bewirkt einfach nichts. Auch wenn das Bit gesetzt ist. Letztlich ist das ein Oder mit 0. Ich habe schon mehrmals gesehen dass Leute denken so könnte man ein Bit löschen.
Hallo,
ich weis was das macht, keine Sorge, hatte es dennoch wie schon gesagt erstmal so getippt für den besseren Überblick im Code.
Damit sich Serenifly nicht weiter aufregt
habe ich den Code in #27 nochmal geändert
Sorry ![]()
Hallo,
Dein Einwand ist / war ja formell richtig. Ich hatte den Code einfach zu zeitig veröffentlicht vor lauter Freude das er endlich funktioniert. Das war nur vorhin aus meiner Sicht anders, weil ich mehrere Tage am coden war und dann kommt einer und ... ![]()
Aber egal, ist ja wie gesagt alles korrekt so. Weiter machen ... Euer Ehren.
Hallo,
nochwas korrigiert. Direkt an den Timer Pins sind natürlich 8MHz möglich.
Durch bedingte Rundungsfehler sind keine 7, 6 oder 5MHz möglich.
Entweder ist der Wert im Compare Match Register 0 (8MHz) oder springt auf 1 (4MHz).
Kann man sich alles nochmal in der Excel anschauen mit den dazugehörigen Abweichungen.
Arduino Timer 1.zip (12.5 KB)
Hallo,
ich lese schon den ganzen Tag das Datenblatt und parallel im Netz.
Kann es sein, dass es nicht möglich ist, die Frequenz vom Timer 1 Toggle Pin von Kanal A und B unabhängig zu schalten?
ich schalte die Toggle Pin's ein
TCCR1A |= (1 << COM1A0); // set Toggle OCnA Pin on compare match > Pin 11 Mega2560
TCCR1A |= (1 << COM1B0); // set Toggle OCnB Pin on compare match > Pin 12 Mega2560
setzte beide Compare Match Register verschieden
OCR1A = 10;
OCR1B = 20;
schalten den CTC Mode ein
TCCR1B |= (1 << WGM12); // turn on CTC mode
setzte den Prescaler auf 64
TCCR1B |= (1 << CS11) | (1 << CS10);
und schalte den Timer Compare Match Interrupt ein für A und B
TIMSK1 |= (1 << OCIE1A);
TIMSK1 |= (1 << OCIE1B);
Effekt ist, Kanal A taktet und Kanal B überhaupt nicht.
lasse ich diese Zeile weg, takten A und B gleich
// OCR1B = 20;
Hab ich einen Denkfehler oder geht das wirklich nicht? Der Timeraufbau von 17.2 im Datenblatt suggeriert mir jedoch das man alle 3 Kanäle durch verschiedene CMR Werte an den Timer Pins unterschiedlich takten lassen könnte.
Dachte ich eigentlich auch. Schau dir mal 15.9.2 an. Da wird nur OCR1A als Vergleichsregister genannt. Bei PWM dagegen wird OCR1x genannt. Das scheint dann unabhängig zu sein. Etwas seltsam dass das bei CTC nicht geht. Habe ich ehrlich gesagt nie so ausprobiert.
Hallo,
habe jetzt die Timerbeschreibung laut Datenblatt fast auswendig drin. ![]()
Ich verstehe das auch so. Eigentlich kann jeder OCnx Pin mit seinem OCRnx unabhängig voneinander geschalten werden. Der Zähler im TCNTn läuft weiter.
Im CTC Modus allerdings, wird der Zähler im TCNTn mit dem OCRnA automatisch gelöscht. Was hat sich Atmel dabei nur gedacht. Es sind tausende Register vorhanden, da hätte eines mehr für die Config das Kraut auch nicht fetter gemacht.
Jetzt lasse ich das im Normal Mode laufen. Habe die OCR1x Werte immer verdoppelt.
Dadurch sehe ich 3 plausible Toggles an den OC1x Pins.
Allerdings gibts dazwischen noch ungeklärte Schalteffekte. Siehe Screenshot.
Hab mal reingemalt was ich denke und was ich nicht weis.
Jetzt war mein Plan, den TCNT1 selbst auf 0 zu setzen, wenn der langsamste Takt, in dem Fall Timer1.C, sein Interrupt Request/Flag bekommen hat.
Ich habe aber das Problem, sobald ich den ISR Code für C aktiviere, taktet alles nur noch im 32ms Takt.
ISR(TIMER1_COMPC_vect) { // Timer 1.C Interrupt
}
/*
01.07.2015
Arduino Mega2560
*/
// globale Variablen
boolean _toggle_T1 = LOW;
const byte _LED = 8;
// Funktionen deklarieren
void set_Timer1_CMR();
void setup() {
Serial.begin(38400);
pinMode(_LED, OUTPUT); // max. 36 bzw. 170kHz
pinMode(11, OUTPUT); // Pin von OC1A > Pin 11 Mega2560 (max. 8MHz)
pinMode(12, OUTPUT); // Pin vom OC1B > Pin 12 Mega2560 (max. 8MHz)
// beim Uno sind das Pin 9 und 10
pinMode(13, OUTPUT); // Pin vom OC1C > Pin 13 Mega2560 (max. 8MHz)
set_Timer1_CMR(); // gewünschte Taktfrequenz in Hz - bei Bedarf mit einer Kommastelle
// 0.2 bis 36kHz bzw. 170kHz bzw. 8MHz)
} // Ende Setup
void loop() {
// do other things here
}
// *** Funktionen *** ----------------------------------------------
void set_Timer1_CMR() // Normal Mode, kein CTC
{
cli(); //stop interrupts
// set Timer-1 Register
TCCR1A = 0; // Reset TCCR1A Register
TCCR1B = 0; // Reset TCCR1B Register
TIMSK1 = 0; // Reset TIMSK1 Register (disable Timer Compare Interrupts)
TCNT1 = 0; // initialize counter value to 0
TCCR1A |= (1 << COM1A0); // set Toggle OCnA Pin on compare match > Pin 11 Mega2560
TCCR1A |= (1 << COM1B0); // set Toggle OCnB Pin on compare match > Pin 12 Mega2560
TCCR1A |= (1 << COM1C0); // set Toggle OCnC Pin on compare match > Pin 13 Mega2560
OCR1A = 99; // Compare Match Register A >> 10kHz (Precaler 8)
OCR1B = 199; // Compare Match Register B
OCR1C = 399; // Compare Match Register C
//TCCR1B |= (1 << WGM12); // turn on CTC mode
TCCR1B |= (1 << CS11); // set Prescaler 8
TIMSK1 |= (1 << OCIE1A); // enable Timer Compare Interrupt A
TIMSK1 |= (1 << OCIE1B); // enable Timer Compare Interrupt B
TIMSK1 |= (1 << OCIE1C); // enable Timer Compare Interrupt C
sei(); //allow interrupts
} // end Funktion
ISR(TIMER1_COMPA_vect) { // Timer 1.A Interrupt
}
ISR(TIMER1_COMPB_vect) { // Timer 1.B Interrupt
}
/*
ISR(TIMER1_COMPC_vect) { // Timer 1.C Interrupt
}
*/
Doc_Arduino:
Im CTC Modus allerdings, wird der Zähler im TCNTn mit dem OCRnA automatisch gelöscht. Was hat sich Atmel dabei nur gedacht.
Doh. Jetzt wo du es sagst. Das ist ja der ganze Sinn von CTC. Clear Timer on Compare Match. Dann ist es natürlich ein Problem mit mehreren Compare Registern zu arbeiten.
PWM funktioniert anders. Da läuft der Timer immer von BOTTOM nach TOP durch und beim Compare Match kann man was machen. Dadurch dass der Timer weiter läuft ist es dann kein Problem mehrere unabhängige Vergleichswerte zu haben.
Hallo,
ich dachte, weil jedes OCRnx noch sein eigenes OCFnx Interrupt/Flag hat, wäre es kein Problem alle Kanäle unabhängig zu betreiben. Allerdings muß ja der Zähler im TCNTn durch irgendwas genullt werden. Das ist nun einmal Kanal A im CTC Mode. Bei näherere Betrachtung doch wieder logisch. Man muß sich nur mit jemanden unterhalten. ![]()
Noch möchte ich keine PWM machen. Ich möchte nur erstmal den TCNT von Hand nullen oder ein Flag von A/B/C zurücksetzen ("set one"). Das klappt aber nicht. Muß ich da als Interrupt einen Capture Event verwenden?
Ich möchte Kanal A/B/C mit unterschiedlichen CMR Werten unterschiedlich takten lassen. Im Normal Mode.
Bsp. Kanal A mit 10kHz und Kanal B mit 5kHz. Ansatzweise funktioniert das ja schon, siehe Screenshot. Muß aber dabei dafür sorgen das Dings selbst zurückzusetzen. Sonst zählt TCNTn munter weiter bis zum Overflow.
Mir fehlt noch das kontrollierte zurücksetzen von ??? damit alle wieder bei 0 anfangen und nicht warten bis TCNT seinen Overflow hat.
Was an den mit ?? makierten Stellen passiert weist Du auch nicht zufällig im Timer-Diagramm?