Hallo, da ich noch recht am Anfang mit meinem Wissen zur µC Programmierung bin, versuche ich ein paar Testprogramme zu schreiben. Ich habe heute versucht den Timer0 des Arduino's (AMEGA168) auszulesen. Das Programm sieht folgendermaßen aus: #define cbi(sfr,bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) //Befehl zum Steuerbit ausschalten #define sbi(sfr,bit) (_SFR_BYTE(sfr) |= ~_BV(bit)) //Befehl zum Steuerbit einschalten volatile unsigned long Zeit = 0; //Inhalt der Zeitvariable void setup() { Serial.begin(9600); Serial.print("Zeitdauer Test: "); Serial.println(Zeit); Setup_timer0(); //Einstellung des Timers (siehe unten) } void loop() { Serial.print("Zeitmessung: "); //Ausgabe der Zeit Serial.println(Zeit); } //********************************************************************** **************** //Deklaration von Funktionen: void Setup_timer0() { //Timer 0 Vorteilereinstellung auf 1024: sbi (TCCR0B, CS02); cbi (TCCR0B, CS01); sbi (TCCR0B, CS00); //Zeit Interrupt wird eingeschaltet; TIMSK0 = 0x01; //T0IE0 direkt auf 1 Bit setzen } //********************************************************************** **************** //Zeit Interrupt Funktion: ISR(TIMER0_OVF_vect) { Zeit++; } Beim Compilern bekomme ich dann folgende Fehlermeldung: core.a(wiring.c.o): In function `__vector_16': C:\Main\Tools\Arduino\hardware\arduino\cores\arduino/wiring.c:49: multiple definition of `__vector_16' Zeittest.cpp.o:C:\Main\Tools\Arduino/Zeittest.ino:53: first defined here Meine Recherchen ergaben das der Timer0 bereits vordefiniert wurde. Er wird unter anderem für die Funktionen delay(), millis() usw. verwendet. Ich habe versuchte die Header Datei wiring.c für den Testzweck zu löschen. Allerdings brachte dies auch nicht den gewünschten Erfolg. Hat jemand von euch eine Idee, wie man das Problem lösen kann? Ich würde würde ungerne einen anderen Timer verwenden, da diese für andere Zwecke schon verwendet werden. Viele Grüße hawkpmullins
hawkpmullins schrieb: > Meine Recherchen ergaben das der Timer0 bereits vordefiniert wurde. Er > wird unter anderem für die Funktionen delay(), millis() usw. verwendet. millis() läuft garantiert über Timer0. Bei delay() bin ich mir nicht sicher. Wenn du unbedingt Timer0 in dieser Form nutzen willst musst du auf die Arduino Library komplett verzichten. Alternativ kannst du deinen Compare Wert in OCR0A laden und mit dem TIMER0_COMPA_vect Interrupt arbeiten, wobei du dann auf die genannten Funktionen verzichten musst.
Danke für die schnelle Antwort! Gibt es keine Möglichkeit selektiv Header Dateien zu deaktivieren? Würde ungerne auf alles verzichten. Letzt endlich müsste ich ja nur verhindern das auf "TIMER0_OVF_vect" von der Arduino Library zugegriffen wird.
Ich habe alternativ versucht deinen Vorschlag mit dem Output Compare Register umzusetzen. Allerdings bin ich mir nicht wirklich sicher, wie das gehen soll. Das Regeister muss ja mit jedem Zyklus neu geladen werden. Wie und Wo muss das gemacht werden?
Ich habe versucht die Registerverschiebung mit Assembler Code umzusetzen. Dafür habe ich in der Schleife void loop() folgendes hinzugefügt: asm volatile ( "MOV 0x27, 0x26"); Dafür bekomme ich jetzt die Fehlermeldung: C:\Users\HAWKPM~1\AppData\Local\Temp/ccfWFHqs.s: Assembler messages: C:\Users\HAWKPM~1\AppData\Local\Temp/ccfWFHqs.s:107: Error: constant value required C:\Users\HAWKPM~1\AppData\Local\Temp/ccfWFHqs.s:107: Error: constant value required Ich werde noch verrückt! Hinzu kommt, dass ich keine Ahnung habe ob man so vorgehen sollte. Auf diese Weise würde ich ja den Speicherüberlauf verpassen und der Interrupt würde nicht ausgelöst werden. Kann man Register auch ohne Assembler verschieben? Am besten wäre ja, wenn die Verschiebung mit dem Timer0 erfolgt, damit das Hilfsregister 0x27 immer frisch aktualisiert wird.
hawkpmullins schrieb: > Ich würde > würde ungerne einen anderen Timer verwenden, da diese für andere Zwecke > schon verwendet werden. "für andere Zwecke schon verwendet" schließt ja aber nicht automatisch aus, dass du damit noch "nebenbei" was anderes machen kannst. hawkpmullins schrieb: > Das Regeister muss ja mit jedem Zyklus neu geladen > werden. Wieso? hawkpmullins schrieb: > Ich habe versucht die Registerverschiebung mit Assembler Code > umzusetzen. > ... > Kann man Register auch ohne > Assembler verschieben? Am besten wäre ja, wenn die Verschiebung mit dem > Timer0 erfolgt, damit das Hilfsregister 0x27 immer frisch aktualisiert > wird. Hä??? Irgendwie bist du hier auf einem völlig abwegigen Trip.
Nachtrag: > #define sbi(sfr,bit) (_SFR_BYTE(sfr) |= ~_BV(bit)) //Befehl zum > Steuerbit einschalten Das ist auf jeden Fall schon mal falsch.
In der Tat ist die Zeile "#define sbi(sfr,bit) (_SFR_BYTE(sfr) |= ~_BV(bit))" falsch, dass "~" muss an der Stelle wohl weg. Vielen Danke für die Hilfe! Aber warum sagst du nicht direkt, an welcher Stelle der Fehler ist? Stefan Ernst schrieb: > Hä??? > Irgendwie bist du hier auf einem völlig abwegigen Trip. Wieso ist das völlig abwegig? Wenn es falsch ist, dann sag mir doch bitte was daran falsch ist. Ich habe, wie gesagt, noch zu wenig Erfahrung mit µC. Wenn das Orginal Register "TCNT0" nicht immer mit dem aktuellen Wert versorgt wird, dann besteht ja das Risiko, dass der Speicherüberlauf vom µC verpasst wird. Wie würde es denn richtig gemacht werden?
hawkpmullins schrieb: > Aber warum sagst du nicht direkt, an welcher Stelle der > Fehler ist? Weil es für dich lehrreicher und befriedigender ist, diese Stelle selbst zu finden. hawkpmullins schrieb: > Wieso ist das völlig abwegig? Wenn es falsch ist, dann sag mir doch > bitte was daran falsch ist. Nun ja, alles halt. Ich kann keinerlei Bezug zwischen diesen Aussagen und der Realität herstellen, "völlig abwegig" eben. hawkpmullins schrieb: > Wenn das Orginal Register "TCNT0" nicht immer mit dem > aktuellen Wert versorgt wird, dann besteht ja das Risiko, dass der > Speicherüberlauf vom µC verpasst wird. Mit "Speicherüberlauf" meinst du wohl das Überlaufen des Zählers. Aber ich habe keinen blassen Schimmer, was du wohl mit "TCNT0 mit dem aktuellen Wert versorgen" meinen könntest. TCNT0 ist das Register, in dem der aktuelle Zählerwert steht, welcher automatisch hochgezählt wird.
So, ich habe jetzt den Quellcode nochmal leicht abgeändert und konnte einen kleinen Erfolg verzeichnen. #define cbi(sfr,bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) //Befehl zum Steuerbit ausschalten #define sbi(sfr,bit) (_SFR_BYTE(sfr) |= _BV(bit)) //Befehl zum Steuerbit einschalten volatile unsigned long Zeit = 0; //Inhalt des Timer 0 void setup() { Serial.begin(9600); Serial.print("Zeitdauer Test: "); Serial.println(Zeit); OCR0A=255; //Vergleichsregister mit Zeitwert laden Setup_timer2(); //Einstellung des Timers (siehe unten) } void loop() { Serial.print("Zeitmessung: "); Serial.println(Zeit); } //********************************************************************** **************** //Deklaration von Funktionen: void Setup_timer2() { //Timer 0 Vorteilereinstellung auf 1024: sbi (TCCR0B, CS02); cbi (TCCR0B, CS01); sbi (TCCR0B, CS00); //Timer0 wird auf CTC- Modus geschaltet sbi (TCCR0A, WGM01); cbi (TCCR0A, WGM00); cbi (TCCR0B, WGM02); //Zeit Interrupt wird eingeschaltet; TIMSK0 |= (1<<OCIE0A); } //********************************************************************** **************** //Zeit Interrupt Funktion: ISR(TIMER0_COMPA_vect) { Zeit++; } Nur jetzt versuche ich noch zu verstehen was genau geschieht. Wenn ich das richtig verstanden habe gebe ich mit dem Register OCR0A den Zählerstand an ab dem der Timer0 den Interuppt auslöst, wenn der Wert in OCR0A gleich dem Timerwert in TCNT0 ist. Wenn ich den Wert in OCR0A kleiner Stelle, so wird der Interrupt schneller ausgelöst. Es dauert in diesem Beispiel 255 Zyklen bis der Interrupt-Bit gesetzt wird und meine Variable Zeit um 1 erhöt wird. Da die Taktfrequenz 16MHz ist und der Vorteiler auf 1024 steht, wird der Interuppt alle 16.32ms [ (16MHz /(1024*255))^-1 ]ausgelöst. Wenn ich nun die Variable Zeit ungefähr jede Millisekunde aktualisieren möchte muss ich den Wert in OCR0A auf 62 setzen und den Vorteiler auf 256 setzen. Dann bekomme ich alle 992µs [ (16MHz /(256*62))^-1 ] eine neue Variable Zeit. Ist das bis hierhin so richtig?
Hallo, Timer 0 wird für delay, millis und micros verwendet Timer 1 wird die Servo Lib verwendet, auf Mega Timer 5 tone() nutzt Timer 2 Timer 0 sollte man wirklich in Ruhe lassen. Denn fast jeder gute Code nutzt millis(). und dann sollte man, muß man die Interrupts sperren bevor man die Timer Register in dessen "Setup" ändert. Sonst wird das nichts.
OldMan schrieb: > Veit D. schrieb: >> Denn fast jeder gute Code >> nutzt millis(). > > Soll das ein Scherz sein? nein. Ist auf die Verwendung von delay bezogen. Fast jeder benötigt hier und da zeitabhängige Aktionen. Mit delay wird das nur Mist. Blockiert. Also muß millis verwendet werden.
OldMan schrieb: > Veit D. schrieb: >> Denn fast jeder gute Code >> nutzt millis(). > > Soll das ein Scherz sein? Nö. Auf einem Arduino ist das Pattern
1 | unsigned long lastDone; |
2 | |
3 | void loop() |
4 | {
|
5 | unsigned long now = millis(); |
6 | |
7 | if( now - lastDone > intervall ) { |
8 | lastDone = now; |
9 | |
10 | ....
|
11 | }
|
12 | |
13 | ...
|
14 | }
|
eine völlig normale Vorgehensweise um zeitabhängige Aktionen zu steuern.
OldMan schrieb: > Veit D. schrieb: >> Denn fast jeder gute Code >> nutzt millis(). > > Soll das ein Scherz sein? Nein! Hat Karl Heinz ja schon begründet. Alternativ: Durch weglassen von setup() und loop() und einführen einer main() verzichtet Arduino auch auf das etablieren der Timer0 ISR Man kann so Timer0 frei schaufeln Sorgt damit allerdings gleichzeitig dafür, dass viele Arduino Funktionen/Libs versagen.
Um den TIMER0_OVF_vect selbst definieren zu können, muss in der Datei wiring.c in Zeile 42 folgendes geändert werden: von:
1 | #if defined(TIM0_OVF_vect)
|
2 | ISR(TIM0_OVF_vect) |
3 | #else
|
4 | ISR(TIMER0_OVF_vect) |
5 | #endif
|
zu:
1 | #if defined(TIM0_OVF_vect)
|
2 | ISR(TIM0_OVF_vect, __attribute__((weak)))) |
3 | #else
|
4 | ISR(TIMER0_OVF_vect, __attribute__((weak))) |
5 | #endif
|
Wird nun eine eigene Funktion definiert, nimmt der Compiler die benutzerdefinierte, ansonsten die in wiring.c. Somit lässt sich der Arduino Hintergrund völlig ausschalten, man kann jedoch trotzdem noch fast alle Funktionen wie z.B. Serial verwenden. millis(), micros(), delay(), delayMicroseconds() und andere davon abhängige Funktionen gehen dann natürlich nicht mehr.
hawkpmullins schrieb: > Hat jemand von euch eine Idee, wie man das Problem lösen kann? Ich würde > würde ungerne einen anderen Timer verwenden, da diese für andere Zwecke > schon verwendet werden. Geht! dr.shihan schrieb: > Um den TIMER0_OVF_vect selbst definieren zu können, muss in der Datei > wiring.c in Zeile 42 folgendes geändert werden: > ...... > Wird nun eine eigene Funktion definiert, nimmt der Compiler die > benutzerdefinierte, ansonsten die in wiring.c. > > Somit lässt sich der Arduino Hintergrund völlig ausschalten, man kann > jedoch trotzdem noch fast alle Funktionen wie z.B. Serial verwenden. > millis(), micros(), delay(), delayMicroseconds() und andere davon > abhängige Funktionen gehen dann natürlich nicht mehr. Naja... Es geht auch ohne die Datei zu ändern. Und ohne Fehlermeldungen.
1 | // Kurzfassung
|
2 | volatile unsigned long Zeit; |
3 | |
4 | ISR(TIMER0_OVF_vect) { |
5 | Zeit++; |
6 | }
|
7 | |
8 | |
9 | int main() |
10 | {
|
11 | // hier timer einstellen
|
12 | for(;;) ; |
13 | }
|
Dann ist man allerdings für alle Timereinstellungen selber
verantwortlich.
Wenn man dann, im Programm, eine der vom Timer0 abhängigen Funktionen
nutzt, hagelt es:
> multiple definition of `__vector_16'
Hm, wenn die Timer nicht reichen, und du softwaretechnisch den Mangel nicht lösen kannst, dann ist evtl die CPU die falsche für deine Anwendung. Auf einem Arduino Due sind mehrere Timer verfügbar. Ich benutze einen STM, der hat >16 Timer. Überlege doch einfach vorher, warum du unbedingt eine 8Bit Architektur mit sehr wenigen Möglichkeiten benutzen möchtest, wenn es doch viele geeignete CPUs gibt. In meinen Programmen benutze ich meist nur den Systick (vergleichbar mit millis() ). Überlege evtl auch mal, ob deine Softwareanwendung wirklich diese Timer benötigt, oder ob es sich nicht auch alleine mit millis() lösen lässt.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.