Forum: Mikrocontroller und Digitale Elektronik Redirect Timer0 to own function and continue with original Arduino Timer0 ISR


von stero (Gast)


Lesenswert?

Hallo,

auf meinem ATmega328 Arduino gehen mir die Timer aus. Bisher habe ich 
vermieden Timer0 zu verwenden, weil Arduino es für einen Zähler einsetzt 
um z.B. die delay() Funktion zu realisieren.

Der Timer0 erzeugt 976,56 mal pro Sekunde einen Interrupt zu einer 
Arduino Funktion, die die Zeit aufaddiert. Ich würde mich jetzt gerne 
vor die Arduino Intterupt Routine setzen. Also den Originalvektor der 
Arduino Routine sichern und durch meine eigene Funktion ersetzen. Nun 
meine Befehle in der neuen Interrupt Funktion abarbeite und dann wieder 
in den Vektor von Arduinos Interruptroutine zurückspringen.

Weiß jemand ob und wie das geht?

Beste Grüße
stero
von Benjamin (Gast)


Lesenswert?

Wenn du viele Timer brauchst verwende doch Software-Timer.

Hänge dich in irgendeine ISR bei der die die Zeitkonstante gerade gut 
passt und rufe dort (am besten kurz bevor die ISR verlassen wird) eine 
Funktion "Tick()" auf.

In der kannst du dann beliebig viele neue Timer erstellen:
1
uint16 u16_Timer1 = 0xFFFF;
2
3
void Tick(void)
4
{
5
  if( u16_Timer1 == 0xFFFF )
6
  {
7
    //mache nix da der Timer nicht gestartet ist.
8
    //zu starten den Timer1 auf einen Wert <0xFFFF setzen
9
  }
10
  else
11
  {
12
    //Timer1 läuft
13
    if( u16_Timer1 > 0 )
14
    {
15
      //Timer ist noch nicht abgelaufen
16
      u16_Timer1--;
17
    }
18
    else
19
    {
20
      //Timer ist abgelaufen..
21
      //irgend ne Funktion oder Makro aufrufen:
22
      timeout_Timer1();
23
      //oder auch ne variable setzen und die dann pollen:
24
      b_timeout_Timer1 = TRUE;
25
    }
26
  }
27
  
28
  //Timer 2, 3, 4, usw. :
29
}
von Stephan R. (stero)


Lesenswert?

Hallo Benjamin,

vielen Dank für deine Antwort.

Ich benutze nicht alle Timer um Interrupts auszulösen, sondern um direkt 
ohne Interruptaufruf die Ausgangspins zu toggeln. Wenn da jedesmal ein 
Intterruptvektor angesprungen werden müßte, wäre das leider zu 
Rechenzeitaufwendig.

Man könnte vielleicht versuchen den Interrupt schneller zu machen, als 
der Originalcode von GCC. Der macht allerhand zeitfressende Sicherungen 
vor und nach der Interruptroutine. Aber da begibt man sich natürlich auf 
dünnes Eis, wenn man das alles selber machen will.
von Ben j. (scarab)


Lesenswert?

ja, es kommt natürlich drauf an welche Zeiten du benötigst und wie 
schnell dein uC taktet.

es hängt von der Größe deiner Interrupt-Vektor-Tabelle ab ob du vor dem 
Aufruf der ISR noch platz hast um einen Ausgang zu toogeln.

Bei den meisten AVRs dürftest du da schlechte Karten haben.

Es gibt allerdings bei gcc einen Befehl bzw. eine #pragma Anweisung um 
eine leere ISR zu erstellen, d.h. es werden keine Register gesichert und 
zurück geschrieben. Wenn du also weiß das du keine Register veränderst 
kannst du das nehmen, dann bist du in 2-3 Takten in dem Code.

Wenn das Ausgang toogeln sehr schnell sein muss, die "ursprüngliche" ISR 
aber nicht kannst du eine leere ISR erstellen, deinen Ausgang setzen, 
dann die Register manuell sichern, dann die "ursprüngliche" ISR 
Aufrufen, dann die Register zurückschreiben und die ISR verlassen.
von Stephan R. (stero)


Lesenswert?

Hallo Benjamin,

das mit dem Toggeln kann der ATmega ja auch ganz ohne CPU Belastung. Ich 
nutze den "clear on compare match" Modus von Timer2 und die PWM Ausgabe 
auf dem dazugehörigen OC2B-Pin.

Ich probiere noch einen anderen Weg, indem ich direkt die Arduino 
Bibliothek mit dem original Interruptaufruf erweitere.

Als letzten Befehl schreibe ich in die original Arduiono Timer0 ISR 
Routine einen Funktionsaufruf an eine über Funktionspointer einstellbare 
Funktion.

Diese Funktion kann ich dann hoffentlich - wenn es endlich mal klappen 
würde - aus dem Hauptprogramm heraus auf meine Erweiterung der Timer0 
ISR zeigen lassen. Eigentlich sollte es klappen, aber er schreibt mir 
immer eine Fehlermeldung mit "undefined reference". Ich arbeite daran... 
:]
von Stephan R. (stero)


Lesenswert?

Hallo Benjamin,

hier gibt es Auswertungen zu den von dir Vorgeschlagenen kurzen 
Interruptzeiten:
http://www.embeddedrelated.com/usenet/embedded/show/1437-1.php

Wie man das viele gepushe und gepoppe verringern kann steht hier im 
Abschnitt "ISR mit eigenem Prolog/Epilog":
http://www.rn-wissen.de/index.php/Avr-gcc

Im Folgenden der interessante Abschnitt daraus. Ich hoffe der Autor 
vergibt mir die ungefragte Kopie:
______________________________________________________________


Wenn man in einer ISR komplett eigenes Zeug machen will, dann definiert 
man eine nackte Funktion. Mit naked befreit man die Routine vom 
Standard-Prolog/Epilog.

Dabei ist darauf zu achten, daß die ISR mit reti (return from interrupt) 
zurückkehrt und evtl. verwendete Register und den Status (SREG) sichert.

#include <avr/io.h>

void _attribute_ ((naked))
SIG_OVERFLOW0 (void)
{
   // Port B.6 = 0
   // Diese Instruktion verändert nicht das SREG und kein anderes 
Register
   // so daß der eigentliche Code nur 1 Befehl lang ist
   _asm_ __volatile (
      "cbi %0, %1" "\n\t"
      "reti"
         :
         : "M" (_SFR_IO_ADDR (PORTB)), "i" (6)
   );
}
von Ben j. (scarab)


Lesenswert?

Stephan R. schrieb:
> Diese Funktion kann ich dann hoffentlich - wenn es endlich mal klappen
> würde - aus dem Hauptprogramm heraus auf meine Erweiterung der Timer0
> ISR zeigen lassen.

Wenn du direkt in der IVT verzweigen willst dürfte die Größe der IVT nur 
ausreichen wenn du das Register-Paar das der IJMP Befehl verwendet 
dauerhaft für die Adresse der jeweiligen ISR reservierst.
Lohnt sich IMO nicht nur um den Sprung in eine naked-ISR einzusparen.
von Stephan R. (stero)


Lesenswert?

Benjamin F. schrieb:
> Wenn du direkt in der IVT verzweigen willst dürfte die Größe der IVT nur
> ausreichen wenn du das Register-Paar das der IJMP Befehl verwendet
> dauerhaft für die Adresse der jeweiligen ISR reservierst.
> Lohnt sich IMO nicht nur um den Sprung in eine naked-ISR einzusparen.

Indem ich mich hinter die ohnehin vorhandene Arduino Timer0 
Interruptroutine hänge, nutze ich, das bereits alle Register gesichert 
und restauriert werden. Bis auf das Anspringen und Zurückspringen zu 
meiner ergänzenden Interruptroutine entsteht kein Overhead und das, ohne 
sich über Register auf Assembler Ebene Gedanken machen zu müssen. 
Wichtig für meine Anwendung ist aber besonders, das ich die anderen 
beiden Timer für andere Aufgaben frei behalte.

Die Lösung, durch Ändern der Original Arduino Dateien sich hinter die 
Arduino Timer0 Interruptroutine zu hängen, funktioniert jetzt. Ich habe 
im Arduino Forum meine modifizierte wiring.c und Arduino.h Datei 
eingestellt. Aufgerufen wird das sehr bequem direkt aus dem Sketch. 
Neben diesen zwei Dateien habe ich auch ein kleines Beispiel gemacht, 
wie man es aus dem Sketch aufruft: 
http://arduino.cc/forum/index.php?topic=87945.0

VG Stephan
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.