Forum: Mikrocontroller und Digitale Elektronik Menü Programmierung ATMega32


von René G. (gess12)


Lesenswert?

Hallo an Alle,

ich bin neu hier und suche Hilfe.
Ich möchte im Rahmen eines Projektes einen µC ATMega32 in C so 
programmieren das ich über ein Menü verschiedene Sachen auswählen kann.

Also soll im ersten Menüpunkt Text auf einem Display ausgegeben werden 
und nach betätigen einer bestimmten Taste, soll zum nächsten Menüpunkt 
gesprungen werden, hier soll auch eine Auswahl über die angeschlossene 
4x4 Matrix Tastatur getroffen werden und danach zum nächsten Menüpunkt 
gesprungen werden.
Das soll dann bis zum Ende so weitergehen, dabei soll aber ab dem 
zweiten Menüpunkt das zurückspringen zum ersten möglich sein.

Ich habe es jetzt über eine Case-Struktur versucht. In der Simulation 
(ich benutze AVR-Studio 4) funktioniert es so wie ich es programmiert 
habe, wenn ich das dann aber im µC programmiere funktioniert es nicht 
mehr ganz so. Nach dem ersten Menüpunkt wird sofort der dritte Menüpunkt 
angezeigt.
Ich habe zwar eine vage Vermutung was die Ursache dafür sein könnte, bin 
mir aber nicht ganz sicher.

Wer hat eine Idee wie ich es mit einfachen Mitteln anstellen kann damit 
es funktioniert.

Vielen Dank schon einmal im Voraus.

LG René

: Verschoben durch User
von prello (Gast)


Lesenswert?

Vermutlich prellt dein Taster. Wie man Tastereingaben entprellt findest 
Du schnell über die Suchfunktion.

Viel Spaß!

Prello

von René G. (gess12)


Lesenswert?

Oh, das ging aber schnell.

Da meine Tastatur an einem 74C922 angeschlossen ist, sollte diese damit 
entprellt sein wenn ich alles richtig verstanden habe.

Ich lese also damit direkt den Wert ein der an der Tastatur gedrückt 
worden ist.

Aber ich kann mich auch täuschen, ich bin noch totaler Anfänger.

René

von Karl H. (kbuchegg)


Lesenswert?

René Geßner schrieb:
> Oh, das ging aber schnell.
>
> Da meine Tastatur an einem 74C922 angeschlossen ist, sollte diese damit
> entprellt sein wenn ich alles richtig verstanden habe.

OK.
Trotzdem. Wie sieht deine Asuwertung aus?
>
> Ich lese also damit direkt den Wert ein der an der Tastatur gedrückt
> worden ist.

Ja, aber hoffentlich nur einmal.
Du willst ja nicht, dass der Cursor nach unten saust SOLANGE eine Taste 
gedrückt ist, sondern nur ein einziges mal.
Danach muss der Benutzer die Taste wieder loslassen und erneut drücken, 
woraufhin der Cursor wieder eine Zeile tiefer geht.

Hast du das so gemacht?

von René G. (gess12)


Lesenswert?

Hallo,

der Fehler liegt wohl doch daran das die Tastatur nicht entprellt ist.
Wenn ich in jeder Menüebene den auszuwählenden Optionen 
aufeinanderfolgende Ziffern zuweise funktioniert es, sobald aber in 
Menüebene 1 und Menüebene 2
die gleichen Ziffern zugewiesen sind rauscht das Programm direkt durch 
zur Menüebene 3 bzw. zur letzten Ebene die programmiert ist.

Ich hatte gehofft das ich mit der Case Struktur das unterbinden kann, da 
ich ja immer in einem Case drin bin. Funktioniert aber so nicht.

Im Moment frage ich den Port direkt ab (Polling) wo die Tastatur dran 
ist.
Später wird das als Interrupt gemacht. Aber da bin ich gerade am lesen 
wie ich das am einfachsten machen kann. Wie gesagt ich bin noch totaler 
Laie und habe eine Projektarbeit zu machen, wo das hier erst der Anfang 
ist.


Um einmal mein Beispiel in Worten zu beschreiben.


Menüebene1
"Platinengroesse"
1= xyz x zyx
2= wer x rew
3= zui x iuz

dann frage ich den Tastaturport ab welche Taste gedrückt worden ist, ist 
es eine 1, 2 oder 3, dann wird der Wert in einer Variablen gespreichert 
der Menüzähler um eins erhöht und das Display gelöscht, damit wäre case 
2 dran

das gleiche wie oben, halt nur anderer Text

Menüebene2
"Belichtungsart"
1= einseitig      // wenn ich hier anstatt 1, die 4 benutze funktioniert 
es
2= zweiseitig     // und hier anstatt der 2 die 5

und so weiter


also brauche ich mich jetzt nur noch schlau machen wie ich schaffe das 
die Ziffer nur einmal abgefragt wird in jedem Case.

René

von Karl H. (kbuchegg)


Lesenswert?

René Geßner schrieb:

> Um einmal mein Beispiel in Worten zu beschreiben.


Du sollstz nichts beschreiben .... du sollst Code zeigen.

Und mitlerweile bin ich mir sicher.
Du hast

  Solange eine Taste gedrückt ist

mit

  eine Taste wird einmal gedrückt und dann muss die Taste losgelassen
  werden.

verwechselt.


> Im Moment frage ich den Port direkt ab (Polling) wo die Tastatur dran
ist.

und das ist auch gut so.

> Später wird das als Interrupt gemacht.

Und das ist Quatsch. Für eine Tastenauswertung braucht man keinen 
Interrupt. Aus Sicht des µC agierst du in Super-Slow-Spezial-Zeitlupe. 
Egal wie schnell du als Mensch versuchst eine Taste 2-mal hintereinander 
zu drücken, deinem µC entlockt das nur ein müdes Gähnen. Da legt er sich 
zwischendurch noch mal schlafen und macht aus seiner Sicht ein 
ausgiebiges Nickerchen.

Interrupts an Tasten vereinfachen die Sache nicht. Ganz im Gegenteil. 
Ganz im Gegenteil.

von Karl H. (kbuchegg)


Lesenswert?

René Geßner schrieb:

> Menüebene1
> "Platinengroesse"
> 1= xyz x zyx
> 2= wer x rew
> 3= zui x iuz
>
> dann frage ich den Tastaturport ab welche Taste gedrückt worden ist, ist
> es eine 1, 2 oder 3, dann wird der Wert in einer Variablen gespreichert
> der Menüzähler um eins erhöht und das Display gelöscht, damit wäre case
> 2 dran
>
> das gleiche wie oben,

und weil du im Gegensatz zu deinem µC schnarchlangsam bist, ist die 
Taste 2 immer noch gedrückt. Woraufhin ...

> halt nur anderer Text
>
> Menüebene2
> "Belichtungsart"
> 1= einseitig      // wenn ich hier anstatt 1, die 4 benutze funktioniert
> es
> 2= zweiseitig     // und hier anstatt der 2 die 5

dein µC hier dann sofort eine gedrückte Taste 2 feststellt und dann auch 
gleich noch diesen Menüpunkt ausführt.


Tasten müssen auch mal losgelassen werden!
Darauf muss dein µC aber warten! Erst danach gilt eine gedrückte 2 als 
nächster Tastendruck.

von René G. (gess12)


Lesenswert?

ok, hier mal mein code,

wie gesagt Anfänger!!!!

while(1)

{
switch ( Mzaehler )
 {
   case 1:
     lcd_setcursor( 0, 1 );
     lcd_string("Platinengroesse?");

      lcd_setcursor( 0, 2 );
       lcd_string("1=100x200");

       lcd_setcursor( 0, 3 );
        lcd_string("2=200x300");

       lcd_setcursor( 0, 4 );
       lcd_string("3=300x400");



  if (PINA & 0x10)
   {
     tast_wert1 = (PINA & 0x0f);
    if(tast_wert1 == 0x01 || tast_wert1 == 0x02 || tast_wert1 ==0x03)
            {
             groesse = tast_wert1;
             Mzaehler++;
       lcd_clear();
      }
          }

  break;




   case 2:
    lcd_setcursor( 0, 1 );
    lcd_string("Art der Belichtung?");

     lcd_setcursor( 0, 3 );
        lcd_string("4 = einseitig");

      lcd_setcursor( 0, 4 );
    lcd_string("5 = zweiseitig");



  if (PINA & 0x10)
   {
     tast_wert = (PINA & 0x0f);
    if (tast_wert == 0x01 || tast_wert == 0x02)
    {
            art = tast_wert;
            Mzaehler++;
      lcd_clear();
     }
   }


  break;


Und so weiter.....

So sieht es im Moment aus.
wie schon gesagt wenn ich in der Ebene 2  mit 4 und 5 fortfahre 
funktioniert es.

René

von Karl H. (kbuchegg)


Lesenswert?

René Geßner schrieb:


> wie schon gesagt wenn ich in der Ebene 2  mit 4 und 5 fortfahre
> funktioniert es.

Und wie schon jetzt 2 mal gesagt: Du musst dein Programm auch darauf 
warten lassen, das keine Taste gedrückt ist. Erst dann geht es mit dem 
nächsten Menüpunkt bzw. der nächsten Taste weiter.


Und strukturier dein Programm mal ein wenig in Funktionen.
Gerade hierarchische Menüs eignen sich hervorragend für eine Aufteilung 
in Funktionen.
Genau wie die Abfrage der Tasten. Das wird auch eine eigene Funktion. Es 
gibt da keinen Grund, den ganzen Code jedesmal wieder neu aufzurollen.

von René G. (gess12)


Lesenswert?

genau das was du beschreibst trifft zu, ich, im vergleich zum µC, 
wirklich schnarchlangsam, deswegen rauscht er durch. In der Simulation 
geht das dann, weil ich da etwas tricksen muss mit den Eingängen, die 
setze ich halt per Hand. das ist der Nachteil wenn man keinen Hardware 
Debugger hat.

von René G. (gess12)


Lesenswert?

ich würde gern eine vernünftige Struktur programmieren wollen, allein 
das Wissen dazu fehlt mir noch. Das was ich gelehrt bekommen habe wurde 
im Raketentempo durchgezogen.

wenn du mal ein Beispiel hast, bezüglich der Funktionen, kannst du es 
mir gern geben. Ich lerne gern von denen die es beherrschen.

René

von Karl H. (kbuchegg)


Lesenswert?

René Geßner schrieb:
> ich würde gern eine vernünftige Struktur programmieren wollen, allein
> das Wissen dazu fehlt mir noch. Das was ich gelehrt bekommen habe wurde
> im Raketentempo durchgezogen.

Fang damit an, dir eine Funktion zu schreiben, die dir einen Tastendruck 
leifert. Jede Taste hat einen Code. WIchtig: Auch die 'keine Taste 
gedrückt' hat einen eigenen Code, damit du diesen Zustand von den 
anderen Tasten unterscheiden kannst.

Jetzt kommt der Trick: Die Funktion liefert nur dann einen 
'Tastendruck-Code', wenn sich der Zustand der Tasten auch tatsächlich 
verändert hat. Ansonsten liefert sie eben 'keine Taste gedrückt'. Damit 
ist sichergestellt, dass dein restliches Programm jede gedrückte Taste 
nur ein einziges mal zu Gesicht bekommt.

Das ist deine erste Funktion.
Definier dir ein paar Konstanten für die Tastencodes
1
#define NO_KEY   -1
2
#define KEY_0     0
3
#define KEY_1     1
4
#define KEY_2     2
5
....

und schreib eine Funktion
1
int8_t lastKeyDown = NO_KEY;
2
3
int8_t keyPressed()
4
{
5
  ...
6
}

die sich den Port ansieht und feststellt, welche Taste jetzt gerade 
gedrückt ist (wenn überhaupt). DIese Taste, bzw. deren Code wird nur 
dann als Returnwert zurückgeliefert, wenn er sich von lastKeyDown 
unterscheidet (und lastKeyDown wird dann auf diesen Wert gesetzt). In 
allen anderen Fällen liefert die Funktion NO_KEY.

Die Funktion testen!
Überleg dir, wie du testen kannst, ob das auch wirklich funktioniert, 
dass jeder Tastendruck nur einmal gemeldet wird, egal wie oft die die 
Funktion hintereinander aufrufst (ein angemessenes Minimum an Aufrufen 
vorausgesetzt)

von René G. (gess12)


Lesenswert?

Vielen Dank schon einmal, ich werde mich gleich morgen Früh daran 
setzen, für heute ist einmal Schluss, sitze schon seit heute Morgen am 
Rechner und versuche das Problem zu lösen.

Ich werde morgen über Erfolg oder Misserfolg berichten. Ich hoffe es 
wird ein Erfolg.

René

von Karl H. (kbuchegg)


Lesenswert?

René Geßner schrieb:
> Vielen Dank schon einmal, ich werde mich gleich morgen Früh daran
> setzen, für heute ist einmal Schluss, sitze schon seit heute Morgen am
> Rechner und versuche das Problem zu lösen.

Du hast am falschen Ende angefangen.
Wie fast immer beginnt die Reise von Benutzereingaben erst mal mit einer 
ordentichen Auswertung von Tasten und Tastendrücken.
Solange die nicht steht, ist alles andere vergebene Liebesmühe.

von René G. (gess12)


Lesenswert?

Hallo kbuchegg,

so ich bin wieder am arbeiten.
Bin gerade dabei die Funktion zu erstellen.
Habe ich es richtig verstanden das ich in dieser Funktion den Port 
abfrage wo die Tastatur dran hängt und den Wert dann in der Variablen 
LastKeyDown ablege?

Ich steh im Moment wieder einmal auf dem Schlauch, solange die Eingaben 
nicht funktionieren kann ich nicht weitermachen in meinem Projekt.

Sorry wenn ich solch blöden Fragen stelle, aber ich habe das Ganze noch 
nicht wirklich verstanden. Ich meine Grundsätzlich schon aber im Detail 
hängt es noch.

René

von Karl H. (kbuchegg)


Lesenswert?

René Geßner schrieb:
> Hallo kbuchegg,
>
> so ich bin wieder am arbeiten.
> Bin gerade dabei die Funktion zu erstellen.
> Habe ich es richtig verstanden das ich in dieser Funktion den Port
> abfrage wo die Tastatur dran hängt und den Wert dann in der Variablen
> LastKeyDown ablege?


Im Prinzip ja

1
int8_t lastKeyDown = NO_KEY;
2
3
int8_t keyPressed()
4
{
5
  int8_t downNow;
6
7
  // vom 922 erst mal abfragen, ob überhaupt eine Taste momentan
8
  // gedrückt ist. Wenn keine Taste gedrückt ist dann meldet der 922
9
  // das mit dem DATA AVAILABLE Pin ...
10
11
  if( ..... )
12
  {
13
    // .. es ist keine Taste gedrückt. Gleich mal merken
14
    downNow = NO_KEY;
15
  }
16
17
  else
18
  {
19
    // ... es ist eine Taste gedrückt. Jetzt erst mal rausfinden welche.
20
    // Der 922 hat die Tastenmatrix ja schon mal in eine Zahl umgewandelt
21
    // die an den Portpins xxx anliegen
22
    uint8_t keyBits = PINA;
23
24
    // jetzt rausfiltern bzw. umcodieren, welche Taste das ist
25
    // Die Konstanten KEY_0, KEY_1 etc. sind ja trickreicherweise
26
    // genau so angeordnet, dass ihr numerischer Wert (im Falle
27
    // der Zifferntasten) genau dem entspricht, was auf der
28
    // Taste aufgedruckt ist.
29
    // Nur muss das dann auch entsprechend hier so umkodiert werden,
30
    // dass bei einer gedrückten Taste 4, dann auch der Wert 4
31
    // bzw. KEY_4 in downNow steht. Aber mit einem Array ist diese
32
    // Umkodierung ja kein Problem
33
34
    downNow = ......
35
36
    // damit steht fest, welche Taste (und zwar in unseren Einheiten)
37
    // jetzt gerade gedrückt ist
38
  }
39
40
  // soweit so gut.
41
  // jetzt ist klar, welche Taste zur Zeit gedrückt IST.
42
  // Jeder Tastendruck wird aber nur einmal gemeldet.
43
  // einmal und nicht öfter.
44
  // D.h. was diese Funktion zurückliefert ist nur dann ein
45
  // Tastencode, wenn dieser nicht identisch ist mit dem zuletzt
46
  // zurückgelieferten Wert
47
48
  if( downNow != lastKeyDown )
49
  {
50
    lastKeyDown = downNow;
51
    return downNow;
52
  }
53
  else
54
    return NO_KEY;    // es ist zwar eine "Taste" gedrückt. Aber die
55
                      // wurde vorher schon mal gemeldet.
56
}


Die Details überlass ich jetzt dir. Aus den Kommentaren sollte 
eigentlich hervorgehen, was an der jeweiligen Stelle zu tun ist. (Und da 
ich deine Hardware nicht kenne und nicht weiß, wie und wo du den 922 
angeschlossen hast, kann ich das gar nicht weiter programmieren). Fehlt 
ja nicht mehr viel.

von René G. (gess12)


Lesenswert?

erst einmal vielen, vielen Dank.
Jetzt verstehe ich das Ganze schon viel besser und hilft mir weiter.

Mein 922 ist am PortA angeschlossen von Pin0 -Pin3 (ABCD) und Pin4 ist 
DA.
/OE liegt auf GND

Der Wert der vom 922 kommt ist der Wert der gedrückten Taste, somit 
entspricht der Wert der ersten 4 Pins am PortA auch dem der gedrückten 
Taste.

Eine kleine Unsicherheit hat sich jetzt aber bemerkbar gemacht.

Du schriebst in deinen Kommentaren:

// vom 922 erst mal abfragen, ob überhaupt eine Taste momentan
  // gedrückt ist. Wenn"""keine""" Taste gedrückt ist dann meldet der 
922
  // das mit dem DATA AVAILABLE Pin ...

das keine Taste macht mich unsicher. Ist es nicht so das, wenn eine 
Taste gedrückt ist das DA Signal auf High liegt?
Oder meintest du das DA damit auf Low liegt?

Ansonsten versuche ich jetzt genau das umzusetzen was du vorgeschlagen 
hast.

René

von Karl H. (kbuchegg)


Lesenswert?

René Geßner schrieb:

> Der Wert der vom 922 kommt ist der Wert der gedrückten Taste, somit
> entspricht der Wert der ersten 4 Pins am PortA auch dem der gedrückten
> Taste.

Ja. Aber ist der Wert an den 4 unteren Portpins (wenn man ihn als Zahl 
auffasst, auch 0, wenn die Taste 0 gedrückt ist

(Ich hab mir das im Datenblatt nicht weiter angesehen, welche Codes der 
922 bei welcher gedrückter Taste liefert).

Es ist nämlich praktisch, wenn in dieser Funktion gleich ein 
entsprechende Umkodierung gemacht wird. Ist die Taste '8' gedrückt, dann 
soll die Funktion idealerweise auch gleich eine numerische 8 liefern und 
nicht 0x0C, weil dsa eben genau so aus dem 922 heraus kommt. (Die Zahlen 
hab ich jetzt erfunden. Wie gesagt, ich hab mir die Tabelle im 
Datenblatt des 922 nicht näher angesehen, welchen Code er bei welcher 
Taste liefert)

>
> Eine kleine Unsicherheit hat sich jetzt aber bemerkbar gemacht.
>
> Du schriebst in deinen Kommentaren:
>
> // vom 922 erst mal abfragen, ob überhaupt eine Taste momentan
>   // gedrückt ist. Wenn"""keine""" Taste gedrückt ist dann meldet der
> 922
>   // das mit dem DATA AVAILABLE Pin ...
>
> das keine Taste macht mich unsicher. Ist es nicht so das, wenn eine
> Taste gedrückt ist das DA Signal auf High liegt?

Ich hab ja auch im Kommentar nicht geschrieben, dass in diesem Fall der 
Pin auf High liegen würde. Ich hab nur geschrieben, dass man durch 
betrachten des DA Pins rausfinden kann, ob zur Zeit gerade eine Taste 
gedrückt ist oder nicht :-)

von René G. (gess12)


Lesenswert?

der 922 liefert an den Ausgängen ABCD genau den Wert der auf der 
Tastatur gedrückt worden ist. A steht dann für PIN0 und D für PIN3
Wenn eine 0 gedrückt wird, liegt das DA-Signal auf High und die vier 
Ausgänge ABCD auf Low, also eine Null.  Bei der Taste F erscheint ein F 
also ein 15.
Das habe ich gleich als erstes ausprobiert, hatte mir eine Platine 
gebaut wo ich dann 4 LEDs an einem anderen Port angeschlossen hatte  die 
mir den Wert des eingehenden Signals gezeigt haben. Das hatte ich schon 
hinbekommen. lach.
Aber ich lerne jetzt viel besser als in der Schule, learning by doing 
ist viel besser. :-)

von René G. (gess12)


Lesenswert?

Kann ich das jetzt so machen????

int8_t lastKeyDown = NO_KEY;

int8_t keyPressed()
{
    int8_t downNow;

  if( !(PINA & (1<<PINA4)) )
  {

    downNow = NO_KEY;
  }

  else
  {
    if ( PINA & (1<<PINA4) )

    downNow = (PINA & 0x0f);
  }


   if( downNow != lastKeyDown )
  {
    lastKeyDown = downNow;
    return downNow;
  }
   else

   return NO_KEY;
}


In der Main frage ich dann den Wert von lastKeyDown ab um damit weiter 
zu arbeiten???

René

von Rumpelsuri (Gast)


Lesenswert?

Frag die Tasten mit einem Timer Tick, zB alle 10ms ab. Ein Tastatur Wert 
wird erst als stabil erachtet wenn er bei zwei sukzessiven Samples 
anliegt.

interrupt timer {
 timercame = 1;
 }

main loop {
 if timercame==1 {
 readkeys..

von René G. (gess12)


Lesenswert?

Jetzt habe ich den Fehler gefunden,
also deine Variante mit der Funktion läuft.
Allerdings waren die Auswirkungen auf das Problem gleich Null.
Liegt aber nicht an der Programmierung. Es liegt an der Hardware, bzw. 
an der Beschaltung. Das Übel ist der 922. Da hätte ich gleich drauf 
kommen müssen. Hardware ist schon eher mein Ding.
Der 922 speichert den Wert der zuletzt gedrückten Taste, über das DA 
Signal wird bekannt gegeben das eine Taste gedrückt worden ist. Da mein 
/OE auf Masse liegt, sind die Ausgänge immer offen und können gelesen 
werden.
Die Ausgänge am 922 ändern sich erst mit einer erneuten 
Tastaturbetätigung, bleibt diese aus, bleibt der letzte Wert 
gespeichert. Und genau das ist das Problem.
Wenn ich also in meiner Menüebene1 die 2 drücke, wird dieser Wert 
gespeichert im 922 und da ich in jeder anderen Menüebene die 2 verwende 
und den PinA abfrage erscheint da immer eine 2 und das Programm läuft 
weiter.

Es gibt aber dafür eine Lösung, ich muss den DA-Ausgang über einen 
Inverter auf den /OE legen, damit werden bei einem DA=Low, die Ausgänge 
des 922 auf Tristate gesetzt.
Es gibt aber auch noch eine Softwarevariante.

Vielen Dank erst einmal an Alle, ihr werdet wieder von mir lesen.
Ich muss noch zwei Schrittmotoren ansteuern. :-)

René

von Formatter (Gast)


Lesenswert?

René Geßner schrieb:
> Kann ich das jetzt so machen????

Hallo René,

schau mal auf die Formatierungsmöglichkeiten. Die stehen direkt über dem 
Feld, wo du René Geßner eingegeben hast:

Formatierung

[ c ] C-Code [ /c ]  für C-Listings

dann sieht das so schön aus wie bei Karl Heinz.

von Peter D. (peda)


Lesenswert?

René Geßner schrieb:
> Es gibt aber dafür eine Lösung, ich muss den DA-Ausgang über einen
> Inverter auf den /OE legen, damit werden bei einem DA=Low, die Ausgänge
> des 922 auf Tristate gesetzt.

Das löst reinweg garnichts.
Was sollen Dir flatternde Inputs bringen?

Du mußt das DA direkt im MC auswerten. d.h. Du brauchst 5 Eingänge, 
anders geht es nicht.

Mit 8 Pins (3 Pins mehr), könntest Du die Matrix einfach direkt 
auswerten und den sauteuren 922 sparen.
Du könntest dann sogar 2 Tasten gleichzeitig erkennen (z.B. eine als 
Umschalttaste).

von René G. (gess12)


Lesenswert?

@Formatter

Danke für den Hinweis, ich werde versuchen das umzusetzen.


René

von René G. (gess12)


Lesenswert?

@peda

Natürlich, da hast du völlig Recht. Das Da-Signal wird auch weiterhin im 
µC ausgewertet. Ich würde dieses Signal nur parallel dazu an einen 
Inverter geben, der dann den /OE Eingang auf Low zieht, also frei gibt, 
wenn das DA-Signal auf High ist. Und zum gleichen Zeitpunkt lese ich den 
Port aus, da ich ja das DA-Signal abfrage. Zu diesem Zeitpunkt ist also 
der Port am 922 offen und ich kann den gespeicherten Wert auslesen, 
sobald das DA-Signal auf Low ist geht der 922 in Tri-State bis zur 
nächsten Eingabe an der Tastatur.
Damit kann der gespeicherte Wert im 922 so lange bleiben, solange keine 
Taste gedrückt worden ist, ohne das mein Programm in den folgenden 
Schritten den Wert lesen kann.
Das ist zumindest meine Theorie, ob die richtig ist werde ich morgen 
sehen, muss mir erst noch einen Inverter kaufen bei Conrad.

Auch bei deinen anderen Hinweisen hast du Recht.
Da an diesen µC eine ganze Menge ran kommt, muss ich mit den Pins von 
den Ports sparen wo es nur geht. Auch das Display läuft nur im 
4Bit-Modus. 4 Pins gespart.

von Karl H. (kbuchegg)


Lesenswert?

René Geßner schrieb:

> In der Main frage ich dann den Wert von lastKeyDown ab um damit weiter
> zu arbeiten???

In der main fragst du den Returnwert der Funktion ab, um damit 
weiterzuarbeiten. Die Variable geht dich nichts an. Die braucht nur die 
Funktion um sich einen Zustand zu merken.


int main()
{

  ....


  while( 1 ) {

    key = keyPressed();

    if( key == KEY_0 )
      Led toggeln;

    else if( key == KEY_1 )
      ....

von Karl H. (kbuchegg)


Lesenswert?

René Geßner schrieb:

> Die Ausgänge am 922 ändern sich erst mit einer erneuten
> Tastaturbetätigung, bleibt diese aus, bleibt der letzte Wert
> gespeichert. Und genau das ist das Problem.

Und genau deswegen jage ich dich durch diese Funktion durch!
Die macht nämlich im Prinzip nichts anderes als eine Flankenerkennung. 
Sie stellt fest, ob sich am Zustand der Tasten etwas verändert(!) hat 
und sagt dir was sich verändert hat.

"verändert" ist aber bei einer Taste einfach:
entweder eine Taste ist gedrückt worden oder sie ist losgelassen worden.

Und genau das ist es, was du brauchst, wenn du eine Tastenbetätigung 
feststellen willst im Gegensatz zu "Ist die Taste jetzt gerade 
gedrückt". Dich interessiert nicht, ob die Taste jetzt gerade gedrückt 
IST. Dich interessiert, ob die Taste im Vergleich zum letzten mal 
nachsehen, in der Zwischenzeit gedrückt (oder losgelassen) wurde. Es ist 
diese Veränderung, in der die dich interessierende Information steckt.

Und um die festzustellen, merkt man sich den Zustand eben, bis zum 
nächsten nachsehen. Wenn vor einer halben Stunde das Licht im Haus 
gegenüber nicht gebrannt hat, jetzt aber schon, dann hat in der 
Zwischenzeit wer das Licht eingeschaltet. Brannte es vor einer halben 
Stunde bereits, dann hat niemand was mit dem Licht gemacht. Brannte das 
Licht vorhin und jetzt ist es aus, dann hat jemand das Licht 
ausgeschaltet.

Das 'last' in lastKeyDown steht für 'vorher, beim letzten mal 
nachsehen'. Im Gegensatz zum 'now' in 'nowDown', welches für 'genau 
jetzt' steht.

Im Falle des Lichts vom Haus nebenan, kann es dir nachtürlich passieren, 
dass du einmal ein/ausschalten nicht registrierst, wenn du nur alle 30 
Minuten nachsiehst. Aber wir haben hier einen µC. Für den ist es 
überhaupt kein Problem alle 0.001 Sekunden(!) nachzusehen. Und dann 
entgeht ihm nichts mehr. Denn so schnell kann ein Mensch einen Schalter 
gar nicht betätigen!

von Karl H. (kbuchegg)


Lesenswert?

An die anderen (speziell rumpelsuri).

Bitte versucht wenigstens dem Weg zu folgen, dem ich ihn aufgezeigt habe 
oder sagt wenigstens dazu, dass ihr einen komplett anderen Ansatz 
vorschlagt.

Der 922 macht ihm das entprellen (das ist so von ihm vorgegeben und 
dagegen kann ich jetzt erst mal nichts tun). Alles was fehlt ist, mit 
den vorhandenen Mitteln eine "Flankenerkennung" zu programmieren. Und da 
wollte ich ihn hintreiben. Es macht wenig Sinn, wenn da jetzt 
Querschläger kommen.

von Karl H. (kbuchegg)


Lesenswert?

> Damit kann der gespeicherte Wert im 922 so lange bleiben, solange
> keine Taste gedrückt worden ist, ohne das mein Programm in den
> folgenden Schritten den Wert lesen kann.

Dein µC kann Zehntausende Programmschritte ausführen, ehe du als 
Benutzer auch nur einmal Pieps sagen kannst.
Du fürchtest dich da jetztz vor etwas, was kein Problem ist.

von Karl H. (kbuchegg)


Lesenswert?

>   if( !(PINA & (1<<PINA4)) )
>  {
>
>    downNow = NO_KEY;
>  }
>
>  else
>  {
>    if ( PINA & (1<<PINA4) )
>
>    downNow = (PINA & 0x0f);
>  }


Spar dir den 2.ten Vergleich.
WEnn beim if der Pin nicht auf 1 war, dann MUSS er beim else auf 1 sein. 
Anders geht es nicht. Der Pin kann nur 2 Zustände haben. Entweder er ist 
0 (dann wird der erste Zweig vom if genommen), oder er ist 1 (dann wird 
das else genommen). Im else steht also schon fest, dass der Pin auf 1 
sein MUSS. Der kann gar nicht anders sein. Wenn die Auswahl auf 
"entweder - oder" lautet UND "enweder" nicht richtig ist, dann muss 
"oder" zwangsläufig richtig sein.


   if( !(PINA & (1<<PINA4)) )
   {
     downNow = NO_KEY;
   }
   else
   {
     downNow = (PINA & 0x0f);
   }

von René G. (gess12)


Angehängte Dateien:

Lesenswert?

@kbuchegg

das mit dem else ist völlig korrekt, da hast du Recht. Habe es auch 
schon geändert.

Also, um sicher zu sein, die Funktion die du mir gegeben hast ist 
sozusagen die Flankenauswertung? Und wenn ich alles richtig gemacht habe 
sollte diese dann funktionieren?

Wenn ich aber das gesamte Projekt auf den µC lade verhält er sich wie 
schon beschrieben. Eine 2 gedrückt in Menüebene1 und er rauscht durch 
bis zur letzten Menüebene.
Deswegen die Idee dann mit dem Inverter.

Ich habe mal das gesamte Projekt als Datei angehangen, wie auch den 
Schaltplan von meiner Platine die ich zum ausprobieren benutze.

Vielleicht siehst du noch etwas was falsch läuft.

Danke schon mal.
René

von Karl H. (kbuchegg)


Lesenswert?

René Geßner schrieb:
> @kbuchegg
>
> das mit dem else ist völlig korrekt, da hast du Recht. Habe es auch
> schon geändert.
>
> Also, um sicher zu sein, die Funktion die du mir gegeben hast ist
> sozusagen die Flankenauswertung? Und wenn ich alles richtig gemacht habe
> sollte diese dann funktionieren?

Ganz genau.
Halte aber immer auch im Hinterkopf, dass ich deine Hardware nicht da 
habe. D.h. ich muss "blind" programmieren ohne das bei mir testen zu 
können.

von Karl H. (kbuchegg)


Lesenswert?

>         keyPressed();
>
>
>       if (lastKeyDown == KEY_1 || lastKeyDown == KEY_2 || lastKeyDown == KEY_3)


Nein!
Lass lastKeyDown in Ruhe!


  key = keyPressed();

  if( key == KEY_1 || key == KEY_2 || key = KEY_3 )


Für den aufrufer ist nur und ausschliesslich der Returnwert von 
keyPressed interessant! Alles andere hat ihn nicht zu interessieren.

von Karl H. (kbuchegg)


Lesenswert?

Und speck das mal für deine ersten Tests ab.
Da main() muss nicht so kompliziert sein. Je mehr Code du bei deinen 
ersten Tests hast, desto mehr Fehler kannst du machen.

1
void MainMenu()
2
{
3
  int8_t key = NO_KEY;
4
  uint8_t selectedOption = 0;
5
6
  lcd_setcursor( 0, 1 );
7
  lcd_string("  Option 1"); 
8
9
  lcd_setcursor( 0, 2 );
10
  lcd_string("  Option 2"); 
11
12
  lcd_setcursor( 0, 3 );
13
  lcd_string("  Option 3"); 
14
15
  lcd_setcursor( 0, 4 );
16
  lcd_string("  Option 4");
17
18
  lcd_setcursor( 0, selectedOption + 1 );
19
  lcd_string( ">" );
20
21
  do {
22
    key = keyPressed();
23
24
    if( key != NO_KEY )
25
    {
26
      lcd_setcursor( 0, selectedOption + 1 );
27
      lcd_string( " " );
28
29
      if( key == KEY_1 )
30
      {
31
        if( selectedOption < 3 )
32
          selectedOption++;
33
      }
34
35
      else if( key == KEY_2 )
36
      {
37
        if( selectedOption > 0 )
38
          selectedOption--;
39
      }
40
41
      lcd_setcursor( 0, selectedOption + 1 );
42
      lcd_string( ">" );
43
    }
44
  } while( key != KEY_9 );
45
46
47
int main(void)
48
{
49
  int8_t key;
50
51
  DDRA = 0xE0;
52
53
  lcd_init();
54
 
55
  while(1)
56
  {
57
    lcd_clear();
58
    lcd_lcd_setcursor( 0, 1 );
59
    lcd_string( "Press key 1" );
60
61
    do {
62
      key = keyPressed();
63
    } while( key != KEY_1 );
64
65
    MainMenu();
66
  }
67
}

Probier das mal aus.
Im 'Menü' kannst du mit den Tasten '1' bzw. '2' den Cursor jeweils einen 
Menüpunkt weiterschieben. Das muss auch wirklich EIN Menüpunkt sein. 
Nicht 2, nicht 3, nicht einmal auf die Taste drücken und der Cursor 
rauscht zum letzten durch. Nein, 1 Tastendruck -> 1 Cursorbewegung.

Mit der Taste 9, geht es aus dem Menü wieder raus.


Das genügt fürs erste um zu sehen, ob die Tastenerkennung und Auswertung 
funktioniert.

von René G. (gess12)


Lesenswert?

jetzt, hat er´s
Jetzt funktioniert es, eine schwere Geburt. Lach.
Nun muss ich nur noch programmieren das ich in den Menüebenen auch 
zurück springen kann, aber das ist ja ähnlich jetzt, muss nur andere 
tastenwerte abfragen.

Danke, Danke, Danke

René

von René G. (gess12)


Lesenswert?

Also ich habe noch einen Fehler gefunden in den Case Strukturen, nachdem 
ich den behoben hatte läuft es.

Nun komm ich zu deinem letzten Vorschlag.
Das wäre nämlich meine nächste Frage gewesen, wie man das vereinfachen 
könnte.

René

von Karl H. (kbuchegg)


Lesenswert?

René Geßner schrieb:
> Also ich habe noch einen Fehler gefunden in den Case Strukturen, nachdem
> ich den behoben hatte läuft es.
>
> Nun komm ich zu deinem letzten Vorschlag.
> Das wäre nämlich meine nächste Frage gewesen, wie man das vereinfachen
> könnte.
>

Da gibts jetzt mehrere Möglichkeiten.

Jedes Menü, auch die Submenüs, ist eine eigen FUnktion. Ganz ähnlich der 
Funktion, die ich 2 Postings vorher geschrieben habe.

Jede Funktion folgt dem Muster

void DasMenu()
{
  int8_t Taste = KEY_1;  // damit beim ersten mal der Text 
hingeschrieben wird

  do {

    if( Taste != NO_KEY )
    {
      LCD löschen
      Menüpunkte hinschreiben
    }

    Taste holen

    if( Taste == 1 )
      Aktion für Taste 1 ausführen

    else if( Taste == 2 )
      Aktion für Taste 2 ausführen

    else if( Taste == 3 )
      Aktion für Taste 3 ausführen

    ...
  } while( Taste nicht die Taste für 'Fertig' )
}

So eine 'Aktion', die bei Betätigung einer Taste ausgeführt wird, könnte 
zb der Aufruf einer Funktion sein, die ihrerseits auch wieder ein Menü 
realisiert.


Das kann man zwar auch noch viel universeller regeln, aber fürs erste 
ist das eine einfache Möglichkeit.

von René G. (gess12)


Lesenswert?

jetzt habe ich deinen letzten Vorschlag in den µC programmiert, also das 
funktioniert super, da bin ich echt neidisch das ich das noch nicht 
kann.

Allerdings gibt es einen kleinen Schönheitsfehler.
Im Display wird "Option11" angezeigt.
Da versuche ich gerade den Fehler zu finden.

René

von René G. (gess12)


Lesenswert?

Also deine Menüstruktur gefällt mir sehr gut, auch das mit den 
Funktionen.
Ich lese mir das jetzt alles in Ruhe durch, damit ich auch voll verstehe 
was da programmiert ist. Denn nur copy paste möchte ich nicht machen, 
ich will es verstehen und weiter anwenden. Aber du bist mir eine sehr 
gute Hilfe und Lehrer zugleich. Danke noch einmal.

René

von René G. (gess12)


Lesenswert?

Fehler gefunden, die zweite 1 war noch vom Menüstart " Press Key 1"
habe noch ein lcd_clear in der MainMenu eingefügt und schon funktioniert 
es.

René

von René G. (gess12)


Lesenswert?

Hallo kbuchegg,

ich brauche wieder deine Hilfe.
Das Menü habe ich jetzt soweit fertig, bzw. weiß wie ich es erweitern 
kann.
Ich kann dir gern einmal das File schicken, damit du drüber schauen 
kannst ob es jetzt vernünftig strukturiert ist.

Im Moment versuche ich gerade eine Variable in einen String umzuwandeln, 
da ich den Wert der Variablen gern im LCD anzeigen möchte.
Ich habe auch den Link gefunden den du irgend wann einmal gepostet 
hattest. Bin auch so vorgegangen wie beschrieben mit itoa. Aber 
angezeigt wird dann das Wort nicht der Wert.

hier mal mein Versuch.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include "lcd-routines.h"
4
#include <stdlib.h> 
5
6
char Buffer[10];
7
8
void .... ()
9
{
10
int8_t Taste = KEY_1;
11
12
 lcd_setcursor( 3, 2 );
13
    itoa( Taste, Buffer, 2 );
14
      lcd_string("Buffer");
15
16
}

Was mache ich noch falsch???

Die Augabe im LCD ist "Buffer"

Danke dir schon mal.

René

von Fabian O. (xfr)


Lesenswert?

Es muss heißen:
1
lcd_string(Buffer);

Du willst ja nicht die Zeichenkette "Buffer" ausgeben sondern den Inhalt 
der Variable Buffer.

von René G. (gess12)


Lesenswert?

Danke, ich glaube langsam aber sicher habe ich einen Programmierkoller.

von René G. (gess12)


Lesenswert?

gibt es eine Möglichkeit aus einem dualen Wert das dazugehörige 
ASCII-Zeichen auf dem LCD auszugeben???

In meinem Fall möchte ich das e auf dem LCD ausgeben, das entspricht 
0x65 bzw. 01100101.

Einen numerischen Wert als String auszugeben geht jetzt. :-)

von Electronics'nStuff (Gast)


Lesenswert?

René Geßner schrieb:
> In meinem Fall möchte ich das e auf dem LCD ausgeben, das entspricht
> 0x65 bzw. 01100101.

lcd_data(0x65);

Sofern ich dich richtig verstanden habe.

von René G. (gess12)


Lesenswert?

ich habe mich falsch ausgedrückt.
Mit lcd-data hatte ich es auch versucht, hatte nur nicht geklappt, da 
ich den Wert wieder aus einer Variablen nehmen möchte. Ich habe aber 
einen Fehler in der Zuweisung der Variablen gemacht. Wenn ich der 
Variablen anstatt einer 1 eine 0x65 zuweise und dann mit 
lcd_data(Variable) auf das LCD schreibe dann kommt auch das e. Wieder 
ein kleines Problem weniger. Ich lerne mehr und mehr.

Danke

von Fabian O. (xfr)


Lesenswert?

Wenn Du ein 'e' auf dem Display ausgeben willst, dann solltest Du das 
direkt hinschreiben:
1
lcd_data('e');

Geht mit einer Variable natürlich auch:
1
char zeichen = 'e';
2
lcd_data(zeichen);

Ich würde Dir aber erstmal dringend dazu raten, ein C-Buch oder 
wenigstens Tutorial durchzuarbeiten. Ohne diese absoluten Grundlagen 
kann man kein vernünftiges Programm entwickeln. Mir würden die 
unzähligen "Trial & Error"-Zyklen auch keinen Spaß machen ...

von René G. (gess12)


Lesenswert?

Bevor ich bei dir anfrage versuche ich natürlich mir das entsprechende 
Wissen im Tutorial zu holen. Nur sehr oft kommt man da nicht wirklich 
weiter, denn oft trifft es nur zum Teil das eigene Problem.
Ich muss auch sagen das ich eher der Autodidakt bin, ich lerne vom 
zusehen.
Ich bin auch nicht mehr der Jüngste. ;-)

Aber ich versuche immer zuerst mir alles durchzulesen.
Mein Problem ist die Zeit. Ich habe eigentlich nur diese Semesterferien, 
4 Wochen, um mein Projekt auf die Beine zu bekommen. Danach geht die 
Technikerschule weiter und die Abschlussprüfungen stehen vor der Tür. 
Natürlich muss dann auch das Projekt fertig sein. Und bis dahin ist es 
noch ein weiter Weg für mich. Eigentlich sind wir auch zu zweit an 
diesem Projekt, aber da bin ich etwas im Stich gelassen worden. So muss 
ich jetzt alles vorerst allein machen und programmieren war noch nie 
meine große Stärke.
Ich bin eher für die Hardware. Es müssen Leiterplatten gefertigt werden, 
kein Problem für mich, es müssen Schrittmotoren angeschlossen werden, 
bekomme ich auch hin. Und und und..... Viele Dinge sind noch zu machen, 
aber eben auch die Programmierung. Und die Zeit rennt....

Und manchmal sind es eben nur die ganz kleinen Dinge die mich aufhalten.
siehe ("Buffer") anstatt (Buffer).
Selbst das hatte ich mir erlesen und ging trotzdem schief. :-)

René

von Karl H. (kbuchegg)


Lesenswert?

René Geßner schrieb:
> Bevor ich bei dir anfrage versuche ich natürlich mir das entsprechende
> Wissen im Tutorial zu holen.

Da wirst du auch in unseren Tutorials hier nicht viel dazu finden.
Umgang mit Variablen sind eigentlich C-Grundlagen und unsere Tutorien 
hier gehen eigentlich davon aus, dass ein gewisser Grundstock an Wissen 
vorhanden ist und konzentrieren sich auf die Spezialitäten, die in der 
µC-Programmierung dann nochmal dazukommen.

Von einem Tutorial über das Lösen von Differentialgleichungen, darfst du 
dir nicht erwarten, dass es dir Gleichungen umformen beibringt. Das wird 
dort schon vorausgesetzt.

> Ich muss auch sagen das ich eher der Autodidakt bin, ich lerne vom
> zusehen.

Genau deswegen wäre es gut, erst mal den µC beiseite zu legen, und auf 
dem PC das erste Drittel eines normalen C-Buchs durchzumachen. Denn das 
sind genau die Dinge, die jeder C Programmierer als 
Minimum-'Ausstattung' eigentlich beherrschen sollte. Dass dann am µC 
noch die Spezialitäten der µC-Programmierung dazu kommen, macht die 
Sache nicht einfacher.

von René G. (gess12)


Lesenswert?

Ich kann dir wirklich nur zustimmen.
Die C-Grundlagen sind eigentlich gelegt worden. Wenn man sie aber nicht 
ständig anwendet gehen sie schnell wieder verloren. Zumal ich im Moment 
mit einer Menge neuem Wissen zugeballert werde was nicht nur µC angeht. 
Da ist Steuerungstechnik, Reglungstechnik und alle wollen die 
wichtigsten sein. :-)

Aber wie schon gesagt, ich verbringe auch viel Zeit damit mir Wissen 
wieder anzueignen durch lesen von verschiedenen Medien.

René

von René G. (gess12)


Lesenswert?

@kbuchegg

Hallo,

könntest du mir wieder einmal unter die Arme greifen?
Ich habe jetzt mein Menü soweit fertig. Eine Sache muss ich aber noch 
erledigen und mir fällt kein richtiger Weg dazu ein.
Ich versuche einmal zu schildern worum es geht.

Ich bin im Menüpunkt Belichtungszeiten, die Zeiten sind Variablen in 
einem Array zusammengefasst jeweils. Die ausgewählte Option bestimmt den 
Stellenwert im Array. Sieht auf dem LCD ungefähr so aus:

        Belichtungszeit
>1:  0:00 min
 2:  0:00 min
 3:  0:00 min

Da ja im Moment alle Zeiten auf Null stehen müssen diese nun veränderbar 
sein. Sollten schon gespeicherte Werte vorliegen soll es mit Enter 
weitergehen. Wenn eine Zeit aber verändert werden soll, drücke ich die > 
Taste und führe damit eine andere Funktion aus.

Sieht dann ungefähr so aus:

   Aendern Zeit x   // x steht für den Zeilenwert des Cursors aus der
                    // aufrufenden Funktion

   0:00 min

wobei die Stelle der Minuten blinkt, mit den Cursortasten < > kann man 
jetzt die drei Stellen anwählen. Entsprechend des Wertes der Spalte vom 
Cursor möchte ich jetzt ein von der Tastatur eingebener Wert den 
entsprechenden Variablen zuweisen und der eingebene Wert soll im LCD an 
der blinkenden Stelle erscheinen. Aber da scheitert es dann bei mir.


hier mal der Programmcode der beiden Menüs:
1
/*
2
*************************************************************
3
********Menü Belichtungszeit*********************************
4
*************************************************************
5
*/
6
7
uint8_t Belichtungszeit()
8
{
9
  int8_t key = NO_KEY;
10
   uint8_t selectedOption=1;
11
  
12
  lcd_clear();
13
  
14
  lcd_setcursor( 3, 1 );
15
    lcd_string("Belichtungszeit");
16
  lcd_setcursor( 0, 2 );
17
    lcd_string(" 1:   :   min");
18
  lcd_setcursor( 5, 2);
19
  lcd_data ( MIN[1]+48);
20
  lcd_setcursor( 7, 2);
21
  lcd_data ( SEK[1]+48);
22
  lcd_setcursor( 8, 2);
23
  lcd_data ( SEK1[1]+48);
24
  lcd_setcursor( 0, 3 );
25
    lcd_string(" 2:   :   min");
26
  lcd_setcursor( 5, 3);
27
  lcd_data ( MIN[2]+48);
28
  lcd_setcursor( 7, 3);
29
  lcd_data ( SEK[2]+48);
30
  lcd_setcursor( 8, 3);
31
  lcd_data ( SEK1[2]+48);
32
  lcd_setcursor( 0, 4 );
33
    lcd_string(" 3:   :   min");
34
  lcd_setcursor( 5, 4);
35
  lcd_data ( MIN[3]+48);
36
  lcd_setcursor( 7, 4);
37
  lcd_data ( SEK[3]+48);
38
  lcd_setcursor( 8, 4);
39
  lcd_data ( SEK1[3]+48);
40
    lcd_setcursor( 0, selectedOption + 1 );
41
    lcd_string( ">" );
42
  
43
 do {
44
     key = keyPressed();
45
46
    if( key != NO_KEY )
47
     {
48
      lcd_setcursor( 0, selectedOption + 1 );
49
      lcd_string( " " );
50
51
      if( key == KEY_11 )
52
      {
53
        if( selectedOption < 3 )
54
          selectedOption++;
55
      
56
      }
57
58
      else if( key == KEY_10 )
59
      {
60
        if( selectedOption > 1 )
61
          selectedOption--;
62
     
63
      }
64
65
      lcd_setcursor( 0, selectedOption + 1 );
66
      lcd_string( ">" );
67
     }
68
   
69
    } 
70
  
71
  while( key != KEY_14 && key != KEY_13 );
72
  
73
74
      if (key == KEY_14)
75
    {
76
     MainMenu();
77
    }
78
79
    if (key == KEY_13)
80
    {
81
       
82
     Zeit(selectedOption);
83
  
84
     }
85
  return 0;
86
}

Ich danke dir schon mal.
René

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.