Forum: Mikrocontroller und Digitale Elektronik Atmega8 Taster funktioniert nicht


von Christopher (Gast)


Lesenswert?

Hey,

ich habe ein kleines Atmega8 Board von meinem Freund rausgekramt.

Die 4 LEDs funktionieren einwandfrei mit den Funktionen und dann hab ich 
mich den Tastern gewipmet. Habe dann den Timer eingebaut der das dann 
alle 10ms überprüfen soll, ob ein Taster gedrückt ist.

Taster1: PD5
Taster2: PB7

Nur passiert nichts wenn ich die Taste drücke und iwann springt der 
switch doch in den default in der main-schleife.

Hoffe ihr könnt mir helfen.

Dankeschön



Hier der Code:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
#include <avr/signal.h>
5
6
int mode = 0;
7
8
SIGNAL (SIG_OVERFLOW0)
9
{
10
  cli();    // Interrupts nicht zulassen
11
  
12
  if ((PIND & (1 << PIND5)))    // Up-Taste gedr¸ckt
13
    mode = mode + 1;
14
  
15
  if ((PINB & (1 << PINB7))) {    // Down-Taste gedr¸ckt
16
    if((mode - 1) != -1) {
17
      mode = mode - 1;
18
    }
19
  }
20
  
21
  sei();    // Interrupts zulassen
22
}
23
24
25
int main(void)
26
{
27
  init();
28
  
29
    for(;;){
30
    
31
    switch (mode) {
32
      case 0:
33
        LED1(1);
34
        LED2(1);
35
        LED3(1);
36
        LED4(1);
37
        break;
38
        
39
      case 1:
40
        LED1(0);
41
        LED2(0);
42
        LED3(0);
43
        LED4(0);
44
        break;
45
        
46
      default:
47
        LED1(1);
48
        LED2(0);
49
        LED3(1);
50
        LED4(1);
51
    }
52
    
53
    }
54
    return 0;               /* never reached */
55
}
56
57
58
59
int init(void) {
60
  //LED Outputs setzen
61
  DDRD = 0x1C;  //1 << 2 | 1 << 3 | 1 << 4;
62
  DDRB = 0x40;  //1 << 6;
63
  //LED Outputs setzen ENDE
64
  
65
  // Timer f¸r Tastenabfrage
66
  TCCR0 |= (1<<CS22) | (1<<CS20);    //8-Bit Timer, Timer clock = system clock/1024
67
  TIFR |= (1<<TOV0);           //Clear TOV0 Timer/Counter Overflow Flag. clear pending interrupts
68
  TIMSK |= (1<<TOIE0);         //Enable Timer0 Overflow Interrupt
69
  
70
  //Interrupts global aktivieren
71
  sei();
72
  
73
  return 0;
74
}
75
76
int LED1(int state) {
77
  
78
  if(state) {
79
    PORTD |= 1 << 2;
80
  } else {
81
    PORTD &= ~(1 << 2);
82
  }
83
  return 0;
84
}
85
86
int LED2(int state) {
87
  
88
  if(state) {
89
    PORTD |= 1 << 3;
90
  } else {
91
    PORTD &= ~(1 << 3);
92
  }
93
  return 0;
94
}
95
96
int LED3(int state) {
97
  
98
  if(state) {
99
    PORTD |= 1 << 4;
100
  } else {
101
    PORTD &= ~(1 << 4);
102
  }
103
  return 0;
104
}
105
106
int LED4(int state) {
107
  
108
  if(state) {
109
    PORTB |= 1 << 6;
110
  } else {
111
    PORTB &= ~(1 << 6);
112
  }
113
  return 0;
114
}

von holger (Gast)


Lesenswert?

1
//int mode = 0;
2
volatile int mode = 0;
3
4
SIGNAL (SIG_OVERFLOW0)
5
{
6
// unnötig, weg damit
7
//  cli();    // Interrupts nicht zulassen
8
  
9
  if ((PIND & (1 << PIND5)))    // Up-Taste gedr¸ckt
10
    mode = mode + 1;
11
  
12
  if ((PINB & (1 << PINB7))) {    // Down-Taste gedr¸ckt
13
    if((mode - 1) != -1) {
14
      mode = mode - 1;
15
    }
16
  }
17
// gefährlich, weg damit  
18
//  sei();    // Interrupts zulassen
19
}

von HappyNewyear (Gast)


Lesenswert?

In der Regel werden Taster nach GND geschaltet und die internen Pull-ups 
aktiviert. Ich weiß nicht, welches Board du hast, aber wenn das so ist, 
dann musst du so überprüfen, ob die Taster gedrückt sind:

if (!(PIND & (1 << PIND5))) ...

von HappyNewyear (Gast)


Lesenswert?

holger schrieb:
> // gefährlich, weg damit
> //  sei();    // Interrupts zulassen

Dann wird das mit den Timern aber nix, oder?

von Stefan B. (Gast)


Lesenswert?

mode muss volatile Typ definiert werden, weil es in der SIGNAL() und 
im Userprogramm main() parallel benutzt wird.

Wenn du in main() so wie oben keine besonderen Vorkehrungen für atomaren 
Datenzugriff triffst (s. Artikel Interrupt und Doku avr-libc, 
Interrupts), dann solltest du für Typ char statt int wählen. Der 
Zugriff auf eine char Variable kann atomar sein.

cli() und sei() haben in SIGNAL() nichts zu suchen.

SIGNAL() ist veraltet. Man benutzt stattdessen ISR() (s. Doku avr-libc, 
Interrupts)

>   TCCR0 |= (1<<CS22) | (1<<CS20);    //8-Bit Timer, Timer clock = system 
clock/1024

In meinem Datenblatt sind in Tabelle 34 CS02 und CS00 für Prescaler 1024 
angegeben.

Wenn dein AVR mit der Werkseinstellung 1 MHz läuft, wird die 
Interruptroutine alle 1000000/1024/256 Hz = 3,8 Hz = 262 ms aufgerufen.

Genau nur so lange auf die Taster zu drücken bis mode = 1 (case 1) ist 
und nicht weiter, wird nur Chuck Norris gelingen. Der hat so kurze 
Reaktionszeiten. Normalos werden case 0 oder default bekommen.

>  DDRD = 0x1C;  //1 << 2 | 1 << 3 | 1 << 4;
>  DDRB = 0x40;  //1 << 6;

Kann man gleich so schreiben:

  DDRD = (1<<PD2) | (1<<PD3) | (1<<PD4);
  DDRB = (1<<PB6);

Die Taster sind laut Quellcode active-high angeschlossen (s. 
AVR-GCC-Tutorial. Passt die externe Beschaltung zu dem Softwarecode? 
Gibt es Pulldown-Widerstände?

von holger (Gast)


Lesenswert?

>holger schrieb:
>> // gefährlich, weg damit
>> //  sei();    // Interrupts zulassen

>Dann wird das mit den Timern aber nix, oder?

Wieso nicht?

Und wenn er die Taster mal entprellt und mode
nicht einfach rauf/runterzählt, dann wirds vieleicht auch
mal was mit dem switch.

von Flo (Gast)


Lesenswert?

Wie hast du den Taster angeschlossen?

Ohne die Info ist das alles lustiges Raten. :-)

von Christopher (Gast)


Angehängte Dateien:

Lesenswert?

Danke für eure schnellen Antworten. Ich kann euch leider nicht sagen wie 
die Taster angeschlossen sind habe das so bekommen und kann das mit 
meinem Wissen nicht erkennen. Hab euch deswegen mal 2 Bilder gemacht.

Außerdem habe ich den Quellcode nach euren Vorschlägen angepasst. Und 
kann jetzt von mode = 0 bis zu mode = ieine hohe zahl schalten. Nur 
genau 1 treff ich nicht wie mach ich das am besten das ich nur eine 
Stufe weiter schalte?
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
5
volatile int mode = 0;
6
7
ISR(TIMER0_OVF_vect)
8
{
9
  cli();    // Interrupts nicht zulassen
10
  
11
  if (!(PIND & (1 << PIND5)))    // Up-Taste gedr¸ckt
12
    mode = mode + 1;
13
  
14
  if (!(PINB & (1 << PINB7))) {    // Down-Taste gedr¸ckt
15
    if((mode - 1) != -1) {
16
      mode = mode - 1;
17
    }
18
  }
19
  
20
  sei();    // Interrupts zulassen
21
}
22
23
24
int main(void)
25
{
26
  init();
27
  
28
    for(;;){
29
    
30
    switch (mode) {
31
      case 0:
32
        LED1(1);
33
        LED2(1);
34
        LED3(1);
35
        LED4(1);
36
        break;
37
        
38
      case 1:
39
        LED1(0);
40
        LED2(0);
41
        LED3(0);
42
        LED4(0);
43
        break;
44
        
45
      default:
46
        LED1(1);
47
        LED2(0);
48
        LED3(1);
49
        LED4(1);
50
    }
51
    
52
    }
53
    return 0;               /* never reached */
54
}
55
56
57
58
int init(void) {
59
  //LED Outputs setzen
60
  DDRD = 0x1C;  //1 << 2 | 1 << 3 | 1 << 4;
61
  DDRB = 0x40;  //1 << 6;
62
  //LED Outputs setzen ENDE
63
  
64
  // Timer f¸r Tastenabfrage
65
  TCCR0 |= (1<<CS02) | (1<<CS00);    //8-Bit Timer, Timer clock = system clock/1024
66
  TIFR |= (1<<TOV0);           //Clear TOV0 Timer/Counter Overflow Flag. clear pending interrupts
67
  TIMSK |= (1<<TOIE0);         //Enable Timer0 Overflow Interrupt
68
  
69
  //Interrupts global aktivieren
70
  sei();
71
  
72
  return 0;
73
}
74
75
int LED1(int state) {
76
  
77
  if(state) {
78
    PORTD |= 1 << 2;
79
  } else {
80
    PORTD &= ~(1 << 2);
81
  }
82
  return 0;
83
}
84
85
int LED2(int state) {
86
  
87
  if(state) {
88
    PORTD |= 1 << 3;
89
  } else {
90
    PORTD &= ~(1 << 3);
91
  }
92
  return 0;
93
}
94
95
int LED3(int state) {
96
  
97
  if(state) {
98
    PORTD |= 1 << 4;
99
  } else {
100
    PORTD &= ~(1 << 4);
101
  }
102
  return 0;
103
}
104
105
int LED4(int state) {
106
  
107
  if(state) {
108
    PORTB |= 1 << 6;
109
  } else {
110
    PORTB &= ~(1 << 6);
111
  }
112
  return 0;
113
}

von Karl H. (kbuchegg)


Lesenswert?

Christopher schrieb:
> Danke für eure schnellen Antworten. Ich kann euch leider nicht sagen wie
> die Taster angeschlossen sind

Wie kannst du dann ein Programm, noch dazu ein so komplexes, schreiben?

Finde erst einmal heraus, ob deine Taster gegen Masse, oder gegen Vcc 
schalten. Als erste Arbeitshypothese würde ich annehmen: Sie schalten 
gegen Masse.

Wenn du ein Multimeter hast, kannst du zb so vorgehen:
Erst mal Spannung weg von der Schaltung. Dann das Multimeter auf Ohm 
stellen und den Widerstand von allen Tasterpins zu Masse und Vcc messen. 
Gibt es einen Pin, der praktisch 0 Ohm gegen Masse hat, dann schalten 
die Taster auch gegen Masse. Du kannst dann einfach mal den Taster 
drücken und gedrückt halten und wieder alle Pins durchmessen. Es werden 
dann auch andere Pins eine Verbindung mit Masse haben.

Hast du kein Messgerät, dann würde ich die Arbeitshypothese benutzen und 
mir ein einfaches(!) Testprogramm schreiben, welches:
   Port auf Input
   Pullup Widerstände für die Taster ein

   In einer SChleife
     If Tasterpin gleich 0
       Led ein
     else
       Led aus

das dann laufen lassen und auf den taster drücken. Wenn die 
Arbeitshypothese stimmt, dann leuchten die LED auf, wenn du auf dem 
Taster drückst.

Und erst dann, gehts mit komplizierteren Dingen weiter.

von Karl H. (kbuchegg)


Lesenswert?

> Außerdem habe ich den Quellcode nach euren Vorschlägen angepasst.
> Und kann jetzt von mode = 0 bis zu mode = ieine hohe zahl schalten.
> Nur genau 1 treff ich nicht wie mach ich das am besten das ich nur
> eine Stufe weiter schalte?

Dein Code schaltet mode um 1 weiter solange die Taste gedrückt ist. 
Was du brauchst, ist Code der feststellt wann der Taster seinen Zustand 
wechselt und nur dann weiterschaltet. Dazu merkt man sich den momentanen 
Zustand des Tasters in einer Variablen und vergleicht dann diesen 
gemerkten Zustand beim nächsten ISR aufruf mit dem dann vorliegenden 
Zustand. Ist er anders, dann wurde der Taster betätigt.
1
unsigned char Previous;
2
3
ISR( ... )
4
{
5
  unsigned char Now;
6
7
   if( !( PIND & ( 1 << ... ) ) )   // Taste gedrückt?
8
     Now = 1;
9
   else
10
     Now = 0;
11
12
   if( Now != Previous ) {     // gab es eine Veränderung?
13
     ... mach was, Taste hat Zustand gewechselt
14
   }
15
16
   Previous = Now;    // der jetzige Zustand wird gemerkt, damit er
17
                      // beim nächsten ISR Aufruf als alter Vergleichswert
18
                      // herhalten kann
19
}

Aber Achtung:
Das stellt eine Veränderung des Tastenzustands fest!
Eine Veränderung ist zb wenn du die Taste drückst. Aber es ist auch eine 
Veränderung, wenn du eine gedrückte Taste wieder loslässt. Aber Gott sei 
Dank hast du ja in 'Now' stehen, ob die Taste nun gedrückt ist oder 
nicht. Aber das einzuflechten überlasse ich jetzt dir.


Aber stell erst mal fest, ob deine Tasten wirklich active-low sind oder 
nicht.

von Stefan B. (Gast)


Lesenswert?

Christopher:

> Außerdem habe ich den Quellcode nach euren Vorschlägen angepasst. Und
> kann jetzt von mode = 0 bis zu mode = i eine hohe zahl schalten. Nur
> genau 1 treff ich nicht wie mach ich das am besten das ich nur eine
> Stufe weiter schalte?

Du könntest die Tasten sehr viel langsamer abfragen, also z.B. statt im 
Abstand von Millisekunden im Abstand von Sekunden. darauf kann ein 
Normalsterblicher reagieren und Loslassen, wenn mode = 1 ist bzw. die 
LEDs entsprechend leuchten.

Solange du so schnell die Taste abfragst, darfst du nur einmal 
Hochzählen, wenn die Taste gedrückt ist und nicht jedesmal, wenn die 
Taste gedrückt bleibt und du die Abfrage wiederholst. Vor dem nächsten 
Hochzählen muss die Taste wieder losgelassen werden. Das Loslassen musst 
du abfragen, ähnlich wie du oben das Drücken abfragst.

Noch sicherer wird Drücken/Loslassen, wenn du das Prellen der Taste 
berücksichtigst. Z.B. wenn du programmierst, dass die Taste mindestens 
ca. 20 Millisekunden gedrückt oder losgelassen sein muss, damit eine 
Aktion ausgelöst wird. Verfahren dazu stehen im Artikel Entprellung.

von Christopher (Gast)


Lesenswert?

Habe jetzt mit diesem Code:
1
int main(void)
2
{
3
  LED1(1);
4
    /* insert your hardware initialization here */
5
    for(;;){
6
         if (!(PIND & (1 << PIND5)))    // Up-Taste gedr¸ckt
7
      LED1(1);
8
    
9
    if (!(PINB & (1 << PINB7))) {    // Down-Taste gedr¸ckt
10
      LED1(0);
11
    }
12
    }
13
    return 0;   /* never reached */
14
}

festgestellt, dass die Taster active-low sein müssen, da dieser code ja 
funktioniert.

Ich versuch jetzt schnell noch die Zustandswechsel einzubauen wie es 
Karl Heinz vorgeschlagen hat.

Danke

von Stefan B. (Gast)



Lesenswert?

Wenn man dem Aufdruck auf der Platine in den Fotos nachgeht, kommt man 
zu dem Schaltplan im Anhang.

Die Taster sind darin active-low (s. AVR-GCC-Tutorial) angeschlossen 
und auf der Platine sind keine externen Pull-up-Widerstände vorgesehen. 
Es fehlt in deinem Programm also die notwendige Initialisierung der 
internen Pull-up-Widerstände, die den PIN im losgelassenen Zustand auf 
log. HIGH ziehen.

von Karl H. (kbuchegg)


Lesenswert?

Christopher schrieb:
> Habe jetzt mit diesem Code:
>
>
1
int main(void)
2
> {
3
>   LED1(1);
4
>     /* insert your hardware initialization here */
5
>     for(;;){
6
>          if (!(PIND & (1 << PIND5)))    // Up-Taste gedr¸ckt
7
>       LED1(1);
8
> 
9
>     if (!(PINB & (1 << PINB7))) {    // Down-Taste gedr¸ckt
10
>       LED1(0);
11
>     }
12
>     }
13
>     return 0;   /* never reached */
14
> }
>
> festgestellt, dass die Taster active-low sein müssen, da dieser code ja
> funktioniert.

Füg da ganz schnell noch ein

  PORTD |= ( 1 << PORTD5 );
  PORTB |= ( 1 << PORTB7 );

ganz am Anfang ein, um die Pullup Widerstände für die Tasten 
einzuschalten. Ansonsten hattest du einfach nur Glück mit deinen Tasten.

von Christopher (Gast)


Lesenswert?

Prima! Vielen lieben Dank!

Funktioniert jetzt alles bestens.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
5
volatile int mode = 0;
6
unsigned char Previous_taster1;
7
unsigned char Previous_taster2;
8
9
10
ISR(TIMER0_OVF_vect)
11
{
12
  unsigned char Now_taster1;
13
  unsigned char Now_taster2;
14
  
15
  if (!(PIND & (1 << PIND5)))  {  // Up-Taste gedr¸ckt
16
    Now_taster2 = 1;
17
  } else {
18
    Now_taster2 = 0;
19
  }
20
  if (!(PINB & (1 << PINB7))) {    // Down-Taste gedr¸ckt
21
    Now_taster1 = 1;
22
  } else {
23
    Now_taster1 = 0;
24
  }
25
  
26
  if(Now_taster1 == 1 && Now_taster1 != Previous_taster1) {
27
    if((mode - 1 ) != -1 ) {
28
      mode -= 1;
29
    }
30
  }
31
  if(Now_taster2 == 1 && Now_taster2 != Previous_taster2) {
32
    mode += 1;
33
  }
34
  
35
  Previous_taster1 = Now_taster1;
36
  Previous_taster2 = Now_taster2;
37
}
38
39
40
int main(void)
41
{
42
  init();
43
  
44
    for(;;){
45
    
46
    switch (mode) {
47
      case 0:
48
        LED1(1);
49
        LED2(1);
50
        LED3(1);
51
        LED4(1);
52
        break;
53
        
54
      case 1:
55
        LED1(0);
56
        LED2(0);
57
        LED3(0);
58
        LED4(0);
59
        break;
60
        
61
      default:
62
        LED1(1);
63
        LED2(0);
64
        LED3(1);
65
        LED4(1);
66
    }
67
    
68
    }
69
    return 0;               /* never reached */
70
}
71
72
73
74
int init(void) {
75
  //LED Outputs setzen
76
  DDRD = 0x1C;  //1 << 2 | 1 << 3 | 1 << 4;
77
  DDRB = 0x40;  //1 << 6;
78
  //LED Outputs setzen ENDE
79
  
80
  //interne Pull-ups für Taster
81
  PORTD |= (1<<PD5); 
82
  PORTB |= (1<<PB7); 
83
  
84
  
85
  // Timer f¸r Tastenabfrage
86
  TCCR0 |= (1<<CS02) | (1<<CS00);    //8-Bit Timer, Timer clock = system clock/1024
87
  TIFR |= (1<<TOV0);           //Clear TOV0 Timer/Counter Overflow Flag. clear pending interrupts
88
  TIMSK |= (1<<TOIE0);         //Enable Timer0 Overflow Interrupt  
89
  //Interrupts global aktivieren
90
  sei();
91
  
92
  return 0;
93
}
94
95
int LED1(int state) {
96
  
97
  if(state) {
98
    PORTD |= 1 << 2;
99
  } else {
100
    PORTD &= ~(1 << 2);
101
  }
102
  return 0;
103
}
104
105
int LED2(int state) {
106
  
107
  if(state) {
108
    PORTD |= 1 << 3;
109
  } else {
110
    PORTD &= ~(1 << 3);
111
  }
112
  return 0;
113
}
114
115
int LED3(int state) {
116
  
117
  if(state) {
118
    PORTD |= 1 << 4;
119
  } else {
120
    PORTD &= ~(1 << 4);
121
  }
122
  return 0;
123
}
124
125
int LED4(int state) {
126
  
127
  if(state) {
128
    PORTB |= 1 << 6;
129
  } else {
130
    PORTB &= ~(1 << 6);
131
  }
132
  return 0;
133
}

von Karl H. (kbuchegg)


Lesenswert?

Christopher schrieb:

> Funktioniert jetzt alles bestens.

Für praktische Zwecke mag das schon ausreichen.
Aber willst du deinem Benutzer wirklich zumuten, dass er 300 mal auf 
Down drücken muss, wenn er 300 mal auf Up gedrückt hat?

>   if(Now_taster2 == 1 && Now_taster2 != Previous_taster2) {
>     mode += 1;
>   }

Da würde ich auch noch eine Sicherung einbauen, so dass mode nicht zu 
gross werden kann.

von Christopher (Gast)


Lesenswert?

Ja so einen Schutz sollte man dann vll noch einbauen. Aber das war jetzt 
mein erstes mal Microcontrollerprogrammieren um einfach mal einen 
Einstieg zu bekommen.

Ansonsten mach ich eig immer nur Java, C#, PHP,... und nicht so nah an 
der Hardware.

Als nächstes werde ich mich mal um die RS232 Schnittstelle kümmern. Mal 
sehen wo ich da anfangen kann.

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.