-         
Ergebnis 1 bis 9 von 9

Thema: Timer auf ATMega8

  1. #1
    Neuer Benutzer Öfters hier
    Registriert seit
    23.02.2010
    Ort
    Düsseldorf
    Beiträge
    24

    Timer auf ATMega8

    Anzeige

    Hallo mal wieder zusammen,

    nach ersten Schaltversuchen und Spielen mit delay.h (siehe früherer Threas von mir), möchte ich mich jetzt mit Interrupts bzw dem Timer beschäftigen.

    Ziel meiner Programmierung: Eine LED soll im Sekunden Takt durch einen Timer geschaltet werden. Leider steige ich die Beschreibung nicht durch:
    http://www.mikrocontroller.net/artic...A4hler_des_AVR

    Gearbeitet wird an dieser Schaltung mit einem ATMega8 bei F_CPU 1000000
    http://olimex.com/dev/images/avr-p28-sch.gif

    Meine Idee:
    Code:
    ISR(TIMER1_OVF_vect)
    {
         PORTC ^= (1<<5);     //Invertiert den Zustand an der LED (PORTC 5)
    }
    Damit das klappt muss ich aber noch den Timer1 aktivieren, den Prescaler einstellen und das Vergleichsregister entsprechend einstellen.

    Und an dieser Stelle setzt es aus...Was müsste ich da einstellen und wieso?

    Lieben Dank im Vorraus.
    Georg

  2. #2
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    02.11.2005
    Alter
    42
    Beiträge
    1.145
    Ok, gehen wir das mal Schritt für Schritt durch:
    - bei einer Taktfrequenz von 1MHz und Prescaler 1 zählt der Zähler jede µs einen Schritt hoch.
    - Um auf 1 Sekunde zu kommen, bräuchtest Du also 1.000.000 Schritte.
    - Der Zähler geht nur bis 65.536. 1.000.000 / 65.536 = 15.259
    - Du brauchst also mindestens einen Prescaler von 16, um mit dem Zähler auf 1s zu kommen. 16 gibt's aber nicht, das nächste ist 64. Damit zählt der Counter also alle 64µs einen Schritt hoch. 1s / 64µs = 15.625. Damit hätten wir also schon mal den Vergleichswert.

    - Nun gibt es mehrere Möglichkeiten, den Timer für Deine Aufgabe einzustellen. Oft wird es so gemacht, dass der Timer im Normalmodus läuft und auf den Maximalwert - Vergleichswert vorgeladen wird und dann mit dem Overflow-Interrupt die gewünschte Aktion ausgeführt wird. Oder man lässt den Timer von 0 an laufen, setzt das OCR Register auf den Vergleichswert und löst damit den Compare-Interrupt aus. Diese beiden Methoden haben jedoch den Nachteil, dass man im Interrupt das Zählerregister manuell entweder auf den Vorladewert oder auf Null setzten muss.
    - Die sinnvollste Alternative ist es, den CTC-Modus zu verweden. Hierbei wird ebenfalls der Compare-Interrupt genutzt, allerdings setzt sich der Timer selbständig wieder auf 0 zurück, wenn der Interrupt ausgelöst wird.

    So sähe das Programm für den CTC-Modus aus:
    Code:
    int main(void)
    {
    	//Timer initialisieren
    	TCCR1A = 0x00;
    	TCCR1B = 0x0B;		//Prescaler 64, CTC-Mode
    	OCR1A = 15625;		//Vergleiswert setzten
    	TIMSK = (1<<OCIE1A);	//OC-Interrupt aktivieren
    	sei();
    
    	while (1)
    	{
    		//Hier passiert nix
    		//geht alles über Interrupts
    		asm volatile ("nop");
    	}
    
    	return 0;
    }
    
    ISR(TIMER0_COMPA_vect)
    {
    	PORTC ^= (1<<5);	//Invertiert den Zustand an der LED  (PORTC 5)
    }
    
    return 0;
    }
    Gruß,
    askazo

  3. #3
    Erfahrener Benutzer Robotik Einstein Avatar von 021aet04
    Registriert seit
    17.01.2005
    Ort
    Niklasdorf
    Alter
    30
    Beiträge
    4.676
    Hast du das Kapitel im DB gelesen?
    Wu musst wie du schon gesagt hast den Timer einstellen und aktivieren. Du musst den Timer einstellen (Prescaler, Startwert,...) und musst. Du brauchst mehrere Register zum Einstellen (TCCRB, TIMSK). Zum Schluss musst du noch die Interrupt freigeben ("sei();").
    Wenn du dir noch unsicher bist könntest du nach Interruptbeispielen suchen (Google).

    edit: Da war einer schneller.

    MfG Hannes

  4. #4
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    55
    Beiträge
    5.789
    Blog-Einträge
    8
    Hallo

    Am Thema Timer habe ich mich vor ein paar Tagen auch versucht, vielleicht hilft es weiter:
    https://www.roboternetz.de/phpBB2/vi...=491141#491141

    Gruß

    mic

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  5. #5
    Neuer Benutzer Öfters hier
    Registriert seit
    23.02.2010
    Ort
    Düsseldorf
    Beiträge
    24
    @askezo: Herzlichen Dank für die Erklärung. Das hat mein Problem direkt auf den Kopf getroffen.

    @021aet04: Welches Kapitel im DB meinst du? Was meinst du mit DB? Einfach bei rn-wissen.de den Artikel Timer/Counter (Avr)

    Ich hab mir nur die Artikel bei mikrocontroller.net angeschaut und wusste dann nicht mehr welche Bit(s) in welchen Registern ich setzen muss damit es funktioniert. War mir etwas zu viel Input - ahhhh, Overflow!!!

    Hab den Code von askazo noch ein wenig umgeschrieben. Für mich etwas übersichtlicher, weil ich die Bits konkret benenne und nicht hiner 0x0b verstecke (ist nicht böse gemeint, bin nur nicht so schnell im Umrechnen auf Binär). Sind das Codes gleicher Wirkung?
    Code:
    #include <avr/io.h>
    #include <stdint.h>
    #include <avr/interrupt.h>
    
    #define LEDon PORTC &= ~(1<<5);
    #define LEDoff PORTC |= (1<<5);
    #define LEDchange PORTC ^= (1<<5);
    
    void initialize();
    
    int main(void)
    {
    	initialize();
    
    	while(1)
    	{
    
    	}
    return 0;
    }
    
    void initialize()
    {
    	DDRC = (1<<5);			//Ausgang an Port C, Pin 5
    	DDRD = 0x00;			//Eingang an Port D
    	
    	PORTD = (1<<2);			//Port (PullUp) für Port D, Pin 2
    
    	TCCR1A = 0x00;			//Timer steuert nicht den Pin an und benutzt kein PWM
    	TCCR1B = (1<<CS11)|(1<<CS10)|(1<<CTC1);	//Prescaler mit CPU/64 und CTC (max-Wert für OCR=65.536)
    
    	TIMSK |= (1<<OCIE1A);		//Vergleichsregister aktivieren
    	
    	OCIE1A = 15625;			//Vergleichswert für CTC => (Prescaler*OCR)/F_CPU=1s
    	
    	sei();
    
    	LEDoff;
    }
    
    ISR(TIMER0_COMPA_vect)
    {
    	LEDchange;
    }
    [Müsste es nicht ISR(TIMER1_COMPA-vect){} heißen, weil ich benutze ja Timer1]


    Und zum Abschluss - herzlichen Dank für eure Antworten !!

  6. #6
    Erfahrener Benutzer Robotik Einstein Avatar von 021aet04
    Registriert seit
    17.01.2005
    Ort
    Niklasdorf
    Alter
    30
    Beiträge
    4.676
    DB => Datenblatt
    Kapitel über Timer/Counter. Dort werden alle Register beschrieben (auf Englisch). Es sind auch kleine Beispiele in ASM und C drinnen.

    "TIMSK |= (1<<OCIE1A)" => da würde ich nur = schreiben, da das Register einen beliebigen Zustand haben kann.

    Bei "sei();" würde ich als Kommentar dazuschreiben was dieser Befehl macht (zumindestens für den Anfang)
    sei(); => generelle Interruptfreigabe => Interrupt einschalten
    cli(); => generelle Interruptsperre => Interrupt ausschalten

    cli hast du aber in deinem Programm nicht.

    Bei "while(1) { }" kannst du auch so schreiben "while(1);". Die Klammer nimmt man, wenn man mehrere Dinge macht.

    Sonst sieht das Programm gut aus (Übersichtlich,...)

    MfG Hannes

  7. #7
    Neuer Benutzer Öfters hier
    Registriert seit
    23.02.2010
    Ort
    Düsseldorf
    Beiträge
    24
    Wenn das TIMSK einen beliebigen Zustand haben kann, wäre es dann nicht besser mit |= zu arbeiten, um gegebebenenfalls früher gesetzte Flags nicht zu überschreiben? Vielleicht habe ich ja an einer anderen Stelle des Programms schon den TOIE0 für Timer0 aktiviert - so theoretisch gesehen. Ist klar, dass das hier nicht der Fall ist.

    Danke für das "Übersichtlich" - ich nehm das mal als Kompliment, wobei es hier ja in der Größe nicht so schwer ist, oder?

  8. #8
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    02.11.2005
    Alter
    42
    Beiträge
    1.145
    Zitat Zitat von 021aet04
    "TIMSK |= (1<<OCIE1A)" => da würde ich nur = schreiben, da das Register einen beliebigen Zustand haben kann.
    Nein, das TIMSK-Register wird nach einem Reset des Controllers immer mit 0x00 initialisiert, kann also keinen beliebigen Zusatnd haben. Von daher ist das mit dem |= schon ganz ok. Vor allem, wenn man die Timer-Initialisierung mal in eine eigene Funktion auslagert und noch weitere Timer-Interrupts einsetzt, kann man sich damit evtl. eine lästige Fehlersuche ersparen.

    Ansonsten ist das Programm ok so. Nur der Kommentar
    //Vergleichsregister aktivieren
    passt nicht. Du aktivierst dort kein Register, sondern einen Interrupt.

    Gruß,
    askazo

  9. #9
    Erfahrener Benutzer Robotik Einstein Avatar von 021aet04
    Registriert seit
    17.01.2005
    Ort
    Niklasdorf
    Alter
    30
    Beiträge
    4.676
    Wenn das TIMSK einen beliebigen Zustand haben kann, wäre es dann nicht besser mit |= zu arbeiten, um gegebebenenfalls früher gesetzte Flags nicht zu überschreiben?
    Stimmt. Wenn man es am Anfang schreibt (Programmanfang) sollte man "=" schreiben. Sonst könnte es sein, dass wenn z.B. das TOIE0 gesetzt ist und man es nicht haben will (weil man es z.B. nicht braucht) trotzdem eine ISR (Interrupt Service Routine = Interrupt) auslöst. Wenn man nachher etwas verändert sollte man "|=" bzw "&= ~" schreiben.

    Danke für das "Übersichtlich" - ich nehm das mal als Kompliment, wobei es hier ja in der Größe nicht so schwer ist, oder?
    Ist ein Kompliment. Entweder man kann es übersichtlich schreiben oder nicht. Es ist egal, ob das Programm 3 oder 1000 Zeilen hat.

    MfG Hannes

Berechtigungen

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