- Calcolatrice RPN 32t con Arduino -
- RPN 32t Calculator with Arduino-
- 14-01-2023 -
Su questo blog trovate il primo progetto che ho realizzato, cercando di imitare la mia calcolatrice HP41, per i dettagli sul funzionamento e la descrizione delle funzioni leggete su questo stesso blog il precedente post
Il mio progetto funziona ma con la limitazione a 16 tasti è difficoltoso l'uso pratico di quella calcolatrice.
Ecco qui un progetto con 32 tasti, in questo modo è certamente più pratico l'utilizzo.
Purtroppo non ho più il prototipo.
Qui ripropongo lo schema e il programma.
Materiale occorrente:
ARDUINO UNO
LCD 2X16
TUE TASTERINI 16 TASTI
Come potete vedere ho provato il progetto su Tinkercad e funziona.
Volendo potreste utilizzare un lcd con I2C
In questa versione con la 2F ( seconda funzione dei tasti) ho lasciato disponibili diversi tasti così ognuno potrà con poche modifiche personalizzare la propria realizzazione.
tastierino sinistra
* En^ enter
ABCD + - * / le 4 operazioni
# , virgola sotto al 9
tastierino di destra
1 lastx recupera valore in x
2 SCI notazione scientifica (on-off)
3 SQR radice quadrata x
A XEQ esegue la funzione nn ( leggete sul primo post le potenzialità di questa funzione e come aggiungere le proprie funzioni )
4 Giù ruota giù la catasta
5 Y^x Y elevato a x
6 STO memorizza x nel registro nn
B RCL richiama in x il registro nn
7 X<>Y scambia x con y
8 SIN calcola il seno di x
9 COS calcola il coseno di x
C TAN calcola la tangente di x
* Del cancella il dato inserito
0 CHS cambia segno
# FIX si sceglie il numero dei decimali
D 2F abilita le seconde funzioni dei tasti
SECONDE FUNZIONI
tastierino di destra
1 1/x restituisce 1/x
2 SCI notazione scientifica (on-off)
3 Ln log x (logaritmo naturale x)
A Log10 logaritmo in base 10 di x
4 di (disponibile)
5 e^x e elevato alla x
6 P>R da coordinate polari a rettangolari
B R>P da coordinate rettangolari a polari
7 di (disponibile)
8 ASIN arcosen x
9 ACOS arcocos x
C ATAN arcotang x
* Clear cancella i registri catasta
0 CleM cancella le memorie
# di (disponibile)
D 2F
Qui sopra ho indicato i tasti e le relative funzioni, si possono aggiungere anche altre funzioni con piccole modifiche al listato.
Il funzionamento è spiegato più in dettaglio nel post precedente.
Qui sotto il listato per ARDUINO uno
/*
* ***********************************
* SERGIO PRENLELOUP
* 09 03 2019
* v. 3.14
* CALCOLATRICE RPN
* SCIENTIFICA
* ***********************************
32 TASTI
4 REGISTRI CATASTA
9 REGISTRI MEMORIA
FUNZIONI
PI, SQRT, Y^X, SIN, COS, TAN, ASIN, ACOS, ATAN
CHS, LN, LOG10, FACT, 1/X, EXP, P>r, r>P,
CONVERSIONE ANGOLI
R>G, G>R, R>C, C>R
UTILITA'
x<>Y , RGIU, LASTX, FIX, STOnn, RCLnn, SCI, CLR, CLM
TASTO XEQ si puo chiamare programma o funzione
tasto 2F attiva disattiva seconda funzione tasti
*/
//
//=============================
// librerie utilizzate
#include <LiquidCrystal.h>
#include <Keypad.h>
// ============================
//--------------------------------------------
//**********************************************************
const byte righe = 4; // per keypad
const byte colonne = 8;
//**********************************************************
//*********************************************************
// codici tasierino
//*********************************************************
byte chiave [righe][colonne] =
{
{49,50,51,43,73,74,75,76}, //1, 2, 3, +,
{52,53,54,45,69,70,71,72}, //4, 5, 6, -,
{55,56,57,44,65,66,67,68}, //7, 8, 9, *,
{61,48,46,47,60,63,64,62} // En, 0, . /, del,
};
//----------------------------------------------------------
// pin assegnati al tastierino
//----------------------------------------------------------
byte pinrighe[righe] = {14,15,16,17};
byte pincolonne[colonne] = {7,6,5,4,3,2,1,0};
// inizializza keypad e lcd
Keypad keypad = Keypad (makeKeymap(chiave),pinrighe,pincolonne,righe,colonne);
LiquidCrystal lcd(8,9,10,11,12,13);
//*****************************************
// pin analogici utilizzati come digitali
// A0 = 14, A1 = 15, A2 = 16, A3 = 17
//*****************************************
//*****************************************
// VARIABILI GLOBALI
//*****************************************
int codice =0; // codice operativo calcolato a partire da key (codice asccii)
int poscifra = 0;// tiene conto delle cifre
boolean stato = false; // questa per attivare o disattivare la seconda funzione tasti
boolean sciee = false; // true visualizza in notazione scientifica
//-----------------------------------------------------------------
float k1 = 10000000;// numero massimo visualizzabile poi si passa in visualizz scie
float k0 = 0.00001; // numero decimale piu piccolo visualizzabile
// per l'inserimento dei numeri decimali
int pdec = 0; // punto decimale
int ndec =0; // conta decimali
int fix = 4; //decimali di default
//****************************************
//*** registri catasta
// ***************************************
double regx = 0.0;
double regy = 0.0;
double regz = 0.0;
double regt = 0.0;
// registri speciali
double regxs = 0.0; // per visualizzare la parte decimale della notazione sci
int esponente = 0; // esponente nella scientifica
double visore = 0.0; // accumula i valori immessi
double lastx = 0.0; // recupero del registro x
//***********************
// memorie utente
//***********************
double mem1 = 0.0;
double mem2 = 0.0;
double mem3 = 0.0;
double mem4 = 0.0;
double mem5 = 0.0;
double mem6 = 0.0;
double mem7 = 0.0;
double mem8 = 0.0;
double mem9 = 0.0;
//***********************
void setup() {
lcd.begin(16, 2);
//INIZIALIZZAZIONE
lcd.clear();
lcd.setCursor(0,0);
lcd.print("calcolatrice RPN");
lcd.setCursor(0,1);
lcd.print(" v.3.14 ");
delay (3000);
lcd.clear();
}
// END SETUP
//********************************************
void loop()
{
aggiornadisplay(); // sub che aggiona il display
// se abbiamo un codice 61 ENTER si esegue
if (codice == 61)
{ enter();
}
tasto();
//-------------------------------------------------------------
// ** si controlla lo stato della calcolatrice
// che può essere funzioni dirette o seconde funzioni
if (stato == true)
{ // siamo in 2F )
funzioni_seconde();
}
if (stato == false)
{// siamo nelle funzioni dirette
funzioni_dirette();
}
}
//**** END LOOP *******************************************************
//**********************************************************************
// funzione attesa tasto dal tastierino restituisce il codice
void tasto()
{
byte key = 0; // contiene il codice tasto premuto
// azzera variabile tasto del tasterino e
// attende la pressione di un tasto sul tastierino
while (key == 0) // rimane qui fino alla pressione di un tasto
{
key = keypad.getKey();
codice = key;
}
}
void aggiornadisplay()
{
//***************
// mostra display
//---------------
// si esegue il controllo se visualizzare in notazione sci o con fix
double testx = regx;
if (testx < 0) testx = testx*-1;
if (testx > k1) sciee = true;
if (testx < k0) sciee = true;
if (testx == 0) sciee = false;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("X:");
lcd.setCursor(4,0);
if (sciee == true)
{
sci(); // sub per notazione scientifica
lcd.print(regxs,5);
lcd.setCursor(14,0);
lcd.print (esponente);
}
else lcd.print(regx,fix); // visualizzazione normale
lcd.setCursor(0,1);
if (stato == false){lcd.print("1:");}
if (stato == true) {lcd.print("2:");}
lcd.setCursor(4,1);
lcd.print(visore,fix);
sciee = false;
//------------------
}
void sci()
{
lastx = regx;
double numero = regx;
int segno = 1;
int segnoe = 1;
esponente = 0;
// se il numero è negativo si trasforma in positivo
// ma si mette a -1 la variabile segno, ci servirà
// per ritornare a negativo dopo aver trovato
// il valore dell'esponte con la log10(numero)
if (numero < 0) {numero = numero *-1; segno = -1;}
double numlog = log10(numero); // si ottiene il logaritmo del numero
// N,xxxxx preleveremo la parte
// intera che rappresenta l'esponente
esponente = int( numlog); // adesso abbiamo il numero intero esponente
numero = numero*segno; // si ripristina il giusto segno
// si esegue il controllo se l'esponente è negativo e si
//esegue la stessa procedura fatta per il numero
if (esponente <0){esponente = esponente *-1; segnoe = -1;}
//-----------------------------------------------------------------------
// se segnoe è negativo il numero è molto piccolo
if (segnoe == -1){regxs = numero * pow(10,esponente);}
else regxs = numero / pow(10,esponente); //altrimenti numero molto grande
//------------------------------------------------------------------------
esponente = esponente * segnoe;
// si ripristina il giusto segno
// all'esponente
}
//*******************************************************
//*******************************************************
//***************************
// FUNZIONI DIRETTE
// SELEZIONA LA FUNZIONE
//***************************
void funzioni_dirette()
{
if (pdec == 0) // si deve scegliere fra numeri e numeri decimali
{
if ((codice >= 48) && (codice <= 57 ))
{impostanumero();}
}
if (pdec == 1) // è stato premuto il punto decimali
{
if ((codice >= 48) && (codice <= 57 ))
{impostanumerodec();}
}
// il codice è relativo ad una funzione oppure è stato attivato il tasto 2F
// per attivare le seconde funzioni
//seleziona la funzione la invia alla sub che la esegue e quindi ritorna
//
if (codice == 43)// somma
{ if (visore != 0)enter();
somma();}
if (codice == 44) //moltiplicazione
{if (visore != 0)enter();
moltiplicazione();}
if (codice == 45) //sottrazione
{if (visore != 0)enter();
sottrazione();}
if (codice == 47) //divisione
{if (visore != 0)enter();
divisione();}
//----------------------------------------
if (codice == 46) // punto decimale
{punto();} // con questa si pone a 1 la pdec
if (codice == 60)//cancella
{del(); }
if (codice == 61)// ENTER
{enter();}
if (codice == 62) //seconda funzione 2f
{ stato = true;} // cambia stato
if (codice == 63) // cambio segno CHS
{cambiasegno();}
if (codice == 64) // FIX numero decimali
{visfix();}
if (codice == 65) // scambia X con Y
{scambio();}
if (codice == 66) // SIN x
{if (visore != 0)enter();
sen_x();}
if (codice == 67) // COS x
{if (visore != 0)enter();
cos_x();}
if (codice == 68) // TAN x
{if (visore != 0)enter();
tan_x();}
if (codice == 69) // Ruota catasta GIU
{ruota();}
if (codice == 70) // Y elevato alla x
{yele_x();}
if (codice == 73)//Lastx ( recupera x )
{last_x();}
if (codice == 74) // inserimento numeri con esponente
{inputsci();}
if (codice == 75) // Radice di X
{if (visore != 0)enter();
sqrt_x();}
//---------------------------------------
if (codice == 76) // XEQ nn richiama dunzione o programma
{Xeq_x();}
if (codice == 71) // STO n
{sto_n();}
if (codice == 72) // RCL n
{rcl_n();}
return;
}
void funzioni_seconde()
{
stato = false; // rimette false per tornare alle funzioni dirette
int codiceF2 = codice + 36;
//esegue una della funzioni F2 seconde
if (codiceF2 == 98) // non fa nulla
{}
if (codiceF2 == 82) // PIgreco
{pi();}
if (codiceF2 == 102) // arcoseno x
{ arcosen();}
if (codiceF2 == 103)//arco coseno di x
{arcocos();}
if (codiceF2 == 104)//arco tangente di x
{arcotan();}
if (codiceF2 == 107) // da polari a rettangolari p>=0 e alfa >=0 e <= pi/2
{P_R();}
if (codiceF2 == 108)// da rettangolari a polari
{R_P();}
if (codiceF2 == 109)//uno su x
{unosu_x();}
if (codiceF2 == 110)//sci on-off
{scion();}
if (codiceF2 == 96)// cancella registri catasta e visore
{ cancella();}
if (codiceF2 == 99) // cancella memerie utente
{cancella_mem();}
if (codiceF2 == 111) // ln
{lastx = regx; regx = log(regx);}
if (codiceF2 == 112)//Log10
{lastx = regx; regx = log10(regx);}
if (codiceF2 == 106)// exp
{lastx = regx; regx = exp(regx);}
/*
if (codiceF2 == 85)// calcolo fattoriale di x max 30
{ fact();}
if (codiceF2 == 88)// da radianti a centesimali
{ lastx = regx; regx = regx*200.0/PI;}
if (codiceF2 == 91)// da centesimali a radianti
{ lastx = regx; regx = regx*PI/200.0;}
// converte da radianti in sessagesimal
if (codiceF2 == 86)
{lastx = regx; regx = regx*180.0/PI; sedec_sessa();}
// da gradi sessagesimali a radianti
if (codiceF2 == 87)
{lastx = regx; sess_sedec();regx = regx*PI/180.0;}
*/
return;
}
//*********************************************
// esegue le funzioni
//*********************************************
// parte intera di un numero si scrive su visore
void impostanumero()
{
poscifra = poscifra +1;
if (poscifra < 9)
{int cifra = codice - 48;
visore = visore * 10 + cifra;
}
else { codice = 61;}
}
//*********************************************
// si scrive su visore il numero decimale
void impostanumerodec()
{
ndec = ndec+1;
if (fix >= ndec)
{
float cifradec = (codice - 48.0);
if (ndec == 1) {visore = visore + (cifradec /10);}
if (ndec == 2){visore = visore + (cifradec /100);}
if (ndec == 3){visore = visore + (cifradec /1000);}
if (ndec == 4){visore = visore + (cifradec /10000);}
if (ndec == 5){visore = visore + (cifradec /100000);}
}
else { codice = 61;}
}
//----------------------------------------------------------
void somma()
{
// somma il contenuto di x con y e
//lo mette in x scorre la catasta
lastx = regx;
regx = (regx + regy);
regy = regz;
regz = regt;
del();
}
void sottrazione()
{
// sottrae x da y
lastx = regx;
regx = (regy - regx);
regy = regz;
regz = regt;
del();
}
// punto decimali
void punto()
{
pdec = 1;
ndec = 0;
codice =0;
}
//cancella carattere
void del()
{
visore = 0;
poscifra =0;
codice = 0;
pdec = 0;
ndec = 0;
}
// ENTER
void enter()
{
regt = regz;
regz = regy;
regy = regx;
regx = visore;
//lastx = regx;
// prima sale la catasta
// poi azzera
del();
}
// moltiplicazione
void moltiplicazione()
{
lastx = regx; // prima salva in lastx il valore di x
regx = (regy * regx); // esegue la moltiplicazione
regy = regz;
regz = regt;
del();
}
//divisione
void divisione()
{
lastx = regx; // prima salva in lastx il valore di x
if (regx == 0) return; //******* divisione per 0
regx = (regy / regx); // esegue la divisione
regy = regz; // abbassa la catasta
regz = regt;
del();
}
// x<>y scambio
void scambio()
{
double scambio = regx;
regx = regy;
regy = scambio;
}
//ruota giu la catasta
void ruota()
{
double ruota = regx;
regx = regy;
regy = regz;
regz = regt;
regt = ruota;
}
//cambiosegno
void cambiasegno()
{
lastx = regx; regx = regx * -1;
}
// FIX----- scelta numero decimali dopo il punto da 0 a 5
void visfix()
{ if (( visore < 0 )|| (visore > 5)) return;
else fix = visore;
}
void pi()
{ lastx = regx; regx = PI;}// richiama pigreco
// seno di x angolo in RADIANTI
void sen_x()
{lastx = regx; regx = sin (regx);}
//tangente x angolo in x in RADIANTI
void tan_x()
{ lastx = regx; regx = tan (regx);}
// coseno x angolo in x in RADIANTI
void cos_x()
{ lastx = regx; regx = cos (regx);}
//radice quadrata
void sqrt_x()
{ lastx = regx; regx = sqrt (regx);}
// potenza eleva y alla x
//pow(base, exponent)
//base: numero (tipo float)
//exponent: la potenza a cui è elevata la base (tipo float)
// si utilizza x ed y della catasta
void yele_x()
{ lastx = regx; regx = pow (regy, regx); }
// questa sub serve ad inserire un numero molto grande o molto piccolo
// in catasta inserendo in y il valore n.nnnnn ed in x l'esponente base 10
void inputsci()
{
double numero = regy;
double esp = regx;
regx = numero * pow(10,esp);
regxs = regx;
esponente = esp;
sciee = true;
}
//--------------------------------------------------
// chiamate da seconde funzioni --------------
//--------------------------------------------------
//Lastx
void last_x()
{ regt = regz;
regz = regy;
regy = regx;
regx = lastx;
}
// clr //cancella catasta e visore
void cancella()
{
regt = 0.0;
regz = 0.0;
regy = 0.0;
regx = 0.0;
lastx = 0.0;
visore = 0.0;
poscifra =0;
codice = 0;
pdec = 0;
ndec = 0;
}
// cancella memorie utente
void cancella_mem()
{
mem1 = 0.0;
mem2 = 0.0;
mem3 = 0.0;
mem4 = 0.0;
mem5 = 0.0;
mem6 = 0.0;
mem7 = 0.0;
mem8 = 0.0;
mem9 = 0.0;
}
void unosu_x()
{
if ( regx == 0)return;
else { lastx = regx;
regx = 1/regx;
}
}
// funzione fattoriale max 30
void fact()
{
int numero = regx;
double fact = 1.0;
regx = fact;
if ((numero < 1)||(numero > 31)) return;
else {
for ( int i = numero; i > 1; i--)
{fact = fact * i;}
regx = fact;
}
}
// end fact
// da polari a rettangolari inserire in x l'angolo alfa
// in gradi RADIANTI
// inserire in y la magnitudine
// restituisce le cordinate in x e y
void P_R()
{
double magni = regy;
double alfa = (regx);
if (magni < 0)return;
if (alfa > (2*PI))return;
if (alfa < 0 )return;
regx = magni*cos(alfa);
regy = magni*sin(alfa);
}
// da rettangolari a polari insere in x la coordinata x ed in y la cooordinata y
// restituisce in y la distanza
// e restituisce in x angolo in RADIANTI
//
void R_P()
{
double alfa = 0;
double magni = sqrt((regx*regx)+ (regy*regy));
alfa = atan2(regy,regx);
regx = alfa;
regy = magni;
}
// converte x da sessagesimale a sessadecimale
// solo il registro x della catasta viene utilizzato
void sess_sedec()
{
lastx = regx;
float a = regx;
int b = int(a); // i gradi
float c = (a-b)*100;
int d = int(c);
float pri = d/60.0; // primi
float sec = (c-d)/36.0; // i secondi
regx= (b+pri+sec);
}
// converte x da sessadecimali a sessagesimali
// solo il registro x della catasta viene utilizzato
void sedec_sessa()
{
lastx = regx;
float a = regx;
int b = int(a); // i gradi
float c = (a-b)*60;
int d = int(c); // i primi
float pri = d/100.0;
float sec = (c-d)*6/1000.0; // i secondi
regx= (b+pri+sec);
}
// funzione ritorna angolo in x in RADIANTI
void arcosen()
{ if ((regx < -1)||( regx > 1)) return;
{lastx = regx; regx = asin(regx);}
}
// funzione ritorna angolo in x in RADIANTI
void arcocos()
{ if ((regx < -1)|| ( regx > 1)) return;
{lastx = regx; regx = acos(regx);}
}
// funzione ritorna angolo in x in RADIANTI
void arcotan()
{lastx = regx; regx= atan(regx);}
// sci on - off
void scion()
{ if (sciee == false) sciee = true;
else sciee = false;
}
//---------------------------------------------------------------------------------------
//
//
// Xeq_n --- esegue la funzione da 00 a 26
// --- è comunque possibile implementare funzioni o calcoli
// ---- e/o veri programmi che restituiscono valori in catasta
// ---- modificando questa sub e aggiungendo la sub da chiamare
void Xeq_x()
{ int mem = visore;
if (( mem < 1 )|| ( mem > 26)) return;
switch (mem)
{
case 22:
// carnot(); //calcolo triangolo 2 lati angolo compreso
break;
case 23:
// erone(); // superficie con tre lati
break;
case 24:
// partitore(); //partitore di tensione
break;
case 25:
// parallelo(); // resistenze in parallelo
break;
case 26:
// sco_iva(); // corporo IVA
break;
}
} // fine void XEQ
//-------------------------------------------------------------------
// STO n SUB PER MEMORIZZARE IL CONTENUTO DI X
//NEL REGISTRO INDICATO IN VISORE DA 1 A 9
// MA SE OCCORRE SI PUO' AUMENTARE LE MEMORIE UTENTE
// IN QUEL CASO OCCORRE VARIARE ANCHE LA SUB RCL
// E LA SUB CHE CANCELLA IL CONTENUTO MEMORIE UTENTE
// CIOE LA cancella_mem
void sto_n()
{
int mem = visore;
if (( mem < 1 )||(mem > 9)) return;
switch (mem)
{
case 1:
mem1 = regx;
break;
case 2:
mem2 = regx;
break;
case 3:
mem3 = regx;
break;
case 4:
mem4 = regx;
break;
case 5:
mem5 = regx;
break;
case 6:
mem6 = regx;
break;
case 7:
mem7 = regx;
break;
case 8:
mem8 = regx;
break;
case 9:
mem9 = regx;
break;
}
}
// fine void sto_n
//-------------------------------------------------------------------------
// richiama la memoria utente indicata in visore e pone il contenuto in x
//RCL n
void rcl_n()
{
int mem = visore;
if (( mem < 1 )||(mem > 5)) return;
switch (mem)
{
case 1:
{lastx = regx; regx = mem1;}
break;
case 2:
{lastx = regx; regx = mem2;}
break;
case 3:
{lastx = regx; regx = mem3;}
break;
case 4:
{lastx = regx; regx = mem4;}
break;
case 5:
{lastx = regx; regx = mem5;}
break;
case 6:
{lastx = regx; regx = mem6;}
break;
case 7:
{lastx = regx; regx = mem7;}
break;
case 8:
{lastx = regx; regx = mem8;}
break;
case 9:
{lastx = regx; regx = mem9;}
break;
}
}
//***********************************************************
// FINE SKETCH
Nessun commento:
Posta un commento
Vi ringrazio per la lettura e vi invito al prossimo post.
Se il post vi è stato utile, vi è piaciuto oppure no, scrivete un commento.
Un saluto a tutti.
Sergio
Copyright - Condizioni d’uso - Disclaimer
http://avventurarduino.blogspot.it/2012/10/inizia-lavventura-arduino-12-settembre.html