I2C Lommeregner: Forskelle mellem versioner
Bar (diskussion | bidrag) m 1 version importeret |
Bar (diskussion | bidrag) |
||
| Linje 315: | Linje 315: | ||
Tal-håndteringen fungerer heller ikke optimalt når tallene bliver store - der ser ud til at være en ikke nærmere analyseret begrænsning i Float-formatet, så det ikke arbejder over 4294967296, hvilket ikke burde være en begrænsning for float. | Tal-håndteringen fungerer heller ikke optimalt når tallene bliver store - der ser ud til at være en ikke nærmere analyseret begrænsning i Float-formatet, så det ikke arbejder over 4294967296, hvilket ikke burde være en begrænsning for float. | ||
{{Atmel Microcontroller}} | |||
[[Kategori:I2C Moduler]] | [[Kategori:I2C Moduler]] | ||
Nuværende version fra 5. nov. 2022, 13:24

Lommeregneren er opbygget od fra et I2C tastatur og et I2C Display hvor begge er koblet på en Arduino ved hjælp af en I2C Bus.
Selve lommeregner logikken er lavet i Arduinoen.
Systemet
Lommeregneren er lavet med logikken i en Arduino, der fungerer som I2C master, og henter oplysninger nede i I2C tastaturet og sender de relevante informationer til I2C displayet.
Lommeregneren kan betragtes ud fra følgende blokdiagram:

Blokdiagram over lommeregneren
I2C Displayet er et færdigt købt modul, mens I2C tastaturet er et modul der er konstrueret på Holstebro HTX.
Lommeregneren er et rent software projekt, som illustrerer hvilke muligheder man har med I2C Moduler
Software ide
Ideen er baseret på at Arduinoen hele tiden looper og tjekker om der er kommet indtastninger på tastaturet.
Når der kommer en indtastning, så samles den op og samles til tal eller der reageres på den regneart man ønsker.
Når der trykkes = så beregnes og der gøres klar til et nyt regnestykke.
Lommeregneren er ikke lavet til at tage højde for regnearternes hieraki, men regner tallene sammen som de tastes ind.
Detaljer i softwaren
Hele softwaren bliver beskrevet i de forskellige funktionsblokke det er lavet i
Definitioner og Initialisering af lommeregneren
I starten inkluderes Wire modulet, så man kan kommunikere ved hjælp af I2C-protokollen, og rutinerne til I2C displayet.
Der laves en initialisering af I2C displayet og angives hvilken adresse I2C tastaturet arbejder på.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x20 for a 16 chars and 2 line display
// Adressen på I2C tastaturet
#define I2C_Address 0x23
Herefter erklæres de variabler der anvendes og der hvor det er nødvendigt sættes den standard værdi de skal have.
// Variabler til programmet
byte error;
byte tast;
float tal1 = 0.0;
float tal2 = 0.0;
char tegn;
char ch;
boolean get1 = true;
boolean komma = false;
float kommafaktor = 0.1;
boolean mellem = false;
boolean start = true;
I Arduinis setup() laves initialisering af seriel port, der anvendes til test, initialisering af LCD'en og der udskrives en hjælpeskærm til den serielle port.
void setup()
{
Wire.begin(); // Start I2C kommunikationen
Serial.begin(9600);
delay(100);
lcd.init(); // initialize the lcd
// Print a message to the LCD.
lcd.backlight();
lcd.print("Lommeregner");
help();
}
Hoved-loopet i lommeregneren
I hoved-loopet tjekkes der på om der kommer noget ind fra den serielle port, og de relevante rutiner kaldes.
Her er det kun hjælp man kan få vist igen, og man kan slå beep på tastaturet til og fra.
void loop()
{
// Reager på de forskellige indtastninger brugeren
// laver på PC'en
if (Serial.available() > 0) {
ch = Serial.read();
if ((ch == 'h') | (ch == 'H')) {
help();
}
if ((ch == 'b') | (ch == 'B')) {
error = Serial.parseInt();
setBeep(error);
}
}
Den sidste del af loopet kalder en rutine der tjekker om der er nogen taster i bufferen. Der holdes en pause, men den har faktisk ingen betydning, blot at den ikke belaser I2C tastaturet så meget.
getTastBuf(); // Håndterer indtastninger på I2C tastaturet
delay(100);
}
Servicering af I2C tastaturet
Starten af tjekket på I2C tastaturet spørger om hvor mange karakterer der er i bufferen, og hvis der ingen er, så sker der ikke noget.
Hvis der er en eller flere taster i bufferen, så hentes den.
void getTastBuf() {
Wire.beginTransmission(I2C_Address);
Wire.write(2); // Kommando der undersøger om der er taster klar
error = Wire.endTransmission();
if (error == 0)
{
delayMicroseconds(100);
Wire.requestFrom(I2C_Address, 1);
if (Wire.available() == 1) {
error = Wire.read();
if (error >= 1) { // Hvis der er taster klar
Wire.beginTransmission(I2C_Address);
Wire.write(1); // Kommando der henter tasten
error = Wire.endTransmission();
if (error == 0) {
delayMicroseconds(100);
Wire.requestFrom(I2C_Address, 1);
if (Wire.available() == 1) {
Håndtering af tal
Det første der sker når man har modtagert tasten er at der tjekkes om det er en start-situation, og hvis det er, så slettes displayet, så der er plads til et nyt regnestykke.
Herefter tjekkes om det er et ciffer der indtastes, og det tilføjes i enten første eller andet tal.
Der håndteres også at der kan tastes komma i tallene.
tast = Wire.read(); // Hent den aktuelle tast
if (start) {
lcd.clear();
start = false;
}
if (tast < 10) { // Hvis det er en taltast
lcd.print(tast);
if (get1) { // Hvis det er første tal der hentes
if (komma) {
tal1 = tal1 + tast * kommafaktor;
kommafaktor *= 0.1;
} else {
tal1 = tal1 * 10 + tast;
}
} else { // Hvis tallet er et efterfølgende
if (komma) {
tal1 = tal1 + tast * kommafaktor;
kommafaktor *= 0.1;
} else {
tal2 = tal2 * 10 + tast;
}
}
} else if (tast == 10) { // Tasten . (komma)
if (! komma) {
lcd.print('.');
}
komma = true;
Selve beregningerne
Hvis der tastes = så gøres klar til afslutningen og beregningen. I testudskriften til den serielle port tages der højde for om der har været mellemregninger.
Der beregnes så ud fra hvilken regneart der er brugt sidst.
Tilsidst skrives resultatet ud både på den serielle testport og til I2C displayet og der gøres klar til næste regnestykke.
} else if (tast == 11) { // Tasten = (beregn og slet)
get1 = true;
komma = false;
kommafaktor = 0.1;
if (! mellem) {
Serial.print(tal1);
}
mellem = false;
if (tegn == '+') { // Beregn ud fra regnearten
tal1 = tal1 + tal2;
} else if (tegn == '-') {
tal1 = tal1 - tal2;
} else if (tegn == '*') {
tal1 = tal1 * tal2;
} else if (tegn == '/') {
tal1 = tal1 / tal2;
}
lcd.print("=");
lcd.setCursor(0,1);
lcd.print(tal1); // Udskriv resultatet
Serial.print(tegn);
Serial.print(tal2);
Serial.print("=");
Serial.println(tal1);
start = true;
tal1 = 0.0;
tal2 = 0.0;
Regnearterne håndteres i den følgende kode, hvor der også tages højde for om det er første gang der er tastet et regnetegn, eller om der skal foretages en mellemregning.
} else if (tast == 15) { // Tasten +
if (! get1) {
mellemregn();
}
tegn = '+';
komma = false;
kommafaktor = 0.1;
lcd.print(tegn);
get1 = false;
} else if (tast == 14) { // Tasten -
if (! get1) {
mellemregn();
}
tegn = '-';
komma = false;
kommafaktor = 0.1;
lcd.print(tegn);
get1 = false;
} else if (tast == 13) { // Tasten *
if (! get1) {
mellemregn();
}
tegn = '*';
komma = false;
kommafaktor = 0.1;
lcd.print(tegn);
get1 = false;
} else if (tast == 12) { // Tasten /
if (! get1) {
mellemregn();
}
tegn = '/';
komma = false;
kommafaktor = 0.1;
lcd.print(tegn);
get1 = false;
}
Mellemregningen håndteres af følgende rutine:
void mellemregn() { // Rutine der håndterer når 2. regnetegn tastes
if (!mellem) {
Serial.print(tal1);
}
if (tegn == '+') {
tal1 = tal1 + tal2;
} else if (tegn == '-') {
tal1 = tal1 - tal2;
} else if (tegn == '*') {
tal1 = tal1 * tal2;
} else if (tegn == '/') {
tal1 = tal1 / tal2;
}
Serial.print(tegn);
Serial.print(tal2);
tal2 = 0;
mellem = true;
}
Muligheden for at slå Beep fra
Det er muligt at slå beep fra via den serielle kommunikation ved at skrive B 0 til den serielle port, og det kan slås til igen ved at skrive B 1. Dette udføres af den viste rutine:
void setBeep(byte mode) { // Til at slå beep til og fra
Wire.beginTransmission(I2C_Address);
Wire.write(4);
Wire.write(mode);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("Beep mode set to: ");
Serial.println(mode);
}
}
Hjælpeskærm
For at hjælpe med brugen kan man få en hjælpeskærm via den serielle port.
// Hjælpeskærm der udskrives på den serielle konsol
void help() {
Serial.println("Regnemaskine med I2C tastatur og I2C Display");
Serial.println("--------------------------------");
Serial.println("H\tDisplays this help-screen");
Serial.println("B n\tSet Beep mode to n");
Serial.println("");
Serial.println("1 2 3 +");
Serial.println("4 5 6 -");
Serial.println("7 8 9 *");
Serial.println(". 0 = /");
Serial.println("");
}
Test af softwaren
Softwaren er blevet udviklet løbende til først at kunne acceptere tal og herefter at kunne regne med en indtastning af regnearterne.
Derefter blev der lagt på, så man kan indtaste flere regnearter i samme regnestykke - dog ikke så regnemaskinen overholder regnearternes hieraki.
Der blev lavet så man kan indtaste kommatal.
Endelig blev resultatet flyttet ned i anden linje så der er bedre plads i displayet, og der blev lavet så resultatet forbliver i displayet indtil der tastes et nyt regnestykke.
Udestående udvikling
Regnemaskinen overholder ikke regnearternes hieraki. Det vil kræve at der ikke kun arbejdes med to tal internt, men at der etableres en form for stack til at gemme det der ikke er beregnet endnu, eller at man placerer alle tal i en stack og så udfører beregningen efterfølgende.
Der tages heller ikke højde for at man kan skrive uden for displayet - den skriver ude i en del af displayets hukommelse der ikke vises på dette display. Dette kan måske løses ved at lade den øverste linje scrolle eller ved at skrive ned på næste linje. Begge dele vil kræve en del omstrukturerring af indtastningen.
Endelig er der ikke nogen håndtering af flere decimaler til resultatet - det kan ganske givet lade sig gøre at angive flere decimaler, men der er ikke afklaret hvordan der skal bestemmes hvor mange decimaler der skal vises.
Tal-håndteringen fungerer heller ikke optimalt når tallene bliver store - der ser ud til at være en ikke nærmere analyseret begrænsning i Float-formatet, så det ikke arbejder over 4294967296, hvilket ikke burde være en begrænsning for float.