Problema lettura quadrature encoder ed aggiornamento display con MAX7221 in SPI

Ciao a tutti,
ho la necessità di modificare una variabile tramite encoder e di riportarne il valore su un display led-ring (15 led disposti a cerchio) pilotato da un MAX7221 in SPI.

La routine di lettura encoder ed aggiornamento variabile, basata su interrupt, da sola, funziona benissimo;

La routine di scrittura su led-ring, da sola, funziona benissimo;

Quando unisco le 2 routine e cerco di aggiornare il display, eseguendo un trasferimento SPI verso il MAX7221 subito dopo l’aggiornamento della variabile, il valore della variabile stessa salta come se l’encoder rimbalzasse in maniera casuale.

#include <SPI.h>

#define CS 8 //pin Chip Select SPI del MAX7221
#define intpin 3 //rotary encoder interrupt pin (Pin A), in pull-up esterno, con circuito di debounce HW - Il Pin C è a massa
#define dirpin 4 //rotary encoder direction pin (Pin B), in pull-up esterno, con circuito di debounce HW - Il Pin C è a massa

volatile int pot=14; //variabile da modificare tramite rotazione encoder (escursione valore variabile limitata da 0 a 28)

// L'array seguente mappa l'accensione dei led, da 1 a 15, in funzione del valore della variabile pot (valore 0=led 1 on, valore 1=led 1 e 2 ON, velore 3=led 2 ON, valore 4=led 2 e 3 ON, valore 5=led 3 ON, etc...)
// I primi 8 led vengono pilotati dalla DIGIT 0 del MAX2771 i led dal 9 al 15 vengono pilotati dalla DIGIT 1. La visualizzazione completa di un valore viene codificata da due byte consecutivi: byte ringmatrix [x] per la DIGIT 0 e byte ringmatrix [x+29] per la DIGIT 1
int ringmatrix [58]={ 
  B00000001,  //1a
  B00000011,  //2a
  B00000010,  //3a
  B00000110,  //4a
  B00000100,  //5a
  B00001100,  //6a
  B00001000,  //7a
  B00011000,  //8a
  B00010000,  //9a
  B00110000,  //10a
  B00100000,  //11a
  B01100000,  //12a
  B01000000,  //13a
  B11000000,  //14a
  B10000000,  //15a
  B10000000,  //16a
  0,          //17a
  0,          //18a
  0,          //19a
  0,          //20a
  0,          //21a
  0,          //22a
  0,          //23a
  0,          //24a
  0,          //25a
  0,          //26a
  0,          //27a
  0,          //28a
  0,          //29a
  
  0,          //1b
  0,          //2b
  0,          //3b
  0,          //4b
  0,          //5b
  0,          //6b
  0,          //7b
  0,          //8b
  0,          //9b
  0,          //10b
  0,          //11b
  0,          //12b
  0,          //13b
  0,          //14b
  0,          //15b
  B00000001,  //16b
  B00000001,  //17b
  B00000011,  //18b
  B00000010,  //19b
  B00000110,  //20b
  B00000100,  //21b
  B00001100,  //22b
  B00001000,  //23b
  B00011000,  //24b
  B00010000,  //25b
  B00110000,  //26b
  B00100000,  //27b
  B01100000,  //28b
  B01000000,  //29b
  };

// routine di trasferimento SPI di un byte (una sola DIGIT) verso il MAX2771
void maxTransfer (byte digit, byte valore){
  digitalWrite(CS, LOW);
  SPI.transfer(digit);
  SPI.transfer(valore);
  digitalWrite(CS, HIGH);
}

//routine attivata via interrupt esterno per l'aggiornamento della variabile pot (valori limitati tra 0 e 28)
// quando lo stato del pin A dell' encoder cambia da 1 a 0 la routine va a leggere lo stato del pin B, incrementando o decrementando la variabile in funzione della direzione di movimento
// a fine ciclo viene chiamata la routine di scrittura del valore della variabile pot (2 byte consecutivi) verso il MAX7221 
void readenc(){
    if(digitalRead(dirpin)==HIGH){
    pot++;
    }
  else{
    pot--;
    }
  if(pot>=28){pot=28;}
  if(pot<=0){pot=0;}
  ringval(pot);
}

//routine di trasmissione di 2 byte per la codifica del valore della variabile pot verso il MAX7221
void ringval(int pot) {
  maxTransfer(0x01, ringmatrix [pot]);
  maxTransfer(0x02, ringmatrix [pot+29]);
}

void setup() {
pinMode(intpin,INPUT);
pinMode(dirpin,INPUT);
attachInterrupt(digitalPinToInterrupt(intpin),readenc,FALLING);
Serial.begin(250000);
pinMode(CS, OUTPUT);
SPI.setBitOrder(MSBFIRST);
// setup registri del MAX7221
SPI.begin();
  maxTransfer(0x0C, 0x01);  // Shutdown (0x00) or NORMAL OPERATION (0x01)
  maxTransfer(0x0A, 0x08);  // Intensity
  maxTransfer(0x09, 0x00);  // Decode-mode: NO DECODE 
  maxTransfer(0x0B, 0x01);  // Only scan 2 digits
  maxTransfer(0x01, 0);       // spegni tutta DIGIT 0 (prima metà del ring)
  maxTransfer(0x02, 0);       // spegni tutta DIGIT 1 (seconda metà del ring)
  maxTransfer(0x0F, 0x01);  // Display test: ON
  delay(500);
  maxTransfer(0x0F, 0x00);  // Display test: OFF (NORMAL OPERATIONS)
    
  ringval(pot); //all' accensione il ledring viene impostato sul valore centrale (valore 14, solo led n.8 acceso)
}

void loop() {
Serial.println(pot); //terminale attivo per visualizzazione del valore della variabile pot
}

Ho provato a lanciare la funzione “ringval()” sia da dentro che da fuori la routine di interrupt, ho provato a modificare la velocità di trasferimento SPI, ho provato ad armare e disarmare l’interrupt soltanto quando serve (tenendolo disarmato durante il trasferimento SPI), ho provato a cambiare sia il collegamento dei pin dell’ encoder, sia quello del pin di CS per evirare interferenze provenienti dai pin clock e MOSI, ma non cambia nulla.
L’unica cosa che migliora di moltissimo ma rende comunque la variazione della variabile non fluida è l’introduzione di almeno un millisecondo di delay() all’ inizio della funzione “maxTransfer()”. :o

Qualcuno riesce a spiegarmi come mai e come posso risolvere per aggiornare la variabile ed il display in maniera fluida (senza pseudo-bounces)?

Grazie a tutti per la pazienza e per il supporto! :slight_smile: