Tinkercad Projekt 6 - Taschenrechner

Einen echten Taschenrechner nachbauen

Ein 2-zeiliges LCD-Display und ein paar Taster werden zu einem Taschenrechner kombiniert.

Schwierigkeitsgrad: sehr anspruchsvoll

Öffne und kopiere den Demo-Entwurf https://www.tinkercad.com/things/jcZamOzBhpK-kkg-robotik-projekt-6-taschenrechner

Stelle die Zoomstufe des Schaltung (Scrollrad an der Maus) so ein, dass du das Display und alle Taster sehen kannst, dabei die Beschriftungen der Taster sich aber nicht gegenseitig überdecken. Starte die Simulation und probiere den Taschenrechner aus. Da die Simulation ein wenig lag't, müssen die Taster immer kurz gedrückt gehalten werden. Versuche mal eine sehr große oder sehr kleine Zahl einzugeben/auszurechnen.
Abbildung 1- Demo-Entwurf

Für diese Übung musst du folgendes können:
Am besten hast du dir zusätzlich die Beschreibung von Projekt 3 durchgelesen.

Der Demo-Entwurf versucht einen sehr einfachen Taschenrechner nachzubauen, in etwa so einen hier:
Abbildung 1 - einfacher voll funktionsfähiger Taschenrechner

Für die Anzeige wird ein LCD (Liquid Crystal Display) verwendet. Für die Eingabe (Tasten) werden Taster mit Pulldown-Widerständen verwendet (Übung 6). Da der Arduino Uno nicht genügend Pins hat, müssen ein paar Funktionen eingestampft werden:
  • Es kann kein Komma eingeben werden. Das Rechnen ist also nur mit Ganzzahlen möglich.
  • Es war kein Platz mehr für eine "Durch"-Taste. Es kann also nicht dividiert werden.
  • Es war kein Platz mehr für eine "+/-"-Taste. Das Vorzeichen für negative Zahlen muss also mit der Subtraktions-Taste eingegeben werden.
  • Es war kein Platz mehr für eine "="-Taste. Das Ergebnis wird also live ausgerechnet, ohne dass man "=" drücken muss.
Das kannst du zur Vereinfachung deines Programms natürlich auch ändern und einen Taschenrechner mit leicht anderen Funktionen bauen.

Hardware

Wie du Taster an den Arduino anschließt und ausliest, hast du in Übung 6 gelernt.

In der Simulation nutzen wir ein 2-Zeiliges Display, wobei in jeder Zeile 16 Zeichen dargestellt werden können. Hier könnte man solch ein Display kaufen: LCD-Display bei Reichelt
Abbildung 2 - 2-zeiliges LCD von Reichelt

Auf der Seite findet sich auch das  Datenblatt. Ich habe es hier noch einmal hochgeladen:
https://www.dropbox.com/s/p65iqsu1k6sw6iw/LCD162CBL%23EAS.pdf?dl=0

Das Datenblatt zeigt auf Seite 2 welche Zeichen standardmäßig dargestellt werden können. Das ist wieder ein ASCII-Zeichensatz (siehe Projekt 3), kann also z.B. keine Umlaute. Man kann aber auch eigene Zeichen definieren und anzeigen:
Abbildung 3 - LCD Zeichensatz

Seite 2 zeigt auch die Pinbelegung, also welcher Pin welche Funktion hat und wo angeschlossen werden muss:
Abbildung 4 - LCD Pinout im Demo-Entwurf
  • Pin 1: GND (Minus-Pol) → Arduino GND
  • Pin 2: VCC (Plus-Pol für 5V Spannungsversorgung des Displays) → Arduino 5V
  • Pin 3: VD (Minus-Pol) → Arduino GND
  • Pin 4: RS (Speicherstelle zum Lesen oder Schreiben auswählen) → irgendein Arduino Pin
  • Pin 5: RW (lesen oder schreiben) → irgendein Arduino Pin
  • Pin 6: E (Display aktivieren) → irgendein Arduino Pin
  • Pin 7: DB0 (Datenleitung 1) → wird benutzen nur 4 Datenleitungen, um Pins zu sparen
  • Pin 8: DB1 (Datenleitung 2) → wird benutzen nur 4 Datenleitungen, um Pins zu sparen
  • Pin 9: DB2 (Datenleitung 3) → wird benutzen nur 4 Datenleitungen, um Pins zu sparen
  • Pin 10: DB3 (Datenleitung 4) → wird benutzen nur 4 Datenleitungen, um Pins zu sparen
  • Pin 11: DB4 (Datenleitung 5) → irgendein Arduino Pin
  • Pin 12: DB5 (Datenleitung 6) → irgendein Arduino Pin
  • Pin 13: DB6 (Datenleitung 7) → irgendein Arduino Pin
  • Pin 14: DB7 (Datenleitung 8) → irgendein Arduino Pin
  • Pin 15: LED+ für LCD-Hintergrundbeleuchtung → Arduino 5V (Vorwiderstand hier...)
  • Pin 16: LED-  für LCD-Hintergrundbeleuchtung → Arduino GND (...oder Vorwiderstand hier)
Du siehst, es gibt eine ganze Menge Datenleitungen. Es können also auf mehreren Leitungen parallel Daten an das Display übermittelt werden. Das ist schneller als seriell mit nur einer Datenleitung. Serielle Kommunikation hast du in Projekt 3 kennen gelernt.

Software

Die LiquidCrystal Bibliothek kann  mit allen möglichen LCDs sprechen. Man muss sie nur korrekt konfigurieren. Wie du Bibliotheken benutzt, hast du in Übung 16 gelernt.

Schau dir doch mal die Doku dazu in der Arduino-Referenz an (gibts leider nur auf englisch):
https://www.arduino.cc/en/Reference/LiquidCrystal

Die Bibliothek muss erstmal importiert und dann konfiguriert werden:

#include <LiquidCrystal.h>
LiquidCrystal lcd(A0, A1, A2, A3, A4, A5);

Der Prozedur lcd() muss man mittteilen, welcher LCD-Pin an welchem Arduino-Pin angeschlossen ist:

LiquidCrystal lcd(pin_rspin_epin_db4pin_db5pin_db6pin_db7);

Für alle, die etwas mit Objektorientierung anfangen können: lcd() ist eine Kurzschreibweise für
LiquidCrystal lcd = LiquidCrystal(A0A1A2A3A4A5);
ist also ein Konstruktor mit dem das Objekt lcd der Klasse LiquidCrystal instanziiert wird.

lcd kann man benennen wie man möchte. Das funktioniert wie bei dem Namen einer Variablen oder Konstanten. In setup() muss dem lcd-Objekt noch mitgeteilt werden wie viel Zeilen und Zeichen pro Zeile das Display hat:

lcd.begin(16, 2); // 2 Zeilen á 16 Zeichen

Ab jetzt kannst du mit dem Display kommunizieren. Das Display versteht unter anderen die folgenden Befehle:
  • lcd.clear(); // angezeigte Zeichen komplett löschen
  • lcd.home();  // Cursor wieder oben links positionieren
  • lcd.setCursor(0, 1); // Cursor in 2. Zeile setzen
  • lcd.autoscroll(); // scrollen, wenn Text zu lang für Display
  • lcd.noAutoscroll();
  • lcd.print("Bla Keks"); // Text auf Display anzeigen
  • lcd.cursor(); // Unterstrich bei Cursor anzeigen
  • lcd.noCursor();
  • lcd.blink(); // Cursor automatisch blinken lassen
  • lcd.noBlink();
Spiel mit diesen Befehlen ein wenig herum. Du kannst auch schon mal einen Taster auslesen und abhängig davon etwas auf dem Display anzeigen, um dein Testprogramm ein wenig interaktiver zu gestalten.

Der Taschenrechner soll rechnen. Das kann der Arduino zum Glück bereits allein (siehe Übung 9). Schreibe folgende Funktionen:
  • addieren(operand1, operand2)
  • subtrahieren(operand1, operand2)
  • multiplizieren(operand1, operand2)
Als Datentyp bietet sich hier long an, da der Taschenrechner keine Komma-Taste hat und wir nicht wissen wie groß die Zahlen sein werden, die der Nutzer eingibt.

Solltes du eine der Tasten als Komma-Taste umwidmen, solltest du lieber den Datentyp double verwenden (Übung 12).

Teste diese Funkionen, indem du sie mit Test-Werten aufrufst und das Ergebnis auf dem Display anzeigst.

Jetzt wird's anspruchsvoll:

Der Nutzer gibt die Zahlen ja jeweils einzeln als Ziffern ein, also die Zahl 1234 wird durch den Nutzer durch Drücken der Tasten '1', '2', '3' und '4' eingegeben. Die eigentliche Zahl muss also in einer Variablen gespeichert und mit jeder neu eingegebenen Ziffer aktualisiert werden:

Pseudocode:

// operand_links = 0
// Taster '1' gedrückt: operand_links = 1
// Taster '2' gedrückt: operand_links = 12
// Taster '3' gedrückt: operand_links = 123
// Taster '4' gedrückt: operand_links = 1234

Das Dezimalsystem, mit dem wir normalerweise rechnen, basiert auf Zehnerpotenzen. Das heißt:

1 = 1 mal (10 hoch 0) → 1 * 10^0
20 = 2 * 10^1
345 = (3 * 10^2) + (4 * 10^1) + (5 * 10^0)

Um aus 2 20 zu machen, muss man sie also mal 10 rechnen:

2 = 2 * 10^0
20 = 2 * 10^1

Jedes Mal, wenn der Nutzer eine neue Ziffer eingibt, könntest du also die bereits eingegebene Zahl mal 10 nehmen und dann die neu eingegebene Ziffer dazurechnen.

operand_links = operand_links * 10 + neue_ziffer;

Probiere mal aus ein Programm zu schreiben, mit dem man mit den Ziffern-Tasten eine Zahl eingeben kann.

Füge irgendwo in loop() eine Verzögerung von mehreren hundert Millisekunden ein, damit die Simulation flüssiger läuft und auch, damit bei einem Tastendruck nicht gleich mehrere Ziffern eingegeben werden.

Man kann sich natürlich auch den Zustand des Tasters in einer Variablen speichern und nur eine Ziffer hinzurechnen, wenn der Taster wirklich gerade frisch gedrückt wurde, also vorher nicht gedrückt war. War der Taster vorher schon gedrückt und ist immer noch gedrückt, wird der gedrückt gehalten und es wird keine Ziffer hinzugerechnet. Damit das wieder geht, muss der Nutzer den Taster  zwischendurch wieder loslassen.

Wenn das funktioniert, kannst du dich daran machen die Operatoren vom Nutzer eingeben zu lassen. Die Operatoren ('+', '-' oder 'x') werden auch über Taster eingeben. Dann kann aber immer noch nicht gerechnet werden, da der Nutzer ja noch den 2. Operand eingeben muss. Der Operator muss also auch in einer Variablen gespeichert werden, z.B. so:

Pseudocode:

// wenn taster_addition gedrückt:
  // operator = '+'

Als Datentyp bietet sich char an.

Jetzt kannst du den Nutzer noch den 2. Operanden eingeben lassen.

Da alles nur nacheinander eingegeben werden kann, muss das Ganze auch als Abfolge programmiert werden:
  1. linken Operand eingeben
  2. Operator eingeben
  3. rechten Operand eingeben
  4. Ergebnis ausrechnen
Dafür bietet sich eine Zustandsmaschine an. Zustandsmaschinen hast du in Übung 8 kennen gelernt.

Pseudosode (es gibt die Varianten (a) und (b)):

// linker_operand = 0
// operator = '?'
// rechter_operand = 0
// zustand = 1

// wenn zustand == 1:
  // wenn ziffern-Taste gedrückt:
    // neue Ziffer zu linker_operand dazurechnen
  // wenn Operator-Taste gedrückt:
    // operator merken
    // zustand = 2
// wenn zustand == 2:
  // wenn Ziffern-Taste gedrückt:
    // neue Ziffer zu rechter_operand dazurechnen
    // (a) Ergebnis ausrechnen und anzeigen
  // wenn Korrektur-Taste gedrückt:
    // linker_operand = 0
    // operator = '?'
    // rechter_operand = 0
    // zustand = 1
  // (b) wenn Ergebnis-Taste gedrückt:
    // zustand = 3
// wenn Zustand == 3:
    // Ergebnis ausrechnen und anzeigen
    // linker_operand = 0
    // operator = '?'
    // rechter_operand = 0
    // zustand = 1

Versuche das umzusetzen.

Wenn du die Schaltung nicht selber aufbauen möchtest: Hier ist der Starter:
Starter: https://www.tinkercad.com/things/kt17CXSolF3-kkg-robotik-projekt-6-taschenrechner-starter

Jetzt kann man dem Taschenrechner noch weitere Features verpassen:
  • Negative Zahlen eingeben: Das muss vor Zustand 1 passieren. Wird die '-' Taste gedrückt, wird linker_operand mal -1 gerechnet vor der eigentlichen Rechnung. Zwischen Zustand 1 und 2: Wird die '-' Taste gedrückt, wird rechter_operand mal -1 gerechnet vor der eigentlichen Rechnung.
  • Mehrere Operanden verketten: Wird in Zustand 2 noch ein Operand eingegeben, wird das Ergbenis der Rechnung für linker_operand übernommen und der Nutzer kann einen 3. Operanden eingeben, der in rechter_operand gespeichert wird.

Kommentare

Beliebte Posts aus diesem Blog

Tinkercad Übung 6 - LED mit Taster ansteuern

Tinkercad Übung 11 - LED dimmen

Tinkercad Übung 15 - Ultraschallsensor auslesen