giovedì 1 maggio 2025

 

 Calcolatrice RPN con 32 tasti

In questo articolo presento la realizzazione di una calcolatrice più semplice di quella a 48 tasti, ma ugualmente potente e facile da usare.




Precedenti post sullo stesso tema:


Calcolatrice RPN con 48 tasti

Calcolatrici HP

Calcolatrice RPN 32 tasti (usando Tinkercad)

Arduino Quadro

- Calcolatrice RPN con Arduino - 
-  RPN Calculator with Arduino-
- 28/08/2017-



Questo progetto utilizza 32 tasti  + 2 pulsanti per l'input.

Un display a 4 righe x 20 con questo display si ha sotto controllo la catasta, ed è più semplice l'utilizzo della calcolatrice.

Il prototipo da me realizzato è molto spartano ma ognuno di Voi potrà realizzare un contenitore migliore. 

E' possibile alimentazione con batteria.

Questo progetto è personalizzabile aggiungendo funzioni e/o parti hardware.

E' possibile realizzarlo con altre schede della famiglia Arduino.

Per questa realizzazione ho usato:

Arduino UNO .

2 tastierini da 16 tasti per formare una tastiera di 4 righe x 8 colonne.

2 tasti funzione Giallo per 2F e Bianco per XEQ.

Display 4 righe 20 caratteri I2C 


Caratteristiche di questa versione:

A _ Con la tastiera a 32 tasti molte funzioni sono eseguibili con la pressione di un tasto.

B _  Quasi tutti i tasti hanno una seconda funzione, ho lasciato liberi i tasti numerici e             il tasto Enter, ma volendo è possibile assegnare anche a questi una seconda                     funzione.

C_ Il display con 4 righe permette di visualizzare i registri X, Y, Z e quindi                             maggior facilità dell'uso della catasta operativa.

D_ Lo sketch è predisposto per inserire nuove funzioni ed assegnarle ai tasti                         disponibili   (1-9) come seconda funzione.

E_ Possibilità d'integrare funzioni o programmi da richiamare con XEQ nn, questi             programmi o funzioni possono essere integrati e richiamabili come le funzioni della       calcolatrice facendo uso della catasta o dei registri di memoria.

F_ Possibilità di alimentazione a batteria diventando portatile.



Questa versione NON supporta la programmazione (diretta).

Manca si la possibilità di programmarla direttamente ma vedremo come sia semplice preparare dei programmi da integrare nella calcolatrice, che poi useremo richiamandoli  con XEQ nn.


Qui sotto trovate lo schema di collegamento :




Come si vede lo schema di collegamento è molto semplice.

I tastierini hanno 8 pin ciascuno visto fronte numeri, i 4 a sinistra identificano la colonna ed i 4 a destra identificano le righe.

Si collegano insieme le 4 righe di ogni tastierino e queste le mandiamo a 4 pin di Arduino 14,15,16,17 (pin  A0, A1, A2, A3).

Nello schema righe (arancione, giallo, verde, blu).

Ogni colonna invece la colleghiamo ad un pin di Arduino 8 colonne + 1 per i due tasti funzione, collegate ai pin 13,12,11,10,9,8,7,6,5.

La pressione di un tasto individua la chiusura dell'incrocio RigaX, e ColonnaY, la libreria Keypad.h provvede a restituire nella variabile key il codice del tasto premuto.

Lo sketch è molto commentato e si possono comprendere i vari passaggi.

Il dispaly I2C 4 righe è collegato a SDA e SCL.

ATTENZIONE il mio display ha questo indirizzo

LiquidCrystal_PCF8574 lcd(0x27);

Il Vostro LCD potrebbe avere altro indirizzo, controllate anche di includere la libreria del vostro lettore LCD, in qualche caso potrebbero essere necessarie delle modifiche nello sketch alle istruzioni che mostrano i dati.

Nello schema ho indicato due batterie per alimentare la calcolatrice, NON ho realizzato una alimentazione a batteria.

Penso di realizzare in nuovo progetto una calcolatrice alimentata a batteria.


Calcolatrici HP

Con questo progetto cerco di imitare la calcolatrice HP-41C, comunque insuperabile.


Per effettuare i calcoli si utilizza la Notazione Polacca Inversa ( RPN)

Il Manuale della HP41C è composto da più di 200 pagine, ci sono esempi per l'uso e spiegazioni delle vari funzioni per l'uso diretto come calcolatrice RPN.

Segue poi una guida alla programmazione con esempi e diagrammi,

 la mia imitazione NON è programmabile direttamente.

Ci sono comunque alcune differenze nell'utilizzo della HP41 e la mia "imitazione".


RPN

Da Wikipedia " La notazione polacca inversa (in inglese reverse polish notation o semplicemente RPN) è una sintassi utilizzata per le formule matematiche.
 Fu inventata dall'australiano Charles L. Hamblin"

Senza fare qui un trattato su questa notazione vi darò alcune indicazioni per operare con calcolatrici che usano questa notazione, comprese anche le calcolatrici da me costruite su imitazione della HP41.

In questa notazione prima si inseriscono gli operandi e poi l'operatore.

Vediamo semplicemente un esempio : 

per fare la somma 3+2 = 5  con la RPN avremo 3 2 + alla pressione dell'operando avremo già il risultato.

Inseriamo il 3 poi inseriamo il 2 quindi inseriamo l'operatore + ed avremo il risultato.

Dobbiamo immaginare di avere una catasta (stack) dove impiliamo gli operandi e gli operatori eseguono l'operazione facendo scorrere la catasta degli operandi in ultimo rimane il risultato:

Vediamo un esempio più complesso:

(5*3)+(4*7) = 43   con RPN si può fare 5 3 * 4 7 * +

Si inserisce il primo operando 5 poi il secondo 3 e l'operatore " * ", poi inseriamo il terzo operando 4 poi il 7 e quindi l'operatore " * " e l'operatore " + "

Su Wiki trovate ulteriori spiegazioni su questa notazione.



Lo sketch 

Lo sketch si compone di più schede per poterlo leggere più agevolmente.

Qui sotto lo schema della scheda principale.

Si richiamano le librerie utilizzate, ci sono le dichiarazioni di costanti e variabili globali, i pin assegnati ai tasti, utilizzati da Keypad, e la dichiarazione di LiquidCristal.

Poi il SETUP con il quale si visualizza sul display la versione dello sketch.

Nella scheda principale abbiamo:


void calcolatrice() // questa è un loop per utilizzare come calcolatrice
{
  uscitacalc = false;
  dirette = true;
 
  while (uscitacalc == false)
  {
    aggiornadisplay(); // sub che aggiorna il display


    tasto(); // attende pressione tasto

    tastiFunzione(); // si controlla se funzioni dirette o seconde funzioni

    if (dirette == true)funzioni();//funzioni dirette

    if (seconde == true) //  seconde funzioni 2F
    { codice = codice + 50;
      funzioni();
    }
   
   }
}



Il  LOOP principale che esegue la funzione "Calcolatrice", che attende la pressione di un tasto, successivamente si controlla se è stato premuto il tasto seconda funzione, 2F se NO si esegue il codice della funzione diretta, altrimenti si esegue il codice della seconda funzione, quindi si ritorna in attesa pressione tasto.




Qui sotto i codici restituiti alla pressione di un tasto

byte chiave [righe][colonne] =
{ {49, 50, 51, 43, 58, 59, 60, 61, 62}, // prima riga   1-2-3-+-sin-cos-tan-xRy-2F
  {52, 53, 54, 45, 66, 67, 68, 69, 70}, // seconda riga 4-5-6 - sto-PI-sqr-fix- XEQ
  {55, 56, 57, 44, 74, 75, 76, 77, 78}, // terza riga   7-8-9 x rcl-eex-chs-R^- no tasto
  {90, 48, 46, 47, 82, 83, 84, 85, 86} //  quarta riga  ENT 0 . / del-x>y-R!- lastx -no tasto

};

All'interno del loop calcolatrice la funzione qui sotto tasto() restituisce il codice

corrispondente a riga - colonna, quindi esegue le funzioni dirette

con i codici relativi oppure se è stato premuto il tasto 2F le seconde funzioni.



//-------------------------------------------------------------------------------
//         ATTESA PRESSIONE TASTO
//*********************************************************************Y

void tasto()
{
  key = 0;  // 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;
  }
}

Eseguita la funzione ritorna nel loop principale.

Descrizione delle altre schede dello sketch


SCHEDA FUNZIONI DIRETTE E SECONDE

Qui trovate tutte le funzioni della calcolatrice dirette e le seconde funzioni (2F)


SCHEDA XEQ-STO-RCL

 dedicata alle funzioni che vengono chiamate con indirizzo nn


SCHEDA PROG

Per i programmi da chiamare con XEQ (nn) numero programma, che potete integrare con i vostri programmi.


SCHEDA NOMI FUNZIONI


In questa trovate tutti i nomi funzioni che vengono visualizzati a destra sulla prima riga del LCD 
In questo sketch ho dato un tempo di 300 ms, potete cambiarlo o non visualizzare il nome funzione.




Indice delle funzioni e loro uso:
----------------------------------------------------------------------
Funzioni dirette
----------------------------------------------------------------------

43, 45, 44, 47 : somma, sottrazione, moltiplicazione, divisione : Si introduce il primo operando, poi il secondo e si preme il tasto operatore il risultato viene posto in X.


90 ENT: ENTER = Introduce in x quanto è contenuto sul "visore" e fa salire il resto della catasta, quello che era in x sale in y, il contenuto di y sale in z, il contenuto di z sale in t.


58,59,60 : SIN, COS, TAN : Restituiscono in X, il valore seno, tangente, coseno

dell'angolo in RADIANTI contenuto in X , in Lastx rimane l'angolo.


61: X^2 : Eleva x al quadrato:

66 : STO: Memorizza in un registro utente ( da 1 a 19) il contenuto di X, immettere in X il valore da memorizzare ( potrebbe anche essere il risultato di precedenti operazioni) poi nel visore scrivere il registro dove si vuole memorizzare (01-19) quindi premere il tasto funzione STO. Il contenuto di x rimane in x e viene duplicato nel registro indicato.

67: PI : costante PI (pigreco) in x.

68: SQR : Esegue la radice quadrata del contenuto in X , pone il risultato in X.

69: FIX: Si possono impostare il numero dei decimali visualizzati ( da 0 a 5 ), scrivere in visore il numero da 0 a 5 e premere il tasto funzione FIX. Di default ho posto a 4 i decimali.


74: RCL: Richiama in X il contenuto del registro ( da 01 a 19), immettere nel visore il numero del registro e premere il tasto funzione RCL, in X avremo il contenuto del registro richiesto, il registro rimane invariato, nel registro lastx viene prima salvato il contenuto precedente di X.

75: EEX : Introduzione di numero in notazione scientifica, introdurre il numero in Y, poi l'esponente in base 10 in X premere EEX.

76: CHS : cambia segno ad X.

77: R^ : Ruota sù la catasta.

82: DEL : Cancella il VISORE ( prima riga ).

83: X>Y: Scambio X con Y, Insieme alla rotazione della catasta si può cambiare l'ordine degli operandi in catasta.

84: R! : Ruota giù, la catasta viene fatta ruotare y in x, z in y, t in z, x in t. Ad ogni pressione ruota fino a tornare nella situazione iniziale, utile per rivedere tutto il contenuto della catasta, ed eventualmente utilizzando lo scambio cambiarne la disposizione.

85: LAS: LASTX = Recupera in X il contenuto precedente ad una operazione, il contenuto di X viene spostato in Y. (Utile per recuperare in caso di operazioni errate.)


--------------------------------------------------------------------------------
Funzioni seconde ( prima premere il tasto 2F giallo)


appare sulla linea del visore il 2, 

per togliere la 2F premere ancora il tasto,

sul visore appare 1.
--------------------------------------------------------------------------------

108, 109, 110: asin, acos, atan: arco seno di x, arco tangente di x, arcoseno di x,

restituiscono in x l'angolo relativo al valore trovato in x.

l'angolo è espresso in RADIANTI. In Lastx si conserva il valore di partenza.


111 : FACT : calcola il fattoriale di x.

116: LOG : logaritmo in base 10.

117: Y^x : Eleva a potenza , introdurre in Y il valore, in X l'esponente, il risultato è posto in X.

118 : xRy : radice X di Y radice ennesima di Y.

119: LN : logaritmo in base e.

124: R>P : Da coordinate rettangolari a polari, x,y inserire le due coorinate e premere il tasto R>P restituisce in y la distanza in x l'angolo in radianti.

125: P>R : da polari a rettangolari inserire in x l'angolo alfa in RADIANTI

inserire in y la magnitudine o distanza. restituisce le coordinate in x e y.

126: 1/X : Uno su X.

127: e^x : Antilogaritmo naturale elva e alla potenza in x.

132: R-hms : Converte da Radianti a sessagesimali.

133: hms-R : Converte da Sessagesimali a Radianti. ATTENZIONE le funzioni seno, coseno ecc.. funzionano con angoli in Radianti.

134: CLST: cancella i registri catasta e visore, cancella anche lastx.

135: 10^x : eleva 10 alla X Antilogaritmo decimale.

93: MOD : resto della divisione Y / X

94: CLX : cancella il registro X.

95: %D : calcola pa differenza percentuale fra Y ed X

96: % : calcola la percentuale x del registro y

97: FRC : parte frazionaria di x ( elimina la parte intera di x)

98: INT : parte intera di un numero ( elimina la parte decimale senza arrotondamento).


--------------------------------------------------------------------------------
Tasto giallo 2F e Tasto Bianco XEQ 

--------------------------------------------------------------------------------

62: (tasto giallo) F2 (seconde funzioni ) abilita e disabilita le seconde funzioni quando è attivo appare 2 nel visore per tornare a funzioni dirette premere ancora e torna 1 sul visore.


70: (tasto bianco) XEQ : Questa funzione richiama ed esegue funzioni o programmi, si scrive nel visore il numero della funzione e si preme il tasto XEQ, prima si dovrà immettere in catasta i valori richiesti dalla funzione e o programma rispettandone anche l'ordine.

In uscita avremo la catasta aggiornata con i risultati.

Possono essere usati anche i registri utente che la funzione e/o programma possono utilizzare come input e/o come output.

120: cat ( seconda funzione tasto bianco) : Esegue il catalogo dei programmi eseguibili con XEQ.


Questa è la tabella di corrispondenza dei tasti e del codice.




La calcolatrice accesa





Tasto giallo attiva le seconde funzioni sotto ai tasti evidenziate in GIALLO, asin,acos, ecc...

Il Tasto BIANCO (XEQ) attiva i programmi o funzioni utente, con la pressione 2F (giallo) si attiva cat indicato in basso al tasto Bianco, cat esegue il catalogo delle funzioni o programmi richiamabili con il tasto Bianco XEQ.

IMPORTANTE
Prima di richiamare una funzione o un programma utente memorizzare i dati richiesti in catasta o in memoria.


------------------------------------

Programmi aggiuntivi da eseguire con XEQ nn

--------------------------------------

Ho scritto qualche programma richiamabile con XEQ nn.
Per utilizzarli scrivere il numero sul visore e premere il tasto XEQ.
SI dovrà inserire i dati nella catasta come richiesto dal programma, prima di lanciare il programma stesso.

questi già pronti:


void catalog()
{
  String nomeProg = "";
  int numProg = 0;
  lcd.clear();
   // se si aggiungono programmi aggiornare la lista qui sotto
    for (int i = 1; i < 7; i++)
    {
    numProg = i;
   
    if (i == 1) nomeProg = "LISTA PROGRAMMI";
    if (i == 2) nomeProg = " TRI. CARNOT   ";
    if (i == 3) nomeProg = " PARALLELO RES ";
    if (i == 4) nomeProg = " TRI. ERONE    ";
    if (i == 5) nomeProg = " PARTITORE V   ";
    if (i == 6) nomeProg = " SCORPORO IVA  ";
     
    lcd.setCursor(1, 0);
    lcd.print(numProg);
    lcd.setCursor(1, 4);
    lcd.print(nomeProg);
    delay (1200);
    lcd.clear();

    }
   
   
  lcd.clear();
 

}

Questi programmi sono realizzabili scrivendoli con l'IDE Arduino nella scheda PROG-DI-XEQnn

Se si vuole aggiungere un programma si
// dovrà  modificare queste parti:
// 1) aggiungere in questa scheda il programma con suo nome
// 2) modificare in questa scheda il prog catalog aggiungendolo
// 3) modificare la scheda XEQ-STO-RCL nella parte relativa
// alla chiamata XEQnn inserendo nel ciclo case
// il nome del nuovo programma


Ho aggiunto il programma (1)  void catalog() 

questo programma visualizza il numero ed il nome dei programmi contenuti nella scheda quindi se aggiungete programmi aggiornate anche il catalog con il nome del nuovo programma. 

 programma (2)   carnot() 

void carnot()  // funzione 02 ****
     {
      //  angolo in x in RADIANTI
     double latoa = regz; // uno dei lati conosciuti
     double latob = regy; // il secondo lato conosciuto
     double angolo = regx;// angolo compreso fra i due lati
     //  si cerca il terzo lato
     //  gli angoli sono inseriti in RADIANTI  INPUT
     //  gli angoli saranno in RADIANTI

     //***********************************
     // due lati ed angolo compreso
     
     double latoc = sqrt(pow(latoa,2)+ pow(latob,2)-(2*latoa*latob)*cos(angolo));

     regx = latoc;  // in x troviamo il lato cercato opposto all'anglo compreso
     regy = latob;  // lato noto
     regz = latoa;  // lato noto
     regt = angolo; // angolo noto  in radianti
     //**************************************************
} // fine carnot


Si calcola il terzo lato del triangolo, conoscendo i due lati e l'angolo compreso, angoli in radianti.


 programma (3)  parallelo() resistori inserire in x ed in y i valore di due resistenze.
( valori espressi nella stessa unità di misura ) in x ritroviamo il valore del parallelo. 

void parallelo() // prog 03
{
 //*************************************************************
 //       PARALLELO RESISTORI
 //
 //  //   r1 -r2
 //*************************************************************
 // si inseriscono in x ed in y i valori di due resistori.
 //
 // questo programma modifica tutto lo stak
 // in uscita avremo in x la resistenza parallelo
 //
   
     double resp = 0;
   
        resp =  1/ ((1/regx)+ (1/regy));
       

      regx = resp;
      regy = 0;
      regz = 0;
      regt = 0;

}
// fine parallelo
//******************************************************************

programma (4)  erone()  Calcolo superfice triangolo noti i tre lati.
si inseriscono i tre lati in x,y,z e si ottiene la superficie in x.


//************************************************************
//              superficie triangolo con Erone
//    noti i tre lati  in x lato a, in y lato b, in z lato c
//   restituisce in x la superficie, in y,z, t i lati a,b,c.
//*************************************************************
//
void erone()  //  fuzione 04 ****
{
 double latoa = regx;
 double latob = regy;
 double latoc = regz;
 
if (latoa >= (latob+latoc)|| latob >= (latoc + latoa)|| latoc >=(latob+latoa)) return;
         
    else {
         
            double perimetro = latoa + latob + latoc;
            double per2 = perimetro/2;
            double super = sqrt(per2*((per2-latoa)*(per2-latob)*(per2-latoc)));

            regx = super;
            regy = latoa;
            regz = latob;
            regt = latoc;

         }
}// fine erone

programma (5)   partitore()  Inserire Vin in x, e i valori delle resistenze in serie 

Vedi listato qui sotto 

//*******************************************************
// partitore di tensione
//*******************************************************
void partitore() // funzione 05
{
  //Vn= VIN(Rn/(R1 + R2 + R3)

    double r1 = regy; // inserire valore R1 in omm
    double r2 = regz; // inserire valore R2 in omm
    double r3 = regt; // inserire valore R3 se non serve inserire 0
    double vin = regx;// inserire Vin in Volt

    double v1 = vin * ( r1 / ( r1+r2+r3));
    double v2 = vin * ( r2 / ( r1+r2+r3));
    double v3 = vin * ( r3 / ( r1+r2+r3));

    regx = v1;  // Volt in v1
    regy = v2;  // Volt in v2
    regz = v3;  // Volt in v3
    regt = 0;
} // fine partitore

programma (6)  sco_iva()  Scorporo iva inserire in y l'aliquota, ed in x il totale con iva.

avremo in x iva, in y l'imponibile, in z il totale ed in t l'aliquota.


//*****************************************************************
//       SCORPORO IVA impostare  in Y l'aliquota
//                    impostare  in X il totale compreso IVA
//           in OUTPUT avremo:
//           x = IVA, y = imponibile, z = il totale, t = aliquota.
// *****************************************************************
//          
void sco_iva()//   funzione 06 ****
{
      //****************************************
      double aliq = regy;
      double total = regx;
      double imponibile = (total*100)/(100+aliq);
      //*****************************************
      //output
      regx = (total - imponibile); // IVA
      regy =  imponibile;
      regz = total;
      regt = aliq;
 
     //*****************************************
} // fine scorporo iva
//---------------------------------------------------------------------------------------
Lo sketch, completo qui sotto:
Nell' IDE di Arduino ho suddiviso lo sketch in schede.




======== inizio  sketch   ================




/* ********************************************************************************
   SERGIO PRENLELOUP
   01-05-2025
   versione v-110-32
    V-220425
    V-240425
    V-040525
    V-070525 //VERSIONE CON 2F CHE RITORNA IN AUTOMATICO ESEGUITA LA SECONDA FUNZIONE
    IN QUESTA VERSIONE LE SECONDE FUNZIONI SONO NELLA VOID SECONDE FUNZIONI
    V-090525
    V-22290
  **********************************************************************************
  Nuovo sketch calcolatrice RPN  NON programmabile con 32 tasti
 

  Modalità Diretta == si eseguono le funzioni assegnate ai tasti in modo diretto,
  in modo simile all'uso della HP-41  alcuni tasti hanno la seconda funzione.

      Si possono richiamare funzioni con XEQ nn
       che si potranno aggiungere con programmazione in C
     
      In questo modo anche questa calcolatrice è in pratica programmabile in "C"
     
 
  Questa è la versione con 32 tasti con due tastierini da 16
PIU due tasti giallo e bianco
 
 
  Il display ha 4 linee

    Mostrano i registri di catasta X, Y , Z
    la riga più in basso serve solo come
     visualizzatore del dato introdotto,
     a sinistra appare 1 quando NON è premuto il tasto 2F funzioni dirette
    oppure 2 quando premuto il tasto per la seconda funzione.

  La calcolatrice si può utilizzare direttamente in modo simile alla HP-41
  Utilizzando catasta e registri di memoria.
     

  La catasta operativa.
  X, Y, Z, T, U,  lastX. ( in questa versione le lascio come variabili globali)

  Ci sono 19 registri di memoria utente possono essere usati direttamente o
  in programmi della funzione XEQnn (variabili globali).
  ***********************************************************************************

  Si possono aggiungere allo sketch funzioni o programmi che utilizzino
   sia la  catasta e/o registri di memoria;
  per richiamarli basterà eseguire XEQ nn (01-99).
  sul blog avventurarduino è indicato come preparare queste funzioni aggiuntive.

  //*********************************************************************************
  FUNZIONI PROGAMMABILI E RELATIVI CODICI.

  il codice è un byte ( quindi da 0 a 255)

  MEMORIE & INDICI = da 01 a 19   alla pressione delle istruzioni:

  STO; RCL; XEQ - si attiva una R nella sinistra del visore e si introducono due cifre

  01 per memoria 1 ... fino a 19  ( funzioni STO e RCL )

  per la funzione XEQ da 01 fino a 40 ma implementabile modificando lo sketch.
  ---------------------------------------------------------------------------------
 CODICI TASTO FUNZIONI DIRETTE
 
  49-1, 50-2, 51-3,  52-4, 53-5, 54-6,  55-7, 56-8, 57-9,  48-0, 46-punto,  
  43 +,  45 -, 44-*, 47-/, 90 ENTER
  ----------------------------------------------------------------------------------
  58 SIN, 59 COS,  60 TAN,  61 DIS,  62 (2F) tasto giallo  
                                      108 ASIN, 109 ACOS, 110 ATAN, 111 DIS,  112 2F
  66 STO, 67 PI,   68 SQRT, 69 FIX,  70 XEQ  tasto bianco  
                                      116 LOG,  117 Y^X,  118 X^2,  119  LN,  120 DIS
  74 RCL, 75 EEX,  76 CHS,  77 DIS,  78 XX                
                                      124 >R,   125 >P,   126 1/X,  127 e^X,  128 XX
  82 DEL, 83 X<>Y, 84 R\ ,  85 LASTX,86 XX                  
                                    132 RAD,  133 hms,  134 CLST, 135 10^X, 136 XX
 
 ************************************************************************************
  CON LA PRESSIONE DEL TASTO F2 (62) tasto giallo SI ATTIVANO LE SECONDE FUNZIONI
  A QUESTI CODICI SI AGGIUNGE +50 OTTENENDO COSI  CODICI DA 93 A 140 DISPONIBILI
  PER SECONDE FUNZIONI
  ATTIVATE DA 108 A 135
 ***********************************************************************************

  HARDWARE UTILIZZATO
 
  ARDUINO UNO
 
  una tastiera composta dall'assemblaggio di 2 tastierini 4x4, ottenendo
  così una tastiera 8 x 4 ( 32 tasti) 4 RIGHE X 8 COLONNE,
  + 1 COLONNA CON DUE TASTI GIALLO E BIANCO
  Ai tasti verranno assegnate 2 o più funzioni.
 
  Display lcd 20x4 (I2C)
 
  Possibilità di alimentazione a batteria.
 *********************************************

 */
 //*******************************************
 // librerie incluse

 //---------------------------------------
 // E' inclusa anche da sistema la <maht.h>
#include <Wire.h>
#include <LiquidCrystal_PCF8574.h>
#include <Keypad.h>

//***********************************************
 const byte righe = 4;   // per keypad
 const byte colonne = 9;
//********************************************************************

//*************************************************************************
// codici tastierino ridotto per 32 tasti + 2 tasti singoli giallo e bianco
//*************************************************************************

byte chiave [righe][colonne] =
{ {49, 50, 51, 43, 58, 59, 60, 61, 62}, // prima riga   1-2-3-+-sin-cos-tan-xRy-2F
  {52, 53, 54, 45, 66, 67, 68, 69, 70}, // seconda riga 4-5-6 - sto-PI-sqr-fix- XEQ
  {55, 56, 57, 44, 74, 75, 76, 77, 78}, // terza riga   7-8-9 x rcl-eex-chs-R^- no tasto
  {90, 48, 46, 47, 82, 83, 84, 85, 86} //  quarta riga  ENT 0 . / del-x>y-R!- lastx -no tasto

};

//-----------------------------------------------------------------------------
//           pin assegnati al tastierino per ARDUINO UNO
//-----------------------------------------------------------------------------
byte pinrighe[righe] = {14, 15, 16, 17};  // pin analogici A0, A1, A2, A3
byte pincolonne[colonne] = {13, 12, 11, 10, 9, 8, 7, 6, 5};

// -----------------------------------------------------------------------------
//  QUI SOTTO VARIABILI E COSTANTI
//
//********************************************************************
byte key; // contiene il codice tasto premuto
byte codice = 0; // codice operativo calcolato a partire da key (codice asccii)
int poscifra = 0;// tiene conto delle cifre
boolean sciee = false; // true visualizza in notazione scientifica
boolean stato = false; // per la calcolatrice ( seconda funzione dei tasti)
boolean dirette = true; //funzioni_dirette
boolean seconde = false; //funzioni_F2
boolean reg = false; // registri memoria (STOnn- RCLnn)

//--------------------------------------------------------------------------------
boolean se = true;  // per le funzion if
boolean xeq = true;  // per inserimento codice in Xeq_nn
boolean uscitacalc = true; // per uscire da caLCOLATRICE si mette a false
//-------------------------------------------------------------------------------
float k1 = 10000000;// numero massimo visualizzabile poi si passa in SCIE
float k0 = 0.00001; // numero decimale più piccolo visualizzabile
// per l'inserimento dei numeri decimali
int pdec = 0; // punto decimale
int ndec = 0; // conta decimali
int fix = 4; //decimali di default

//*********************************************************************
// *******    V A R I A B I L I G L O B A L I  ************************
//*********************************************************************

//*** registri catasta
float lastx = 0.0; // recupero del registro x
float regx = 0.0;
float regy = 0.0;
float regz = 0.0;
float regt = 0.0;
float regu = 0.0;

// registri speciali
float regxs = 0.0; // per visualizzare la parte decimale della notazione sci
int esponente = 0; // esponente nella scientifica
float visore = 0.0; // accumula i valori immessi
//************************************************************************

//************************************************************************
//          STRINGHE
//************************************************************************
//String stringaMenu0 =  String("");
//String stringaMenu1 =  String("");

//----------------------------

//***********************
//   memorie utente
//***********************
//--------------------------

float mem1 = 0.0;
float mem2 = 0.0;
float mem3 = 0.0;
float mem4 = 0.0;
float mem5 = 0.0;
float mem6 = 0.0;
float mem7 = 0.0;
float mem8 = 0.0;
float mem9 = 0.0;
float mem10 = 0.0;
float mem11 = 0.0;
float mem12 = 0.0;
float mem13 = 0.0;
float mem14 = 0.0;
float mem15 = 0.0;
float mem16 = 0.0;
float mem17 = 0.0;
float mem18 = 0.0;
float mem19 = 0.0;
//***********************

// inizializza keypad e lcd
//--------------------------------------------------------------------------------
Keypad keypad = Keypad (makeKeymap(chiave), pinrighe, pincolonne, righe, colonne);

LiquidCrystal_PCF8574 lcd(0x27);
//--------------------------------------------------------------------------------


void setup()
{
  Wire.begin();
  Wire.beginTransmission(0x27);
  lcd.begin(20, 4); // initialize the lcd
  lcd.setBacklight(255);
  lcd.home();
  lcd.clear();
 
  lcd.setCursor(0, 1);
//lcd.print("1234567890123456");
  lcd.print("v-506-v110-32-07"); // VERSIONE
  lcd.setCursor(0, 2);
//lcd.print("1234567890123456");
  lcd.print("Calcolatrice RPN");
  lcd.setCursor(0, 3);
 //lcd.print("1234567890123456");
  lcd.print("byte 22290-090525");
 
 
  delay (3000);

}


//-----------------------------------------------------------------------
//    LOOP PRINCIPALE
//---------------------------------------------------------------------


void loop()
{
   calcolatrice();
}

//**** END LOOP ******************************************************
//********************************************************************

//*******************************************************************
//  **********    F U N Z I O N I     **********
//*******************************************************************

void calcolatrice() // questa è un loop per utilizzare come calcolatrice
{
  uscitacalc = false;
  dirette = true;
 
  while (uscitacalc == false)
  {
 

    aggiornadisplay(); // sub che aggiorna il display


    tasto(); // attende pressione tasto

    tastiFunzione(); // si controlla se funzioni dirette o seconde funzioni

    if (dirette == true)
    {
      funzioni();//funzioni dirette
    }
   
    if (seconde == true)
    {
      codice = codice + 50;
      seconde_Funzioni();  // in questo caso esegue le seconde funzioni
    }

  }
}
//***************** fine calcolatrice ******************************
// con questa si controlla la seconda funzione
//  (2F 62) se premuto attiva se già attive disattiva
void tastiFunzione()
{
  if ( codice == 62) // F2 seconde funzioni
  {
    if (seconde == false)
    {
      seconde = true;
      dirette = false;
    }
    else
    {
      seconde = false;
      dirette = true;
    }

  }  
}

   

//-----------------------------------------------------------
// qui si controlla il codice
// del tasto premuto e si indirizza l'esecuzione
// della relativa funzione e quindi si ritorna.
// queste funzioni sono per l'uso diretto della calcolatrice
//------------------------------------------------------------

void funzioni()
{
  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();


    }
  }
 
  //-------------------------------------------------------------


 
  if (codice ==  90)// ENTER
  {
    nome_funzione(codice);
    enter();

  }
 
  if (codice == 43)// somma
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    somma();
  }

  if (codice == 44)// moltiplicazione
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    moltiplicazione();
  }

  if (codice == 45) //sottrazione
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    sottrazione();
  }

  if (codice ==  46) // punto decimali
  {
    punto(); // con questa si pone a 1 la pdec
  }


  if (codice == 47) //divisione
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    divisione();
  }
  //----------------------------------------
  // dal 48 al 57 ci sono le cifre da 0 a 9
  //----------------------------------------

  if (codice ==  58) // seno di x angolo in RADIANTI
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    senox();
  }

  if (codice ==  59)// coseno  x  angolo in x in RADIANTI
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    cosenx();
  }


  if (codice ==  60)//tangente  x  angolo in x in RADIANTI
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    tangx();
  }

 if (codice == 61) //SQx_Y() radice x di Y
  {
    nome_funzione(codice);
    if (visore != 0)enter();

    xquadro();
  }


 //---------------------------------------
    // codice 62 2F seconde funzioni

    // al codice viene aggiunto +50
 //-----------------------------------------


 //-----------------------------------------
 
  if (codice ==  66)// STO n
  {
    nome_funzione(codice);
   // impostareg();
    if ((visore > 0) && (visore <= 19))
    {
      sto_n();
      del();
    }
  }
  //-----------------------------------------
  if (codice ==  67)//  // PI GRECO
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    pigreco();
  }

 
  if (codice ==  68) //radice quadrata
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    sqrtx();
  }

 if (codice ==  69)//fix decimali
  {
    visfix();
  }

 
  if (codice ==  70) // Xeq esegue funzione da 1 a ...
  {
   
    nome_funzione(codice);
    //impostareg();
    if ((visore > 0) && (visore <= 40))
    {
      Xeq_nn();
      del();
   
    }
  }

 //-------------------------------------
 
  //-------------------------------------
  if (codice ==  74) //RCL n
  {
    nome_funzione(codice);
   // impostareg();
    if ((visore > 0) && (visore <= 19))
    {
      rcl_n();
      del();
    }
  }
  //--------------------------------------

  if (codice ==  75)//EEX
  {
    nome_funzione(codice);
    inputsci();
  }


  if (codice ==  76)// CHS cambio segno (moltiplica x per -1
  {
    nome_funzione(codice);
    cambiasegno();  //  CHS
  }
 
  if (codice ==  77) //ruota SU catasta
  {
   nome_funzione(codice);
   ruotaSU();

  }
 
  //-------------------------------------
  // 78 FUNZIONE NON PRESENTE no tasto
  //-------------------------------------
 
  if (codice ==  82)//cancella carattere
  {
    del();
  }

  if (codice ==  83)//  x<>y scambio x con y
  {
    nome_funzione(codice);
    scambio();
  }

  if (codice ==  84) // R! scorre catasta giù
  {
    nome_funzione(codice);
    ruota();
  }

  if (codice ==  85)//Lastx ( recupera x )
  {
    nome_funzione(codice);
    last_x();
  }
  //-------------------------------------------
  // 86 TASTO NON PRESENTE

  return;
}
 //--------------------------------------------------
 //end funzioni_dirette
 //--------------------------------------------------

 //***************************
 //
 //     SECONDE FUNZIONI
 //
 //**************************-
void seconde_Funzioni()
{


  if (codice == 93) // MODULO PONE IN X IL RESTO DELLA DIVISIONE Y/X
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    resto();
      seconde = false;
      dirette = true;
  }
 
  if (codice == 94) // cancella regisro X
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    cancellaX(); //cancella registro x
     seconde = false;
      dirette = true;
  }
 
 
  if (codice == 95) // differenza percentuale
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    percentoDi();
      seconde = false;
      dirette = true;
  }
 

 // PERCENTUALE
  if (codice == 96) // percento x%y
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    percento();
      seconde = false;
      dirette = true;
  }

  if (codice == 97) // FRC PARTE FRAZIONARIA DI X
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    parteFRC();
    seconde = false;
      dirette = true;
  }

  if (codice == 98) // PARTE INTERA DI X
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    parteINT();
    seconde = false;
      dirette = true;
  }

  if (codice ==  108)// arco seno di x
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    arcosen_x();
    seconde = false;
      dirette = true;
  }
  if (codice ==  109)// arco coseno di x
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    arcocos_x();
    seconde = false;
      dirette = true;
  }
 

  if (codice ==  110)// arco tangente di x
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    arcotan_x();
    seconde = false;
      dirette = true;
  }


 //112 2F TASTO GIALLO
 

  if (codice == 111) //fact() // funzione fattoriale max 30
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    fact();
    seconde = false;
      dirette = true;
  }

  if (codice ==  116)////Log10
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    log10x();
    seconde = false;
      dirette = true;
  }
 
  if (codice ==  117)// potenza eleva y alla x
  {
    nome_funzione(codice);
    Ysux();
    seconde = false;
      dirette = true;
  }


  if (codice ==  118)// quadrato di x
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    SQx_Y();
    seconde = false;
      dirette = true;
  }


  if (codice ==  119) // ln logaritmo naturale
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    logN();
    seconde = false;
      dirette = true;
  }

 // CODICE 120 2F DI TASTO  CATALOGO FUNZIONI xeq
  if (codice ==  120) // catalogo programmi xeq
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    catalog();
    seconde = false;
      dirette = true;
  }
 //----------------------------------------------------

 
  if (codice ==  124)// da rettangolari a polari
  {
    nome_funzione(codice);
    R_P ();
    seconde = false;
      dirette = true;
  }
 
   
  if (codice ==  125)
  {
    nome_funzione(codice);
    P_R(); // da polari a rettangolari p>=0 e alfa >=0 e <= pi/2
    seconde = false;
      dirette = true;
 
  }



  if (codice == 126) // 1/x
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    unosux();
    seconde = false;
      dirette = true;
  }


  if (codice == 127) // e alla x exp(x)
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    expX ();
    seconde = false;
      dirette = true;
  }



  if (codice ==  132)// converte da radianti in sessagesimali
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    radHMS();
    seconde = false;
      dirette = true;
  }

  if (codice ==  133)// da gradi sessagesimali a radianti
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    HMSrad();
    seconde = false;
      dirette = true;
  }
  // CLST  //
  if (codice == 134) // CANCELLA CATASTA E VISORE
  {
    nome_funzione(codice);
    cancella(); //cancella catasta e visore
    seconde = false;
      dirette = true;
 
  }

 

  if (codice == 135) //10alla x
  {
    nome_funzione(codice);
    if (visore != 0)enter();
    po10x();
    seconde = false;
      dirette = true;
  }
  return;

}
 
// ********************
// end seconde funzioni
// *********************
void visualizzaNome(String nomeFunzione)
{
  lcd.setCursor(15, 3);
  lcd.print(nomeFunzione);
  delay(300);  // TEMPO DOVE SI MOSTRA A SINISTRA IL NOME DELLA FUNZIONE
}


//--------------------------------------------
//         ATTESA PRESSIONE TASTO
//********************************************

void tasto()
{
  key = 0;  // 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;
  }
}
//**************************************************
//  AGGIORNA IL DISPLAY
//**************************************************
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("Z:");
  lcd.setCursor(4, 0);
  lcd.print(regz, fix);

  lcd.setCursor(0, 1);
  lcd.print("Y:");
  lcd.setCursor(4, 1);
  lcd.print(regy, fix);

  lcd.setCursor(0, 2);
  lcd.print("X:");
  lcd.setCursor(4, 2);

  if (sciee == true)
  {
    sci(); // sub per notazione scientifica
    lcd.print(regxs, 5);
    lcd.setCursor(14, 2);
    lcd.print (esponente);
  }

  else lcd.print(regx, fix); // visualizzazione normale

  lcd.setCursor(0, 3);
  if (dirette == true)lcd.print("1:");

  if (seconde == true)lcd.print("2:");

  if (visore != 0)
  {
    lcd.setCursor(4, 3);
    lcd.print(visore, fix);
  }
  sciee = false;
 
}
//---------------------------------------------------------
// questa sub prepara "regxs" ed "esponente" per visualizzare in notazione sci
// ATTENZIONE regxs serve solo per la visualizzazione di numeri in notazione
// scientifica in abbinamento al registro "esponente"
// non viene utilizzato direttamente nei calcoli i due rappresentano comunque
// il contenuto di "regx" che deve essere utilizzato per il calcolo.

//**************************************************************
//       PER NOTAZIONE SCIENTIFICA SI CALCOLA ESPONENTE
//*************************************************************

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
}

// ************************************************************************

byte impostacodice() // imposta codice DEL TASTO BATTUTO
{
  byte cod = 0;
  tasto();   // inserisce nella variabile globale codice il
  // tasto battuto  ( da 43 a 90 )


  tastiFunzione(); // lavora sulle boolean delle funzioni

  //  controlla lo stato delle funzioni e crea il codice effettivo
  //  aggiungendo al codice creato da tasto() 50,150 il +100 è per le alfa

  // se il tasto premuto è un tasto funzione ritorna
   if (codice == 65 )return cod;
  // in questo modo ritorna cod = 0

  // con queste si crea il codice effettivo considerando
  //il tasto funzione relativo

  if (dirette == true)cod = codice;//funzioni dirette

  if (seconde == true) //  deconde funzioni 2F
  { codice = codice + 50;
    cod = codice;
  }
 

  return cod;

}

//---------------------------------------------------------------

void impostanumero()
{
  poscifra = poscifra + 1;
  if (poscifra <  9)
  { int cifra =  codice - 48;
    visore = visore * 10 + cifra;

  }
  else {codice = 90;}//enter
}
//****************************************************************
// 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 = 90;
  }

}

//----------------------------------------------------------
//               FUNZIONI DIRETTE E SECONDE CON 2F
//----------------------------------------------------------

void senox()// SENO di x (con x in radianti)
{
  lastx = regx;
  regx = sin (regx);
}
void cosenx()// COSENO di x (con x in radianti)
{
  lastx = regx;
  regx = cos (regx);
}

void tangx() // TANGENTE di x (con x in radianti)
{
  lastx = regx;
  regx = tan (regx);
}
void unosux() // 1/X
{
  if (regx == 0)return;
  lastx = regx;
  regx = ( 1 / regx);
}
void sqrtx() // RADICE QUADRATA DI X
{
  lastx = regx;
  regx = sqrt (regx);
}

void xquadro() // X ELEVATO AL QUADRATO
{
  lastx = regx;
  regx = regx * regx;
}
void pigreco() // PIGRECO
{
  lastx = regx;
  regu = regt;
  regt = regz;
  regz = regy;
  regy = regx;
  regx = PI;
}
void clx() // AZZERA IL REG X
{
  lastx = regx;
  regx = 0.0;
}
void log10x() // LOGARITMO DI X IN BASE 10
{
  lastx = regx;
  regx = log10(regx);
}
void logN() // LOGARITMO DI X IN BASE e
{
  lastx = regx;
  regx = log(regx);
}

void po10x()//10alla x
{
  if (regx < 30)
  {
    lastx = regx;
    regx = pow(10, regx);
  }
}

void expX ()// exp e^x
{
  lastx = regx;
  regx = exp(regx);
}


void Ysux()// Y^x ( Y elevato alla X )
{
  lastx = regx;
  regx = pow (regy, regx);
}

void SQx_Y()// RADICE ENNESIMA DI Y  (radice x di Y)
{
  lastx = regx;
  regx = pow (regy,(1/regx));
}



void HMSrad() // da gradi sessagesimali H.ms a Radianti
{
  lastx = regx;
  sess_sedec();
  regx = regx * PI / 180.0;
}
void radHMS() //da radianti a gradi sessagesimali H.ms
{
  lastx = regx;
  regx = regx * 180.0 / PI;
  sedec_sessa();
}
void somma()//43
{
  lastx = regx;
  regx = (regx + regy);
  regy = regz;
  regz = regt;
  regt = regu;

  del();
}

void  sottrazione()//45
{
  lastx = regx;
  regx = (regy - regx);
  regy = regz;
  regz = regt;
  regt = regu;

  del();
}

// punto decimali
void punto()//46
{
  pdec = 1;
  ndec = 0;
  codice = 0;
}

//cancella carattere dal visore
void del() //82
{
  visore = 0;
  poscifra = 0;
  codice = 0;
  pdec = 0;
  ndec = 0;
}


void enter()//90 TASTO ENTER INTRODUCE  IL VISORE IN CATASTA
{
  regu = regt;
  regt = regz;
  regz = regy;
  regy = regx;
  regx = visore;

  del();
}

// moltiplicazione  //44
void moltiplicazione()
{
  lastx = regx;
  regx = (regy * regx); // esegue la moltiplicazione
  regy = regz;
  regz = regt;
  regt = regu;
  del();
}

//divisione  //47
void divisione()
{

  if (regx == 0) return;  //******* divisione per 0

  lastx = regx;  // prima salva in lastx il valore di x
  regx = (regy / regx);      // esegue la divisione
  regy = regz;
  regz = regt;
  regt = regu;
  del();
}
// clr   // CLST  //
void cancella() //cancella catasta e visore
{
  regu = 0.0;
  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;
}

// clx
void cancellaX() //cancella registro x
{
  lastx = regx;
  regx = 0.0;
 
  visore = 0.0;
  poscifra = 0;
  codice = 0;
  pdec = 0;
  ndec = 0;
}


void cancella_mem()// cancella memorie utente
{
  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;
  mem10 = 0.0;
  mem11 = 0.0;
  mem12 = 0.0;
  mem13 = 0.0;
  mem14 = 0.0;
  mem15 = 0.0;
  mem16 = 0.0;
  mem17 = 0.0;
  mem18 = 0.0;
  mem19 = 0.0;

}



//Lastx
void last_x()//85 RECUPERA IL CONTENUTO DI X
{
  regu = regt;
  regt = regz;
  regz = regy;
  regy = regx;
  regx = lastx;
}

//  x<>y scambio
void scambio()//83 SCAMBIA I REGITRI X Y NELLA CATASTA
{
  float scambio = regx;
  regx = regy;
  regy = scambio;
}
//ruota giu la catasta
void ruota()//84  RUOTA GIU' LA CATASTA
{
  float ruota = regx;
  regx = regy;
  regy = regz;
  regz = regt;
  regt = regu;
  regu = ruota;
}

 //  86 ruota SU la catasta
void ruotaSU()
{
  float ruota = regu;
  regu = regt;
  regt = regz;
  regz = regy;
  regy = regx;
  regx = ruota;
}
//cambiosegno
void cambiasegno()//86 CAMBIA SEGNO AL REGISTRO X
{
  lastx = regx; regx = regx * -1;
}

// FIX----- scelta numero decimali dopo il punto da 0 a 5
void visfix()//77
{ if (( visore < 0 ) || (visore > 5)) return;
  else fix = visore;
}

void arcosen_x()// 61 ASIN ritorna angolo in x in RADIANTI
{
  if ((regx < -1) || ( regx > 1)) return;
  {
    lastx = regx;
    regx = asin (regx);
  }
}

void arcocos_x()//62 ACOS ritorna angolo in x in RADIANTI
{
  if ((regx < -1) || ( regx > 1)) return;
  {
    lastx = regx;
    regx = acos (regx);
  }
}


void arcotan_x()//63 ATAN ritorna angolo in x in RADIANTI
{
  lastx = regx;
  regx = atan (regx);
}



// converte x da sessagesimale a sessadecimale
// solo il registro x della catasta viene utilizzato
void sess_sedec()  // FUNZIONE
{
  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() // FUNZIONE
{
  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);
}

// 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;
}
// 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;
}



void fact() // funzione fattoriale max 30
{
  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

void percento()
{
  lastx = regx;
  regx = (regx * regy / 100);
}

void percentoDi()
{
  if ( regy == 0) return;
 
  lastx = regx;
  regx = (( regx - regy)* 100)/ regy;
}

void parteFRC()
{
  lastx = regx;
  int parteint = (int) regx;
  regx = (regx - parteint);
}

void parteINT()
{
  lastx = regx;
  regx = (int) regx;
 
}

void resto()
{
  lastx = regx;
  int parteint = (int)(regy/regx);
  int re = regy - (parteint * regx);
  regx = re;
}



//********************************

// -----------------------------------------------------------------
//              NOME FUNZIONE
// questa funzione ritorna una stringa con il nome della funzione
// relativa al codice passato, che sarà visualizzata
// a destra sul display per 300 ms.
//-------------------------------------------------------------------

String nome_funzione(byte codice)
{
  String nomeFunzione = "";


  if ((codice  >= 48) && (codice <= 57 ))return nomeFunzione;// numeri
  if ((codice  >= 0 ) && (codice <= 42 ))nomeFunzione = (codice);// registri
  if (codice == 46)return nomeFunzione;// punto decimale

  //---------------------------------------------------------

 

  if (codice == 43)nomeFunzione = "SOMMA";
  if (codice == 44)nomeFunzione = "PER";
  if (codice == 45)nomeFunzione = "MENO";
  if (codice == 47)nomeFunzione = "DIV";
  //-------------------------------------------
  if (codice == 58)nomeFunzione = "SIN";// SENO
  if (codice == 59)nomeFunzione = "COS";// COSENO
  if (codice == 60)nomeFunzione = "TAN";// TANGENTE
  if (codice == 61)nomeFunzione = "X^2";// X al quadrato
 
  //-------------------------------------------------------
  if (codice == 66)nomeFunzione = "STO";// STOnn INSERIMENTO IN MEMORIA nn
  if (codice == 67)nomeFunzione = "PI";// PI GRECO
  if (codice == 68)nomeFunzione = "SQRT";// SQRT radice quadrata di x
 
  if (codice == 69)return nomeFunzione;// FIX  fissa i decimali visibili  
 
 
  if (codice == 74)nomeFunzione = "RCL";// RCLnn RICHIAMA LA MEMORIA nn
  if (codice == 75)nomeFunzione = "EEX";// EEX INSERIMENTO ESPONENTE
  if (codice == 76)nomeFunzione = "CHS";// CAMBIO SEGNO
  if (codice == 86)nomeFunzione = "R_SU"; // RUOTA SU LA CATASTA

  if (codice == 82)nomeFunzione = "DEL";// DEL CANCELLA VISORE
  if (codice == 83)nomeFunzione = "X<>Y";// X<>Y SCAMBIO X CON Y
  if (codice == 84)nomeFunzione = "RGIU";// RGIU SPOSTA GIù LA CATASTA
  if (codice == 85)nomeFunzione = "LASTX";// LASTX RICHIAMA IN IN X IL REGISTRO LASTX

  //------------------------------------------------------------  
  if (codice == 90)nomeFunzione = "ENTER";


  //*********************************************
  //  seconde funzioni + 50 da 93 a 140
  //*********************************************

  if (codice == 93)nomeFunzione = "MOD";// MODULO DIVISIONE Y MOD X IL RESTO è IN X
  if (codice == 94)nomeFunzione = "CLX";// cancella X ( mette a zero x)
  if (codice == 95)nomeFunzione = "% DI";// DIFFERENZA PERCENTUALE FRA X ED Y
  if (codice == 96)nomeFunzione = " % ";// PERCENTUALE x DI Y
  if (codice == 97)nomeFunzione = "FRC";// PARTE FRAZIONARIA DI X
  if (codice == 98)nomeFunzione = "INT";// PARTE INTERA DI X
 

  if (codice == 108)nomeFunzione = "ASIN";// ARCOSENO DI X
  if (codice == 109)nomeFunzione = "ACOS";// ARCOCOSENO DI X
  if (codice == 110)nomeFunzione = "ATAN";// ARCOTANGENTE DI X
  if (codice == 111)nomeFunzione = "FACT";// FATTORIALE DI X
   
 
  if (codice == 116)nomeFunzione = "LOG";// LOGARITMO BASE 10
  if (codice == 117)nomeFunzione = "Y^x";// Y ELEVATO A X
  if (codice == 118)nomeFunzione = "xRy";// radice x di Y
  if (codice == 119)nomeFunzione = "LN";// LOG N
  if (codice == 120)nomeFunzione = "CAT"; // catalog di XEQ
 
 
  if (codice == 124)nomeFunzione = "R>P";// DA RETTANGOLARI A POLARI
  if (codice == 125)nomeFunzione = "P>R";// DA POLARI A RETTANGOLARI
  if (codice == 126)nomeFunzione = "1/X";// 1/X
  if (codice == 127)nomeFunzione = "e^X";// e ALLA X
 
  if (codice == 130)nomeFunzione = "130";//
  if (codice == 131)nomeFunzione = "131";//
 
  if (codice == 132)nomeFunzione = "R-hms";// >Da radianti a gradi sessagesimali
  if (codice == 133)nomeFunzione = "hms-R";// >Da GRADI/MINUTI/SECONDI - a RADIANTI
  if (codice == 134)nomeFunzione = "CLST";// CANCELLA CATASTA E VISORE
  if (codice == 135)nomeFunzione = "10^x ";// 10 ^x

   
  visualizzaNome(nomeFunzione);

  return nomeFunzione;

}

//********************************

//********************************
//
//  In questa scheda PROG-DI-XEQnn
//  ci sono i prorammi aggiuntivi
// che sono chiamati con XEQnn.
// Il primo è l'elenco dei programmi "catalog"
// fa una funzione simile alla catalog della HP dove mostra
// il numero da inserire nella XEQnn ed il nome del programma
// per eseguire questi programmi bisogna inserire
// nei registri e/o nelle memorie i valori richiesti.
// il programma viene eseguito ed nei registri si trovano i
// risultati nei registri della catasta e/o nelle memorie
//
// Se si vuole aggiungere un programma si
// dovrà  modificare queste parti:
// 1) aggiungere in questa scheda il programma con suo nome
// 2) modificare in questa scheda il prog catalog aggiungendolo
// 3) modificare la scheda XEQ-STO-RCL nella parte relativa
// alla chiamata XEQnn inserendo nel ciclo case
// il nome del nuovo programma
//************************************************************


void catalog()
{
  String nomeProg = "";
  int numProg = 0;
  lcd.clear();
   // se si aggiungono programmi aggiornare la lista qui sotto
    for (int i = 1; i < 10; i++)
    {
    numProg = i;
   
    if (i == 1)nomeProg = "LISTA PROGRAMMI";
    if (i == 2)nomeProg = " TRI. CARNOT   ";
    if (i == 3)nomeProg = " PARALLELO RES ";
    if (i == 4)nomeProg = "Triangolo Erone";
    if (i == 5)nomeProg = " PARTITORE V   ";
    if (i == 6)nomeProg = " SCORPORO IVA  ";
    if (i == 7)nomeProg = "INPUT-OUT-PROG ";
    if (i == 8)nomeProg = "RAD -> CENTESI ";
    if (i == 9)nomeProg = "CENTES. -> RAD ";

    lcd.setCursor(0, 0);
    lcd.print(numProg);
    lcd.setCursor(4, 0);
    lcd.print(nomeProg);
    delay (1800);
    lcd.clear();

    }
 

}


//********************************************
//
//  1° ESEMPIO PROGRAMMA CALCOLO TRIANGOLO CON CARNOT
//
 void carnot()  // funzione 02 ****
     {
      //  angolo in x in RADIANTI
     double latoa = regz; // uno dei lati conosciuti
     double latob = regy; // il secondo lato conosciuto
     double angolo = regx;// angolo compreso fra i due lati
     //  si cerca il terzo lato
     //  gli angoli sono inseriti in RADIANTI  INPUT
     //  gli angoli saranno in RADIANTI

     //***********************************
     // due lati ed angolo compreso
     
     double latoc = sqrt(pow(latoa,2)+ pow(latob,2)-(2*latoa*latob)*cos(angolo));

     regx = latoc;  // in x troviamo il lato cercato opposto all'anglo compreso
     regy = latob;  // lato noto
     regz = latoa;  // lato noto
     regt = angolo; // angolo noto  in radianti
     //**************************************************
} // fine carnot




void parallelo() // prog 03
{
 //**************************************
 //       PARALLELO RESISTORI
 //
 //  //   r1 -r2
 //**************************************
 // si inseriscono in x ed in y i valori di due resistori.
 //
 // questo programma modifica tutto lo stak
 // in uscita avremo in x la resistenza parallelo
 //
   
     double resp = 0;
   
        resp =  1/ ((1/regx)+ (1/regy));
       

      regx = resp;
      regy = 0;
      regz = 0;
      regt = 0;

}
// fine parallelo
//************************************


//***********************************
//              superficie triangolo con Erone
//    noti i tre lati  in x lato a, in y lato b, in z lato c
//   restituisce in x la superficie, in y,z, t i lati a,b,c.
//*************************************************************
//
void erone()  //  fuzione 04 ****
{
 double latoa = regx;
 double latob = regy;
 double latoc = regz;
 
if (latoa >= (latob+latoc)|| latob >= (latoc + latoa)|| latoc >=(latob+latoa)) return;
         
    else {
         
            double perimetro = latoa + latob + latoc;
            double per2 = perimetro/2;
            double super = sqrt(per2*((per2-latoa)*(per2-latob)*(per2-latoc)));

            regx = super;
            regy = latoa;
            regz = latob;
            regt = latoc;

         }
}// fine erone
 

//*******************************************************
// partitore di tensione
//*******************************************************
void partitore() // funzione 05
{
  //Vn= VIN(Rn/(R1 + R2 + R3)

    double r1 = regy; // inserire valore R1 in omm
    double r2 = regz; // inserire valore R2 in omm
    double r3 = regt; // inserire valore R3 se non serve inserire 0
    double vin = regx;// inserire Vin in Volt

    double v1 = vin * ( r1 / ( r1+r2+r3));
    double v2 = vin * ( r2 / ( r1+r2+r3));
    double v3 = vin * ( r3 / ( r1+r2+r3));

    regx = v1;  // Volt in v1
    regy = v2;  // Volt in v2
    regz = v3;  // Volt in v3
    regt = 0;
} // fine partitore

//*****************************************************************
//       SCORPORO IVA impostare  in Y l'aliquota
//                    impostare  in X il totale compreso IVA
//           in OUTPUT avremo:
//           x = IVA, y = imponibile, z = il totale, t = aliquota.
// *****************************************************************
//          
void sco_iva()//   funzione 06 ****
{
      //****************************************
      double aliq = regy;
      double total = regx;
      double imponibile = (total*100)/(100+aliq);
      //*****************************************
      //output
      regx = (total - imponibile); // IVA
      regy =  imponibile;
      regz = total;
      regt = aliq;
 
     //*****************************************
} // fine scorporo iva
//---------------------------------------
//----PROG 7    INSERIMENTO DATI --------
 void input_out_prog()
 {
    lcd.clear();
 // lcd.print("01234567890123456789");
    lcd.setCursor(0, 0);
    lcd.print("Inserire i dati nei ");
    lcd.setCursor(0, 1);
    lcd.print("registri x,y,z,t   ");
    lcd.setCursor(0, 2);
    lcd.print("angoli in RAD      ");
    lcd.setCursor(0, 3);
    lcd.print("num poi tasto XEQ ");
    tasto();
    lcd.clear();

 }
//------------------------------------  
//--------------------------------------------------------
//   SCHEDA FUNZIONI CON INDICE ..  STO - RCL - XEQ
//-------------------------------------------------------

// IN QUESTA SCHEDA CI SONO
//TRE VOID CON LA STRUTTURA SWITCH - CASE
// CHE MEMORIZZANO ( con STO nn)
// richiamano delle memorie ( con RCL nn)

// in questa scheda si trova anche la strurrura switch-case
// per chiamare i programmi utente con XEQ nn

// STO nn  SUB PER MEMORIZZARE IL CONTENUTO DI X
//NEL REGISTRO INDICATO IN VISORE  DA 1 A 19
// 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 > 19)) 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;
    case 10:
      mem10 = regx;
      break;

    case 11:
      mem11 = regx;
      break;
    case 12:
      mem12 = regx;
      break;
    case 13:
      mem13 = regx;
      break;
    case 14:
      mem14 = regx;
      break;
    case 15:
      mem15 = regx;
      break;
    case 16:
      mem16 = regx;
      break;
    case 17:
      mem17 = regx;
      break;
    case 18:
      mem18 = regx;
      break;
    case 19:
      mem19 = regx;
      break;
  }
  visore = 0;
} // fine void sto_n
//---------------------------------------


//RCL  richiama in x uno dei registri
void rcl_n()
{
 // indirizzo = visore;
  int mem = visore;

  if (( mem < 1 ) || (mem > 19)) return;


  switch (mem)
  {
    case 1:
      {
        visore = mem1;
        enter();
      }
      break;
    case 2:
      {
        visore = mem2;
        enter();
      }
      break;
    case 3:
      {
        visore = mem3;
        enter();
      }
      break;
    case 4:
      {
        visore = mem4;
        enter();
      }
      break;
    case 5:
      {
        visore = mem5;
        enter();
      }
      break;

    case 6:
      {
        visore = mem6;
        enter();
      }
      break;
    case 7:
      {
        visore = mem7;
        enter();
      }
      break;
    case 8:
      {
        visore = mem8;
        enter();
      }
      break;
    case 9:
      {
        visore = mem9;
        enter();
      }
      break;

    case 10:
      {
        visore = mem10;
        enter();
      }
      break;

    case 11:
      {
        visore = mem11;
        enter();
      }
      break;

    case 12:
      {
        visore = mem12;
        enter();
      }
      break;
    case 13:
      {
        visore = mem13;
        enter();
      }
      break;
    case 14:
      {
        visore = mem14;
        enter();
      }
      break;
    case 15:
      {
        visore = mem15;
        enter();
      }
      break;

    case 16:
      {
        visore = mem16;
        enter();
      }
      break;
    case 17:
      {
        visore = mem17;
        enter();
      }
      break;
    case 18:
      {
        visore = mem18;
        enter();
      }
      break;
    case 19:
      {
        visore = mem19;
        enter();
      }
      break;

  }

  visore = 0;
}

//------------------------------------------------
void Xeq_nn()
{
  //indirizzo = visore;
  int mem = visore;
  if (( mem < 1 ) || ( mem > 40)) return;

  switch (mem)
  {
    case 1:
      {
        //catalog(); // nomi dei programmi di XEQnn
        catalog();
      }
      break;
    case 2:
      {
         visualizzaNome("CARNOT");
        carnot(); // soluzione triangolo due lati angolo compreso
      }
      break;
    case 3:
      {
         visualizzaNome("PARAL");
        parallelo(); // calcola la rt di due resistenze in parallelo
      }
      break;
    case 4:
        {
           visualizzaNome("ERONE");
          erone(); // 04 calcola triangolo con erone
        }
      break;
    case 5:
        {
           visualizzaNome("PARTIT");
          partitore(); // 05 calcola partitore di tensione
        }
      break;

    case 6:
       {
         visualizzaNome("SCIVA");
         sco_iva(); // 06  scorporo iva
       }
      break;
    case 7:
       {
         input_out_prog();//07 indicazioni su input output per i programmi
       }

      break;
    case 8: // DA RADIANTI A CENTESIMALI
      {
        visualizzaNome("RAD>C");
        lastx = regx;
        regx = regx * 200.0 / PI;
      }

      break;
    case 9:
        //  da centesimali a radianti
      {
        visualizzaNome("C>RAD");
        lastx = regx;
        regx = regx * PI / 200.0;
      }
      break;
    case 10:
      //---
      break;
    case 11:
      //----

      break;
    case 12:
      //----
      break;
    case 13:
        //----

      break;

    case 14:
      //-----
      break;
    case 15:
      //-----
      break;

    case 16:
      //----
      break;

    case 17:
      //----
      break;

    case 18:
      // disponibile
      break;

    case 19:
      //----
      break;

    case 20:
      // da radianti a centesimali
      {
        lastx = regx;
        regx = regx * 200.0 / PI;
      }
      break;

    case 21:
      //  da centesimali a radianti
      {
        lastx = regx;
        regx = regx * PI / 200.0;
      }
      break;

    case 22:
      // disponibile
      break;


    case 23:
      // disponibile
      break;

    case 24:
      // disponibile
      break;

    case 25:
      //------------------
     
      break;



    case 26:
      // disponibile
      break;

    case 27:
      // disponibile
      break;

    case 28:
      // disponibile
      break;

    case 29:
      // disponibile
      break;

    case 30:
      // disponibile
      break;

    case 31:
      // disponibile
      break;

    case 32:
      // disponibile
      break;

    case 33:
      // disponibile
      break;

    case 34:
      // disponibile
      break;
    case 35:
      // disponibile
      break;

    case 36:
      // disponibile
      break;

    case 37:
      // disponibile
      break;

    case 38:
      // disponibile
      break;

    case 39:
      // disponibile
      break;

    case 40:
      // disponibile
      break;

  }
  visore = 0;
} // fine void XEQ

//********************************


//**************************************************
//         FINE LISTATO
//**************************************************
//SCHEDA XEQ-STO-RCL PER LE FUNZIONI CON CHIAMTA nn
//---------------------------------------------------
//SCHEDA FUNZIONI DIRETTE E SECONDE
//---------------------------------------------------
//SCHEDA PROG PER I PROGRAMMI UTENTE CHIAMATI CON XEQnn
//------------------------------------------------------------------
// SCHEDA NOMI FUNZIONI CON STRINGHE VISUALIZZATE DEI NOMI FUNZIONI
//******************************************************************







Lo sketch usa 22290 byte (69%) dello spazio disponibile per i programmi.
 Il massimo è 32256 byte.
Le variabili globali usano 1102 byte (53%) di memoria dinamica,
 lasciando altri 946 byte liberi per le variabili locali.
 Il massimo è 2048 byte.



Lasciate un commento se avete domande o riscontrate errori.

Grazie!!


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