Display 7 segmenti via BCD

Salve a tutti!
Ho un problema, derivante dal fatto che sul software non sono particolarmente bravo:
sto cercando di pilotare 6 digit 7 segmenti ad anodo comune tramite un HFE4543 per la parte di selezione del segmento e un HFE4028 per la selezione della cifra.
Volevo chiedervi una mano per cominciare a visualizzare un numero.

Il BCD funziona con 4 livelli HIGH o LOW in base ai quali ho un numero da 0 a 9.
Pensavo di fare le definizioni delle cifre (c0,..., c9) come array di valori alti e bassi.
La stessa cosa pensavo di fare con le cifre.
Il problema è che non ho idea di come passare dal numero in cifra a una lista di istruzioni su quali pin tenere HIGH e quali LOW per visualizzare la cifra corretta (intanto) e poi visualizzarla sul digit corretto.

/*digit1 prima a destra dei secondi, digit6 prima a sinistra delle ore*/
#define A0=2;
#define A1=3;
#define A2=4;
#define d1[]{LOW,LOW,HIGH};
#define d2[]{LOW,HIGH,LOW};
#define d3[]{LOW,HIGH,HIGH};
#define d4[]{HIGH,LOW,LOW};
#define d5[]{HIGH,LOW,HIGH};
#define d6[]{HIGH,HIGH,LOW};
int digit[]{A2,A1,A0};
#define B0=5;
#define B1=6;
#define B2=7;
#define B3=8;
#define c0[]{LOW,LOW,LOW,LOW];
#define c1[]{LOW,LOW,LOW,HIGH};
#define c2[]{LOW,LOW,HIGH,LOW};
#define c3[]{LOW,LOW,HIGH,HIGH};
#define c4[]{LOW,HIGH,LOW,LOW};
#define c5[]{LOW,HIGH,LOW,HIGH};
#define c6[]{LOW,HIGH,HIGH,LOW};
#define c7[]{LOW,HIGH,HIGH,HIGH};
#define c8[]{HIGH,LOW,LOW,LOW};
#define c9[]{HIGH,LOW,LOW,HIGH};
int cifra[]{B3,B2,B1,B0};
#define SEC=9; /*4 led blu che segnano i secondi. sono collegati al digit6 prima a sinistra delle ore*/


void setup() {
  pinMode(AO.OUTPUT);
  pinMode(A1.OUTPUT);
  pinMode(A2.OUTPUT);
  pinMode(B0.OUTPUT);
  pinMode(B1.OUTPUT);
  pinMode(B2.OUTPUT);
  pinMode(B3.OUTPUT);
  pinMode(SEC.OUTPUT);
}

void loop() {
}

Intanto ho scritto questo però ripeto, non so come passare dalla cifra ai comandi da dar eper accendere i segmenti esatti.

Grazie mille

Riccardo

impara ad usare il max 7219 o simili è stato fatto per questo
puoi variare anche la luminosità delle cifre via software

Gli HEF4543 dispongono di Latch Enable (LE), quindi puoi usare 4 pin di Arduino per il codice BCD, parallelato per i 6 integrati, e 6 pin per i LE:
mandi il BCD del primo display, metti il LE del primo 4543 alto, lo rimetti basso (bastano 15nS: il periodo del clock di Arduino, quindi, è sufficiente),
mandi il BCD del secondo display, metti il LE del secondo 4543 alto, lo rimetti basso e così via.

Per velocizzare il ciclo e semplificare lo sketch, scegliendo 4 bit di una stessa porta puoi usare le tecniche di port management
Numero 1:
PORTx&=B11110001; PORTx|=B00000001

Numero 7:
PORTx&=B11110111; PORTx|=B00000111

elrospo:
impara ad usare il max 7219 o simili è stato fatto per questo
puoi variare anche la luminosità delle cifre via software

Un altro progetto è l'utilizzo di un ICM7218 e un PCF8574 per avere 8 display 7 segmenti via i2c

Datman:
Gli HEF4543 dispongono di Latch Enable (LE), quindi puoi usare 4 pin di Arduino per il codice BCD, parallelato per i 6 integrati, e 6 pin per i LE:
mandi il BCD del primo display, metti il LE del primo 4543 alto, lo rimetti basso (bastano 15nS: il periodo del clock di Arduino, quindi, è sufficiente),
mandi il BCD del secondo display, metti il LE del secondo 4543 alto, lo rimetti basso e così via.

Per velocizzare il ciclo e semplificare lo sketch, scegliendo 4 bit di una stessa porta puoi usare le tecniche di port management
Numero 1:
PORTx&=B11110001; PORTx|=B00000001

Numero 7:
PORTx&=B11110111; PORTx|=B00000111

In questo progetto ho già il circuito stampato, per cui uso appunto 4543 per i segmenti e il 4028 per i display, non posso cambiare.
Quello che mi intressa, al di la dell'hardware è appunto capire come pilotarlo via software.
Tu scrivi PORTx& ... ma non so cosa voglia dire. Porta pazienza ma di codice a questo livello non me ne intendo, avrei bisogno di partire proprio dalle basi. Speravo di trovare una libreria o un progetto già fatto, ma non ho trovato nulla.

Grazie mille per le risposte intanto.

Riccardo

Bisognerebbe vedere lo schema elettrico...

(upload://4Rv2HkEbLVcwM83pv43de9NwSW.gif)]

Questo è lo schema elettrico, anche se i pin segnati non sono quelli reali da datasheet.

Non devi cambiare nulla, salvo il fatto che il 4028 decodifica da 3 a 6 per gli anodi comuni.
Vuoi impiegare un 328 al posto del PIC?

SI esattamente. Prima vorrei fare il codice per Arduino, poi utilizzerei un 328 standalone con la possibilità di programmarlo direttamente sul circuito. Chiaramente prima però devo inserire il bootloader via ICSP inserendolo sulla board di Arduino.

Con un programmatore ISP come l'USB ASP, il bootloader non serve.
Nelle mie realizzazioni metto sempre un connettore a 6 pin e aggiorno da lì.

Io pensavo di mettere l bootloader e uscire con i pin Tx e Rx per usare un convertitore FTDI da seriale TTL a USB.

Comunque il vero problema attuale è che non so fare il codice.

Una variabile contenente un numero da 0 a 9 in memoria è già rappresentata in modo binario, ed ha già i bit alti/bassi come serve a te, senza bisogno di array di conversione.

Supponiamo che i pin su cui scrivere il valore BCD siano definiti come PIN_A, PIN_B, PIN_C, PIN_D, siano configurati come uscite, e siano collegati ai corrispondenti D0, D1, D2, D3 del 4543.

Allora se abbiamo una variabile 'n' con valore da 0 a 9, per scrivere i bit giusti in uscita è sufficiente:

digitalWrite(PIN_A, n & 1);
digitalWrite(PIN_B, (n >> 1) & 1);
digitalWrite(PIN_C, (n >> 2) & 1);
digitalWrite(PIN_D, (n >> 3) & 1);

Mi sto informando su come funziona & nella sezione Reference di arduino.cc .

Devo bene capire cosa vuol dire fare l'operazione (n >> 1..2..3) & 1 ...

Quindi se io ho un numero a 6 cifre tipo "hhmmss" devo dividere ogni cifra e stamparla nel suo digit, non posso inserirla come se fosse una "stringa". O meglio posso darla in pasto a una funzione che usa la stringa come un array e ogni singola cifra dell'array di 6 elementi viene stampata nel proprio display.

Altra domanda per far chiarezza. Io parlo di display sia per riferirmi a tutti e 6 i display, sia per il singolo digit. C'è un nome italiano per differenziale le cose? Io so di cosa sto parlando ma non vorrei essere frainteso.

Grazie comunque per la dritta. Ora devo provarla (capendo bene come funziona) e capire come posso fare la stessa cosa sul 4028 per gestire i singoli digit e i 4 led dei puntini che si accendono ogni secondo (forse sarebbe stato meglio dividerli dalla prima cifra delle ore e gestirli separatamente).

P.S.: da quello che ho capito & restituisce 1 se entrambi i bit nella stessa posizione sono 1, 0 in tutti gli altri casi. Praticamente io aggiungo 1, 2 e 3 al numero decimale e poi lo confronto con i bit del numero 1, cioè 0000000000000001. Però continuo a non capire.

Hai considerato, vero, che la piedinatura del 328 è un po' differente da quella del PIC?
Alcune cose sono uguali, ma alcuni piedini hanno funzioni completamente diverse.

Se tu avessi la massima libertà (in realtà è sufficiente tagliare qualche pista e metterci dei filetti), addirittura potresti passare i numeri direttamente alla porta, ad esempio per scriverlo nei 4 LSB:

PORTx&=240+numero;
PORTx|=numero;

PORTx & = (dove x è il nome della porta: B, C o D) è la forma abbreviata di PORTx = PORTx & ... ... ...
(nota: nella maggioranza dei casi [sempre?] gli spazi non sono necessari)

& è la moltiplicazione bit per bit, come una porta AND
| è la somma bit per bit, come una porta OR

Quindi:

PORTx&=240+numero;
moltiplica bit per bit il contenuto della porta x per un valore binario con i 4 MSB a 1 (cioè 240) e i 4 LSB corrispondenti al tuo numero: in questo modo porta a 0 i bit che devono andare a 0.

PORTx|=numero;
somma bit per bit il contenuto della porta x a un valore binario con i 4 LSB corrispondenti al tuo numero: in questo modo porta a 1 i bit che devono andare a 1.

ricki158:
Quindi se io ho un numero a 6 cifre tipo "hhmmss" devo dividere ogni cifra e stamparla nel suo digit

Bisogna partire da un dato certo... come sono gestiti i valori?
Una variabile per le ore, una per i minuti e una per i secondi?
Con i valori in binario puro o suddivisi in BCD nei nibble delle variabili? (questo secondo è il formato che viene letto da un RTC)
Oppure sei variabili per unità e decine delle varie grandezze? (più lungo e brigoso).

parlo di display sia per riferirmi a tutti e 6 i display, sia per il singolo digit. C'è un nome italiano per differenziale le cose?

In genere si parla di display a N cifre, N righe x Ncolonne ecc, dove cifra è sinonimo di digit.

come posso fare la stessa cosa sul 4028 per gestire i singoli digit

Nello stesso modo. Solo che comanderai degli altri pin di uscita prendendo il valore da una variabile che servirà a contenere il numero del digit da accendere (basandosi sullo schema da 1, le unità di secondi, a 6, le decine di ore con cui si alimentano contemporaneamente anche i punti).

P.S.: da quello che ho capito & restituisce 1 se entrambi i bit nella stessa posizione sono 1, 0 in tutti gli altri casi. Praticamente io aggiungo 1, 2 e 3 al numero decimale e poi lo confronto con i bit del numero 1, cioè 0000000000000001. Però continuo a non capire.

L'operazione & 1 serve per azzerare tutti i bit tranne il primo a destra (chiamato bit 0 o LSB), in questo modo il risultato può essere solo 0 o 1 e quindi conosco esattamente il valore di quel bit.

L'operazione >> x serve per spostare di x posizioni a destra tutti i bit della variabile, in questo specifico caso serve per portare quello che mi interessa nella posizione più a destra per testarlo con & 1 e ottenere sempre un risultato 0 o 1.

La cosa comoda è che in questo modo con digitalWrite scrivo sui vari pin esattamente i valori dei bit contenuti nelle quattro posizioni più a destra della variabile 'n' (considera che HIGH è sinonimo di 1 e LOW di 0)

Datman:
Hai considerato, vero, che la piedinatura del 328 è un po' differente da quella del PIC?
Alcune cose sono uguali, ma alcuni piedini hanno funzioni completamente diverse.

Si certamente ho cambiato i pin adattandoli a quelli del 328, sistemando i pin come ho scritto nel codice che ho postato all'inizio.

Se tu avessi la massima libertà (in realtà è sufficiente tagliare qualche pista e metterci dei filetti), addirittura potresti passare i numeri direttamente alla porta, ad esempio per scriverlo nei 4 LSB:

PORTx&=240+numero;
PORTx|=numero;

PORTx & = (dove x è il nome della porta: B, C o D) è la forma abbreviata di PORTx = PORTx & ... ... ...
(nota: nella maggioranza dei casi [sempre?] gli spazi non sono necessari)

& è la moltiplicazione bit per bit, come una porta AND
| è la somma bit per bit, come una porta OR

Quindi:

PORTx&=240+numero;
moltiplica bit per bit il contenuto della porta x per un valore binario con i 4 MSB a 1 (cioè 240) e i 4 LSB corrispondenti al tuo numero: in questo modo porta a 0 i bit che devono andare a 0.

PORTx|=numero;
somma bit per bit il contenuto della porta x a un valore binario con i 4 LSB corrispondenti al tuo numero: in questo modo porta a 1 i bit che devono andare a 1.

Ok piano piano sto cominciando a capire.
L'unica cosa che non capisco sul lato hardware è quando dici che potrei passare i numeri direttamente alla porta.

Scusami ma Arduino l'ho usato perlopiù didatticamente, leggi un sensore, stampa una variabile. Non sono mai arrivato a questo livello. Per questo motivo sono un po' "lento".

Se scrivi il valore nei 4 LSB (o MSB) di una porta a 8 bit, assegni i 4 bit del valore ai 4 bit della porta.

Claudio_F:
Bisogna partire da un dato certo... come sono gestiti i valori?
Una variabile per le ore, una per i minuti e una per i secondi?
Con i valori in binario puro o suddivisi in BCD nei nibble delle variabili? (questo secondo è il formato che viene letto da un RTC)
Oppure sei variabili per unità e decine delle varie grandezze? (più lungo e brigoso).

Allora diciamo che per adesso non lo so. Devo appena comprare il modulo DCF77 e provare a vedere, ma credo esistano già delle librerie. Io ho usato esattamente quello schema elettrico per farmi un orologio, per cui non ho nessun RTC.

In genere si parla di display a N cifre, N righe x Ncolonne ecc, dove cifra è sinonimo di digit.

Ottimo allora, grazie della delucidazione!

Nello stesso modo. Solo che comanderai degli altri pin di uscita prendendo il valore da una variabile che servirà a contenere il numero del digit da accendere (basandosi sullo schema da 1, le unità di secondi, a 6, le decine di ore con cui si alimentano contemporaneamente anche i punti).

Ok allora. Devo capire bene come funziona la prima parte sui segmenti.

L'operazione & 1 serve per azzerare tutti i bit tranne il primo a destra (chiamato bit 0 o LSB), in questo modo il risultato può essere solo 0 o 1 e quindi conosco esattamente il valore di quel bit.

L'operazione >> x serve per spostare di x posizioni a destra tutti i bit della variabile, in questo specifico caso serve per portare quello che mi interessa nella posizione più a destra per testarlo con & 1 e ottenere sempre un risultato 0 o 1.

La cosa comoda è che in questo modo con digitalWrite scrivo sui vari pin esattamente i valori dei bit contenuti nelle quattro posizioni più a destra della variabile 'n' (considera che HIGH è sinonimo di 1 e LOW di 0)

Ok, ho circa capito. Solo non mi è chiaro perché devo spostare le cifre.
Allora, le prime 4 cifre in codice binario formano le prime 2^4=16 cifre decimali, da 0 a 15. Ora se io scrivo 0 in decimale, in binario a 16 bit (ma posso anche usare una variabile a 8 bit per occupare meno spazio) sarà 0000000000000000. Prendo la prima cifra a destra, la confronto con AND e la scrivo sul pin A0 del 4543, prendo la seconda cifra, la sposto di una posizione e confronto la prima cifra a destra con AND e la scrivo sul pin A1 del 4543 e così via per le altre due cifre che mi interessano.
Non mi è chiaro però come mai si fa così. Probabilmente perché è questa la base del controllo BCD di un numero, ma il mio livello di informatica ed elettronica è troppo base per avere di queste conoscenze.

Datman:
Se scrivi il valore nei 4 LSB (o MSB) di una porta a 8 bit, assegni i 4 bit del valore ai 4 bit della porta.

Credo di aver capito, ho risposto a Claudio_F mentre mi rispondevi. Piano piano sto chiarendo le mie idee. Mi resta solo il concetto di porta che dicevi tu, ovvero fare modifiche hardware che non ho capito. Già adesso i pin digitali scrivono direttamente sulle porte A0, A1, A2, A3 del 4543 senza tagliare piste e ponticellare.

I pin 21...24 del 328 sono Aref, Gnd, PC0, PC1! Non sono i 4 bit LSB o MSB di una porta!

Ok scusatemi ho capito:

in BCD praticamente io scrivo i 4 bit della cifra che va da 0 a 9 sulle 4 porte di entrata del HFE4543.
Per questo motivo per ogni cifra in codice binario, sposto le cifre in maniera tale da scrivere ogni bit e poi confrontarlo. Non mi è chiaro ancora il motivo per cui devo confrontare ogni bit... ma piano piano sto capendo tutto.

No guarda, lo schema elettrico è quello ma ho naturalmente cambiato i piedini per adattarlo al ATMega328P.

Tu dicevi direttamente di assegnare un valore a una porta digitale senza dare un valore numerico a una variabile, richiamare la variabile quando scelgo la modalità del pin (se INPUT o OUTPUT) e alla fine scrivere un digitalWrite richiamando il nome della variabile?

Io comunque ho modificato il codice in questo:

/*definizione delle porte del HFE4028 che comanda le digit*/
#define A0=2;
#define A1=3;
#define A2=4;

/*definizione delle porte del HFE4543 che comanda i 7 segmenti*/
#define B0=5;
#define B1=6;
#define B2=7;
#define B3=8;

#define SEC=9; /*4 led blu che segnano i secondi. sono collegati al digit6 prima a sinistra delle ore*/

int n=0; /*numero da scrivere, massimo 6 cifre, formato "HHMMSS" */
int d=0; /*digit1 prima a destra dei secondi, digit6 prima a sinistra delle ore*/

void setup() {
  
  pinMode(A0.OUTPUT);
  pinMode(A1.OUTPUT);
  pinMode(A2.OUTPUT);
  pinMode(B0.OUTPUT);
  pinMode(B1.OUTPUT);
  pinMode(B2.OUTPUT);
  pinMode(B3.OUTPUT);
  pinMode(SEC.OUTPUT);
}

void loop() {
  
  /*scrivi il numero nella variabile d (digit) in BCD e porta HIGH o LOW le porte del HFE4028*/
  digitalWrite(A0,d&1);
  digitalWrite(A1,(d>>1)&1);
  digitalWrite(A2,(d>>2)&1);
  
  /*scrivi il numero nella variabile n (cifra decimale) in BCD e porta HIGH o LOW le porte del HFE4543*/
  digitalWrite(B0,n&1);
  digitalWrite(B1,(n>>1)&1);
  digitalWrite(B2,(n>>2)&1);
  digitalWrite(B3,(n>>3)&1);
}