Giocando con un encoder

ciao a tutti. proseguo i miei personali studi su Arduino ed avendo in mente un paio di progetti in cui avrò necessità di utilizzare degli encoder meccanici, sto cercando di padroneggiarli.
ho scritto il seguente sketch con questo scopo.
ora, funziona tutto ed anche bene....però ho un problema nelle istruzioni delle ultime righe "gestite"
da un interrupt. è come se ignorasse una porzione di codice. è in maiuscolo nell'identazione.
sono gradite e apprezzate tutte le forme di consigli e miglioramenti.
grazie.

Claudio.

[code]
/*Questo sketch permette di testare un encoder su display lcd , è una 
 * evoluzione dello sketch per ibt_2 .funziona alla grande.
 */

#include <LiquidCrystal.h>
#define pinClk 5  //Definizione pin encoder
#define pinDt  3  //Definizione pin encoder
#define pinSW  4  //Definizione pin encoder
#define tastoStart 2  //Definizione pin pulsante di avvio (con interrupt)
#define rele_timer 6  //Definizione pin relé conto alla rovescia da implementare
#define rele_millis 7 //Definizione pin relé con tempo che definirò da pc

LiquidCrystal lcd(12, 11, 13, 10, 9, 8);  //pin di collegamento lcd

int era_Clk;                  // Conserva stato ultima posizione encoder
int era_Dt;                   // Conserva stato ultima posizione encoder
int era_SW;                   // Conserva stato ultima posizione encoder
int contatore = 0;            // Variabile per calcolo incremento-decremento 

void setup(){
   
  lcd.begin(16,2);
  Serial.begin(9600);
  
  pinMode (pinClk, INPUT);     //pin Clk ingresso
  pinMode (pinDt, INPUT);      //pin Dt ingresso
  pinMode (pinSW, INPUT_PULLUP);         //pin SW ingresso
  pinMode (tastoStart, INPUT_PULLUP);    //pin ingresso
  attachInterrupt(0, int_tasto, RISING); //pin 2 interrupt
  
  era_Clk = digitalRead(pinClk); // Esegui prima lettura dei valori sui pin
  era_Dt =  digitalRead(pinDt);  // Esegui prima lettura dei valori sui pin
  era_SW = digitalRead(pinSW);   // Esegui prima lettura dei valori sui pin
  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("GRAZIE FORUM");
  lcd.setCursor(0, 1);
  lcd.print("CONTATORE = ");
  }

void loop() {
  

  int ora_Clk = digitalRead(pinClk);  // Leggi valori tempo reale
  int ora_Dt =  digitalRead(pinDt);   // Leggi valori tempo reale
  int ora_SW =  digitalRead(pinSW);   // Leggi valori tempo reale

  
  if (ora_Clk != era_Clk) {          // Se la lettura corrente è diversa dall'ultima memorizzata
                                     

    Serial.print("[era_Clk, era_Dt | ora_Clk, ora_Dt] = ");   // scrivo i dati su seriale.
    Serial.print(" ");
    Serial.print(era_Dt);
    Serial.print(" | ");
    Serial.print(ora_Clk);
    Serial.print(" ");
    Serial.print(ora_Dt);

    
    if (ora_Dt == ora_Clk) {         //Se il valore di dt è uguale al valore corrente di clk
      Serial.print(" -");            //metto il segno meno
      contatore --;                  //decremento il valore
    } else {                         //altrimenti
      Serial.print(" +");            //metto il segno più
      contatore ++;                  //aumento il valore
    }

    Serial.print(" => ");            //Scrivo lo stato del contatore
    Serial.println(contatore);
    lcd.setCursor(9, 1);
    lcd.print("          ");        
    lcd.setCursor(13, 1);  
    lcd.print(contatore);           
    delay(50);
    era_Clk = ora_Clk;                //Aggiorno i valori all'ultima lettura fatta, ultima posizione.
    era_Dt = ora_Dt;                  //Aggiorno i valori all'ultima lettura fatta, ultima posizione.
  }
   if ((era_SW ==0) && (ora_SW==1)){
 
    Serial.print("[era_SW, ora_SW ] = ");
    Serial.print(era_SW);
    Serial.print(" ");
    Serial.print(ora_SW);
    Serial.print(" | ");
    Serial.println("TASTO PREMUTO");  // scrivo i dati su seriale.
  }
   era_SW = ora_SW;                  //Aggiorno i valori all'ultima lettura fatta, ultima posizione.
   } 
   
void int_tasto()
{
  if (tastoStart, HIGH);              //Se il tasto viene premuto
  Serial.print("INTERRUPT");          //Scrivo su seriale il cambio di stato
  Serial.println (" ON");  
  lcd.clear();                        //Stampo su lcd 
  lcd.setCursor(0,0);                 //COME NON DETTO, NON STAMPA SU DISLAY
  lcd.print("INTERRUPT ON");          //COME NON DETTO, NON STAMPA SU DISLAY
  delay(5000);
  lcd.clear();                        //COME NON DETTO, NON STAMPA SU DISLAY
  lcd.setCursor(0, 0);                //Stampo su lcd
  lcd.print("GRAZIE FORUM");
  lcd.setCursor(0, 1);
  lcd.print("CONTATORE = ");
 }
    
    
[/code]

Allora, gli interrupt globali sono disabilitati durante l'esecuzione di un ISR (la tua funzione agganciata è come una ISR). ISR (Interrupt Service Request). Sostanzialmente hai fatto tutto quello che nelle ISR non si deve fare, incluso il delay(). :smiley:

Solitamente nelle ISR ci si limita ad alzare/abbassare un flag e poi il tutto viene gestito nel loop.

Ciao.

Ottimo :grin:. Grazie, riguarderò. Certo che non si finisce mai :smirk:.

Forse non è chiaro @Maurotec su questo: if (tastoStart, HIGH);
E' una riga che non ha nessun senso.

Inoltre, usi l'interrupt per rilevare la pressione del pulsante ma non lo usi per rilevare la rotazione dell'encoder?... Se decidi di fare qualcosa con l'interrupt, deve essere per la rotazione dell'encoder!

In effetti, quella riga mi ha destabilizzato parecchio, minando le poche convinzioni che possiedo. Mi rendo conto che a questo punto sto svicolando dal titolo (colpa di sollecitava titoli accattivanti :smirk:).
Ora non ho il PC e la scheda con me, quindi non posso smanettare, però sto riguardando gli interrupt e mi viene un dubbio:
Il loop viaggia continuamente,in questo caso premo un tasto e richiamo una data istruzione....e poi? Il loop riprende dall'inizio? Dal punto in cui è stato richiamato l' interrupt? O sono io che lo devo fare "ripartire"?
Grazie.

Si non mi sono soffermato poiché non è questo che impedisce la stampa. Ma gli è già capitato di scrivere righe senza senso simili.

Ciao.

1 Like

Non è il pulsante dell'encoder, è un tasto separato.
Purtroppo sono un casinista nato, il tutto nasce dal rendere un motoriduttore a due fili un servomotore. Da qui un ponte H , e poi un encoder. Cercando esempi sugli encoder ho trovato una vecchia discussione e da qui un progetto che mi ha interessato e che vorrei realizzare. :scream::scream:

L'interrupt interrompe l'esecuzione del programma, che poi continua da dove era stata interrotta.

Questa è la domanda che dovevi farti. La risposta è molto complessa e richiede un poco di tempo, ma ti serve sapere come funziona la CPU.

Considera che la CPU non ha idea della funzione loop(), la CPU ha un registro PC che punta ad indirizzo di memoria flash. Preleva una istruzione da flash e la mette in un registro (FETCH). Da ora in poi FETCH contiene la prossima istruzione, pronta per essere spostata in DECODE e per essere eseguita.

Questo avviene in ciclo sincronizzato dal clock (16MHz), questo ciclo può essere interrotto da IRQ (Interrupt Request) ecc.

Prima che il registro PC contenga l'indirizzo della ISR, in un altro registro viene salvato il valore di PC, quando la ISR termina il valore di PC viene ripristinato per cui salta alla istruzione seguente quella che stava eseguendo prima del salto alla ISR.

Ci ho provato a sintetizzare, ma non sono soddisfatto.
Cerca in rete "fetch instruction cycle"
Troverai quella della architettura von neumann.
La differenza tra von e AVR è che le istruzioni si trovano in flash.

Ciao.

Come detto anche da "maurotec": il loop viaggia continuamente. un interrupt interrompe il programma (in questo caso il loop) ma finito il codice dell'interrupt, il programma riprende dove era stato interrotto tutto automaticamente.

Un programma Arduino è comunque un programma C/C++ che di nascosto è circa:

void main()
{ setup();            // richiamata una sola volta alla partenza
  while(1) loop();    // ciclo infinito
}

Ok. fin qui vi seguo ma con un "però". In questo specifico caso, la schermata di "presentazione" del display (nel setup) non riappare più, se non con un reset della scheda. ....

Il setup() viene eseguito solo all'accensione e al reset, cioè ogni volta che parte il main().

Ci sono,ho capito (parolone :wink:). Più tardi smanetterò un po' e vediamo che succede.

Arduino mi sembra sempre più un fantastico palazzo moderno di vetro e acciaio, circondato da tanto verde. Tornando a casa può succedere che ti appaia diverso dal solito. A volte appare luminoso e le sue ombre si muovono all'unisono , altre volte ti incute un debole timore,ma diamine, sei a casa.

Ho preso il PC e smanetto.
Non ho avuto modo di studiare bene il tutto, ma una cosa vorrei sapere:

primo estrapolo di codice:

[code]
void int_tasto()
{                    
  Serial.print("INTERRUPT");          //Scrivo su seriale il cambio di stato
  Serial.println (" ON");  
  lcd.clear();                        //Stampo su lcd 
  lcd.setCursor(0,0);                 //STAMPA SU LCD
  lcd.print("INTERRUPT ON");          //STAMPA SU LCD 
  /*MA POI GIUSTAMENTE RIMANE QUI LA SCHERMATA*/
 }

[/code]

secondo :

[code]
void int_tasto()
{                    
  Serial.print("INTERRUPT");          //Scrivo su seriale il cambio di stato
  Serial.println (" ON");  
  lcd.clear();                        //Stampo su lcd 
  lcd.setCursor(0,0);                 //COME NON DETTO, NON STAMPA SU DISLAY
  lcd.print("INTERRUPT ON");          //COME NON DETTO, NON STAMPA SU DISLAY
  
  lcd.clear();                   
  lcd.setCursor(0, 0);                //Stampo su lcd
  lcd.print("GRAZIE FORUM2");         //Stampo un due cosi so quale schermata leggo
  lcd.setCursor(0, 1);
  lcd.print("CONTATORE = ");
 }

[/code]

ecco , vorrei sapere perché non stampa più le due righe "urlate". passa direttamente alle successive , che visualizza correttamente.

Grazie.

vabbè , altra figuraccia. Era molto meglio se studiavo prima di smanettare.
Non si può rispondere alla precedente domanda...Anzi no, lo avete già fatto molto prima.

Torno sui miei passi.

Grazie.

setCursor(0,0);   

La prima e

lcd.setCursor(0, 0);                //Stampo su lcd

La seconda.

Almeno cambia colonna o riga.

1 Like

Chi ti ha detto che non le stampa? Non c'è motivo per cui le successive le stampa e quelle no, sta semplicemente facendo quello che gli chiedi di fare.

Ciao, Ale.

lo dico io ....che ho gli occhi sul display :wink:

Credo sia doveroso (e utile, per me😈 ) postare il codice corretto e funzionante, o almeno come avevo immaginato di farlo lavorare.

Ovvio che accetto qualsiasi commento. Scherzi a parte, grazie davvero a tutti.

[code]
/*Questo sketch permette di testare un encoder su display lcd , è una 
 * evoluzione dello sketch per ibt_2 .funziona alla grande.
 */

#include <LiquidCrystal.h>
#define pinClk 5  //Definizione pin encoder
#define pinDt  3  //Definizione pin encoder
#define pinSW  4  //Definizione pin encoder
#define tastoStart 2  //Definizione pin pulsante di avvio 
#define rele_timer 6  //Definizione pin relé conto alla rovescia da implementare
#define rele_millis 7 //Definizione pin relé con tempo che definirò da pc

LiquidCrystal lcd(12, 11, 13, 10, 9, 8);  //pin di collegamento lcd

int era_Clk;                  // Conserva stato ultima posizione encoder
int era_Dt;                   // Conserva stato ultima posizione encoder
int era_SW;                   // Conserva stato ultima posizione encoder
int contatore = 0;            // Variabile per calcolo incremento-decremento 
int era_tastoStart;           // Variabile valore tasto di avvio

void setup(){
   
  lcd.begin(16,2);
  Serial.begin(9600);
  
  pinMode (pinClk, INPUT);     //pin Clk ingresso
  pinMode (pinDt, INPUT);      //pin Dt ingresso
  pinMode (pinSW, INPUT_PULLUP);         //pin SW ingresso
  pinMode (tastoStart, INPUT_PULLUP);    //pin ingresso
  
  
  era_Clk = digitalRead(pinClk); // Esegui prima lettura dei valori sui pin
  era_Dt =  digitalRead(pinDt);  // Esegui prima lettura dei valori sui pin
  era_SW = digitalRead(pinSW);   // Esegui prima lettura dei valori sui pin
  era_tastoStart = digitalRead(tastoStart); // Esegui prima lettura dei valori sui pin
  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("GRAZIE FORUM");
  lcd.setCursor(0, 1);
  lcd.print("CONTATORE = ");
  }

void loop() {
  

  int ora_Clk = digitalRead(pinClk);  // Leggi valori tempo reale
  int ora_Dt =  digitalRead(pinDt);   // Leggi valori tempo reale
  int ora_SW =  digitalRead(pinSW);   // Leggi valori tempo reale
  int ora_tastoStart = digitalRead(tastoStart); // Leggi valori tempo reale
  
  if (ora_Clk != era_Clk) {          // Se la lettura corrente è diversa dall'ultima memorizzata
                                     

    Serial.print("[era_Clk, era_Dt | ora_Clk, ora_Dt] = ");   // scrivo i dati su seriale.
    Serial.print(" ");
    Serial.print(era_Dt);
    Serial.print(" | ");
    Serial.print(ora_Clk);
    Serial.print(" ");
    Serial.print(ora_Dt);

    
    if (ora_Dt == ora_Clk) {         //Se il valore di dt è uguale al valore corrente di clk
      Serial.print(" -");            //metto il segno meno
      contatore --;                  //decremento il valore
    } else {                         //altrimenti
      Serial.print(" +");            //metto il segno più
      contatore ++;                  //aumento il valore
    }
    Serial.print(" => ");            //Scrivo lo stato del contatore
    Serial.println(contatore);
    lcd.setCursor(9, 1);
    lcd.print("          ");        
    lcd.setCursor(13, 1);  
    lcd.print(contatore);           
    delay(50);
    era_Clk = ora_Clk;                //Aggiorno i valori all'ultima lettura fatta, ultima posizione.
    era_Dt = ora_Dt;                  //Aggiorno i valori all'ultima lettura fatta, ultima posizione.
  }
   if ((era_SW ==0) && (ora_SW==1)){
 
    Serial.print("[era_SW, ora_SW ] = ");
    Serial.print(era_SW);
    Serial.print(" ");
    Serial.print(ora_SW);
    Serial.print(" | ");
    Serial.println("TASTO PREMUTO");  // scrivo i dati su seriale.
  }
   era_SW = ora_SW;                  //Aggiorno i valori all'ultima lettura fatta, ultima posizione.
    
  if ( ora_tastoStart != era_tastoStart){  
                      
    Serial.print("INTERRUPT");          //Scrivo su seriale il cambio di stato
    Serial.println (" ON");  
    lcd.clear();                        //Stampo su lcd 
    lcd.setCursor(0,0);                
    lcd.print("INTERRUPT ON");          
    delay(1000);
    lcd.clear();                   
    lcd.setCursor(0, 0);                
    lcd.print("GRAZIE FORUM2");         //Stampo un due cosi so quale schermata leggo
    lcd.setCursor(0, 1);
    lcd.print("CONTATORE = ");
    contatore = 0;
    
  }
 /*ora_tastoStart = era_tastoStart;*/    //non è necessario perchè ha solo due valori possibili
}  
[/code]