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


von Paddyr (Gast)


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

von P. S. (Gast)


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.

von Jean (Gast)


Lesenswert?

Hi,
mal auf die schnelle geschrieben. Die Kommentare musste selbst in den 
Registern einstellen.
Sollte aber funzen oder zumindest annähernd.
1
#include <avr\io.h>
2
#include <interrupt.h>
3
4
volatile char Taster , Merker;
5
int main()
6
{
7
  Taster = 0;
8
  Merker = 0;
9
  switch(Taster)
10
  {
11
    case 1:
12
    {
13
      //Timer konfigurieren und starten
14
      //LED ein
15
      Taster = 0;
16
      break;
17
    }
18
    case 2:
19
    {
20
      //Timer aus
21
      //LED aus
22
      Taster = 0;
23
      break;
24
    }
25
    default:
26
    {
27
      break;
28
    }
29
  }
30
}
31
32
  //CTC Interrupt
33
  ISR(TIMER1_COMPA_vect)
34
  {
35
    //LED aus
36
    //Taster = 0;
37
  }
38
39
  ISR(INT0_vect)
40
  {
41
    if(!Taster && !Merker)
42
    {
43
      Taster = 1;
44
      Merker = 1;
45
    }
46
    else if(!Taster && Merker ==1)
47
    {
48
      Taster = 2;
49
      Merker = 0;
50
    }
51
    else
52
    {
53
      //TODO
54
    }
55
  }

von Jean (Gast)


Lesenswert?

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

von Spera (Gast)


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^^

von Thisamplifierisloud (Gast)


Lesenswert?

Den

volatile char Merker;

tät ich als

static char Merker=0;

in der ISR deklarieren.

Wegen der Sauberkeit !

von Paddyr (Gast)


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

von P. S. (Gast)


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.

von Karl H. (kbuchegg)


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.

von Falk B. (falk)


Lesenswert?


von Manuel (Gast)


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:
1
#define F_CPU 8000000UL    // 8MHz
2
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6
#include <inttypes.h>
7
8
// ------------------------------------------------------------------------------------------------
9
// DEFINES
10
// ------------------------------------------------------------------------------------------------
11
12
#define COUNTER_INCS_PER_MILLISECOND_T0  1000    // (8.000.000 / 8) / 1000 
13
#define COUNTER_INCS_PER_SECOND_T0      1000000
14
15
// PIN Definitionen
16
#define Taster    PB0      // PIN des Tasters
17
#define LED         PB1        // PIN der LED, die blinkt
18
19
// ------------------------------------------------------------------------------------------
20
// Funktionsdeklarationen
21
// ------------------------------------------------------------------------------------------
22
void init(void);      //Funktion zum Initialisieren der Ports
23
void debounce (uint8_t *, uint8_t);   //Funktion zum Entprellen
24
25
// ------------------------------------------------------------------------------------------
26
// globale Variablen, die von Interruptroutinen verändert werden
27
// ------------------------------------------------------------------------------------------
28
volatile unsigned long ulCurrentCounterTimer0G=0;  // enthält den aktuellen Wert des laufenden Timers 0 in Datenticks
29
volatile unsigned long ulSecondCounter0G = 0;       // enthält (annähernd) die Sekundenanzahl des Timers
30
volatile unsigned char zeitmerker = 0;        // Zeitmerker für den Vergleich, ob Zeit schon abgelaufen ist
31
32
// ------------------------------------------------------------------------------------------
33
// globale Variablen
34
// ------------------------------------------------------------------------------------------
35
36
/* 
37
 * Timer 0 Overflow Interrupt
38
 */
39
ISR(TIMER0_OVF_vect)
40
{
41
  // --------------------------------------------------------------------------------------------
42
  // Bei jedem Overflow wird the TimerCounter um 256 (Ticks) inkrementiert.
43
  // Nach ca. 1 Sekunde wird der Sekundenzähler erhöht
44
  //---------------------------------------------------------------------------------------------
45
  ulCurrentCounterTimer0G+=256;  //bei jedem overflow wird der Timer um 256 inkrementiert
46
  
47
  if (ulCurrentCounterTimer0G>=COUNTER_INCS_PER_SECOND_T0)
48
    {
49
    //es ist eine Sekunde vergangen, Sekundenzähler erhöhen
50
    ulSecondCounter0G++;
51
    zeitmerker = ulSecondCounter0G;
52
53
    // den Counter und den Tickzähler wieder zurücksetzen
54
55
        TCNT0 = 0;    //Counter wird zurückgesetzt, der Preload Wert wird nicht ausgerechnet (Zeit, die der TC zu groß ist)
56
    ulCurrentCounterTimer0G = 0;  //Tickzähler wird zurückgesetzt
57
    }
58
}     
59
60
// Das Hauptprogramm (Einsprungpunkt)
61
int main()
62
{
63
    // Ports und Interrupts initialisieren
64
  init();   
65
    
66
    while(1)
67
    {
68
      // ----------------------------------------------------------------------------------------
69
      // in der Hauptschleife des Programmes werden die Taster eingelesen
70
      // ----------------------------------------------------------------------------------------
71
    
72
    // den Taster abfragen, Funktion kehrt zurück, wenn der Status stabil ist (Entprellung)
73
        debounce( (uint8_t*)&PINB, (1<<Taster) );
74
75
    if ( !(PINB & (1 << Taster)) )  //frägt den Zustand des Tasters ab
76
    {
77
      PORTB |= (1<< LED );  //PB1 an dem die LED hängt anschalten
78
      TCCR0 |= (1<<CS01);   //Den Timer starten
79
    }
80
81
    if ( PINB & (1 << LED) )  //frägt den Zustand des Ausganges der LED ab
82
    {  
83
      if (zeitmerker>=25)
84
85
      {
86
        PORTB &= ~(1<< LED );  //PB1 an dem die LED hängt ausschalten
87
      
88
         // --------------------------------------------------------------------------------------
89
        // Timer stoppen und Rücksetzen
90
        // --------------------------------------------------------------------------------------
91
         TCCR0 &= ~(1<<CS01);   //Den Timer stoppen
92
         ulSecondCounter0G = 0;
93
         ulCurrentCounterTimer0G=0;
94
         zeitmerker=0;
95
         TCNT0 = 0;
96
      }
97
98
      else 
99
      {
100
        // den Taster abfragen, Funktion kehrt zurück, wenn der Status stabil ist (Entprellung)
101
            debounce( (uint8_t*)&PINB, (1<<Taster) );
102
103
        if ( !(PINB & (1 << Taster)) )  //frägt den Zustand des Tasters ab, prüft ob dieser wieder gedrückt wird für Abbruch
104
        
105
        {
106
          PORTB &= ~(1<< LED );  //PB1 an dem die LED hängt ausschalten
107
108
                
109
           // --------------------------------------------------------------------------------------
110
           // Timer stoppen und Rücksetzen
111
           // --------------------------------------------------------------------------------------
112
           TCCR0 &= ~(1<<CS01);   //Den Timer stoppen
113
           ulSecondCounter0G = 0;
114
           ulCurrentCounterTimer0G=0;
115
           zeitmerker=0;
116
           TCNT0 = 0;
117
118
        }
119
120
      }
121
    }
122
    }
123
}
124
125
 /* 
126
 * Funktion init(): Initialisierung des µC 
127
 */ 
128
void init(void) 
129
{
130
  // --------------------------------------------------------------------------------------------
131
  // PORTB konfigurieren
132
  //
133
  //  B0 --> Eingang für Taster
134
  //  B1 --> Ausgang für LED
135
  // --------------------------------------------------------------------------------------------
136
  DDRB = 0x00; 
137
  DDRB |= (1 << DDB1) ;    // B1 (Transistor) (LED) als Ausgang deklarieren, Rest (und damit PB0) Eingänge
138
  PORTB |= (1<<PB0);        // internen Pull-Up an PB0 aktivieren //
139
    
140
141
  // --------------------------------------------------------------------------------------------
142
  // Timer 0 (8-Bit) 
143
  // --------------------------------------------------------------------------------------------
144
  TCCR0 &= ~(1<<CS01);  //Timer0 zunächst gestoppt, es wird mit einem Prescaler von 8 gearbeitet
145
   TCNT0 = 0;         // Counter auf 0 initialisieren
146
  TIMSK |= (1<<TOIE0);   // Bei overflow wird timer interrupt 0 ausgelöst 
147
  
148
   sei();          // setzt globales Interrupt Enable Bit
149
  return;
150
}
151
152
/* 
153
 * Funktion debounce(): Funktion zum entprellen von Tasten, Schalter, etc 
154
 * "Warteschleifenvariante mit Maske und Pointer (nach Christian Riggenbach)" 
155
 * gefunden auf microcontroller.net
156
 */ 
157
void debounce( uint8_t *port, uint8_t maske ) 
158
{
159
    uint8_t   port_puffer;
160
    uint8_t   entprellungs_puffer;
161
 
162
    for( entprellungs_puffer=0 ; entprellungs_puffer!=0xff ; )
163
    {
164
        entprellungs_puffer<<=1;
165
        port_puffer = *port;
166
        _delay_us(150);
167
        if( (*port & maske) == (port_puffer & maske) )
168
        {
169
            entprellungs_puffer |= 0x01;
170
        }
171
    }
172
}

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.