mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Frage zu Timer und genereller Programmaufbau/Ablauf


Autor: Paddyr (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

mein Name ist Patrick und ich beschäftige mich seit kurzem mit µC's. Bin 
gerade dabei, mit Timer herum zu experimentieren.
Dabei beabsichtige ich folgenden Versuchsaufbau:
Atmega8, interner Takt 8Mhz
Taster wird eingelesen, eine LED soll dadurch leuchten und zwar eine 
bestimmte Zeit (über Timer eingestellt) ca. 5sec.
Ich möchte jedoch, dass bei erneutem Drücken der Tastre die LED sofort 
ausgeht oder eben von selbst nach 5 sec. Danach soll die LED wieder 
durch erneutes Drücken des Tasters für 5sec angehen (oder eben bis der 
Taster wieder gedrückt wird)
Soweit so gut. Den Timer habe ich initialisiert, Prescaler, etc. 
eingestellt, Interrupts aktiviert. In der Interrupt Routine zähle ich 
die Sekunden mittels Variablen, soweit habe ich das aus dem Tutorial 
entnehmen können.
Nur wie ich den Timer dann in das Programm integriere, ist für mich 
fraglich bzw. wie der µC das Programm abarbeitet.
Geschrieben wird das Programm in C und ist bis jetzt aufgrund des 
fehlenden Verständnisses nicht komplett, sonst würde ich den Code hier 
mal posten.

Für mich stellt sich generell der Frage, wie ich an so ein Problem 
programmiertechnisch herangehe.
Im Hauptprogramm lese ich den PIN ein, wo der Taster hängt. Ist dieser 
gedrückt, setze ich den Ausgang der LED auf HIGH. Nur wie integriere ich 
dann den Timer in das Programm? Und was macht der µC während dieser 
"Wartezeit". Nach der Wartezeit soll der Ausgang wieder auf LOW gesetzt 
werden.
Nur wie bekomme ich es dann hin, dass wenn der Taster erneut gedrückt 
wird, dass die LED sofort ausgeht bzw. der Ausgang eben direkt auf LOW 
gesetzt wird?
Kann ich dies einfach durch eine IF Abfrage lösen? Oder würde man hier 
ein weiteres (exgternes) Interrupt benötigen, welches durch den Taster 
ausgelöst wird?

Vielleicht kann mir jemand einmal die Arbeitsweise näher bringen oder 
vielleicht ein Beispiel C Code eines ähnlichen Programms posten. Im 
Tutorial konnte ich dazu leider nichts finden.

Danke.

Viele Grüße
Paddy

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In diesem Fall wuerde ich sagen: Initialisiere den Timer, starte ihn und 
schicke das Hauptprogramm in eine Endlosschleife. Aendere den 
Timerintervall so ab, dass er 1000 mal pro Sekunde auftritt. Jetzt 
fragst du jede Millisekunde die Taste ab (das erspart dir erstmal 
Entprellroutinen, ist nicht perfekt, aber geht so).

Die LED steuerst du ueber eine Variable, die du bei jedem 
Interruptaufruf runter zaehlst, solange sie nicht 0 ist. Wird sie 0, 
schaltest du die LED aus.

Wird eine Taste gedrueckt, pruefst du die Variable: Ist sie nicht 0, ist 
die LED gerade an. Also schaltest du sie aus und setzt den Zaehler auf 
0. Ist sie 0, ist die LED gerade aus. Also schaltest du sie ein und 
setzt den Zaehler auf 5000.

Autor: Jean (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
mal auf die schnelle geschrieben. Die Kommentare musste selbst in den 
Registern einstellen.
Sollte aber funzen oder zumindest annähernd.
#include <avr\io.h>
#include <interrupt.h>

volatile char Taster , Merker;
int main()
{
  Taster = 0;
  Merker = 0;
  switch(Taster)
  {
    case 1:
    {
      //Timer konfigurieren und starten
      //LED ein
      Taster = 0;
      break;
    }
    case 2:
    {
      //Timer aus
      //LED aus
      Taster = 0;
      break;
    }
    default:
    {
      break;
    }
  }
}

  //CTC Interrupt
  ISR(TIMER1_COMPA_vect)
  {
    //LED aus
    //Taster = 0;
  }

  ISR(INT0_vect)
  {
    if(!Taster && !Merker)
    {
      Taster = 1;
      Merker = 1;
    }
    else if(!Taster && Merker ==1)
    {
      Taster = 2;
      Merker = 0;
    }
    else
    {
      //TODO
    }
  }

Autor: Jean (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ups,
habe den ISR0 Einstellungs Kommentar am Anfang der main() vergessen !
Oje und sei() auch vergessen, peinlich !
Entprellung fehlt natürlich auch !

Autor: Spera (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jean schrieb:
> Ups,
> habe den ISR0 Einstellungs Kommentar am Anfang der main() vergessen !
> Oje und sei() auch vergessen, peinlich !
> Entprellung fehlt natürlich auch !

Und while fehlt auch^^

Autor: Thisamplifierisloud (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Den

volatile char Merker;

tät ich als

static char Merker=0;

in der ISR deklarieren.

Wegen der Sauberkeit !

Autor: Paddyr (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

vielen Dank für die Antworten
Bei dem Vorschlag von Peter Stegemann frage ich die Zustände des Tasters 
im Hauptprogramm ab oder?
Für mich ist als Anfänger nicht ganz klar, was ich im Hauptprogramm 
abfragen muss und was muss in die Interrupt Routinen?
@Jean,
vielen Dank für das Programm. Ich denke, dies ist bestimmt sehr 
hilfreich. Möchte aber zunächst versuchen, das ganze selbst schreiben 
(man will ja lernen). Aber zur Not dient es bestimmt als gute 
Spicklösung oder Anregung, falls ich nicht weiterkomme.

Viele Grüße
Paddy

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Paddyr schrieb:
> Hallo zusammen,
>
> vielen Dank für die Antworten
> Bei dem Vorschlag von Peter Stegemann frage ich die Zustände des Tasters
> im Hauptprogramm ab oder?

Nein, auch im Interrupt. Der tritt ja jede Millisekunde auf und kann das 
elegant erledigen.

Stelle doch mal deinen aktuellen Code ein... oder naehere dich dem 
Ganzen schrittweise:

Mach mal im Timerinterrupt nur die LED an und aus, so im Sekundentakt.

> Für mich ist als Anfänger nicht ganz klar, was ich im Hauptprogramm
> abfragen muss und was muss in die Interrupt Routinen?

Muessen tust du mal gar nichts - allerdings gibt es gewisse 
Vorgehensweisen, die sich als praktisch erwiesen haben ;-)

Ich erledige z.B. die ganzen Zeichenaufgaben (ich habe bei meinem 
Hauptprojekt umfrangreiche, grafische Menues) im "Hauptprogramm". 
Einfach aus dem Grund, weil diese Aufgaben die niedrigste Prioritaet 
haben und jederzeit von den Timern, die in mehr oder weniger strikten 
Intervallen Aufgaben zu erledigen haben, unterbrochen werden koennen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter hat es ja schon angesprochen.

Für einen Neuling würde ich unbedingt zu der grundelgenden Arbeitsweise 
raten:
Sieh die Timer-ISR wie eine Art Uhr an, die in regelmässigen Abständen 
'tickt', also Code ausführt.
Dieser Code kann zb sein: eine Variable erhöhen (das wäre dann zb eine 
normale Uhr) oder erniedrigen (das wäre dann eine Countdown-Uhr). Mehr 
aber sollte die ISR nicht wirklich machen (Taster auf gedrückt abfragen 
ist ok).
Alles weitere, was es zb bedeutet, wenn eine Countdown-Uhr auf 0 
heruntergezählt wurde, findet in der Hauptschleife statt. Das ist die 
grundlegende Schleife innerhalb main

int main()
{
   ...

   while( 1 ) {
     ....
   }
}

in der die eigentliche, komplette Logik steckt. Dort wird zb die 
Countdown-Uhr auf 5 Sekunden eingestellt. Die ISR zählt diese wieder auf 
0 herunter. Wiederrum in der Hauptschleife findet auch die Überwachung 
statt, ob die Countdown-Uhr noch läuft oder ob sie bereits wieder auf 0 
heruntergezählt wurde.

Ganz wichtig: In der Hautpschleife keine Warteschleifen veranstalten. 
Also nicht darauf aktiv warten, dass die Countdown-Uhr 0 erreicht hat. 
Du musst das eher so sehen, dass die Hauptschleife eine Reihenfolge von 
Aktionen enthält, die nacheinander wieder und immer wieder ausgeführt 
werden. Also sowas wie eine Vorschrift:
  * zuerst schau nach ob eine taste gedrückt wurde
  * dann sieh nach, ob die Uhr schon runter ist
  * und das ganze ständig wiederholen

Dieser Ansatz ist selbstverständlich nicht der einzig mögliche. Wie 
Peter schon schrieb: "müssen tust du gar nichts". Aber ein derartiger 
Aufbau hat sich in der Praxis bewährt und funktioniert bei vielen 
Aufgabenstellungen sehr gut. Daher sollte man gerade als Neuling sich an 
diese Strukturen gewöhnen.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

so, das Programm habe ich nun fertig geschrieben, aber es läuft nicht.
Eckdaten:
Prozessor Atmega8
PB0 hängt der Taster gegen Masse, an PB0 ist 5V angelegt, interne 
Pullups aktiviert
PB1 hängt die LED, die leuchten soll

Sobald die Schaltung an Spannung anliegt, leuchtet die LED bereits ohne, 
dass der Taster gedrückt wurde.
Wird der Taster gedrückt, so entsteht ein Kurzschluss.
Da ich blutiger Anfänger bin, finde ich leider den Fehler nicht. 
Vielleicht kann mir jemand helfen.
Hier der Sourcecode:
#define F_CPU 8000000UL    // 8MHz

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <inttypes.h>

// ------------------------------------------------------------------------------------------------
// DEFINES
// ------------------------------------------------------------------------------------------------

#define COUNTER_INCS_PER_MILLISECOND_T0  1000    // (8.000.000 / 8) / 1000 
#define COUNTER_INCS_PER_SECOND_T0      1000000

// PIN Definitionen
#define Taster    PB0      // PIN des Tasters
#define LED         PB1        // PIN der LED, die blinkt

// ------------------------------------------------------------------------------------------
// Funktionsdeklarationen
// ------------------------------------------------------------------------------------------
void init(void);      //Funktion zum Initialisieren der Ports
void debounce (uint8_t *, uint8_t);   //Funktion zum Entprellen

// ------------------------------------------------------------------------------------------
// globale Variablen, die von Interruptroutinen verändert werden
// ------------------------------------------------------------------------------------------
volatile unsigned long ulCurrentCounterTimer0G=0;  // enthält den aktuellen Wert des laufenden Timers 0 in Datenticks
volatile unsigned long ulSecondCounter0G = 0;       // enthält (annähernd) die Sekundenanzahl des Timers
volatile unsigned char zeitmerker = 0;        // Zeitmerker für den Vergleich, ob Zeit schon abgelaufen ist

// ------------------------------------------------------------------------------------------
// globale Variablen
// ------------------------------------------------------------------------------------------

/* 
 * Timer 0 Overflow Interrupt
 */
ISR(TIMER0_OVF_vect)
{
  // --------------------------------------------------------------------------------------------
  // Bei jedem Overflow wird the TimerCounter um 256 (Ticks) inkrementiert.
  // Nach ca. 1 Sekunde wird der Sekundenzähler erhöht
  //---------------------------------------------------------------------------------------------
  ulCurrentCounterTimer0G+=256;  //bei jedem overflow wird der Timer um 256 inkrementiert
  
  if (ulCurrentCounterTimer0G>=COUNTER_INCS_PER_SECOND_T0)
    {
    //es ist eine Sekunde vergangen, Sekundenzähler erhöhen
    ulSecondCounter0G++;
    zeitmerker = ulSecondCounter0G;

    // den Counter und den Tickzähler wieder zurücksetzen

        TCNT0 = 0;    //Counter wird zurückgesetzt, der Preload Wert wird nicht ausgerechnet (Zeit, die der TC zu groß ist)
    ulCurrentCounterTimer0G = 0;  //Tickzähler wird zurückgesetzt
    }
}     

// Das Hauptprogramm (Einsprungpunkt)
int main()
{
    // Ports und Interrupts initialisieren
  init();   
    
    while(1)
    {
      // ----------------------------------------------------------------------------------------
      // in der Hauptschleife des Programmes werden die Taster eingelesen
      // ----------------------------------------------------------------------------------------
    
    // den Taster abfragen, Funktion kehrt zurück, wenn der Status stabil ist (Entprellung)
        debounce( (uint8_t*)&PINB, (1<<Taster) );

    if ( !(PINB & (1 << Taster)) )  //frägt den Zustand des Tasters ab
    {
      PORTB |= (1<< LED );  //PB1 an dem die LED hängt anschalten
      TCCR0 |= (1<<CS01);   //Den Timer starten
    }

    if ( PINB & (1 << LED) )  //frägt den Zustand des Ausganges der LED ab
    {  
      if (zeitmerker>=25)

      {
        PORTB &= ~(1<< LED );  //PB1 an dem die LED hängt ausschalten
      
         // --------------------------------------------------------------------------------------
        // Timer stoppen und Rücksetzen
        // --------------------------------------------------------------------------------------
         TCCR0 &= ~(1<<CS01);   //Den Timer stoppen
         ulSecondCounter0G = 0;
         ulCurrentCounterTimer0G=0;
         zeitmerker=0;
         TCNT0 = 0;
      }

      else 
      {
        // den Taster abfragen, Funktion kehrt zurück, wenn der Status stabil ist (Entprellung)
            debounce( (uint8_t*)&PINB, (1<<Taster) );

        if ( !(PINB & (1 << Taster)) )  //frägt den Zustand des Tasters ab, prüft ob dieser wieder gedrückt wird für Abbruch
        
        {
          PORTB &= ~(1<< LED );  //PB1 an dem die LED hängt ausschalten

                
           // --------------------------------------------------------------------------------------
           // Timer stoppen und Rücksetzen
           // --------------------------------------------------------------------------------------
           TCCR0 &= ~(1<<CS01);   //Den Timer stoppen
           ulSecondCounter0G = 0;
           ulCurrentCounterTimer0G=0;
           zeitmerker=0;
           TCNT0 = 0;

        }

      }
    }
    }
}

 /* 
 * Funktion init(): Initialisierung des µC 
 */ 
void init(void) 
{
  // --------------------------------------------------------------------------------------------
  // PORTB konfigurieren
  //
  //  B0 --> Eingang für Taster
  //  B1 --> Ausgang für LED
  // --------------------------------------------------------------------------------------------
  DDRB = 0x00; 
  DDRB |= (1 << DDB1) ;    // B1 (Transistor) (LED) als Ausgang deklarieren, Rest (und damit PB0) Eingänge
  PORTB |= (1<<PB0);        // internen Pull-Up an PB0 aktivieren //
    

  // --------------------------------------------------------------------------------------------
  // Timer 0 (8-Bit) 
  // --------------------------------------------------------------------------------------------
  TCCR0 &= ~(1<<CS01);  //Timer0 zunächst gestoppt, es wird mit einem Prescaler von 8 gearbeitet
   TCNT0 = 0;         // Counter auf 0 initialisieren
  TIMSK |= (1<<TOIE0);   // Bei overflow wird timer interrupt 0 ausgelöst 
  
   sei();          // setzt globales Interrupt Enable Bit
  return;
}

/* 
 * Funktion debounce(): Funktion zum entprellen von Tasten, Schalter, etc 
 * "Warteschleifenvariante mit Maske und Pointer (nach Christian Riggenbach)" 
 * gefunden auf microcontroller.net
 */ 
void debounce( uint8_t *port, uint8_t maske ) 
{
    uint8_t   port_puffer;
    uint8_t   entprellungs_puffer;
 
    for( entprellungs_puffer=0 ; entprellungs_puffer!=0xff ; )
    {
        entprellungs_puffer<<=1;
        port_puffer = *port;
        _delay_us(150);
        if( (*port & maske) == (port_puffer & maske) )
        {
            entprellungs_puffer |= 0x01;
        }
    }
}


Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.