mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Atmega8 Taster funktioniert nicht


Autor: Christopher (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/signal.h>

int mode = 0;

SIGNAL (SIG_OVERFLOW0)
{
  cli();    // Interrupts nicht zulassen
  
  if ((PIND & (1 << PIND5)))    // Up-Taste gedr¸ckt
    mode = mode + 1;
  
  if ((PINB & (1 << PINB7))) {    // Down-Taste gedr¸ckt
    if((mode - 1) != -1) {
      mode = mode - 1;
    }
  }
  
  sei();    // Interrupts zulassen
}


int main(void)
{
  init();
  
    for(;;){
    
    switch (mode) {
      case 0:
        LED1(1);
        LED2(1);
        LED3(1);
        LED4(1);
        break;
        
      case 1:
        LED1(0);
        LED2(0);
        LED3(0);
        LED4(0);
        break;
        
      default:
        LED1(1);
        LED2(0);
        LED3(1);
        LED4(1);
    }
    
    }
    return 0;               /* never reached */
}



int init(void) {
  //LED Outputs setzen
  DDRD = 0x1C;  //1 << 2 | 1 << 3 | 1 << 4;
  DDRB = 0x40;  //1 << 6;
  //LED Outputs setzen ENDE
  
  // Timer f¸r Tastenabfrage
  TCCR0 |= (1<<CS22) | (1<<CS20);    //8-Bit Timer, Timer clock = system clock/1024
  TIFR |= (1<<TOV0);           //Clear TOV0 Timer/Counter Overflow Flag. clear pending interrupts
  TIMSK |= (1<<TOIE0);         //Enable Timer0 Overflow Interrupt
  
  //Interrupts global aktivieren
  sei();
  
  return 0;
}

int LED1(int state) {
  
  if(state) {
    PORTD |= 1 << 2;
  } else {
    PORTD &= ~(1 << 2);
  }
  return 0;
}

int LED2(int state) {
  
  if(state) {
    PORTD |= 1 << 3;
  } else {
    PORTD &= ~(1 << 3);
  }
  return 0;
}

int LED3(int state) {
  
  if(state) {
    PORTD |= 1 << 4;
  } else {
    PORTD &= ~(1 << 4);
  }
  return 0;
}

int LED4(int state) {
  
  if(state) {
    PORTB |= 1 << 6;
  } else {
    PORTB &= ~(1 << 6);
  }
  return 0;
}

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

//int mode = 0;
volatile int mode = 0;

SIGNAL (SIG_OVERFLOW0)
{
// unnötig, weg damit
//  cli();    // Interrupts nicht zulassen
  
  if ((PIND & (1 << PIND5)))    // Up-Taste gedr¸ckt
    mode = mode + 1;
  
  if ((PINB & (1 << PINB7))) {    // Down-Taste gedr¸ckt
    if((mode - 1) != -1) {
      mode = mode - 1;
    }
  }
// gefährlich, weg damit  
//  sei();    // Interrupts zulassen
}


Autor: HappyNewyear (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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))) ...

Autor: HappyNewyear (Gast)
Datum:

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

Dann wird das mit den Timern aber nix, oder?

Autor: Stefan B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Flo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie hast du den Taster angeschlossen?

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

Autor: Christopher (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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?
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

volatile int mode = 0;

ISR(TIMER0_OVF_vect)
{
  cli();    // Interrupts nicht zulassen
  
  if (!(PIND & (1 << PIND5)))    // Up-Taste gedr¸ckt
    mode = mode + 1;
  
  if (!(PINB & (1 << PINB7))) {    // Down-Taste gedr¸ckt
    if((mode - 1) != -1) {
      mode = mode - 1;
    }
  }
  
  sei();    // Interrupts zulassen
}


int main(void)
{
  init();
  
    for(;;){
    
    switch (mode) {
      case 0:
        LED1(1);
        LED2(1);
        LED3(1);
        LED4(1);
        break;
        
      case 1:
        LED1(0);
        LED2(0);
        LED3(0);
        LED4(0);
        break;
        
      default:
        LED1(1);
        LED2(0);
        LED3(1);
        LED4(1);
    }
    
    }
    return 0;               /* never reached */
}



int init(void) {
  //LED Outputs setzen
  DDRD = 0x1C;  //1 << 2 | 1 << 3 | 1 << 4;
  DDRB = 0x40;  //1 << 6;
  //LED Outputs setzen ENDE
  
  // Timer f¸r Tastenabfrage
  TCCR0 |= (1<<CS02) | (1<<CS00);    //8-Bit Timer, Timer clock = system clock/1024
  TIFR |= (1<<TOV0);           //Clear TOV0 Timer/Counter Overflow Flag. clear pending interrupts
  TIMSK |= (1<<TOIE0);         //Enable Timer0 Overflow Interrupt
  
  //Interrupts global aktivieren
  sei();
  
  return 0;
}

int LED1(int state) {
  
  if(state) {
    PORTD |= 1 << 2;
  } else {
    PORTD &= ~(1 << 2);
  }
  return 0;
}

int LED2(int state) {
  
  if(state) {
    PORTD |= 1 << 3;
  } else {
    PORTD &= ~(1 << 3);
  }
  return 0;
}

int LED3(int state) {
  
  if(state) {
    PORTD |= 1 << 4;
  } else {
    PORTD &= ~(1 << 4);
  }
  return 0;
}

int LED4(int state) {
  
  if(state) {
    PORTB |= 1 << 6;
  } else {
    PORTB &= ~(1 << 6);
  }
  return 0;
}

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

Bewertung
0 lesenswert
nicht 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.

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

Bewertung
0 lesenswert
nicht 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.
unsigned char Previous;

ISR( ... )
{
  unsigned char Now;

   if( !( PIND & ( 1 << ... ) ) )   // Taste gedrückt?
     Now = 1;
   else
     Now = 0;

   if( Now != Previous ) {     // gab es eine Veränderung?
     ... mach was, Taste hat Zustand gewechselt
   }

   Previous = Now;    // der jetzige Zustand wird gemerkt, damit er
                      // beim nächsten ISR Aufruf als alter Vergleichswert
                      // herhalten kann
}

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.

Autor: Stefan B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Christopher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe jetzt mit diesem Code:
int main(void)
{
  LED1(1);
    /* insert your hardware initialization here */
    for(;;){
         if (!(PIND & (1 << PIND5)))    // Up-Taste gedr¸ckt
      LED1(1);
    
    if (!(PINB & (1 << PINB7))) {    // Down-Taste gedr¸ckt
      LED1(0);
    }
    }
    return 0;   /* never reached */
}

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

Autor: Stefan B. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

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

Bewertung
0 lesenswert
nicht lesenswert
Christopher schrieb:
> Habe jetzt mit diesem Code:
>
>
int main(void)
> {
>   LED1(1);
>     /* insert your hardware initialization here */
>     for(;;){
>          if (!(PIND & (1 << PIND5)))    // Up-Taste gedr¸ckt
>       LED1(1);
> 
>     if (!(PINB & (1 << PINB7))) {    // Down-Taste gedr¸ckt
>       LED1(0);
>     }
>     }
>     return 0;   /* never reached */
> }
>
> 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.

Autor: Christopher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Prima! Vielen lieben Dank!

Funktioniert jetzt alles bestens.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

volatile int mode = 0;
unsigned char Previous_taster1;
unsigned char Previous_taster2;


ISR(TIMER0_OVF_vect)
{
  unsigned char Now_taster1;
  unsigned char Now_taster2;
  
  if (!(PIND & (1 << PIND5)))  {  // Up-Taste gedr¸ckt
    Now_taster2 = 1;
  } else {
    Now_taster2 = 0;
  }
  if (!(PINB & (1 << PINB7))) {    // Down-Taste gedr¸ckt
    Now_taster1 = 1;
  } else {
    Now_taster1 = 0;
  }
  
  if(Now_taster1 == 1 && Now_taster1 != Previous_taster1) {
    if((mode - 1 ) != -1 ) {
      mode -= 1;
    }
  }
  if(Now_taster2 == 1 && Now_taster2 != Previous_taster2) {
    mode += 1;
  }
  
  Previous_taster1 = Now_taster1;
  Previous_taster2 = Now_taster2;
}


int main(void)
{
  init();
  
    for(;;){
    
    switch (mode) {
      case 0:
        LED1(1);
        LED2(1);
        LED3(1);
        LED4(1);
        break;
        
      case 1:
        LED1(0);
        LED2(0);
        LED3(0);
        LED4(0);
        break;
        
      default:
        LED1(1);
        LED2(0);
        LED3(1);
        LED4(1);
    }
    
    }
    return 0;               /* never reached */
}



int init(void) {
  //LED Outputs setzen
  DDRD = 0x1C;  //1 << 2 | 1 << 3 | 1 << 4;
  DDRB = 0x40;  //1 << 6;
  //LED Outputs setzen ENDE
  
  //interne Pull-ups für Taster
  PORTD |= (1<<PD5); 
  PORTB |= (1<<PB7); 
  
  
  // Timer f¸r Tastenabfrage
  TCCR0 |= (1<<CS02) | (1<<CS00);    //8-Bit Timer, Timer clock = system clock/1024
  TIFR |= (1<<TOV0);           //Clear TOV0 Timer/Counter Overflow Flag. clear pending interrupts
  TIMSK |= (1<<TOIE0);         //Enable Timer0 Overflow Interrupt  
  //Interrupts global aktivieren
  sei();
  
  return 0;
}

int LED1(int state) {
  
  if(state) {
    PORTD |= 1 << 2;
  } else {
    PORTD &= ~(1 << 2);
  }
  return 0;
}

int LED2(int state) {
  
  if(state) {
    PORTD |= 1 << 3;
  } else {
    PORTD &= ~(1 << 3);
  }
  return 0;
}

int LED3(int state) {
  
  if(state) {
    PORTD |= 1 << 4;
  } else {
    PORTD &= ~(1 << 4);
  }
  return 0;
}

int LED4(int state) {
  
  if(state) {
    PORTB |= 1 << 6;
  } else {
    PORTB &= ~(1 << 6);
  }
  return 0;
}

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

Bewertung
0 lesenswert
nicht 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.

Autor: Christopher (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

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.