Forum: Mikrocontroller und Digitale Elektronik Problem bei Tastendruck


von J. K. (jk-med)


Lesenswert?

Hallo zusammen,

ich habe ein kleines Projekt gestartet, damit ich meine 
Eisenbahnbeleuchtung steuern kann.

Dazu habe ich insgesamt drei Taster (Schließer) vorgesehen, die 
folgendes machen sollen:

- Wird Taster-1 gedrückt, soll immer wiederkehrend ein Blinkmuster 
erfolgen (z.B. 3 sec. AN, 1 sec. AUS).

- Wird Taster-2 gedrückt, soll nur einmalig ein Muster ausgegeben werden 
(z.B. 1 sec. AN, 3 sec. AUS, 2 sec. AN).

- Wird Taster-3 gedrückt, soll das wiederkehrende Blinkmuster (von 
Taster-1) gelöscht werden und die LED ausgehen.


Hier der Code:
1
#define F_CPU      8000000
2
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6
7
8
9
#define TASTER_1     PINC4
10
#define TASTER_2     PINC3
11
#define TASTER_RESET  PINC5
12
13
#define OUTPUT_PORT    PORTD              
14
#define OUTPUT_PIN    PD0                  // hier hängt die LED dran
15
16
volatile unsigned char Taster;
17
18
19
void init_timer_0(void)
20
{
21
  TCCR0 |= (1 << CS02) | (1 << CS00);              // Prescaler auf F_CPU / 1024
22
  TIMSK |= (1 << TOIE0);                  // overflow interrupt erlauben
23
}
24
25
26
27
ISR (TIMER0_OVF_vect)                    // Interrupt Service Routine (bei Timer-Overflow von Timer0)
28
{
29
  if (!(PINC & (1 << TASTER_1)))    { Taster = 1; }
30
  if (!(PINC & (1 << TASTER_2)))    { Taster = 2; }
31
  if (!(PINC & (1 << TASTER_RESET)))  { Taster = 0; }
32
  
33
}
34
35
void main (void)
36
{
37
  Taster = 0;
38
  
39
  DDRC = 0;                        // Port C als Eingang
40
  PORTC = 0xFF;                      // interne Pullups aktivieren
41
  
42
  DDRD |= (1 << DDD0);                  // Port D als Ausgang
43
  OUTPUT_PORT = 0;
44
  
45
  init_timer_0();
46
  sei();
47
  
48
  while(1)
49
  {
50
    switch(Taster)
51
    {
52
      case 1:
53
        while(1)
54
        {
55
          OUTPUT_PORT |= (1 << OUTPUT_PIN);      //wiederkehrendes Blinken
56
          _delay_ms(3000);
57
          OUTPUT_PORT &= ~(1 << OUTPUT_PIN);
58
          Taster=0;
59
        }
60
        break;
61
        
62
      
63
      case 2:                        // 1 sec. AN, 3 sec. AUS, 2 sec. AN
64
        OUTPUT_PORT |= (1 << OUTPUT_PIN);
65
        _delay_ms(1000);
66
        OUTPUT_PORT &= ~(1 << OUTPUT_PIN);
67
        _delay_ms(3000);
68
        OUTPUT_PORT |= (1 << OUTPUT_PIN);
69
        _delay_ms(2000);
70
        OUTPUT_PORT &= ~(1 << OUTPUT_PIN);
71
        Taster=0;
72
        break;
73
      
74
      
75
      
76
      case 0:
77
      OUTPUT_PORT &= ~(1 << OUTPUT_PIN);            //...ausschalten
78
      break;
79
    }
80
  }
81
}

Nun das Problem:
 1.) Warum wird nach Tastendruck auf Taster-1 kein weiterer Tastendruck 
mehr erkannt? Wie kann ich das machen, dass der Timer-Interrupt 
registriert, dass während case 1 abgearbeitet wird, was gedrückt wurde?

 2.) Ähnliches Problem: Warum löscht er mir das Blinken nicht, wenn ich 
auf Taster-3 drücke?

Ich bin mir bewusst, dass die _delay_ms()-Routine ein rotes Tuch für 
manche hier ist. Aber für den Zweck sollte das reichen! :-)

Vielen Dank euch!

Grüße

von Dietrich L. (dietrichl)


Lesenswert?

J. K. schrieb:
1
 case 1:
2
         while(1)
3
         {
4
           OUTPUT_PORT |= (1 << OUTPUT_PIN);
5
           _delay_ms(3000);
6
           OUTPUT_PORT &= ~(1 << OUTPUT_PIN);
7
           Taster=0;
8
         }
9
         break;

Dies ist eine Endlosschleife ohne Ausgang! Der Timerinterrupt läuft 
schon weiter, Du fragst das Ergebnis 'Taster' in der Schleife aber nicht 
ab.

Gruß Dietrich

von J. K. (jk-med)


Lesenswert?

Danke Dietrich!

Das heißt, es müsste so lauten?
1
case 1:
2
         while(Taster==1)
3
         {
4
           OUTPUT_PORT |= (1 << OUTPUT_PIN);
5
           _delay_ms(3000);
6
           OUTPUT_PORT &= ~(1 << OUTPUT_PIN);
7
         }
8
         Taster=0;
9
         break;

von J. K. (jk-med)


Lesenswert?

Das klappt soweit. Aber: wie könnte ich einen längeren Blinkrhythmus mit 
der Taste-3 unterbrechen?

Dass quasi der Interrupt bei einem Tastendruck von Taster-3 sofort den 
laufenden Blinkrhythmus unterbricht?


Danke!

von holger (Gast)


Lesenswert?

>Das klappt soweit. Aber: wie könnte ich einen längeren Blinkrhythmus mit
>der Taste-3 unterbrechen?

_delay_ms(3000); anders machen.

for(i = 0; i < 3000; i++)
{
  _delay_ms(1);
  if(Taster == 0) break;
}

Dann kannst du jede Millisekunde abbrechen.

von J. K. (jk-med)


Lesenswert?

Oh weh...da ist dann schon das nächste Problem.

Ich habe mir eine Funktion geschrieben:
1
void wait_ms(int time)
2
{
3
   for(i = 0; i < time; i++)
4
   {
5
      _delay_ms(1);
6
      if(Taster == 0) break;
7
   }
8
   return;
9
}

Doch das "break" beendet ja nur die Schleife?!
Ich bräuchte etwas, was außerhalb der Funktion "breaked".

Habt ihr eine Idee?

von holger (Gast)


Lesenswert?

>Ich habe mir eine Funktion geschrieben:

Sehr schön.
Dann eben:
1
uint8_t wait_ms(int time)
2
{
3
   for(i = 0; i < time; i++)
4
   {
5
      _delay_ms(1);
6
      if(Taster == 0) return 1;
7
   }
8
   return 0;
9
}

Wenn die Funktion mit 0 rauskommt weitermachen,
wenn sie mit >= 1 rauskommt abbrechen.

von Peter D. (peda)


Lesenswert?

Hier findest Du einiges, was ganz nützlich ist:

Beitrag "AVR Sleep Mode / Knight Rider"

Eine Entprellroutine, die Dir viel Ärger bei der Tastenabfrage vom Hals 
schafft.

Und eine tabellengesteuerte Blinkroutine.
Man kann statt des Blinkmuster auch die Zeitabstände in die Tabelle 
schreiben. Oder auch beides.

Die Sleep-Funktionen läßt man einfach komplett raus.
Du benutzt ja bestimmt ein Netzteil und keine Batterie.

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

J. K. schrieb:
> Habt ihr eine Idee?
Du musst dein Programmkonzept umstellen!

Weg von diesem "Schritt-für-Schritt"-Denken hin zu einer 
Zustandsautomaten-Denkweise. Dann sorgst du nur erst mal dafür, dass du 
eine einzige Hauptschleife hast, die mit maximaler Geschwindigkeit und 
ohne nennenswerte Unterbrechungen durchläuft. Und nur wenn nötig, wird 
in dieser Hauptschleife etwas ausgeführt (z.B. Leds umschalten). Und 
weil du ständig an der Tastenabfrage vorbeikommst, kannst du sofort auf 
eine Taste reagieren.

Als kleiner Denkanstoß hier mal mein Ansatz zu Verwaltung von Zeiten:
http://www.lothar-miller.de/s9y/categories/22-Zeiten

Du siehst: Zeiten werden dort nicht über die Verschwendung von 
Rechenkapazität mit "delay_ms()" gemacht, sondern über das 
Vorausberechnen eines Zeitpunktes, bei dem wieder eine Aktion ausgeführt 
werden soll (z.B. LED toggeln). So ein Programm kannst du einfach 
erweitern und unter Kontrolle halten: es muss nur die Durchlaufzeit der 
Hautschleife hinreichend gering bleiben, und du hast immmer eine 
"sofortige" Reaktion.

: Bearbeitet durch Moderator
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.