Forum: Mikrocontroller und Digitale Elektronik Arduino: Verwendung von Timer0


von hawkpmullins (Gast)


Lesenswert?

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

von ein gast (Gast)


Lesenswert?

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.

von hawkpmullins (Gast)


Lesenswert?

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.

von hawkpmullins (Gast)


Lesenswert?

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?

von hawkpmullins (Gast)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

Nachtrag:

> #define sbi(sfr,bit) (_SFR_BYTE(sfr) |= ~_BV(bit))    //Befehl zum
> Steuerbit einschalten

Das ist auf jeden Fall schon mal falsch.

von hawkpmullins (Gast)


Lesenswert?

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?

von Stefan E. (sternst)


Lesenswert?

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.

von hawkpmullins (Gast)


Lesenswert?

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?

von Veit D. (devil-elec)


Lesenswert?

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.

von OldMan (Gast)


Lesenswert?

Veit D. schrieb:
> Denn fast jeder gute Code
> nutzt millis().

Soll das ein Scherz sein?

von Veit D. (devil-elec)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Ulrich F. (Gast)


Lesenswert?

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.

von dr.shihan (Gast)


Lesenswert?

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.

von EAF (Gast)


Lesenswert?

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'

von EAF (Gast)


Lesenswert?

EAF schrieb:
> ... viel Quatsch...
Nehme alles zurück.

von PittyJ (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.