-         

Seite 1 von 3 123 LetzteLetzte
Ergebnis 1 bis 10 von 22

Thema: Timer verwenden als Wait Funktion

  1. #1

    Timer verwenden als Wait Funktion

    Anzeige

    SMARTPHONES & TABLETS-bis zu 77% RABATT-Kostenlose Lieferung-Aktuell | Cool | Unentbehrlich
    Hallo,

    ich hab hier schon viele Threads die zum Thema Timer und Warteschleifen durchgelesen.

    Ich blick aber eines nicht: Ich schreib irgendein Programm. Nun komm ich an einen Punkt, z. B. Tastendruck o.ä. und ab jetzt will ich eine gewisse Zeitspanne messen. Wie geht dass? Wenn ich den Timer am Anfang des Programms anstoße und bis zu einem gewissen Punkt laufen lasse, dann kommt der Interrupt. Wie lese ich den Interrupt jetzt aus? (ich hab in der ISR programmiert, dass immer ein PIN seinen Zustand ändert, aber dass will ich ja eigentlich nicht, ich will es von meiner main aus starten...) Und zweitens läuft der Timer ja immer weiter. Wenn ich in einer Endlosschleife bin, wird die Zeitspanne ja sehr ungenau, da der Timer ja dann nicht mehr bei 0 los läuft, sondern bei irgendwas........... Ich wollte mir eine Funktion (soe wie _delay_ms) bauen, die ich in meiner main quasi immer dann aufrufe wenn ich eine bestimmte Zeit brauche . Und mein ganzes Programm so aufzubauen dass ich genaue Timestamps hab will ich auch nicht. Ich bin am verzweifeln.

    Kann mir irgend jemand erklären wie der ganze Mist funktioniert?

    LG Anna

  2. #2
    Erfahrener Benutzer Roboter Experte Avatar von sternst
    Registriert seit
    07.07.2008
    Beiträge
    672
    Deine Wait-Funktion müsste etwa so aussehen:

    - Nötigen Timer-Wert berechnen (sofern unterschiedliche Wartezeiten möglich sein sollen)
    - Wert in den Timer laden
    - Timer starten (Timer zählt jetzt von diesem Wert an aufwärts)
    - In einer Schleife warten, bis das Timer-Overflow-Flag gesetzt ist
    - Timer anhalten
    - Overflow-Flag löschen

    Den Interrupt brauchst du nur, wenn das Programm in der Wartezeit etwas anderes erledigen soll. Allerdings wird es dann deutlich komplexer.

    PS: Ich sehe aber nicht, welchen Vorteil das gegenüber den Delay-Funktionen haben soll. Oder meinst du vielleicht eher sowas wie einen Timeout?
    MfG
    Stefan

  3. #3
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    20.05.2006
    Ort
    Lippe
    Alter
    48
    Beiträge
    524
    Hallo,

    es gibt für die Timer den normalen Modus und den CTC-Modus. Alle Werte beziehen sich jetzt mal auf einen 8Bit Timer.
    Im normalen Modus fängt der Timer mit der über den Prescaler eingestellten Geschwindigkeit an zu laufen. Da du bestimmt einen Vergleichswert eingestellt hast wird dieser irgendwann erreicht. In diesem Moment wird ein Compareflag gesetzt. Ist der Interrupt für diese Flag aktiv wird in die Routine gesprungen. Der Zähler läuft weiter bis zum Wert 255. Im nächsten Schritt wird der Wert 0 erreicht und es wird ein Overflowflag gesetzt. Ist der Interrupt aktiv wird wieder in die zugehörige Funktion gesprungen.
    Im CTC-Modus läuft der Timer genauso bis zum Vergleichswert. Es wird wieder das Flag gesetzt und der Timer wird automatisch auf 0 Gestellt.

    Um jetzt eine Zeit zumessen, kannst du den Timer im CTC-Modus so einstellen, dass die Zeit vom Start bis zum Erreichen des Vergleichswertes eine ms dauert. Dazu musst du einen geeigneten Prescaler wählen und den richtigen Vergleichswert einstellen. Die Formeln dazu findest du im Datenblatt. Es gibt dafür auch ein Tool, habe aber gerade keinen Link zur Hand. Du aktiviertst den OCIE (Interrupt beim Vergleich). In die Interruptfunktion schreibst du einfach Zähler++;
    Wenn du jetzt wissen willst, wie viel Zeit verstrichen ist, schaust du dir Zähler an. Du musst nur aufpassen, irgend wann läuft auch Zähler über und wird wieder 0.


    Gruß

    Jens

  4. #4
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    08.05.2005
    Ort
    Issum
    Alter
    46
    Beiträge
    2.236
    Hallo,

    Ich weiß nicht so genau, ob ich Dein Problem richtig verstanden habe, aber ich habe verschiedene Zeiten und wait Funktionen gleichzeitig gebraucht, und habe eine einfache Lösung bei Butterfly Code gefunden und zwar wird als Basis der Timer 0 verwendet, der alle 10 ms überläuft.
    Code:
    #include "timer0.h"
    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    static uint8_t CountDownTimer[COUNTDOWNTIMERZAHL];
    
    void Timer0Init(void) {
    	uint8_t a;
    	for (a=0;a<COUNTDOWNTIMERZAHL;a++) 
    		CountDownTimer[a] = 255;
    	TCNT0 = 256-156; /*Ergibt Timing von 10 mS*/
    	TCCR0 = (1<<CS02)|(1<<CS00); /*Prescaller 1024*/
    	TIMSK |=(1<<TOIE0);
    }
    
    ISR (TIMER0_OVF_vect) {
    	TCNT0 = 256-156;
    	uint8_t a;
    	for (a=0;a<COUNTDOWNTIMERZAHL;a++) {
    		if (CountDownTimer[a] != 255 && CountDownTimer[a] != 0)
    			CountDownTimer[a]--;
    	}
    }
    
    uint8_t AllocateCountdownTimer(void) {
    	uint8_t a;
    	for (a=0;a<COUNTDOWNTIMERZAHL;a++) {
    		if(CountDownTimer[a]==255) {
    			CountDownTimer[a] = 0;
    			return a+1;
    		}
    	}
    	return 0;
    }
    
    void SetCountdownTimer(uint8_t timer, uint8_t value){
    	cli();
    	CountDownTimer[timer-1] = value;
    	sei();
    }
    uint8_t GetCountdownTimer(uint8_t timer){
    	uint8_t value;
    	cli();
    	value = CountDownTimer[timer-1];
    	sei();
    	return value;
    }
    
    void ReleaseCountdownTimer(uint8_t timer) {
    	cli();
    	CountDownTimer[timer-1] = 255;
    	sei();
    }
    und hier die passende Header Datei
    Code:
    #ifndef __TIMER0_H__
    #define __TIMER0_H__
    
    #include <avr/io.h>
    
    #define COUNTDOWNTIMERZAHL 5
    
    void Timer0Init(void);
    uint8_t AllocateCountdownTimer(void);
    uint8_t GetCountdownTimer(uint8_t timer);
    void SetCountdownTimer(uint8_t timer, uint8_t value);
    void ReleaseCountdownTimer(uint8_t timer);
    
    #endif /*__TIMER0_H__*/
    Eine kurze Erklärung:
    Timer0Init() setzt den Timer in Gang und macht Platz für maximal COUNTDOWNTIMERZAHL Timer.

    AllocateCountdownTimer() reserviert dann einen Timer und wird etwa so aufgerufen

    Code:
    uint8_t my_timer = AllocateCountdownTimer();
    mit SetCountdownTimer(uint8_t timer, uint8_t value) kann man dann einen Timer mit einem Wert belegen.
    Will ich z.B. einen Timer, der 200ms läuft macht man es so
    Code:
    SetCountdownTimer(my_timer,20); /*20*10mS ergibt 200 mS*/
    man kann jetzt hin und wieder im Programm testen, ob der Timer schon abgelaufen ist

    Code:
    if (GetCountdownTimer(my_timer)) {
       /*Tue was, die Zeit ist um*/
    }
    mit
    Code:
    ReleaseCountdownTimer(my_timer);
    wird der Timer wieder gelöscht.

    Gruß Sebastian
    Software is like s e x: its better when its free.
    Linus Torvald

  5. #5
    Erfahrener Benutzer Robotik Visionär Avatar von oberallgeier
    Registriert seit
    01.09.2007
    Ort
    Oberallgäu
    Beiträge
    7.585

    Re: Timer verwenden als Wait Funktion

    Hei beginner 1101,

    Zitat Zitat von beginner1101
    ... Thema Timer und Warteschleifen ... z. B. Tastendruck o.ä. und ab jetzt will ich eine gewisse Zeitspanne messen. Wie geht dass? ...
    Es gibt wie immer mehrere Möglichkeiten. Ich weiß nicht welchen Controller Du verwendest, ich hab hier mal EINE Möglichkeit für ´nen mega168 (war grad zur Hand), so wie sie mir grad einfällt:

    Ich schreib mir zwei Routinen. Eine Initialisierung des Timers (Interrupt bei Compare Match A - es wird KEIN Pin geschaltet). Dazu eine ISR (Interrupt Service Routine) - die mir einen Wert, hier genannt Izeit_1 (uint16 - geht also bis ungefähr 65000) hochzählt von 0 bis 60 000. Bei 60000 wird Izeit_1 wieder auf 0 gesetzt, damit ist kein Überlauf möglich. Die Routinen sind hier:

    Code:
    /* ============================================================================== */
    /* ===  Initialisierung fuer Timer2 mega168 ===================================== */
    void TC2TMR_init(void)                  // Init Tmr/Cntr 2, 8-Bit auf 20 kHz = 50 µs
    {	     
        TCCR2A |= (1<<WGM21);               // Timer im CTC-Mode, Top=OCR2A   doc S 157
        TCCR2B |= (1<<CS21);		// Prescaler 1/8 / Clock <- CPU	  doc S 158
        OCR2A = 125;                        // Preset 125 für 50µs bei 20Mhz  
        TIMSK2 |= (1<<OCIE2A);		// Tmr/Cntr2 CompareA interrupt enabled
    }     
    /* ============================================================================== */
    
    
    /* ============================================================================== */
    /* ===  Nicht unterbrechbare ISR für timer2 ===================================== */
    /* Routine zählt hoch im Takt 20 kHz = 50 µs.                                     */
    ISR(TIMER2_COMPA_vect)          // Vektor 7
    {
        if (Izeit_1 < 60000)        //Timer bis 60 000 - 3 sec Datenerfassung möglich
          Izeit_1 ++;               //  war: alle drei Sekunden wird 60000 erreicht
          				//  und Izeit_1 bleibt in der uint16-Grenze
        else
        {   
            Izeit_1 = 0;		// ansonsten: Rückstellen auf Null
            PORTC ^= (1<<PC5);      // Zur Anzeige gLED am PC5/SCL toggeln
        }     
              
    }         
    /* ============================================================================== */
    Soweit meine Vorbereitungen.

    Nun kommt (D)ein Tastendruck. Also setze ich im main erstmal cli(); - damit ich gefahrlos die Variable Izeit_1 manipulieren kann - und setze Izeit_1 im main auf 0. Danach natürlich wieder sei();.

    Nun kannste jeden beliebigen Wert mit der Genauigkeit meines 50µs-Zeittaktes bis fast 60 000 - das wären 6 Sekunden - abfragen und danach weitergehen im Ablauf.

    Dazu würde ich nach dem Tastendruck und nach dem Setzen von Izeit_1 ein Schleife schreiben : cli(); Abfrage ob Izeit_1 > als Wartezeit ist; sei(); und danach fortfahren mit dem Ablauf.

    Meine Initialisierungsroutine ist auf Zeiteinheiten von 50 µs abgestimmt (bei m-einen 20 MHz-Quarzen). Du kannstmusstsollst die Initialisierung des Timers auf Deine Bedürfnisse abstimmen. Den Pin auf PC5 toggeln wäre eine Testmöglichkeit - im normalen Leben sollst Du so eine Zeile löschen oder auskommentieren.

    Dies ist nur EINE Möglichkeit. Ist die verständlich?

    Natürlich ist es auch möglich den Timer für eher kurze Zeiten direkt zu schalten und in der ISR ein Flag zu setzen, das im Main abgefragt wird. Wie gesagt - es führen viele Code-Wege nach Rom.

    Viel Erfolg
    Ciao sagt der JoeamBerg

  6. #6
    Hallo an alle,

    vielen vielen Dank schon mal für die vielen Infos. Ich werd mir alles in Ruhe durchlesen. Ich meld mich dann wieder .

    THX und lG Anna

  7. #7
    Hallo,

    danke nochmals an alle.

    @Joe:
    hab mir mal deinen Code angeschaut. Wie kommst du auf deinen OCR2A Wert von 125? Laut Datenblatt ist die Formel doch (OCR2A=(fCLK/(2*N*fOCR))-1= 61.5 (bei deinen gewählten Werten).

    @sebastian

    Ich hab mal deinen Code verwendet. Hab die Timer_Init auf meinen Controller (AT90CAN) angepasst. Den Rest hab ich gelassen. Ich hab noch ein main-Programm eingefügt. Ich will zur Probe einfach mal eine LED nach einer gewissen Zeit schalten. Es funktioniert nicht. Ich hab den main-Code mal angehängt
    Code:
    int main(void)
    {
      DDRA = 0xff;
      PORTA = 0xff;
      uint8_t my_timer = AllocateCountdownTimer();
      while(1)
      {
        SetCountdownTimer(my_timer,200); /*20*10mS ergibt 2000 mS*/
        if (GetCountdownTimer(my_timer)) 
    	{ 
          PORTA = 0x00;
        }
        ReleaseCountdownTimer(my_timer);
      }
    }
    Im Prinzip ist der Code von der Funktion her genau dass was ich brauche

    Bis dann lG Anna

  8. #8
    Erfahrener Benutzer Robotik Visionär Avatar von oberallgeier
    Registriert seit
    01.09.2007
    Ort
    Oberallgäu
    Beiträge
    7.585
    Hei, Anna, beginner1101,

    Zitat Zitat von beginner1101
    Hallo,

    danke nochmals an alle...
    Gern geschehen.

    Zitat Zitat von beginner1101
    @Joe:
    hab mir mal deinen Code angeschaut. Wie kommst du auf deinen OCR2A Wert von 125? Laut Datenblatt ist die Formel doch (OCR2A=(fCLK/(2*N*fOCR))-1= 61.5 (bei deinen gewählten Werten).
    Gratuliere - endlich mal jemand der nicht nur Fragen stellt, sondern auch Doc´s liest und nachvollzieht. Leider erschließt sich die Formel weder meinen (mehreren) mega168ern noch mir (im Klartext - die ist nach meiner Ansicht und offenbar auch nach Ansicht meiner ATMEL-mega168er einfach falsch). Wieso? Erstens - und das ist der wichtigste Grund - die Zielfrequenz wird mit dem Ergebnis dieser Formel nicht erreicht. Der zweite Grund - der Text des doc´s beschreibt einen anderen mathematischen Zusammenhang.

    Zitat Zitat von ATMEL-doc2545 - Seite 146
    17.7.2 Clear Timer on Compare Match (CTC) Mode
    In Clear Timer on Compare or CTC mode (WGM22:0 = 2), the OCR2A Register is used to manipulate the counter resolution.
    Ok, das kennen wir und das können wir nachvollziehen. Keine Probleme. Mein WGM22:0 = 2 - (weil WGM21 = 1).

    Weiter im Text:
    Zitat Zitat von ATMEL-doc2545 - Seite 146
    In CTC mode the counter is cleared to zero when the counter value (TCNT2) matches the OCR2A.
    Also jedes Mal, wenn mein Timer auf meinen OCR2A stößt, wird er genullt. Clear Timer on Compare A. Der Timer wird aufwärts gezählt entsprechend meinem Vorteiler. Steht auch irgendwo. Als ich mit dem Lesen so weit war (irgendwann mit tiny13 oder so) ,

    .........................

    ... hatte ich mir ein Excelsheet gemacht - da ich faul bin, besonders rechenfaul. Daher hatte ich nie (tut mir leid) diese Formel angesehen. Nun rechne ich diese Geschichte mal nach der Methode Milchmädchen (bitte um Nachsicht):

    20 MHz / 8 (prescaler) = 2,5 MHz und da der Timer von unten nach oben zählt, erreicht er die "125" :
    2,5 MHz / 125 (preset) = 20 kHz - voilà

    . . . . und diesen Wert glauben auch meine mega168er . Ok?
    Ciao sagt der JoeamBerg

  9. #9
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    08.05.2005
    Ort
    Issum
    Alter
    46
    Beiträge
    2.236
    Hallo Anna,

    Drei Sachen,

    Am Anfang des Programms mußt Du Timer0Init(); aufrufen.
    Du mußt die Interrupts mit sei();erlauben
    Code:
      if (GetCountdownTimer(my_timer))
       {
          PORTA = 0x00;
        }
    Wenn Du den Timer mit 200 belädst, zählt der runter bis Null
    Außerdem rufst Du die Set Funktion in der Endlosschleife immer wieder auf
    So wäre das besser:
    Code:
    int main(void)
    {
      DDRA = 0xff;
      PORTA = 0xff;
      Timer0Init();
      uint8_t my_timer = AllocateCountdownTimer();
     SetCountdownTimer(my_timer,200); /*20*10mS ergibt 2000 mS*/
      while(1)
      {
        
        if (!GetCountdownTimer(my_timer))
       {
          PORTA = 0x00;
        }
        ReleaseCountdownTimer(my_timer);
      }
    }
    Ich hoffe das klappt so

    Gruß Sebastian
    Software is like s e x: its better when its free.
    Linus Torvald

  10. #10
    Hallo,

    nach dem ich nach langem mal wieder Zeit habe mich mit Mikrocontrollern zu beschäftigen, habe ich mich mal wieder um die Timer gekümmert.
    Ich hab versucht den Vorschlag von "oberallgaier" zu realisieren. Vom Prinzip her habe ich es verstanden ich bekomm aber immer folgende Fehler meldung:

    Code:
    ../Timerfunktion.c:26: error: 'Izeit_1' undeclared (first use in this function)
    ../Timerfunktion.c:26: error: (Each undeclared identifier is reported only once
    ../Timerfunktion.c:26: error: for each function it appears in.)
    ../Timerfunktion.c: In function 'main':
    ../Timerfunktion.c:52: error: 'Izeit' undeclared (first use in this function)
    make: *** [Timerfunktion.o] Error 1
    Build failed with 4 errors and 0 warnings...
    In meiner main-Routine declariere ich die variable Izeit_1. Warum kennt er dann die Variable nicht in der ISR. Ich rufe die ISR doch erst später, nach der Deklaraqtion auf......

    Code:
    #include <stdlib.h>
    #include <avr/pgmspace.h>
    #include <stdio.h>
    #include <util/delay.h>
    
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    /* ============================================================================== */
    void Timer0_init(void)                  
    {        
        TCCR0 |= (1<<WGM01) | (1<<CS01);              
        OCR0 = 125;                        
        TIMSK |= (1<<OCIE0);      
    }     
    /* ============================================================================== */
    
    
    /* ============================================================================== */
    /* ===  Nicht unterbrechbare ISR für timer2 ===================================== */
                           
    ISR(TIMER0_COMP_vect)          // Vektor 7
    {
        if (Izeit_1 <= 60000)        //Timer bis 60 000 - 3 sec Datenerfassung möglich
          Izeit_1 ++;               //  war: alle drei Sekunden wird 60000 erreicht
                      //  und Izeit_1 bleibt in der uint16-Grenze
        else
        {   
            Izeit_1 = 0;      // ansonsten: Rückstellen auf Null
            PORTC ^= (1<<PC5);      // Zur Anzeige gLED am PC5/SCL toggeln
        }     
             
    }         
    /* ============================================================================== */
    
    int main(void)
    {
    uint16 Izeit_1=0;
    Timer0_init();
    DDRC |= 0xff; 
    PORTC=0x00;
    while(1)
    {
    cli();
    Izeit_1=0;
    sei();
    if (Izeit>5000)
    {
    PORTC ^= (1<<PC1);
    }
    cli(); 
    }
    }
    Wäre super wenn mir jemand helfen könnte.

    Bis dann lG Anna

Seite 1 von 3 123 LetzteLetzte

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •