mikrocontroller.net

Forum: Compiler & IDEs Taster abfragen UND dann.


Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Ich habe folgende Frage, auf deren Lösung ich einfach nicht komme:

Ich habe einen Taster an PORTC angeschlossen. Nun soll auf dem Display 
die Anzahl der Tastendrücke (Zählvariable: iMessung) angezeigt werden.

z.B. 1x Drücken = "Messung-1", 3x Drücken = "Messung-3" bis Messung-5.

Zur Displayansteuerung verwendete ich Peter Fleury's Library.
Mein Display zeigt einfach nicht den Wert der Variablen iMessung an. 
Woran liegt das?


Hier der Code:
#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "lcd.h"
#include <inttypes.h>


static const PROGMEM unsigned char copyRightChar[] =
{
  0x07, 0x08, 0x13, 0x14, 0x14, 0x13, 0x08, 0x07,
  0x00, 0x10, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00
};


/*
*
* function prototypes
*/ 
void wait_until_key_pressed(void);


void wait_until_key_pressed(void)
{
    unsigned char temp1, temp2;
    unsigned int i;
    
    do {
        temp1 = PIND;                  // read input
        for(i=0;i<65535;i++);
        temp2 = PIND;                  // read input
        temp1 = (temp1 & temp2);       // debounce input
    } while ( temp1 & _BV(PIND2) );
    
              
}

Disp_Messung(short Anzahl)
{
  switch(Anzahl)
  {
    case 1:  lcd_puts("Messung-1");
    break;
    case 2: lcd_puts("Messung-2");
    break;
    case 3: lcd_puts("Messung-3");
    break;
    case 4: lcd_puts("Messung-4");
    break;
    case 5: lcd_puts("Messung-5");
    break;
  }
}

void Disp_Ctrl(void)
{
wait_until_key_pressed();

   
       lcd_command(LCD_DISP_ON);
        
       
        
       lcd_command(_BV(LCD_CGRAM));  /* set CG RAM start address 0 */
}




int main(void)
{
  DDRC =0x00;
    char buffer[7];
    int  num=134;
    unsigned char i;
    short iMessung =1;
    
    DDRD &=~ (1 << PD2);        /* Pin PD2 input              */
    PORTD |= (1 << PD2);        /* Pin PD2 pull-up enabled    */


    /* initialize display, cursor off */
    lcd_init(LCD_DISP_ON);

    for(;;)
  {
  if (PINC == 0xFE)
    {
      iMessung++;
    }
                              /* loop forever */
        
       
        lcd_clrscr();
        
        
        Disp_Messung(iMessung);

       
         Disp_Ctrl();
              
  }    
}
Vielen Dank!

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> for(i=0;i<65535;i++);
Das ist schon mal ein klasse Kandidat für den Optimizer. Mach sowas 
nicht. Leere Zählschleifen werden gerne wegoptimiert. Entweder schreib 
was in die Schleife rein (z.B. ein "asm(nop::);) oder (besser) nimm für 
kurze Wartezeiten die fertigen Funktionen aus der delay.h.

> if (PINC == 0xFE)
Wie soll PINC denn in Deinem Programm jemals 0xFE werden? Hast Du 
externe Pull-Ups da dran? Abgesehen davon: Du wartest am Ende der 
Endlosschleife darauf, dass ein Taster an PIND2 gedrückt wird. Wenn das 
der Fall ist, müsste gleichzeitig der an PINC0 befindliche Taster 
gedrückt sein, damit überhaupt eine Änderung erfasst wird. Und dabei 
müssen auch noch alle anderen Pins von Port C High-Pegel haben.

Ich denke, da ist einiges verkehrt...

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

Bewertung
0 lesenswert
nicht lesenswert
Das liegt daran, dass dein Programm zwar jede Menge Code
beinhaltet, dieser Code aber völlig für die Katz ist.

Du überwachst hier:

    for(;;)
  {
  if (PINC == 0xFE)
    {
      iMessung++;
    }

den Eingang. Und solange dieser Eingang auf 0xFE
steht, wird die Variable hochgezählt.
Dieser Eingang steht aber lange auf 0xFE. Wenn du
da einen Taster dran hast, und die Taste auch nur
100 ms lang gedrückt hältst, dann kann dein µC die
konmplette Schleife wahrscheinlich ein paar Hundert
mal ausführen. Dementsprechend wurde die Variable
iMessung auch 100 mal erhöht.

Dein Code macht: Solange eine Taste gedrückt ist,
zähle die Variable hoch

Dein Code soll machen: Nur in dem Moment, in dem sich
der Zustand der Taste ändert, und zwar von nicht
gedrückt auf gedrückt, soll die Variable erhöht
werden.

PS: Deine debounce Funktion kannst du in der Pfeife
rauchen. Die funktioniert nicht richtig.
Hol dir aus dem Wiki die PeDa-Entprell Routinen und
benutze sie.

http://www.mikrocontroller.net/articles/Entprellun...

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also, ich habe das STK500 und da habe ich mir einen PORT (nämlich den 
PORT-C) über ein Flachkabel nach außen gelegt, wo ich einfach einen 
Taster von Masse nach PIN0 schalte. Also ich habe da keinen Pullup dran.


diese "wait until keypressed" funktion kommt von Peter Fleurys Lib. Ich 
habe keine Ahnung was sie macht, ich weiß nur, wenn ich sie 
auskommentiere, flimmert mein Display eigenartig, drum hab ich es drin 
gelassen. Ich wäre gerne bereit, eine Alternative (auch zwecks 
Zählschleife) von euch anzunehmen, da ihr ja wirklich die Profis seid.

Ich möchte eigentlich erstmal nur diesen einen PIN (portC -> PIN0) 
abfragen, und dann sofort per Tastendruck die dadurch inkrementierte 
Variable "iMessung" auf dem Display ausgeben.


Vielen Dank

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

Bewertung
0 lesenswert
nicht lesenswert
Wie gesagt:
geh auf die Web Seite auf der der Entprellcode liegt und
hol dir diesen Code. Der ist zwar für den Anfang wahrlich
nicht einfach zu durchschauen, die Details wie er funktioniert
musst du aber fürs erste nicht wissen.
Auf der Seite ist auch ein  main() angegeben, welches den
Code benutzt und in Aktion zeigt. Damit sollte es dann
ein leichtes sein, dein Vorhaben umzusetzen.

> Ich möchte eigentlich erstmal nur diesen einen PIN (portC -> PIN0)
> abfragen, und dann sofort per Tastendruck die dadurch inkrementierte
> Variable "iMessung" auf dem Display ausgeben.
>

Das ist dann fast so einfach wie einem Baby den Schnuller klauen.

....
Hier kommt der Rest aus der Seite rein. Also die ISR und die
paar Hilfsfunktionen. Achte darauf, dass du die defines so
anpasst, dass sie zu deiner Hardware passen. Alles was mit LED
zu tun hat, brauchst du klarerweise nicht. Das betrifft die Makros
LED_DDR, LED_PORT, LED0, LED1 und LED2


int main( void )
{
  DDRC = 0x00;   // den Port an dem die Taster hängen vorbereiten
  PORTC = 0xFF;
 
                // die Tastenroutinen basieren auf einem Timer
                // diesen mal anwerfen
  TCCR0 = (1<<CS02)|(1<<CS00);      // divide by 1024
  TIMSK = 1<<TOIE0;        // enable timer interrupt

  lcd_init( LCD_DISP_ON );
 
  sei();
 
  for(;;) {                                       // main loop
                                                  // single press
    if( get_key_press( 1<<KEY0 ) ) {
      iMessung++;
      lcd_clrscr();
      Disp_Messung(iMessung);
    }
  }
} 

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Funktioniert der Timer_interrupt auch, obwohl ich schon einen Timer in 
Verwendung habe?

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
noch jemand da??

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Björn wrote:
> noch jemand da??
RTFM!
AVR-GCC-Tutorial...

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> noch jemand da??

Logisch.

> Funktioniert der Timer_interrupt auch, obwohl ich schon einen Timer
> in Verwendung habe?

Warum sollte es nicht funktionieren? Du brauchst getrennte Timer, wenn 
du den Code direkt übernehmen willst.

Aber wenn du im eigenen Programmteil schon einen Timer am laufen hast 
(ich sehe das in deinem Sourcecode nicht), könntest du überlegen, wie du 
das Entprellen dort mit erledigst. Dort wäre das Entprellen 
wahrscheinlich Kleinkram für nebenher.

Autor: Björn (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Also ich schicke nun mal meine main.c, damit ihr seht um was es 
geht....Ich brauche unbedingt eine Routine in meinem Code, die die 
Tastendrücke verarbeitet und daraufhin am Display einen entsprechenden 
Code ausgibt.
Habe einfach mal eine Endlosschleife um eine Tastenabfrage gemacht, aber 
es rührt sich NULL!

Vielen Dank!

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sowas geht mit dem neuen GCC nicht mehr, diese for-schleife wird 
gnadenlos wegoptimiert:

void wait_until_key_pressed(void)
{
    unsigned char temp1, temp2;
    unsigned int i;
    
    do {
        temp1 = PIND;                  // read input
        for(i=0;i<65535;i++);
        temp2 = PIND;                  // read input
        temp1 = (temp1 & temp2);       // debounce input
    } while ( temp1 & _BV(PIND2) );
    
    loop_until_bit_is_set(PIND,PIND2); /* wait until key is released */
}


mach das stattdessen mit _delay_ms(). 20-30ms reichen. Den Maximalwert 
bei _delay_ms() beachten, siehe Abschnitt Warteschleifen im 
AVR-GCC-Tutorial

void wait_until_key_pressed(void)
{
    unsigned char temp1, temp2;
    unsigned int i;
    
    do {
        temp1 = PIND;                  // read input
        _delay_ms(10);
        _delay_ms(10);
        _delay_ms(5);
        temp2 = PIND;                  // read input
        temp1 = (temp1 & temp2);       // debounce input
    } while ( temp1 & _BV(PIND2) );
    
    loop_until_bit_is_set(PIND,PIND2); /* wait until key is released */
}


Jetzt kontrolliere die Sourcen, ob noch mehr solcher Daumendrehschleifen 
drin sind.

_delay_ms() erfordert ein #include <util/delay.h> und ein Übersetzen mit 
Optimierung (-Os zum Beispiel).

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok habe ich gemacht....

aber das problem mit den schaltern ist immer noch das gleiche ;)

könntest mir da vll auch noch helfen??


danke!

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
2. Teil der Fehlersuche
(da die obige Funktion in deinem Code überhaupt nicht benutzt wird)

int main(void)
{
  unsigned char tastenzaehler;
  unsigned char taste_gedrueckt;
    
  DDRD &=~ (1 << PD2);        /* Pin PD2 input              */
  PORTD |= (1 << PD2);        /* Pin PD2 pull-up enabled    */

  /* initialize display, cursor off */
  lcd_init(LCD_DISP_ON);

  DDRD =0xFF;   //UART
  DDRB =0x00;   //Lichtschranken
  DDRA =0xFF;   //Port f�r LCD
  DDRC =0x00;   //Port f�r div. Steuer-Taster

  PORTA = 0x00;

  UCSRB |= (1<<TXEN);                            //Senden aktivieren
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);  

  UBRRH = 00;   //Baudrate einstellen 9600 bei 8 MHz
  UBRRL = 51;

  for(;;)
  {
    taste_gedrueckt = 0;
    
    //
    // Solange die Taste an Pin0 von PORTC nicht losgelassen 
    // wurde: warten
    //
    while(PINC == 0xFE)
    {
      // Entprellen: Taste ist mindestens 25ms lang gedrückt
      _delay_ms(10);
      _delay_ms(10);
      _delay_ms(5);
      if (PINC == 0xFE)
        taste_gedrueckt = 1;
    }

    if (taste_gedrueckt)
    {
      taste_gedrueckt = 0;
      tastenzaehler++;

      // ADD: hier ggf. auch noch das Loslassen entprellen
      _delay_ms(10);
      _delay_ms(10);
      _delay_ms(5);
    }

    switch (tastenzaehler)
    {
      case 0:
        //Ausgangstext auf dem Display
        lcd_clrscr();    //Display l�schen
        lcd_puts("Mit SCROLLtaster\nDurchlauf wahlen");
        break;
      case 1:
        lcd_clrscr();
        lcd_puts("Messung 1");
        break;
      case 2:
        lcd_clrscr();
        lcd_puts("Messung 2");
        break;
      case 3:
        lcd_clrscr();
        lcd_puts("Messung 3");
        break;
      default:
        tastenzaehler = 0;
        break;
    }
  }
}


Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok danke:


Nächstes Problem:    Object file not found.... habe avr-studio 
geschlossen und wieder geöffnet, jetzt geht das kompilieren plötzlich 
mit oben genanntem fehler nicht!! :(

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Schon Rebuild All im Menü Build probiert?

Kommen beim Kompilieren Fehler? Kannst du die Ausgabe im Message-Fenster 
komplett per Copy&Paste hier angeben? Alles dort mit Gelben oder Roten 
Punkten ist fischig und muss kontrolliert werden.

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja bringt auch nix, kompilieren hat es sich ja vorher gelassen.


jetzt kommt dauernd dieser GCC-plugin-error wegen dem *.elf

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
nicht mal ein neues Projekt völlig unabhängig von all dem vorherigen 
funktioniert mehr. HILFE!

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wo soll denn diese komische .elf-Datei sein?? ich finde die in keinem 
Ordner!

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Björn wrote:
> wo soll denn diese komische .elf-Datei sein?? ich finde die in keinem
> Ordner!

Genau das meldet dir das GCC-Plugin auch als Fehler.

*.elf ist das kompilierte und gelinkte fertige Programm.

DIese Datei fehlt, wenn das Kompilieren fehlgeschlagen ist.

Warum das Fehlgeschlagen ist, kann zwei Ursachen haben.

1/ Der Quelltext enthält Fehler.

2/ Es gibt Probleme mit dem Projekt- bzw. Speicherpfad. Ist da bei dir 
ein Leerzeichen oder ein Sonderzeichen drin?

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok funktioniert wieder, war an einem Pfadnamen gelegen und an einem 
syntax-fehler im Code.



Ähm zu deinem Codebeispiel, erstmal vielen lieben Dank :)

also nach dem Einschalten des Board erscheint mal garnix auf dem 
Display, dann wenn ich eine Taste drücke flaggert die Zahl nach 
"Messung" zwischen 2 und 3 und nach etwa 1 sec. bleibt sie dann auf 
einer von beiden stehen.

Danke!

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das das flackert liegt an den vielen Löschaktionen mit lcd_clrscr();

Mein Beispiel ist ja kein fertiges Programm, sondern etwas mit dem du 
selber ein Stück weitermachen kannst. Versuche es zu verstehen und passe 
es dir an.

Zwei Sachen noch:

1/ Tastenzaehler initialisieren. Der Compiler meldet korrekterweise eine 
Warnung bei obigem Code ohne die Zuweisung. Wegen der zufälligen 
Initialisierung kommt bei obigem Code auch keine Anfangsmeldung.

  unsigned char tastenzaehler = 0;    // <==


2/ Die Abfrage auf den Tastendruck besser nur auf genau das eine 
interessante Bit vergleichen:

//  while(PINC == 0xFE)
    while(!(PINC & (1<<PC0)))
    {
      // Entprellen: Taste ist mindestens 25ms lang gedrückt
      _delay_ms(10);
      _delay_ms(10);
      _delay_ms(5);
//    if (PINC == 0xFE)
      if (!(PINC & (1<<PC0)))
        taste_gedrueckt = 1;
    }

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also ich füge mal meinen Code-Schnipsel ein, habe jetzt selber irgendwie 
ohne Entprellung erstmal hinbekommen:

Es gibt aber noch ein Problem.: Ich schalte das Board ein. Es kommt die 
Meldung "Mit Scrolltaster Durchlauf wählen". Sobald ich nun die 
Scrolltaste drücke DAUERT ES etwa 1 sec, bis das Display reagiert (Die 
Anzeige ändert). Wenn ich schließlich bei "Messung 3 ?" angelangt bin, 
bleibt es nur 1 sec. im Display und danach ist das ganze display 
"gelöscht".

Woran liegt das?
void Disp_Messung(short i)
{
  switch (i)
  {
    case 1:   lcd_puts("Messung - 1 ?");
    case 2:    lcd_puts("Messung - 2 ?");
    case 3:    lcd_puts("Messung - 3 ?");
  }
}
                            
        lcd_clrscr();                        //Display l?schen
        lcd_puts("Mit SCROLLtaster\nDurchlauf wahlen");
    //lcd_puts("Mit SCROLLtaster\nDurchlauf wahlen");    //Ausgangstext auf dem Display
                                                                 //Steuerbefehl an das Display senden
     for(;;)
    {
      
    
    
      if(PINC == 0xFE)
      {
    lcd_clrscr();
        tastenzaehler++;
    Disp_Messung(tastenzaehler);
        }


Danke!

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
1/ C-Grundlagen switch case
Es fehlt ein break; in den case Abfragen. dadurch bekommt das Programm 
Durchfall. Ein auftretender case 1 fällt durch case 2 und case 3...

2/  for(;;)
    {
      if (PINC == 0xFE)

Wie oft schätzt du kommt dein Programm an dieser Stelle pro Sekunde 
vorbei? Zig tausend Male! Auch wenn du die Taste drückst. Der AVR ist 
sauschnell!

Wenn du die Taste drückst, wird jedesmal das LCD gelöscht (das dauert), 
der tastenzsehler erhöht (bis er überläuft und von 0 anfängt) und dann 
dreimal hintereinander kurz was anderes angezeigt und gleich 
weitergestolpert in die nächste Runde.

Ich empfehle dir stark, dein Programm mal im Einzelschritt im Simulator 
durchzusteppen.  Dir werden Sachen auffallen, die du so nie gewollt hast 
;-)

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok Danke, wird wohl das problem sein, bin nun in der Arbeit....

Werde es abends noch mal ausprobieren ;)


naja, ich bin eben kein Profi, drum bin ich ja teilweise auf euere gute 
Hilfe angewiesen.


Ich werde euch abend noch berichten!

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!


Ich habe nun noch eine Idee: Ich möchte desweiteren in der 
switch-Anweisung die Zeit in einer Array speichern, die verstreicht, 
solange ein Taster gedrückt ist.

Könntet ihr mir dabei helfen?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sollte gehen.

Im Grunde ist das eine Anwendung der Timer so wie es im AVR-Tutorial 
bzw. im AVR-GCC-Tutorial ausführlich erklärt ist. Timer mit diesen 
Anleitungen so einrichten, dass er im Interrupt die Zeit hochzählt. Im 
Hauptprogramm Startzeit merken, Endzeit abfragen, Differenz bilden, 
Ergebnis formatieren und ausgeben. Pronto.

Hast du den Rest des Programms schon fertig geschrieben und entwanzt und 
nach Wunsch lauffähig auf'm µC, so dass man den die Zeitbehandlung nur 
noch einbauen muss, oder hapert es noch an allem?

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.