[Risolto] Problemi con LCD Nokia 5110 e interrupt

Vorrei costruire un misuratore di giri al minuto e distanza percorsa utilizzando un sensore a infrarossi TCRT5000 collegato al pin 2 (interrupt 0). I dati sono aggiornati una volta al secondo.

Fino a quando i dati sono inviati via Serial tutto funziona senza problemi ininterrottamente per ore. Se modifico il file per inviare gli stessi dati al display, parte tutto regolarmente ma, arrivati a un valore di distanza di circa 1400-1500 metri, i valori restano fissi.
Ho provato a riavviare, a scollegare e ricollegare, ad alimentare da una fonte esterna o solo dal pc ma il risultato è sempre lo stesso.

Ho utilizzato due sensori collegandone uno ad un arduino con il display e relativo programma e l’altro ad un arduino collegato al pc che trasmette i dati via serial. All’avvio tutto come dovrebbe fornendo anche gli stessi valori di giri al minuto (massima differenza di 0,2%). Alla distanza critica di circa 1500 m i valori sullo schermo si bloccano mentre il terminale del pc continua a riceverli (ho interrotto a una distanza di 200.000 metri).

Per il funzionamento del display ho utilizzato la libreria LCD5110_Basic. Ho provato anche altre librerie, compresa quella di adafruit, ma la sola differenza è un peggioramento, probabilmente dovuto al peso delle librerie stesse.

Questo è il codice che invia i dati via Serial:

volatile unsigned long timer1 = 0;
volatile unsigned long result = 0;
volatile long  giri = 0;

unsigned long microsRPM;

unsigned long lastmillis = 0;

int diametro = 120; //mm del diametro
int circonferenza = diametro*3.14;
long distanza = 0;



void setup() {
  
  Serial.begin(9600);
  attachInterrupt(0, rpm_fan, FALLING);

}


void loop() {

  if (millis() - lastmillis == 1000) { 
    detachInterrupt(0);//Disable interrupt when calculating
    
    microsRPM = 60000000 / result;
    distanza = (giri*circonferenza)/1000;
    attachInterrupt(0, rpm_fan, FALLING); //enable interrupt

    //invia i dati a serial
    Serial.print("  micros RPM:  ");
    Serial.print(microsRPM);
    Serial.print("  distanza: "); 
    Serial.println(distanza);
    
    lastmillis = millis(); 
    }
}

void rpm_fan() {
  
  giri++;
  result = micros() - timer1;
  timer1 = micros();
}

Questo è quello per l’LCD5110:

#include <LCD5110_Basic.h>
/*
 * Arduino UNO
LCD5510: RST - CE - DC - Dim - Clk - Vcc - BL -Gnd
Pin:     6     7    5    11    13     5V    GND GND 
Hard SPI?                MOSI  SCK
* Arduino Leonardo
LCD5510: RST - CE - DC - Dim - Clk - Vcc - BL -Gnd
Pin:     6     7    5    MOSI  SCK    5V    GND GND 
Hard SPI?                MOSI  SCK
*/
LCD5110 lcd(13,11,5,6,7);
extern unsigned char SmallFont[]; //dimensioni font caratteri
extern unsigned char MediumNumbers[]; //dimensioni font numeri
extern unsigned char BigNumbers[];

volatile unsigned long timer1 = 0;
volatile unsigned long result = 0;
volatile long  giri = 0;

unsigned long microsRPM;

unsigned long lastmillis = 0;

int diametro = 120; //mm del diametro
int circonferenza = diametro*3.14;
long distanza = 0;



void setup() {
  
  attachInterrupt(0, rpm_fan, FALLING);
  lcd.InitLCD(58);
}


void loop() {
 
  if (millis() - lastmillis == 1000) { 
    noInterrupts();//Disable interrupt when calculating
    
    microsRPM = 60000000 / result;
    distanza = (giri*circonferenza)/1000;
    interrupts(); //enable interrupt

    //dati con LCD 5510
    lcd.clrScr();
    printDati();
    //lcd.update();    
    lastmillis = millis(); // Uptade lasmillis
    
  }
}

void rpm_fan() {
  //half_revolutions++;
  giri++;
  result = micros() - timer1;
  timer1 = micros();
}

void printDati() {
 lcd.setFont(SmallFont);
 lcd.print("Distanza",0,0);
 lcd.print("RPM",0,32);
 lcd.setFont(MediumNumbers);
 lcd.printNumI(distanza,0,8);
 lcd.setFont(BigNumbers);
 lcd.printNumI(microsRPM,24,24);
 
}

Le uniche differenze sono l’inserimento della libreria supplementare e le parti per visualizzare i dati in sostituzione di quelle con serial.

Ho letto e seguito le indicazioni in vari forum riguardo gli interrupt e l’LCD5110, compresi i collegamenti consigliati e come e quando interrompere l’interrupt ma non ho risolto il problema.

Sapreste aiutarmi?

Ho modificato la linea di codice relativa all'aggiornamento delle informazioni mostrate sul display da
if (millis() - lastmillis == 1000)
a
if (millis() - lastmillis >= 1000)
ed ora sembra funzionare. Anche aumentando la frequenza scendendo da 1000 ms a 250 ms o variando il numero di giri al minuto da 6 a 4500 non si blocca.
Essendo la misurazione della velocità legata ai mirosecondi tra due interrupt e non al numero di interrupt per ogni refresh dello schermo, ovvero i millis() nella riga di codice indicata, la precisione della misurazione non cambia.
Probabilmente per qualche ragione dopo qualche ciclo, la condizione millis() - lastmillis == 1000 non si verifica, il valore di lastmillis non è modificato e millis() - lastmillis continua ad aumentare rendendo impossibile la condizione per eseguire il codice nel ciclo if.

1202:
Probabilmente per qualche ragione dopo qualche ciclo, la condizione millis() - lastmillis == 1000 non si verifica, il valore di lastmillis non è modificato e millis() - lastmillis continua ad aumentare rendendo impossibile la condizione per eseguire il codice nel ciclo if.

Esattamente. Non puoi dare per scontato che la loop() parta ad ogni millisecondo, per cui usare sempre il “>=” e mai “==” tanto più se hai gli interrupt. Basta che il loop parta un millisecondo dopo che quella if non esegue più nulla, per cui hai fatto bene.

1202:

millis() - lastmillis == 1000

non si verifica, il valore di lastmillis non è modificato e millis() - lastmillis continua ad aumentare rendendo impossibile la condizione per eseguire il codice nel ciclo if.

E devi avere una fortuna bestiale per beccare esattamente in quella riga un millis() 1000 volte maggiore di lastmillis
Facile che quando passa di li sia 1001 volte maggiore o 1002 ma ... esattamente 1000 ... solo un miracolo

nid69ita:
E devi avere una fortuna bestiale per beccare esattamente in quella riga un millis() 1000 volte maggiore di lastmillis
Facile che quando passa di li sia 1001 volte maggiore o 1002 ma … esattamente 1000 … solo un miracolo

Di solito utilizzo sempre >= o <=. In questo caso ho preso == da indicazioni su altri forum e relative al codice per registrare i giri al minuto per avere “maggiore precisione”. I primi secondi funziona e, raramente, funzionava per molti minuti. Questo non mi ha spinto a cercare la soluzione modificando quella parte del programma ma pensando che ci fosse qualche incompatibilità tra interrupt e libreria.