Shift di Registro - Encoder rotativo con modulo MAX7219 e display 7-segmenti

Saluti,
Primo post e spero che sia la sezione giusta perchè nonostante consulti il forum da diversi mesi, standoci dentro ho scoperto che non è molto semplice navigarlo. Ma imparerò anche questo...

Per scopo didattico personale, ma sicuramente gli affiderò un utilizzo pratico e reale, sto "lavorando" su questa realizzazione:
https://www.instructables.com/Arduino-KY-040-Rotary-Encoder-and-MAX7219-Interfac/

Tutto funziona bene, ma io vorrei capire e non ci sono riuscito perchè occorre un bagaglio che va oltre le mie conoscenze e/o ricordi attuali: tante cose le studiai, ma dopo 8 lustri mi è difficile riassemblare il puzzle degli Shift Register, Esadecimale, BCD e Binario

Alla fine del void loop c'è questa serie di stringhe:

  mydisplay.setDigit(0, 3, ((count_two >> 12) & 0x0F), false);
  mydisplay.setDigit(0, 2, ((count_two >> 8) & 0x0F), false);
  mydisplay.setDigit(0, 1, ((count_two >> 4) & 0x0F), false);
  mydisplay.setDigit(0, 0, ((count_two >> 0) & 0x0F), false);

so a cosa servono e capisco la sintassi, ma non capisco quanto segue:
(count_two >> 12) & 0x0F)
(count_two >> 8) & 0x0F)
(count_two >> 4) & 0x0F)
(count_two >> 0) & 0x0F)

-Cosa significa tradotto in numeri decimali?
-Perchè lo sviluppatore ha scelto/adottato questo sistema numerico?
-Sarebbe stato possibile utilizzare il sistema decimale?
-Quali sono i pro di utilizzare tale sistema?
-Come si sarebbe potuto scrivere più "Terra-Terra"?
Se si, avrebbe avuto la stessa efficienza ?

Chiedo risposte comprensibili e pratiche, come se fossero spiegate ad un bambino.
Grazie per aver letto

Con l'operazione >> stai spostando i bit verso destra.
Si potrebbe fare con delle divisioni intere delle potenze del 2
11001100 (204) >> 4 ottieni 00001100 (12)
2 alla 4=16 204/16=12.75 intero=12

Con l'operazione & (and su bit) usando quei valori esa 0F stai azzerano i 4 bit a sinistra e mantenendo inalterati i 4 bit a destra
Non mi viene in mente una alternativa. Forse prendere il resto di una divisione con potenze del 2

Grazie per la risposta. Ma non l'ho capità.
Ovviamente sto mancando di lucidità.
In termini pratici cosa succede?
Quei valori cosa significano?

Bisogna considerare la rappresentazione binaria della variabile count_two, che è definita come:

unsigned int count_two = 0;

count_two (su architetture 8..16 bit) è una variabile a 16 bit senza segno.

L'autore del codice vuole "estrarre" quattro bit per volta da questa variabile, per passarli alla funzione mydisplay.setDigit

Per fare questo applica una maschera AND (operatore &) ai quattro bit più bassi (quelli a destra nella rappresentazione binaria). Il valore 0x0F equivale a 15 in decimale, o B1111 in binario, e in pratica serve per azzerare tutti i bit alti oltre il quarto.

Per estrarre i bit più alti, prima li scorre tutti di 4, 8 o 12 posizioni verso destra, e poi applica l'AND.

È possibile farlo aritmeticamente invece che logicamente? Si, ricordando che ogni shift di uno a destra significa dividere per due:

(count_two / 4096) % 16
(count_two / 256) % 16
(count_two / 16) % 16
(count_two) % 16

ma è sicuramente meno efficiente.

A margine, la funzione che assembla il BCD nei 16 bit, a partire da un valore compreso tra 0 e 9999, è sbagliato che sia chiamata HexToBCD. L'esadecimale in quella conversione con c'entra nulla e può solo creare confusione :roll_eyes: Un nome adeguato sarebbe stato semplicemente toBCD

Davvero molto chiaro ma l'effetto è stato lieve: la tristezza è aumentata.
La vecchiaia è una brutta bestia! Gli Shifting quando ero verde li facevo a mente nel tram mentre tornavo a casa dalla scuola, ora non so più cosa siano!
... andrò a riaprire i libri ed i vecchi quaderni, gelosamente conservati, quando avrò un ritaglio di tempo.

Andando sul pratico, ho eliminato la parte che converte i valori:

-ho disattivato la riga 95

  count_two  = HexToBCD(rotation);

-ho cambiato le righe da 97 a 100 (quelle riportate nel post iniziale)
impostando il calcolo classico come segue:

  mydisplay.setDigit(0, 3, (rotation / 1000) % 10, false);
  mydisplay.setDigit(0, 2, (rotation / 100) % 10, false);
  mydisplay.setDigit(0, 1, (rotation / 10) % 10, false);
  mydisplay.setDigit(0, 0, (rotation) % 10, false);

-ho disattivato le righe seguenti da 119 a 132:

unsigned int HexToBCD(unsigned int number)
{
    unsigned char i=0;
    unsigned int k = 0;

    while(number)
    {
        k = ( k ) | ((number%10) << i*4);
        number = number / 10;
        i++;
    }

    return(k);
}

Insomma ho bypassato tutta la parte di conversione.
Non sono andato per tentativi, sono consono di ciò che ho fatto e tutto funziona.

Avrò perso anche efficienza (che non noto perchè il codice è minimale)
Ma ovviamente mi vengono dei dubbi.
Sapendo che alle macchine è meglio parlargi in HEX,
ho perso anche in verstilità per qualche possibile scopo extra e specifico?
Cosa c'è che non potrebbe andare con queste istruzioni in forma più elementare?

Avrò perso anche efficienza (che non noto perchè il codice è minimale)

Infatti in questo caso il tempo di processo (un loop che gira una volta ogni 100 ms) è lunghissimo rispetto a qualche decina di µs in più, che sono perciò irrilevanti.

E naturalmente tutto doveva funzionare uguale.

No, di per sé l' HEX è un concetto astratto compreso e interpretato a livello di compilatore (che lo traduce in byte binari esattamente come per il decimale), ma...

...qualcosa di subdolo: la lunghezza implicita dei valori nei calcoli aritmetici, che, se non specificata e non determinabile dai valori stessi, è assunta pari a un int, a sua volta di lunghezza dipendente dalla piattaforma (16, 32, 64 bit). Quindi sono possibili problemi di overflow, che nel caso specifico non si sono verificati perché si sta già lavorando con variabili a 16 bit.

Naturalmente se si vuole è sempre possibile dichiarare esplicitamente e in modo indipendente dalla piattaforma, sia le dimensioni delle variabili, che dei campi di calcolo intermedi.

Che programma contorto!

Non sono in grado di poterlo "pesare", ma in effetti ho avuto la medesima impressione.
Cercavo uno StartPoint e questo mi faceva comodo perchè posseggo diversi moduli di questo display ed alcuni encoder rotativi (nudi e crudi a 5 pin senza modulo preassemblato).

Il mio scopo è di visualizzare solo 3 digit e me lo autocostruirò correttamente dimensionato nella forma.
Mi serve per controllare dei valori in un campo di input in Raspberry.
Quidi dovrò ancora lottare un po per interfacciarlo.
Allego lo sketch completo, in forma elementare,e parzialmente bonificato.


// ###############


#include <LedControl.h>

// Serial debug
#define SERIAL_DEBUG

// Rotary encoder digital IO pins
#define PinA 2
#define PinB 3
#define PinSW 4

// MAX7219 digital IO Pins
#define DIN 7
#define CLOCK 6
#define LOAD 5

int unit;
int dec;
int cent;

// inputs: DIN pin, CLK pin, LOAD pin. quantity of MAX7219 Module
LedControl lc = LedControl(DIN, CLOCK, LOAD, 1);

unsigned int valore = 128;  //  Il display si posiziona su 128 all'avvio
unsigned long time = 0;

// E' coinvolta nel While per il Serial Print ad inizio Loop.
unsigned int num = 0;




void setup() {
  lc.shutdown(0, false);  // turns on display
  lc.setIntensity(0, 2);  // intensità luminosa (impostabile da 0 a 15)

#ifdef SERIAL_DEBUG
  Serial.begin(9600);
  Serial.print("Rotary Encoder Test");
#endif

  pinMode(PinA, INPUT_PULLUP);   //utilizzare resistori esterni
  pinMode(PinB, INPUT_PULLUP);   //utilizzare resistori esterni
  pinMode(PinSW, INPUT_PULLUP);  //utilizzare resistori esterni

  attachInterrupt(0, blinkA, LOW);
  attachInterrupt(1, blinkB, LOW);

  time = millis();

  delay(200);
}

void loop() {

  lc.clearDisplay(0);


  while (num != valore) {
    num = valore;
#ifdef SERIAL_DEBUG
    Serial.println(num);
#endif
  }


  Run();


  if (digitalRead(PinSW) == 0) {  // se Pulsante è LOW ripristina al valore Medio della Scala = 128
    delay(10);
    valore = 128;  // ripristina a 128


#ifdef SERIAL_DEBUG
    Serial.println("Switch Pressed");
#endif
  }


  if (cent <= 0) {
    lc.setDigit(0, 2, ' ', false);  // leading zero cent
  } else if (cent > 0) {
    lc.setDigit(0, 2, cent, false);
  }
  if ((cent <= 0) && (dec <= 0)) {
    lc.setDigit(0, 1, ' ', false);  // leading zero dec
  } else if (dec >= 0) {
    lc.setDigit(0, 1, dec, false);
  }
  lc.setDigit(0, 0, unit, false);

  delay(200);  // non ridurre altrimenti flickera
}



//Interupt routine for incrementing the variable
void blinkA() {
  if ((millis() - time) > 3)  // millisecondi minimi per cambio valore durante la rotazione
    valore++;
  if (valore > 255) {
    valore = 255;
  }
  time = millis();
}

//Interupt routine for decrementing the variable
void blinkB() {
  if ((millis() - time) > 3)  // millisecondi minimi per cambio valore durante la rotazione
    valore--;
  if (valore > 255) {
    valore = 0;
  }
  time = millis();
}

void Run() {
  unit = abs(valore) % 10;
  dec = abs(valore / 10) % 10;
  cent = abs(valore / 100) % 10;
}


// *****

Se vado off-topic posso aprire un post separato, perchè vorrei soffermarmi su un problema che ho avuto sempre difficoltà a risolvere con questo modulo assiene alla libreria "LedControl" ad esso dedicata (è molto vecchia, mai aggiornata da anni, ma pare che sia anche la più pratica).

La soppressione degli zero iniziali non mi riesce a dovere.
Nello sketch a corredo ho usato una soluzione artigianale
che, se pur funzionante, non è sicuramente quella giusta.
Essendo coinvolti solo 2 digit accetto il compromesso per il mio scopo, ma ovviamente non è la forma corretta da utilizzare se dovessi applicarlo a tutte le 8 cifre.

Ho provato con gli Array, ieri, qualche mese fa, e qualche anno fa, ma non mi riesce. Ho provato anche con i puntatori ma non ci sono riuscito. Non riesco a passare un valore calcolato a ciascun digit quando desidero sopprimere gli zero iniziali. Ovviamente non sono allenato e non ho basi solide.
Per questo chiedo suggerimenti.

Aggiungo: grazie Claudio

Puoi leggere questa discussione:

Per il contatore:

Anche questa, per la visualizzazione:

Questo l'ho fatto con il MAX7219:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.