Code für Taschenrechner

Hallo,

habe mal wieder ein kleines Problem - diesmal aber mit einem ganz anderen Projekt: einem Taschenrechner. Ich habe ihn mithilfe eines LCDs, eines Funduino Mega und ein paar Printtastern gebaut.
Den Code habe ich mir folgendermaßen überlegt:

Der Arduino startet normal im Setup-Teil. Hier werden das LCD initialisiert und ein kleiner Willkommensgruß ausgegeben. Am Ende des Setup-Teils wird in die Funktion ‘Startmenu’ übergeben.

Diese Funktion setzt zu aller erst die Variable ‘mode’ (Modus, in dem sich der Rechner befindet: Hauptmenü, Rechnen,…) auf den Wert 1. Dann wird das Hauptmenü optisch auf dem LCD aufgebaut.
Jetzt folgt:

if((mode==1) && (pressed_key=='1')) Rechner();
  else Taster();

(“Wenn du dich im Startmenü befindest und soeben Taster 1 gedrückt wurde, wechsle in die Funktion ‘Rechner’!”)

Da die gedrückte Taste eben nicht 1 ist bzw. noch gar keine gedrückt wurde, wechselt der Arduino in die Taster-Funktion, denn erst in dieser werden die Inputs ausgelesen. Mein Taschenrechner hat 16 Tasten, die über Widerstände an zwei analoge Eingänge angeschlossen sind. Das Auslesen geschieht mit dem Speichern des analogRead-Wertes in die Variablen a14_val und a15_val. Je nach Wert dieser Variablen wiederum wird die Variable ‘pressed_key’ z.B. auf 1 gesetzt.
Am Ende der Taster-Funktion wird in die Funktion gewechselt, von der aus die Taster-Funktion aufgerufen wurde - im Fall des ersten Anschaltens wieder ins Startmenu. Diese Endlosschleife wird solange wiederholt, bis eine Taste gerückt wurde und in einen anderen Modus gewechselt wird.

Somit braucht es den Loop-Teil eigentlich gar nicht. Ich habe ihn aber trotzdem als Fehlermeldung ‘missbraucht’, falls irgendwann mal keine Funktion definiert sein sollte und der Arduino in den Loop-Teil abrutscht.

Ich hoffe, das ist verständlich geschrieben. Meine erste Frage zum obigen Text: Ist diese Herangehensweise überhaupt sinnvoll?

Soweit der Plan, aber jetzt zu meinem eigentlichen Problem: Der Arduino stürzt immer nach einer gewissen Zeit (wenige Sekunden bis 1,5 Minuten) ab und startet neu. Komischerweise wird die ‘Überlebenszeit’ von 1min40s auf nur 10s verkürzt, wenn ich ihm befehle, die Werte der analogen Inputs an den PC zu senden.
Auch rutscht er immer in den Loop-Teil ab, sobald die Rechner-Funktion aufgerufen wird.

Hier mal der Code:

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,20,4);

// Variablen für analoge Eingänge
short a14_val, a15_val;

char pressed_key, pressed_key_m;

bool shift;
// aktueller Modus - 1: Startmenü; 2: Rechner; 3: ...
byte mode, mode_m;
// Cursor: Zeichen / Zeile
byte Cursor[1];
short C_interval = 500;
bool Cursor_p = true, lcd_p;
long C_blnk_start;

void Taster() {
  // wird im Wechsel zum aktuellen Modus aufgerufen
  // Auslesen der Taster und seriellen Schnittstelle
  a14_val = analogRead(A14);
  a15_val = analogRead(A15);
  pressed_key = 'n';
  if(Serial.available() > 0)  {
    pressed_key = Serial.read();
    if(pressed_key == 's') shift = true;
  }
  // Zahlen
  if(a14_val == 49) shift = true;
  if(a15_val == 157) pressed_key = '0';
  if((a15_val > 140) && (a15_val < 150)) pressed_key = '1';
  if(a15_val == 66) pressed_key = '2';
  if(a15_val == 49) pressed_key = '3';
  if(a15_val == 129) pressed_key = '4';
  if(a15_val == 82) pressed_key = '5';
  if(a15_val == 31) pressed_key = '6';
  if(a15_val == 113) pressed_key = '7';
  if(a15_val == 98) pressed_key = '8';
  if(a15_val == 13) pressed_key = '9';
  // Zeichen
  if(a14_val == 98) pressed_key = 'q';  // Quadrat
    if(shift && (pressed_key == 'q')) pressed_key = 'w'; // Wurzel
  if(a14_val == 13) pressed_key = 'p';  // Pi
    if(shift && (pressed_key == 'p')) pressed_key = 'l'; // dekadischer Logarithmus
  if(a14_val == 82) pressed_key = 'a';  // Klammer auf
    if(shift && (pressed_key == 'a')) pressed_key = 'n'; // NOCH OFFEN
  if(a14_val == 32) pressed_key = 'z';  // Klammer zu
    if(shift && (pressed_key == 'z')) pressed_key = 'n'; // NOCH OFFEN
  if(a14_val == 66) pressed_key = 'e';  // Exe / Enter
    if(shift && (pressed_key == 'e')) pressed_key = 'b'; // back / exit
  if(pressed_key != 'n') shift = false;

  // ändert sich der Modus, wird der Inhalt des LCD gelöscht
  if(mode != mode_m) lcd.clear();
  mode_m = mode;
  // Zurück in die Funktion, von der aus gestartet wurde
  if(mode == 1) Startmenu();
  if(mode == 2) Rechner();

  // ansonsten rutscht Arduino automatisch in Loop-Teil
}
void Startmenu()  {
  mode = 1;
  lcd.setCursor(8,0);
  lcd.print("Men\365");
  lcd.setCursor(0,1);
  lcd.print("1 Rechner");
  lcd.setCursor(0,2);
  lcd.print("2 Programme");
  lcd.setCursor(0,3);
  lcd.print("3 Einstellungen");
  if((mode==1) && (pressed_key=='1')) Rechner();
  Taster(); 
}
void Rechner()  {
  mode = 2;
  lcd.setCursor(Cursor[0],Cursor[1]);
  // Blinken des Cursors
  if(millis() - C_blnk_start > C_interval)  {
    C_blnk_start = millis();
    if(Cursor_p == true)  lcd.print("|"), Cursor_p = false;
    else lcd.print(" "), Cursor_p = true;
  }
  // Rechner-Hauptteil
  if((pressed_key != pressed_key_m) or Serial.available() > 0) lcd_p = false;
  if((48 <= pressed_key <= 57) and lcd_p == false) lcd.print(pressed_key);

  if(lcd_p == false) {
    // Cursor um 1 nach rechts verschieben
    Cursor[0]++;
    // wenn Zeile voll ist:
    if(Cursor[0] == 20) Cursor[0] = 0, Cursor[1]++;
    // wenn LCD voll ist:
    if(Cursor[0] == 20 and Cursor[1] == 3) Cursor[0] = 0, Cursor[1] = 0, lcd.clear();
    lcd_p = true;
  }

  if(pressed_key=='b') Startmenu();
  Taster();
}
void setup() {
  Serial.begin(9600);
  lcd.init();                      // initialize the lcd 
  lcd.init();
  lcd.backlight();
  lcd.setCursor(3,1);
  lcd.print("Taschenrechner");
  lcd.setCursor(8,2);
  lcd.print("V1.0");
  digitalWrite(A15, HIGH);
  digitalWrite(A14, HIGH);
  delay(2500);
  lcd.clear();
  delay(100);
  Startmenu();
}

// Loop wird 'missbraucht' als Fehlererkennung
void loop() {
  // landet der Arduino im Loop-Teil -> unbehandelte Ausnahme
  lcd.clear();
  lcd.setCursor(7,0);
  lcd.print("Uppsi!");
  lcd.setCursor(0,1);
  lcd.print(":(");
  lcd.setCursor(0,3);
  lcd.print("unbekannter Fehler");
  delay(3000);
  lcd.clear();
  setup();
}

HiHo,

dein Problem ist,
dass du ein Konstruk gebaut hast, was in unendlicher Rekursion endet.

Du springst in Startmenü
von da in Rechner
von da wieder in Taster
dort wieder in Rechner
von da wieder in Taster
usw.

Bei jedem Funktionsaufruf
wird im Speicher ein neuer Stack angelegt der allerdings nur dann wieder abgebaut wird
wenn die Funktion wieder verlassen wird.

Da bei dir die Funktionen sich am Ende immer wieder gegenseitig aufrufen und dadurch niemals Verlassen werden wird jedes mal ein neuer Stack angelegt bis dein RAM voll läuft bzw. der Stack in den Heap crasht.

Resultat ist dann das dein gesammtes Programm abschmiert.

Für dein Vorhaben empfiehlt sich ein endlicher Automat.
Such hier im Forum mal nach "endlicher Automat".

Hier gibt es iwo ein Tutorial
weiß nur gerade nimmer von wem das ist :smiley:

In Startmenu() rufst du Taster() auf.
Und in Taster() dann Startmenu()

Das ist eine klassische indirekte Rekursion.

Ruckizucki läuft dir so der Stapel voll.
Und dann kracht es.

Das Tutorial:

Was ist dann der Unterschied, wenn ich einfach nur die Loop-Funktion durchlaufen lasse? Ist auch unendlich.

Techniker89:
Was ist dann der Unterschied, wenn ich einfach nur die Loop-Funktion durchlaufen lasse? Ist auch unendlich.

Unsinn!
Es gibt einen deutlichen Unterschied, zwischen einer infiniten Rekursion und einer infiniten Schleife.

Solltest du den nicht kennen, dann bitte die Nase in Wikipedia oder ein gutes Buch stecken.

Das Problem liegt - wie oben von mir beschrieben - daran,

dass bei einem Funktionsaufruf ein neuer Funktionsstack im RAM anglegt wird
mit Rücksprung-Addresse, Parametern, Return-werten usw.

Dieser Stack wird nur dann freigegeben wenn die Funktion wieder verlassen wird.
Das ist aber nicht der Fall, wenn du innerhalb einer Funktion eine weitere aufrufst
sondern erst dann, wenn wieder in die aufrufende Funktion zurück gesprungen wird und die Funktion zu ende ist.

Bei denem Code ist dass der Fall, wenn mode weder 1 noch 2 ist.
Ansonsten hast du unendliche indirekte Rekursion.

Bedeutet, dass ohne jemals zurück zu springen jedes mal eine Funktion aufgerufen wird.
Das bedeutet, dass jedes mal ein neuer Stack angelegt wird.
Irgendwann wächst dir der Stackspeicher in den Heap rein und dann wars das.

Google mal nach Stack, Heap, indirekter Rekursion, unendliche Rekursion und Funktionsaufruf

Edit:

Eine Schleife ist kein rekursiver Funktionsaufruf sondern ein einfaches "zurück an den Anfang der Schleife Springen"

Danke für eure Antworten!

Aber irgendwie kriege ich es nicht hin, das Modell des endlichen Automaten auf meinen Taschenrechner zu übertragen.

Aber irgendwie kriege ich es nicht hin, das Modell des endlichen Automaten auf meinen Taschenrechner zu übertragen.

40 Minuten, für das allumfassende Verständnis für endliche Automaten ist arg knapp.
Rechne mit Stunden, wenn nicht sogar mit Tagen.

Ja, ich glaube auch, dass du dir da ruhig etwas mehr Frustrationstoleranz zulegen könntest!
Zumindest für das erste Mal.
Denn die Denke muss umgestellt werden.

Denn hast du das einmal verstanden, dann kannst du nur noch so denken.

Ein endlicher Automat wechselt von einem Zustand in einen anderen, und führt nur bei einem solchen Übergang irgend etwas aus. Dabei wird auch ein Übergang in den selben Zustand als Übergang betrachtet. Das passiert am besten in einem switch Statement, das regelmäßig (in loop) durchlaufen wird. In jedem case wird geprüft, ob ein Ereignis vorliegt, das in genau diesem Zustand einen Übergang auslösen soll. Falls ja, wird die zugehörige Aktion ausgeführt und der neue Zustand gespeichert. Dann ist dieser Schritt beendet, und es geht weiter beim nächsten Durchlauf durch loop().

Hast Du schon mal ein Zustandsdiagramm gezeichnet, mit allen Ereignissen und entsprechenden Übergängen zwischen den Zuständen? Oder kannst Du zuindest die Zustände aufzählen, in denen sich Dein Automat befinden kann, und worauf er in jedem dieser Zustände reagieren soll?

Du solltest erst mal Deine Tastatur prüfen, ob die alle Tasten richtig erkennt, und nicht auch noch irgendwas anderes zwischendrin. Ich bezweifle stark, daß sich 16 Tasten an einem einzigen Analogeingang überhaupt richtig lesen und entprellen lassen. Bevor das nicht sichergestellt ist, brauchst Du garnicht erst weiterzumachen.

Hallo,

ich habe den Rechner nun noch einmal von Grund auf neu programmiert. Wenn ich irgendwas absolut nicht leiden kann, dann ist es, wenn ein Code manchmal funktioniert und manchmal nicht. Dann weiß ich nie, wo ich suchen soll. Kann mir vielleicht jemand helfen? Der Code ist sicher nicht die beste Lösung und auch nicht sehr übersichtlich (Sorry!), aber ich bin ja auch noch Anfänger…

Übersetzung meiner Variablenbezeichnung:
_a → aktiv
_p → printed
_m → zwischengespeichert
Cx, Cy → Cursor X und Y

#include <LiquidCrystal_I2C.h>
#include <math.h>
LiquidCrystal_I2C lcd(0x27,20,4);


// Taster
short A14_val, A15_val, pin_a = 1023, pin_a_m = 1023;
bool shift, lcd_p = true;

// Modi
enum Modi {Rechner, Quiz, Menu};
byte Modus = Rechner, Cx, Cy;
byte Modus_m;

// Setzen des (optischen) Cursors
bool Cursor_print;
unsigned long Cursor_Start;
const short Cursor_intervall = 500;

// Rechenoperationen
enum Zeichen {Null,eins,zwei,drei,vier,fuenf,sechs,sieben,acht,neun,nix,hoch,rad,lg,sinus,kosinus,tangens,asinus,akosinus,atangens,ac,e,pi,exe,back,plus,minus,mal,durch,ans,Platzhalter};
Zeichen pressed_key = nix, pressed_key_m;
// einzelne Ziffern, die eingegeben werden (max. 10)
byte input[10];

void setup() {
  lcd.init();
  lcd.init();
  lcd.backlight();
  Serial.begin(9600);
  lcd.setCursor(3,1);
  lcd.print("Taschenrechner");
  lcd.setCursor(8,2);
  lcd.print("V1.3");
  digitalWrite(A15, HIGH);
  digitalWrite(A14, HIGH);
  pinMode(0,INPUT_PULLUP);
  delay(2500);
  lcd.clear();
  delay(100);
  // alle Variablen zurücksetzen
    pressed_key = nix;
    pressed_key_m = pressed_key;
    pin_a = 1023, pin_a_m = 1023;
    Cx = 0, Cy = 0;
    lcd_p = true, shift = false;
    Modus = Rechner, Modus_m = Rechner;
}

void loop() {
  // Auslesen der Tasterwerte, Weiterleiten an den PC
  A14_val = analogRead(A14);
  A15_val = analogRead(A15);
  Serial.print("A14:  "), Serial.print(A14_val), Serial.print("                    A15:"), Serial.println(A15_val);

    // Ermöglichung von Schnapszahlen
    if(A14_val < A15_val) pin_a = A14_val;
    if(A14_val > A15_val) pin_a = A15_val;
    // geheime Zurücksetz-Funktion, wenn Tasten '0' und 'exe' gleichzeitig gedrückt werden
    if((A14_val == 49) && (A15_val == 156)) lcd.clear(), lcd.setCursor(0,0), lcd.print(":("), lcd.setCursor(5,1), lcd.print("Windows 95"), lcd.setCursor(1,2), lcd.print("wird neu gestartet"), delay(3000), setup();
  // Shift setzen
    if(digitalRead(0) == LOW) shift = true;
      else shift = false;

// Umwandeln der Stromstärkewerte am Pin in gedrückten Taster
    // Nummernfeld
    if(A15_val == 156 && shift == false) pressed_key = Null;
      if(shift && A15_val == 156) pressed_key = ans; // Answer
    if(A15_val == 142 && shift == false) pressed_key = eins;
      if(shift && A15_val == 142) pressed_key = asinus; // Arkussinus
    if(A15_val == 66 && shift == false) pressed_key = zwei;
      if(shift && A15_val == 66) pressed_key = akosinus; // Arkuskosinus
    if(A15_val == 49 && shift == false) pressed_key = drei;
      if(shift && A15_val == 49) pressed_key = atangens; // Arkustangens
    if(A15_val == 128 && shift == false) pressed_key = vier;
      if(shift && A15_val == 128) pressed_key = sinus; // Sinus
    if(A15_val == 82 && shift == false) pressed_key = fuenf;
      if(shift && A15_val == 82) pressed_key = kosinus; // Kosinus
    if(A15_val == 31 && shift == false) pressed_key = sechs;
      if(shift && A15_val == 31) pressed_key = tangens; // Tangens
    if(A15_val == 113 && shift == false) pressed_key = sieben;
      if(shift && A15_val == 113) pressed_key = hoch; // x^y
    if(A15_val == 98 && shift == false) pressed_key = acht;
      if(shift && A15_val == 98) pressed_key = rad; // y^(1/x)
    if(A15_val == 13 && shift == false) pressed_key = neun;
      if(shift && A15_val == 13) pressed_key = lg; // dekadischer Logarithmus

    // Zeichen
    if(A14_val == 82 && shift == false) pressed_key = plus;  // plus
      if(shift && A14_val == 82) pressed_key = ac; // AC
    if(A14_val == 13 && shift == false) pressed_key = minus;  // minus
      if(shift && A14_val == 13) pressed_key = Platzhalter; // nicht belegt
    if(A14_val == 66 && shift == false) pressed_key = mal;  // mal
      if(shift && A14_val == 66) pressed_key = e; // Eulersche Zahl
    if(A14_val == 31 && shift == false) pressed_key = durch;  // geteilt durch
      if(shift && A14_val == 31) pressed_key = pi; // Pi
    if(A14_val == 49 && shift == false) pressed_key = exe;  // Exe / Enter
      if(shift && A14_val == 49)  { // back
        pressed_key = back;
        if((Modus == Rechner) or (Modus == Quiz)) lcd.clear(), Modus = Menu;
      }
      




    
    if((pin_a_m - pin_a) > 100) lcd_p = false;
    pin_a_m = pin_a;
    //if(pressed_key_m != pressed_key) lcd_p = false;
    //pressed_key_m = pressed_key;
    

  // Cursor an Ursprungsposition setzen, wenn sich der Modus ändert
    if(Modus_m != Modus) lcd.setCursor(0,0), Cx=0, Cy=0;
    Modus_m = Modus;


    
  // eigentlicher Rechner
  // Anzeige LCD
  if(Modus == Rechner)  {
    lcd.setCursor(Cx, Cy);
    // blinkender Cursor
    if(millis() - Cursor_Start >= Cursor_intervall) {
      if(Cursor_print) lcd.print("|"), Cursor_print = false;
      else lcd.print(" "), Cursor_print = true;
      Cursor_Start = millis();
    }
  if(lcd_p == false)  {
    // Schreiben der versch. Taster auf das LCD
    if((pressed_key >= 0) && (pressed_key <= 9)) lcd.print(pressed_key);
    if(pressed_key == plus) lcd.print("\53");
    if(pressed_key == minus) lcd.print("\55");
    if(pressed_key == mal) lcd.print("\52");
    if(pressed_key == durch) lcd.print("\375");
    if(pressed_key == sinus) lcd.print("sin"), Cx++, Cx++;
    if(pressed_key == kosinus) lcd.print("cos"), Cx++, Cx++;
    if(pressed_key == tangens) lcd.print("tan"), Cx++, Cx++;
    if(pressed_key == asinus) lcd.print("sin\351"), Cx++, Cx++, Cx++;
    if(pressed_key == akosinus) lcd.print("cos\351"), Cx++, Cx++, Cx++;
    if(pressed_key == atangens) lcd.print("tan\351"), Cx++, Cx++, Cx++;
    if(pressed_key == hoch) lcd.print("^");
    if(pressed_key == rad) lcd.print("\350");
    if(pressed_key == lg) lcd.print("lg"), Cx++;
    if(pressed_key == e) lcd.print("e");
    if(pressed_key == pi) lcd.print("\367");
    if(pressed_key == ans) lcd.clear(), lcd.setCursor(0,0), lcd.print("Ans"),lcd.setCursor(3,0), Cx=3, Cy=0;
    // Sonderfall: AC (Leeren des LCD, Löschen aller Zahlen)
    if(pressed_key == ac) lcd.clear(), Cx = 0, Cy = 0;
    lcd_p = true;
    
    // Weiterrücken des Cursors
    Cx++;
    if(Cx >= 20) Cx=0, Cy++;
    if(Cy >= 4) lcd.clear(), lcd.setCursor(0,0), lcd.print("Ans"),lcd.setCursor(3,0), Cx=3, Cy=0;  
  }
  }
  // interne Rechnung
  // fehlt noch
  if(pressed_key == exe) lcd.clear(), lcd.setCursor(0,0), lcd.print("unerwarteter Fehler:"), lcd.setCursor(0,2), lcd.print("Ich kann noch nicht"), lcd.setCursor(0,3), lcd.print("rechnen!"), delay(2000), lcd.clear(), lcd.setCursor(4,0), lcd.print("Bitte warten"), lcd.setCursor(0,2), lcd.print("Der Rechner startet"), lcd.setCursor(0,3), lcd.print("das System neu..."), delay(3000), lcd.noBacklight(), delay(1500), lcd.clear(), setup();

    
  
}

Konkretes Problem: Wenn ich vergleichsweise schnell (aber mit normalem Tempo) Zahlen eintippe, kommt völliger Murks bei raus. Mal wird es komplett ignoriert, mal springt der Cursor vor, ohne dass ein neues Zeichen angezeigt wird. Ist letzteres der Fall, kommt beim Druck auf eine andere Taste die ursprünglich gedrückte.
Warte ich zwischen dem Drücken von Tasten etwa 2-3 Sekunden, passiert obiges nur sehr selten.

Hoffentlich könnt ihr mir helfen. :confused:
(An dieser Stelle möchte ich auch meinen im Code versteckten Humor entschuldigen)

Gruß
Paul

Hallo,
"dann ist es, wenn ein Code manchmal funktioniert und manchmal nicht"

setze an verschiedenen Stellen ein "Serial.print("Ergegnis")" im Sketch-
Dann kommst Du ziemlich schnell dahinter, wo es nicht rund läuft.
Nur mal so als Tip
Gruß und Spaß
Andreas

Techniker89:
... Wenn ich vergleichsweise schnell (aber mit normalem Tempo) ...

... ist das so, wie wenn ich die Luft anhalte (und zwar mit wenig Atmen) :slight_smile:

Techniker89:
... Zahlen eintippe, kommt völliger Murks bei raus. Mal wird es komplett ignoriert, mal springt der Cursor vor, ohne dass ein neues Zeichen angezeigt wird. Ist letzteres der Fall, kommt beim Druck auf eine andere Taste die ursprünglich gedrückte.
Warte ich zwischen dem Drücken von Tasten etwa 2-3 Sekunden, passiert obiges nur sehr selten.

Ich habe Deinen Code nur sehr oberflächlich quergelesen und ich kann mich an kein Konstrukt erinnern, der dem Entprellen dienen würde.

Techniker89:
(An dieser Stelle möchte ich auch meinen im Code versteckten Humor entschuldigen)

So lange ich zum Lachen nicht in den Keller muss, ist alles im grünen Bereich.

Ich wollte eigentlich ein Flussdiagramm posten, das ich mir mal als „Parser“ für Taschenrechner ausgeknobelt hatte. Das war gar nicht so einfach, aber heftig kürzer als Dein Programm. Mein Flussdiagramm passt in angenehm großer Lesegröße auf eine A4-Seite. Zum Implementieren kam ich bislang nicht. Wenn ich das noch finde, reiche ich das nach.

Gruß

Gregor

Techniker89:
(An dieser Stelle möchte ich auch meinen im Code versteckten Humor entschuldigen)

ROFL, selten so gelacht ^^

Allerdings ist mir aufgefallen das du bei deinem versteckten Humor ganz hinten aus der Loop-Funktion die Setup-Funktion aufrufst um die Variablen zurückzusetzen.
Wenn du die Variablen auch woanders als im Setup-Teil zurücksetzen willst dann mach dir eine Funktion wie "void setVariables();" oder sowas und schmeiss den Kram aus dem Setup dort hinein. Dann kannst du von überall aus setVariables(); aufrufen um diese zurückzusetzen.
Regel Nr. 1? keine Ahnung ^^
Rufe niemals Funktionen auf von denen sie aufgerufen wurden oder werden könnten, also Funktion1 darf Funktion2 aufrufen aber Funktion2 nicht Funktion1. Das wäre ja so als während dich jemand hukepack trägt du ihn hukepack tragen sollst, das geht nicht ^^
Aber das haben wir ja schon durch.

MfG
Andi

Deine gesamte Tasterschaltung ist mir sehr suspekt. Kannst Du da mal ein Schaltbild einstellen? Und Du fragst immer auf exakte Analogwerte ab. Das kann eigentlich nicht sauber funktionieren. Wenn Du die Taster in einem Widerstandsnetzwerk angeordnet hast, musst Du dir Spannungsbereiche für jede Taste festlegen, und abfragen, ob die Spannung in diesem Bereich liegt. Die abgefragten Werte liegen auch sehr dicht beieinander und nutzen den gesamten Bereich des Analogeingangs scheinbar nicht aus. Der geht ja von 0---1023. Kleinste Spannungsänderungen im 1-stelligen mV Bereich führen bei dir schon dazu, dass eine Taste nicht erkannt wird.

Techniker89:
Hoffentlich könnt ihr mir helfen. :confused:

Ich befürchte, nein.

Denn ich kann noch nicht mal verstehen, was das für ein Taschenrechner werden soll und wie viele Tasten er eigentlich hat.

Oben schreibst Du: "Mein Taschenrechner hat 16 Tasten,"

Aber Dein Codeversuch sieht so aus als wären es deutlich mehr Tasten:

enum Zeichen {Null,eins,zwei,drei,vier,fuenf,sechs,sieben,acht,neun,nix,hoch,rad,lg,sinus,kosinus,tangens,asinus,akosinus,atangens,ac,e,pi,exe,back,plus,minus,mal,durch,ans,Platzhalter};

Und analog angeschlossene Tasten scheinst Du im Code auch nicht daraufhin sauber abzufragen, ob soe gedrückt werden.

Dein Code scheint nicht auf die Statusänderung von "keine Taste gedrückt"" zu "Taste gedrückt" zu reagieren, sondern nur auf die analog ausgelesenen Werte. So dass eine Taste, wenn sie runtergerückt und im gedrückten Zustand festgehalten wird, nicht nur eine, sondern mehrere Aktionen auslösen kann. Aber das kann ich bei der krautigen Programmlogik auch nicht erkennen.

Meiner Meinung nach sollte ein "Taschenrechner" beim Drücken einer Taste genau eine Aktion ausführen und fertig. Und falls die Taste nach dem Drücken festgehalten wird, ändert sicht nichts mehr, bis diese Taste wieder losgelassen wurde.

Für 5 Euro gibts fertige 16 Stellige Tastaturen mit sauber programmierten Libs.

Analogwerte Abfragen und auf == vergleichen ist so ziemlich das fehleranfälligste Konstrukt das es gibt

Allenfalls könnte man sowas auf Wertebereiche abfragen. Jedoch muss zusätzlich ein flag gesetzt werden, das gerade eine Taste gedrückt wurde und erst wenn KEINE TASTE GEDRÜCKT anliegt wird das Flag rückgesetzt und man kann die nächste Taste drücken. Es bleibt die Anfälligkeit für Störimpulse. Und entprellen dürfte auch schwer fallen. Taste prellen, auch wenn sie das in 1/10.000sec machen. Deine CPU arbeitet im 1/16.000.000 sec Bereich (16MHz).

Den Code durch Funktionsblöcke etwas entzerren. Solche Löschaktionen auslagern in eigene Subroutinen, du kannst da zb void Display_clear() definieren und jedesmal aufrufen wenn du das Display in Grundstellung bringen willst. Dort drin kannst du dann alles sauber auflisten. Wenn der Block als solches dann funktioniert, nicht mehr anrühren, nur noch aufrufen. Spart dir Tippfehler und erleichtert die Fehlersuche.

Solche Endlosen If construkte sind auch nur schwer zu verstehen. Und vorallem wieder Fehleranfällig. Hier wäre ein Unterschieden nach Shift und nicht Shift sinnvoll. Und dann auf Kleiner vergleichen.

Eventuell ist der Analogwert berechenbar. Wenn ich richtig verstehe hast du dir die Werte ausgemessen und kommst auf 13,31,49,66,82,98,113,128,142,156. Abzüglich Mess ubnd Widerstandstoleranz steigerst du um 16.

int Analogvalue = (Wert+8)/16; Integer runden dir immer schön ab. Durch aufaddieren von 8 wird ab Analogwert 8 eine 1 ermittelt bis hin zu 23. Ab 24 wird eine 2i berechnet bis 40. So bekommst du eine Toleranz der Werte ohne aufwendige If-Vergleichsorgien.

Das dann mit einem Array mappen. Also Ein Array von [1..10] das die Tasten ergibt. in dem Fall: 9, 6, 3, 2, 5, 8, 7, 4, 1, 0.

int Analogvalue
Analogvalue = (Wert+8)/16
If Shift
Taste = Shift_Tastenmap[Analogvalue]
Else
Taste = Tastenmap[Analogvalue]

Jetzt nur mal so als Problemlösung für die vorhandene Tastatur(code nur symbolisch, Syntax anpassen). Jedenfalls deutlich weniger Schreibkram und Fehleranfälligkeit. Und zusätzlich tolerant gegen kleine Schwankungen der Werte.

Besser wäre auf jedenfall aber eine Matrix-Tastatur wie man sie Handelsüblich bekommt. Leider braucht die 8 Pins, 4 Ausgänge, 4 Eingänge. Im Normalfall ist das beim Taschenrechner aber frei.

Und dann Abfragen auf keine Taste gedrückt, bevor die nächste Taste dran kommen darf.