Tra i moduli di costruzione cinese con display a 7 segmenti, si trovano
diversi modelli che impiegano 8 cifre comandate da due 75HC595. Il costo è
talmente contenuto che non ha senso costruirselo con componenti acquistati in
negozio.
Abbiamo provato questo modello:
|
Si tratta di due blocchi da 4 cifre ciascuno comandati da 2 shift register
HC595.
Quello provato è venduto da dx.com, che riporta una etichetta JTRON sul
retro.
Simile a questo ne esistono molti altri, di diversi costruttori,
che hanno schemi elettrici diversi. |
Comune
praticamente a tutti, però, è il fatto che non viene fornita alcuna
documentazione tecnica, ma solo, al massimo, un driver per Arduino o simili.
Per poter utilizzare il prodotto al di fuori di questo ambito, occorre per
prima cosa rilevare lo schema delle connessioni, altrimenti non si conosce la
relazione tra le uscite degli shift register e la matrice del display.
Per quello in prova, lo schema è il seguente:
I due blocchi di display sono dei 3641BS, ad anodo comune.
I due HC595 sono collegati in cascata.
Il primo comanda gli
anodi dei display e le sue uscite devono andare a livello 1 per accendere la
cifra.
Il secondo comanda i segmenti e le uscite devono andare a livello 0 per
accenderli.
Il dato seriale da inviare sarà, quindi, una stringa di 16 bit: i primi
trasmessi saranno relativi ai segmenti da accendere, mentre i successivi saranno
relativi al digit.
Un circuito comandato da un display controller specializzato, ad
es. il MAX7219, necessita solo di ricevere comandi e dati e provvede
autonomamente al multiplex dei segmenti.
Nel caso in esame, invece, occorre che il microcontroller determini e comandi i
ciclo di multiplex.
Questo richiede di accendere un digit per volta e, complessivamente, il refresh per gli 8 digit non deve superare il tempo di permanenza
dell'immagine sulla retina.
In pratica, un refresh a 3ms (333Hz) è troppo lento: il ciclo complessivo
occupa 3x8=24ms e questo comincia a evidenziare uno sfarfallio delle cifre.
Invece, un tempo inferiore, ad esempio 2.5ms (400Hz) è il massimo impiegabile
(2.5x8=20ms) e 2ms (500Hz) è ottimale (2x8=16ms).
Questo genere di cadenze è facilmente ottenibile con uno dei
timer del microcontroller.
Non ci sono resistenze...
Si nota immediatamente un punto che parrebbe a sfavore: la corrente nei
segmenti non dispone di alcuna resistenza di limitazione.
Si tratta, però, di una combinazione circuitale ben particolare: in condizioni statiche, accendendo
un solo segmento o tutti e sette più il punto decimale, la corrente assorbita
è praticamente costante nell'area dei 50mA (da 47 a 52mA).
Questo vuol dire che, per segmento, la corrente passa da 42mA (1
solo segmento acceso) a 6.5mA (cifra 8 + dp). Questo si nota osservando la differenza tra la
luminosità della cifra 1 (24mA per segmento) e quella della cifra 8 (6.5mA per
segmento).
Segmenti accesi |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
Corrente [mA] |
42.3 |
48.2 |
49.4 |
50 |
50.6 |
51.3 |
51.7 |
51.8 |
Il fenomeno è dovuto alla caduta di tensione sulle resistenze di conduzione
dei MOSFET di uscita degli HC595 (nella versione prodotta da Titan), che sale con l'aumentare della corrente,
passando da 140mV a oltre 2.5V ed ha l'inaspettato effetto di mantenere la
corrente complessiva pressochè costante.
ATTENZIONE: la misura è stata effettuata accendendo in
modo statico UNA SOLA cifra per volta. Se malauguratamente più cifre
sono accese contemporaneamente, la corrente complessiva sale a valori tali da
riscaldare gli shift register e metterli in pericolo .
Più cifre potranno essere visibili contemporaneamente solo in un ciclo di
multiplex, che, per ogni step, ne accende una sola.
Il pericolo sussiste perchè il pin di Output Enable (OE)
è a livello 0, ovvero le uscite sono sempre abilitate, e il pin di Reset
è collegato al positivo e quindi all'arrivo della tensione di alimentazione i
latch possono contenere valori casuali che accenderanno casualmente più
segmenti. Se la situazione permane senza che il multiplex sia avviato, la
corrente assorbita può superare ampiamente i 100mA.
Occorre, quindi, tra le prime azioni del processore, azzerare il contenuto dei
latch inviando due bytes a 0 (o il primo byte a 1 per i segmenti e il secondo a
0 per gli anodi).in tal senso, un miglioramento del circuito potrebbe essere
quello di scollegare il Reset dalla Vcc e interporre una rete RC per dare un
impulso di clear ai latch all'arrivo della tensione.
Durante il ciclo di multiplex, quindi, la corrente media è 50mA circa; la differenza percepita
per la diversità della corrente nei segmenti a seconda del numero presentato si attenua per il
basso duty cycle impiegato (1/8).
In sostanza, i valori delle correnti non superano i massimi assoluti e sul breve periodo non
dovrebbe essere fonte di problemi, ma il concetto di usare la resistenza di
conduzione dei MOSFET come limitatore di corrente non è il massimo della
correttezza progettuale. A lungo termine è da verificare
la reale durata del circuito, anche se, essendo multiplexato, la presenza di
tempi off in cui il calore può essere dissipato migliora la situazione.
L'interfaccia di comunicazione.
Un connettore a 5 pin consente di inviare segnali al modulo,
mentre un altro connettore è previsto per collegare più moduli in cascata:
# |
Pin
ingresso |
Pin
uscita |
Indicazione |
Funzione |
Indicazione |
Funzione |
1 |
GND |
Alimentazione negativa |
GND |
Alimentazione negativa |
2 |
DIO |
Ingresso dati seriali |
QH |
Uscita dati seriali |
3 |
RCLK |
Impulso di latch |
RCLK |
Impulso di latch |
4 |
SCLK |
Clock di trasmissione |
SCLK |
Clock di trasmissione |
5 |
Vcc |
Alimentazione positiva |
Vcc |
Alimentazione positiva |
La possibilità di collegare più moduli in cascata si scontra,
però, con la necessità di usare una frequenza di refresh molto alta: con 16
display occorre almeno 1kHz (1ms).
L'interfaccia verso il microcontroller è seriale a due fili, dato e clock
(chiamati DIO e SCLK), ai quali si aggiunge il segnale di latch
(RCLK). I pin OE e RESET dei chip sono connessi rispettivamente al GND ed
alla Vcc.
HC595
è adatto ad essere gestito dall'interfaccia SPI del microcontroller e si presta bene ad essere comandato attraverso il modulo
MSSP
presente su molti PIC.
Anche se è possibile manipolare i bit necessari senza usare questa periferica,
va considerato che HC595 è un componente veloce e ciò consente di usare un clock di trasmissione molto elevato,
con una sensibile riduzione dei tempi dell'operazione di scrittura. Da prove fatte, un clock
a 1MHz è di tutta sicurezza, mentre si può arrivare tranquillamente a 4MHz e
più.
In
base a queste considerazioni è stato scritto un semplice driver in Assembly per
PIC Enhanced Midrange.
Il driver.
Per
le prove è stato usato un PIC16F1509 che consente di avere un clock di 16MHz
con l'oscillatore interno ed è dotato di modulo MSSP.
Determiniamo
una macchina a 8 stati che è gestita nel ciclo di interrupt e dal contatore dgtcntr.
Ogni stato corrisponde al refresh di un digit.
La maschera dei segmenti da accendere è conservata in un buffer di memoria RAM
(dispbuf) a 8 bytes, ognuno dei quali
contiene la situazione dei segmenti.
I punti decimali sono conservati in una locazione RAM (dpstac)
apposita e sono aggiunti come ottavo bit alle maschere.
Le
maschere dei segmenti sono caricate in un buffer momentaneo txbuf1,
mentre in txbuf2 sono inseriti i bit per
l'accensione degli anodi.
banksel dgtcntr
; dgtcntr = 7: aggiorna digit 8 - first
; dgtcntr = 6: aggiorna digit 7
; dgtcntr = 5: aggiorna digit 6
; dgtcntr = 4: aggiorna digit 5
; dgtcntr = 3: aggiorna digit 4
; dgtcntr = 2: aggiorna digit 3
; dgtcntr = 1: aggiorna digit 2
; dgtcntr = 0: aggiorna digit 1 - last
decf dgtcntr,f ; digitcounter-1
movf dgtcntr,w ; come indice per il digit
brw
bra Int_0
; digit 1
bra Int_1
; digit 2
bra Int_2
; digit 3
bra Int_3
; digit 4
bra Int_4
; digit 5
bra Int_5
; digit 6
bra Int_6
; digit 7
bra Int_7
; digit 8
; il digit 0 è rinfrescato per ultimo
Int_0:
; digit più a sinistra
banksel dgtcntr ; last digit
movlw .8
; reload digit counter
movwf dgtcntr ;
for next loop
movlw 0x80
; select common of the digit
movwf txbuf2
movf dispbuf,w ; load data
btfsc dpstatc,0 ; add dp?
bcf WREG,7
; y - add dp
movwf txbuf1
; and copy to tx buffer
bra Int_end
; digit on
Int_1:
banksel txbuf1
movlw 0x40
; select common of the digit
movwf txbuf2
movf dispbuf+1,w ; load data
btfsc dpstatc,1 ; add dp?
bcf
WREG,7
; y - add dp
movwf txbuf1
bra Int_end
; digit on
....
|
txbuf1
e txbuf2 sono utilizzati dalla routine di
trasmissione:
Int_end:
pagesel HC595Wr2Byte
call HC595Wr2Byte
.....
;************************************************************************
; HC595Wr2Byte - sub - Write a byte to HC595
; Bytes on txbuf2:1
; Locking routine - wait for BF
;************************************************************************
HC595Wr2Byte:
GLOBAL HC595Wr2Byte
banksel txbuf1
; send txbuf1 to second HC595
movf txbuf1,w
banksel SSPBUF
movwf SSPBUF
sswb1 btfss SSPSTAT,BF ; wait for end of data transmission
bra sswb1
movf SSPBUF,W
; clear the flag
banksel txbuf2
movf txbuf2,w
; send txbuf2 to first HC595
banksel SSPBUF
movwf SSPBUF
sswb2 btfss SSPSTAT,BF ; wait for end of data transmission
bra sswb2
movf SSPBUF,W
; clear the flag
banksel LATB
bsf LCK
; pulse latch clock
bcf LCK
return |
I
due bytes sono shiftati in successione attraverso il modulo MSSP in
configurazione SPI:
; configure MSSP for Master SPI
banksel SSPSTAT
; SSPSTAT 00000000 SPI mode
; SMP b7 1------- 1 = In data sampled at end of data out time
;
0 = In data sampled at middle of data out time
;
allways 0 for slave mode
; CKE b6 -1------ 1 = Tx on transition from active to Idle clock
;
0 = Tx on transition from Idle to active clock
; D/A b5 --0----- 0 - I2C only
; P b4 ---0---- 0 - I2C only
; S b3 ----0--- 0 - I2C only
; R/W b2 -----0-- 0 - I2C only
; UA b1 ------0- 0 - I2C only
; BF b0 -------0 1 = Receive complete, SSPxBUF is full
;
0 = Receive not complete, SSPxBUF is empty
movlw b'11000000'
movwf SSPSTAT
; data transmitted on rising edge
; SSPCON1 00000000 SPI mode
; WCOL b7 0------- 0 - I2C only
; SSPOV b6 -0------ 1 = New byte is received
;
0 = No overflow
; SSPEN b5 --1----- 1 = Enables serial port
;
0 = Disables serial port
; CKP b4 ---0---- 1 = Idle state for clock is a high level
;
0 = Idle state for clock is a low level
; SSMP b3:0 ----0001 0000 = SPI Master, clk = FOSC/4
;
0001 = SPI Master, clk = FOSC/16
;
0010 = SPI Master, clk = FOSC/64
;
0011 = SPI Master, clk = TMR2 output/2
;
0100 = SPI Slave , clk = SCKx pin, SS enabled
;
0101 = SPI Slave , clk = SCKx pin, SS disabled
movlw b'00100001'
movwf SSPCON1
; data at FOSC/16, enable SPI |
La
cadenza dell'interruzione è stabilita con precisione a 2ms dal Timer2:
; configure Timer2 to generate periodic interrupt every 2ms
; postscaler 1:8, prescaler 1:4
banksel T2CON
; T2CON b'00000000'
; ni b7 0-------
; T2OUTPS b6:3 -0111--- 0111 Postscaler 1:8
; TMR2ON b2 -----1-- 1 = timer on
;
0 = timer off
; T2CKPS b1:0 ------01 00 = Prescaler 1:1
;
01 = Prescaler 1:4
;
10 = Prescaler 1:16
;
11 = Prescaler 1:64
movlw b'00111101'
movwf T2CON
movlw D'249'
movwf PR2 |
L'uso
del Timer2 presenta il vantaggio del prescaler e del postscaler, il che consente
tempi piuttosto lunghi, mentre all'overflow non occorre ricaricare il contatore.
Basta cancellare il flag di interruzione:
; IRQ vector
IRQVEC CODE 0x4
; irq of Timer2 ?
Int_serv:
bcf PIR1,TMR2IF ; cancella flag |
Ricordiamo che gli Enhanced Midrange hanno il salvataggio
automatico del contesto all'ingresso in interrupt e il ripristino automatico al
retfie, cosa comune con i PIC18F.
Se si intende eseguire il programma su un midrange, occorre ricordarsi di
aggiungere queste azioni.
Il
programma prevede luso di un doppio buffer per i dati da presentare sul
display. Esistono quindi:
-
databuf,
8 bytes, che contiene i valori da portare a display e che sarà movimentato
dal programma principale
-
dispbuf
che contiene i dati trasformati in maschere segmenti
Un
ulteriore byte dpstat contiene lo stato dei punti decimali, che può esesere
così gestito separatamente dai valori nuumerici. Anche questo è copiato in un
byte "operativo" (dpstatc).
La
conversione fra valori numerici e maschere è effettuata con una semplice lookup
table del genere retlw:
; Convert data from 0 to F in 7-segments mask
; Direct connection: bit0=segm a, bit1=segm b, etc.
segtblF_16e:
brw
retlw b'00111111' ; 6F "0" -|-|F|E|D|C|B|A
retlw b'00000110' ; 06 "1" -|-|-|-|-|C|B|-
retlw b'01011011' ; 5B "2" -|G|-|E|D|-|B|A
retlw b'01001111' ; 4F "3" -|G|-|-|D|C|B|A
retlw b'01100110' ; 66 "4" -|G|F|-|-|C|B|-
retlw b'01101101' ; 6D "5" -|G|F|-|D|C|-|A
retlw b'01111101' ; 7D "6" -|G|F|E|D|C|-|A
retlw b'00000111' ; 07 "7" -|-|-|-|-|C|B|A
retlw b'01111111' ; 7F "8" -|G|F|E|D|C|B|A
retlw b'01101111' ; 6F "9" -|G|F|-|D|C|B|A
retlw b'01110111' ; 77 "A" -|G|F|E|-|C|B|A
retlw b'01111100' ; 7C "b" -|G|F|E|D|C|-|-
retlw b'00111001' ; 69 "C" -|-|F|E|D|-|-|A
retlw b'01011110' ; 5E "d" -|G|-|E|D|C|B|-
retlw b'01111001' ; 79 "E" -|G|F|E|D|-|-|A
retlw b'01110001' ; 71 "F" -|G|F|E|-|-|-|A |
Una
routine provvede alla copia da uno all'altro buffer, usando i due puntatori
indiretti FSR1
e FSR2:
CopyBuf:
; Convert data buffer in segments and copy to display buffer
banksel lpcntr
movlw 8
; 8 step
movwf lpcntr
movlw LOW (databuf) ; set indirect pointer
1
movwf FSR1L
; to data buffer
movlw HIGH (databuf)
movwf FSR1H
movlw LOW (dispbuf) ; set indirect pointer
0
movwf FSR0L
; to display buffer
movlw HIGH (dispbuf)
movwf FSR0H
cblp moviw INDF1++
pagesel segtblF_16e ; convert to segments
call segtblF_16e
xorlw 0xFF
; invert mask for common anodes
movwi INDF0++
decfsz lpcntr,f
bra cblp
; copy dp status
banksel databuf
movf dpstat,w
; dp status
movwf dpstatc
return |
Le
istruzioni specifiche per il controllo dei puntatori movwi
e moviw
rendono le operazioni quanto mai semplici.
La
tabella di conversione dei segmenti è positiva (1=segmento acceso), ma viene
invertita con un XOR dato che si deve applicare ai display che sono ad anodo
comune.
Lo
spostamento dei buffer viene effettuato se necessario all'inizio del ciclo di
refresh del display:
; first digit scanned
Int_7:
; right digit
; refresh cycle start point - new data to display?
banksel flags
; new data ready?
btfss flags,1
; flag set
bra nonew
; n - don't copy buffer
bcf flags,1
; y - clear flag
pagesel CopyBuf
; copy buffer
call CopyBuf
nonew:
banksel txbuf1
movlw 0x01
; select common of the digit
movwf txbuf2
movf dispbuf+7,w ; load data
btfsc dpstatc,7
; add dp?
bcf WREG,7
; y - add dp
movwf txbuf1 |
Il programma principale, quando i dati da presentare sono variati, informa la
routine di interrupt del fatto alzando un bit di flag. Se il flag non è posto a
1, la copia viene saltata.
Il programma demo carica numeri da 1 a 8 con i punti decimali accesi e lancia l'interrupt,
terminando con un loop bloccato su se stesso che lascia all'interrupt la
gestione completa del display.
Nota:
Usando un Enhanced Midrange, occorre l'ambiente
MPLAB-X e almeno il PICKIT3.
Documentazione.
|