- LiTime Speicher und Akkus         
Ergebnis 1 bis 9 von 9

Thema: Atmega32: Frequenzabhängiges Problem mit INT0 und Timer0

  1. #1
    Neuer Benutzer Öfters hier
    Registriert seit
    12.02.2008
    Beiträge
    8

    Frage Atmega32: Frequenzabhängiges Problem mit INT0 und Timer0

    Anzeige

    Powerstation Test
    Hallo zusammen!

    ich hab schon einige Zeit hier im Forum mitgelesen. Ich versuche, mich in die Materie µC einzuarbeiten, programmieren tue ich in C mittels AVR Studio 5.1. Nun komme ich aber nicht mehr weiter und stelle deshalb meine erste Frage an Euch!

    Ziel: Auf eine steigende und fallende Flanke am Pin Int0 nach einer einstellbaren Zeit reagieren (mittels externen Interrupt 0 und Timer0 Overflow Interrupt).

    Vorgehen: Zunächst wird der Timer0 im "normal mode" gestartet, dessen Overflow Interrupt bleibt zunächst aber deaktiviert. Nun gebe ich ein Rechtecksignal auf den Int0-Pin meines Atmega32 (eingebaut im RN control @ 16MHz) und konfiguriere den µC so, dass ein Interrupt bei fallender und steigender Flanke auf Int0 ausgelöst wird (ISR(INT0_vect). Nun möchte ich eine gewisse Zeit nach Eingang dieses Interrupts etwas anderes machen/reagieren. Ich aktiviere also in der ISR(INT0_vect) den Timer0 Overflow Interrupt, initialisiere den Timer0 auf z.B. 1, schalte den Int0-Interrupt aus und schalte Pin PA1 auf high. Nun zählt Timer0 hoch und läuft über, wobei der Timer0 Overflow Interrupt ausgelöst wird (ISR(TIMER0_OVF_vect). In dieser ISR schalte ich zunächst den Overflow Interrupt aus, aktiviere wieder den externen Int0 Interrupt und ziehe PA1 auf low.
    Die Zeit zwischen dem externen Interrupt 0 und dem folgendem Overflow Interrupt des Timer 0 (= Delay) wird festegelegt durch den verwendeten Prescaler des Timer 0 (in meinem Fall 64) und den Wert, den man TCNT0 in der Int0 ISR füttert. In meinem Codebeispiel wird TCNT0 1 gesetzt (8 bit Register), ich erwarte also ein Delay von 1/(16000000/64)*255 = ~1 ms. Dieses Delay versuche ich mittels dem Schalten des Pin PA1 sichtbar zu machen. Ich erwarte demnach einen Puls an PA1 für die Dauer von ~1ms nach einer eingehenden Flanke auf Int0.
    Zum Testen verwende ich einen Funktionsgenerator (PCGU1000 von Velleman), der die Flanken generiert und ein DSO (Rigol DS1102E), welches Pin PA1 überwacht.

    Es tritt dabei folgendes Problem auf: Je nach Frequenz des Funktionsgeneratorsignals (Rechteck), funktioniert das Ganze oder nicht. Bei 100 Hz auf Int0 ist der high-Puls auf PA1 immer (!) extrem kurz (µs). Bei 300 Hz ist der Puls immer ~1 ms lang, wie erwartet. Bei 250 Hz funktioniert es manchmal, manchmal nicht. Woran liegt das? Hat der µC Probleme, "langsam" steigende und fallende Flanken mittels Pin Change Interrupt auszuwerten? Vielleicht kennt ja jemand dieses Verhalten und kann mir das erklären. Wie kommen bei den "Fehl-Triggern" (100 Hz) die kurzen High-Pulse zustande (im Bereich von µs!)?

    Hier der Code:
    Code:
    #define F_CPU 16000000UL
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    
    
    void InitTimer0()
    {
        
        TCCR0 |= (1<<CS01) | (1<<CS00) | (1<<COM00)    ;    //Setup timer0 in normal mode with a prescaler of 64; enable OVF interrupt in ISR to start interrupt generation
        DDRB |= (1<<PB3);                                //set OC0 as output
       
    }
    
    void StopTimer0()
    {
        TCCR0 &= ~(1<<COM00);                //Set pin mode to normal operation; if this is not done, the pin will stay at the last logical level when stoppting timer
        TCCR0 &= ~(1<<CS02);                //Stops timer according to datasheet
        TIMSK &= ~(1<<TOIE0);                //disable timer0 OVF interrupt;
    }
    
    
    int main (void)
    {  
       MCUCR |= (1<<ISC00);        //interrupt on any logical change on Int0 "PD2"
       GICR |= (1<<INT0);        //enable INT0 interrupt
       DDRA |= (1<<PA1);        //Pin PA1 as output
       
       InitTimer0();            //start timer 0
       
       sei(); // Enable the Global Interrupt Enable flag so that interrupts can be processed
       
          
       while(1)                    
       {    
            //nothing here except interrupts    
        }
              
    }
    
    
    
    ISR(INT0_vect)                        //ISR when logical change on INT0 "PD2"
        {    
            
            GICR &= ~(1<<INT0);            //disable this IRF; enable again when the timer0 OVF ISR fires
            TIMSK |= (1<<TOIE0);        //enable timer0 OVF interrupt; will fire about 1ms after this ISR has completed
            TCNT0 = 1;                    //Write 1 to timer 0 to restart counting at bottom+1; any 8 bits work to adjust delay
            PORTA |= (1<<PA1);            //Set PA1 high when INT0 fires
            //SFIOR |= (1<<PSR10);        //Reset prescaler
        }
    
    ISR(TIMER0_OVF_vect)
        {    
            TIMSK &= ~(1<<TOIE0);        //disable timer0 OVF interrupt -> enable again in next INTO ISR
            //StopTimer0();                //Stop timer0; not needed as OVF interrupt is t
            GICR |= (1<<INT0);            //turn on INT0 ISR again                                            
            PORTA &= ~(1<<PA1);            //Set PA1 low again when Timer0 OVF ISR fires -> High pulse (~1ms in duration) after logic level change on INT0 pin
        }
    Zudem hänge ich zwei Bilder an, die dieses Verhalten zeigen.

    1. Bild - Timer0 Fehler: 100 Hz Rechteck auf Kanal 1 (gelb) verursacht zu kurzen High Puls auf PA1 (2. Kanal; blau). Anstiegszeiten des Rechtecksignals an Int0 ist ~30ns, Anstiegszeit von Pin PA1 ~24 ns. Zoom auf eine Flanke.

    Klicke auf die Grafik für eine größere Ansicht

Name:	100HzSquareWaveZoomedIn_FalseTimer.png
Hits:	6
Größe:	3,5 KB
ID:	22175

    2. Bild - Timer0 läuft korrekt: 300 Hz Rechteck auf Kanal 1 (gelb) verursacht korrekten High Puls auf PA1 (2.Kanal; blau). Anstiegszeiten sind wie in Bild 1. Mehrere Flanken sichtbar.

    Klicke auf die Grafik für eine größere Ansicht

Name:	300HzSquareWave_CorrectTimer.png
Hits:	4
Größe:	3,4 KB
ID:	22176


    Ich hoffe, ich habe nichts vergessen. Falls doch, gebt kurz Bescheid .

    Vielen Dank für Eure Hilfe!

    Christian

  2. #2
    Erfahrener Benutzer Roboter Experte Avatar von sternst
    Registriert seit
    07.07.2008
    Beiträge
    672
    Dein Problem ist, dass du den Timer durchlaufen lässt, dich aber nicht um das Overflow-Flag kümmerst. Deshalb geht das Ganze in die Hose, sobald der Abstand zwischen zwei Flanken größer wird wie 2 Timer-Zyklen. Dann nämlich ist zum Zeitpunkt des Overflow-Interrupt-Einschaltens das entsprechende Flag bereits gesetzt, so dass der Interrupt sofort auslöst (daher der nur sehr kurze Puls).

    Ließ dir mal diesen Thread durch:
    http://www.mikrocontroller.net/topic/255562#new
    Was ich dort zum TXC-Flag geschrieben habe, gilt auch genau so für das Overflow-Flag.
    MfG
    Stefan

  3. #3
    Neuer Benutzer Öfters hier
    Registriert seit
    12.02.2008
    Beiträge
    8

    Danke!

    Hi Stefan,

    vielen Dank für Deinen Hinweis! Ich habe nun zwei Varianten getestet, um das beschriebene Problem zu verhindern.

    1. Möglichkeit: Ich lösche das Timer0 Overflow Flag manuell, indem ich eine 1 ins TOV0 schreibe. Das funzt wunderbar! Hier der Code zur Dokumentation:

    Code:
    / 
    #define F_CPU 16000000UL
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    
    
    void InitTimer0()
    {
        //TCCR0 |= (1<<WGM01) | (1<<WGM00);                //Fast PWM
        TCCR0 |= (1<<CS01) | (1<<CS00) | (1<<COM00)    ;    //Setup timer0 in normal mode with a prescaler of 64; enable OVF interrupt in main loop to start interrupt generation
        DDRB |= (1<<PB3);                                //set OC0 as output
        //TIMSK |= (1<<TOIE0);                            //enable timer0 OVF interrupt;
    }
    
    void StopTimer0()
    {
        TCCR0 &= ~(1<<COM00);                //Set pin mode to normal operation; if this is not done, the pin will stay at the last logical level when stoppting timer
        TCCR0 &= ~(1<<CS02);                //Stops timer according to datasheet
        TIMSK &= ~(1<<TOIE0);                //disable timer0 OVF interrupt;
    }
    
    
    int main (void)
    {  
       MCUCR |= (1<<ISC00);        //interrupt on any logical change on Int0 "PD2"
       GICR |= (1<<INT0);        //enable INT0 interrupt
       DDRA |= (1<<PA1);        //Pin PA1 as output
       
       InitTimer0();            //start timer 0
       
       sei(); // Enable the Global Interrupt Enable flag so that interrupts can be processed
       
          
       while(1)                    
       {    
            //nothing here except interrupts    
        }
              
    }
    
    
    
    ISR(INT0_vect)                        //ISR when logical change on INT0 "PD2"
        {    
            
            GICR &= ~(1<<INT0);            //disable this IRF; enable again when the timer0 OVF ISR fires
            TIFR |= (1<<TOV0);            //clear Timer 0 Overflow flag by writing one to the register!
            TIMSK |= (1<<TOIE0);        //enable timer0 OVF interrupt; will fire about 1ms after this ISR has completed
            TCNT0 = 250;                    //Write x to timer 0 to restart counting; any 8 bits work to adjust delay
            PORTA |= (1<<PA1);            //Set PA1 high when INT0 fires
            //SFIOR |= (1<<PSR10);        //Reset prescaler
        }
    
    ISR(TIMER0_OVF_vect)
        {    
            TIMSK &= ~(1<<TOIE0);        //disable timer0 OVF interrupt -> enable again in next INTO ISR
            //StopTimer0();                //Stop timer0
            GICR |= (1<<INT0);            //turn on INT0 IRF again                                            
            PORTA &= ~(1<<PA1);            //Set PA1 low again when Timer0 OVF ISR fires -> High pulse after logic level change on INT0 pin
        }
    Mit diesem Code läuft der Timer0 durchgehend. Ich habe mal das Delay zwischen Eintreffen des externen Interrupts und dem Hochziehen von PA1 mitm Oszi gemessen. Das Delay beträgt ca. 2µs. Siehe Bild:

    Klicke auf die Grafik für eine größere Ansicht

Name:	NewFile22.jpg
Hits:	3
Größe:	48,6 KB
ID:	22199

    Falls man den Timer0 für andere Sachen benötigt, während der externe Interrupt abgearbeitet ist, kann man Folgendes machen...

    2. Möglichkeit: Ich starte und stoppe den Timer, indem ich den Prescaler an- bzw. ausschalte (siehe Datenblatt). Um das Timer Overflow Flag und das entsprechende Enable Bit braucht man sich nicht mehr zu kümmern. Hier der Code zur Dokumentation:

    Code:
    #define F_CPU 16000000UL
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    
    /*
    void InitTimer0()
    {
        //TCCR0 |= (1<<WGM01) | (1<<WGM00);                //Fast PWM
        TCCR0 |= (1<<CS01) | (1<<CS00) | (1<<COM00)    ;    //Setup timer0 in normal mode with a prescaler of 64; enable OVF interrupt in main loop to start interrupt generation
        DDRB |= (1<<PB3);                                //set OC0 as output
        //TIMSK |= (1<<TOIE0);                            //enable timer0 OVF interrupt;
    }
    
    void StopTimer0()
    {
        TCCR0 &= ~(1<<COM00);                //Set pin mode to normal operation; if this is not done, the pin will stay at the last logical level when stopping timer
        TCCR0 &= ~(1<<CS01) | ~(1<<CS00);    //Stops timer according to data sheet
        TIMSK &= ~(1<<TOIE0);                //disable timer0 OVF interrupt;
    }
    */
    
    int main (void)
    {  
       MCUCR |= (1<<ISC00);        //interrupt on any logical change on Int0 "PD2"
       GICR |= (1<<INT0);        //enable INT0 interrupt
       DDRA |= (1<<PA1);        //Pin PA1 as output
       
       /*This will init timer0 without starting it*/
       DDRB |= (1<<PB3);        //set OC0 as output
       TCCR0 |= (1<<COM00);        //Setup timer0 in normal mode
       TIMSK |= (1<<TOIE0);        //enable timer0 OVF interrupt;
       
       sei(); // Enable the Global Interrupt Enable flag so that interrupts can be processed
       
          
       while(1)                    
       {    
            //nothing here except interrupts    
        }
              
    }
    
    
    
    ISR(INT0_vect)                                //ISR when logical change on INT0 "PD2"
        {    
            
            GICR &= ~(1<<INT0);                    //disable this IRF; enable again when the timer0 OVF ISR fires
            TCCR0 |= (1<<CS01) | (1<<CS00);        //start Timer0 by setting prescaler (here 64)!
            TCNT0 = 250;                        //Write x to timer 0 to restart counting; any 8 bits work to adjust delay
            PORTA |= (1<<PA1);                    //Set PA1 high when INT0 fires
        }
    
    ISR(TIMER0_OVF_vect)
        {    
            TCCR0 &= ~(1<<CS01) | ~(1<<CS00);    //Stops timer according to data sheet (unset prescaler)
            GICR |= (1<<INT0);                    //turn on INT0 IRF again                                            
            PORTA &= ~(1<<PA1);                    //Set PA1 low again when Timer0 OVF ISR fires -> High pulse after logic level change on INT0 pin
        }
    Am Oszi schaut das dann so aus:

    Klicke auf die Grafik für eine größere Ansicht

Name:	NewFile33.jpg
Hits:	3
Größe:	44,3 KB
ID:	22200

    Es ist zu erkennen, dass der Interrupt schneller verarbeitet wird als bei Möglichkeit 1 (ca. 200ns schneller). Das ist nicht viel, aber man kann den Timer nun in den Pausen für was anderes hernehmen. Ich werd's also so machen.

    Vielleicht helfen diese Codeschnippsel ja jemand. Danke nochmals für die Hilfe!

    Schönen Abend noch,

    Christian

  4. #4
    Erfahrener Benutzer Roboter Experte Avatar von sternst
    Registriert seit
    07.07.2008
    Beiträge
    672
    Ich lösche das Timer0 Overflow Flag manuell
    Nur leider auch gleich alle anderen gesetzten Flags in dem Register mit. Überlege mal, was |= genau macht.


    Es ist zu erkennen, dass der Interrupt schneller verarbeitet wird als bei Möglichkeit 1 (ca. 200ns schneller).
    Ich hoffe mal, du meinst mit "Interrupt schneller verarbeitet wird" nicht so was wie "Interrupt reagiert schneller". Wenn du den Pin am Ende der ISR auf High setzt, dann hast du mit unterschiedlich langen ISRs natürlich unterschiedliche Ergebnisse. Setze den Pin am Anfang der ISR, dann hast du auch diesen Unterschied nicht.
    MfG
    Stefan

  5. #5
    Neuer Benutzer Öfters hier
    Registriert seit
    12.02.2008
    Beiträge
    8
    Hi Stefan,

    danke für Deine Antwort. Genau, meine Messungen sollten nur zeigen, welche ISR schneller abgearbeitet wird. Ich kenne mich nicht aus, welche Instruktion länger zum Verarbeiten benötigt, deshalb habe ich das rein interessehalber gemessen. Auf die Reaktionszeit der ISR sollte das natürlich keinen Einfluss haben (deshalb setzte ich PA1 am Ende der Routine).
    Ich verstehe leider nicht, was Du mit Deinem ersten Satz meinst. Ich denke Du zielst auf die Zeile in meinem Code ab:
    TIFR |= (1<<TOV0); //clear Timer 0 Overflow flag by writing one to the register!
    Hier setzte ich nur das Bit #0 (aka TOV0) auf Eins, der Rest des Registers bleibt unverändert. Oder habe ich da was grundlegend falsch verstanden?
    Zum Löschen des Flags muss man laut Datenblatt eine eins schreiben, deswegen die ODER Verknüpfung. Was mache ich dort also Deiner Meinung nach falsch?

    Viele Grüße,

    Christian

  6. #6
    Erfahrener Benutzer Roboter Experte Avatar von sternst
    Registriert seit
    07.07.2008
    Beiträge
    672
    Zitat Zitat von Kokomoking Beitrag anzeigen
    Hier setzte ich nur das Bit #0 (aka TOV0) auf Eins, der Rest des Registers bleibt unverändert.
    Das gilt für ein normales Register, aber bei einem Register voller Interrupt-Flags gilt das "der Rest des Registers bleibt unverändert" eben nicht. Nochmal: was macht die Zeile genau. Nehmen wir an, Bit 0 und 1 sind gesetzt, was passiert dann bei der Zeile Schritt für Schritt?
    MfG
    Stefan

  7. #7
    Neuer Benutzer Öfters hier
    Registriert seit
    12.02.2008
    Beiträge
    8
    Warum wird das bei einem "Interrupt-Register" unterschiedlich gehandhabt? Hast Du eine Referenz im Datenblatt, wo ich das nachlesen könnte?

    Meiner Meinung passiert folgendes: Wir nehmen an, dass in dem Register BIT0 = 0 und BIT1 = 1 bereits gesetzt sind, nun kommt die besagte Codezeile. (1<<TOV1) erzeugt eine Bitmaske, in der nur BIT0 1 ist. Nun wird dieses Byte mit dem Register ODER verknüpft und BIT0 des Register (=0) ODER 1 = 1, dass Bit0 wird also gesetzt. BIT1 des Registers (=1) wird mit 0 ODER verknüpft und bleibt gesetzt. Wäre es ursprünglich Null gewesen, wäre es jetzt immer noch null. Das ist zumindest mein Verständnis, wie man einzelne Bits in C setzen kann, ohne andere Bits zu beeinflussen. Zum Löschen eines einzelnen Bits wird man analog invertieren (die Bitmaske) und UND verknüpfen.

    Bitte erklär mir den Unterschied bei Interrupt Flag Registern.

    Cheers,

    Christian

  8. #8
    Neuer Benutzer Öfters hier
    Registriert seit
    12.02.2008
    Beiträge
    8
    Ok, ich les da grad was auf AVR freaks und in der AVRlibc Hilfe. Das REgister wird bei ner ODER Verknüpfung erst gelesen, dann verglichen, und dann wieder geschrieben. Damit werden dan BIT0 und BIT1 gesetzt. Das ist natürlich Schmarrn...JEtzt muss ich nur noch rausfinden, was TIFR = _BV(TOV0) übersetzt heißt...

  9. #9
    Neuer Benutzer Öfters hier
    Registriert seit
    12.02.2008
    Beiträge
    8
    Ok, TIFR = _BV(TOV0) heißt wohl einfach soviel wie TIFR = (1<<TOV0). Ich probier das gleich mal aus. Scheint ein häufiger Fehler zu sein, in manchen Tutorials ist es auch falsch drin (also mittels ODER Verknüpfung)...

    Tausend Dank für den Hinweis Stefan!

    Cheers,

    Christian

Ähnliche Themen

  1. PWM-Messung mit ATTiny15 - INT0 TIMER0
    Von Ulfens im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 9
    Letzter Beitrag: 30.04.2009, 14:37
  2. Timer0 beim Atmega32 will nicht
    Von WarChild im Forum Allgemeines zum Thema Roboter / Modellbau
    Antworten: 10
    Letzter Beitrag: 24.03.2009, 21:46
  3. Timer0 des ATmega32 tickt zu langsam
    Von Djon im Forum C - Programmierung (GCC u.a.)
    Antworten: 7
    Letzter Beitrag: 11.09.2008, 16:27
  4. counter mit timer0 an atmega32
    Von Che Guevara im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 3
    Letzter Beitrag: 30.08.2008, 16:08
  5. Problem mit int0 beim ATMEGA32... (kein Rising möglich?)
    Von boeseTURBO-CT im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 1
    Letzter Beitrag: 24.05.2004, 20:46

Stichworte

Berechtigungen

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

LiTime Speicher und Akkus