ESERCITAZIONE # 9
Gestione di un display a 7 segmenti
Tutti conoscono i display a LED a 7 segmenti, caratteristici per la loro
luminosità tipicamente rossa.
In questa esercitazione comandiamo un display del genere per presentare
numeri e simboli.
|
Ecco come si presenta esternamente uno di questi componenti.
Elettricamente si tratta di 8 diodi LED (1 per ogni segmento più
quello per il punto decimale) assemblati in un contenitore plastico
sagomato in modo tale che essi illuminino delle aree ristrette che
formano i segmenti.
Esistono display di numerosissime varietà, forme e colori, ma,
principalmente, li possiamo suddividere in due categorie:
- display ad anodo comune: tutti gli anodi dei LED sono collegati
assieme
- display a catodo comune: tutti i catodi sono collegati assieme
Questo facilita il comandare i display in sistemi multiplexati. |
|
I segmenti hanno un indicatore standard, costituito da una lettera.
Accendendo i LED opportuni sarà possibile ottenere tutti i simboli
numerici più una vasta gamma di simboli alfabetici e altro.
Ad esempio, il numero 8 sarà rappresentato accendendo tutti i
LED.
Il numero 3 si evidenzierà accendendo i segmenti A, B, C, D, G.
Il numero 6 si otterrà accendendo il segmenti A, C, D, E, F, G
Una H maiuscola apparirà accendendo i segmenti B, C, B, E, F, G.
E così via. |
Il modo più semplice di comandare il display è facendolo direttamente dai
pin del PIC come si trattasse (e così in effetti è) di comuni LED.
Quindi colleghiamo ogni segmento ad un pin di un por, ad esempio PORTC, avendo cura di inserire
una resistenza di limitazione in serie ad ogni segmento.
Il valore della resistenza può variare da 220 ohm a 1 kohm a seconda della
sensibilità del display. Mediamente 470 ohm può andare bene.
|
=
|
|
Fino a qui non c'è niente di strano, ma dobbiamo porre l' attenzione su
un punto particolare: come abbiamo visto prima c'è una relazione tra i segmenti
accesi e la cifra presentata. Dunque c'è una relazione tra lo stato dei pin del
PIC che comandano i LED e i segmenti accesi.
Ovvero, e questa è la chiave del problema, c'è un rapporto tra il numero
binario posto nel registro di uscita del PORT e il numero presentato.
Nell' esempio schematico qui sopra abbiamo collegato un display a catodo
comune, ovvero costituito da 8 LED con i catodi uniti assieme, mentre gli anodi
sono disponibili separatamente.
Ne risulta che un LED (= segmento) è acceso quando il suo pin di comando
assume il livello alto (1) e spento quando assume il livello basso (0).
Ora, sarebbe un lavoro poco produttivo definire, ogni volta che dobbiamo fare
apparire una cifra, la corrispondente sequenza di bit 1 e 0 da scrivere nel PORT.
Ed in effetti questa non è la via corretta.
L' alternativa valida è quella di creare una tabella di "scambio" che
faccia equivalere il carattere voluto con la giusta combinazione di bit.
Queste tabelle, dette lookup tables (tabelle di ricerca) sono uno degli
elementi più comuni nella programmazione dei microcontroller; attraverso queste
trattiamo i dati in uscita, che corrispondono in questo caso a caratteri
numerici e letterali, semplicemente come elementi esadecimali puri.
La stessa via sarà sfruttata in mille altre situazioni, ad esempio per generare
forme d' onda, per correggere valori letti in ingresso, per scambiare formato ai
dati, ecc.
Creiamo quindi una tabella che collega carattere da presentare sul
display-LED accesi-bit a 1
Detto così sembra complesso, ma non lo è per niente.
Per prima cosa verifichiamo la coincidenza pin-segmento; nello schema
presentato sopra abbiamo:
Segmento |
Bit del PORT |
A |
0 |
B |
1 |
C |
2 |
D |
3 |
E |
4 |
F |
5 |
G |
6 |
dp |
7 |
Verifichiamo quali segmenti vanno accesi per ottenere i caratteri numerici
e facciamo una tabella di collegamento tra caratteri e segmenti:
Carattere |
Segmenti |
A |
B |
C |
D |
E |
F |
G |
0 |
x |
x |
x |
x |
x |
x |
|
1 |
|
x |
x |
|
|
|
|
2 |
x |
x |
|
x |
x |
|
x |
3 |
x |
x |
x |
x |
|
|
x |
4 |
|
x |
x |
|
|
x |
x |
5 |
x |
|
x |
x |
|
x |
x |
6 |
x |
|
x |
x |
x |
x |
x |
7 |
x |
x |
x |
|
|
|
|
8 |
x |
x |
x |
x |
x |
x |
x |
9 |
x |
x |
x |
x |
|
x |
x |
Nella configurazione a catodo comune, per accendere il LED il pin deve andare
a livello 1, quindi aggiungiamo alla tabella il valore logico che devono
assumere i pin, in binario e in esadecimale.
Il valore esadecimale è aggiunto in quanto è più semplice da trattare che non
la lunga stringa di 1 e 0 del numero binario; e una coppia di cifre esadecimali
rappresenta proprio la condizione logica di un byte (8 bit), che è la lunghezza
del dato trattato dai PIC che stiamo analizzando.
(Maggiori informazioni sui sistemi
di numerazione qui)..
Carattere |
Segmenti |
|
Livello Pin |
Binario |
HEX |
dp |
G |
F |
E |
D |
C |
B |
A |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
0 |
|
|
|
x |
x |
x |
x |
x |
L |
L |
H |
H |
H |
H |
H |
H |
00111111 |
3F |
1 |
|
|
|
|
|
x |
x |
|
L |
L |
L |
L |
L |
H |
H |
L |
00000110 |
06 |
2 |
|
x |
|
x |
x |
|
x |
x |
L |
H |
L |
H |
H |
L |
H |
H |
01011011 |
5B |
3 |
|
x |
|
|
x |
x |
x |
x |
L |
H |
L |
L |
H |
H |
H |
H |
01001111 |
4F |
4 |
|
x |
x |
|
|
x |
x |
|
L |
H |
H |
L |
L |
H |
H |
L |
01100110 |
66 |
5 |
|
x |
x |
|
x |
x |
|
x |
L |
H |
H |
L |
H |
H |
L |
H |
01101101 |
6D |
6 |
|
x |
x |
x |
x |
x |
|
x |
L |
H |
H |
H |
H |
H |
L |
H |
01111101 |
7D |
7 |
|
|
|
|
|
x |
x |
x |
L |
L |
L |
L |
L |
H |
H |
H |
00000111 |
07 |
8 |
|
x |
x |
x |
x |
x |
x |
x |
L |
H |
H |
H |
H |
H |
H |
H |
01111111 |
7F |
9 |
|
x |
x |
|
x |
x |
x |
x |
L |
H |
H |
L |
H |
H |
H |
H |
01101111 |
6F |
Per accendere il punto decimale basterà portare a livello alto (1) il pin 7.
Questo sistema consente di utilizzare qualsiasi combinazione pin-segmenti che
sia necessaria, ad esempio, a causa dei collegamenti sul circuito stampato.
Ora si tratta di convertire questa tabella in modo tale da poter essere
compresa dal processore.
La via più semplice è quella di scrivere una tabella RETLW.
Possiamo altresì aggiungere alle cifre numeriche alcuni caratteri
alfabetici, ottenuti per accensione di un adeguato numero di segmenti.
;--------------------------------------------------------------
; 7 segments display code Table
;--------------------------------------------------------------
;**************************************************************
;* Table for 7 segment display
;* Displayport: 0 Segment A AAAAA
;*
1 Segment B F B
;*
2 Segment C F B
;*
3 Segment D GGGGG
;*
4 Segment E E C
;*
5 Segment F E C
;*
6 Segment G DDDDD
;*
7 Segment dp dp
GetDigitCode
andlw 0x0F
; filter for 16 codes only
rlncf WREG, f
addwf PCL, f
; segments table for H drive
; pin 76543210
; segment pGFEDCBA pos hex
retlw b'00111111' ; 0 - 0x3F
retlw b'00000110'
; 1 - 0x06
retlw b'01011011'
; 2 - 0x5B
retlw b'01001111'
; 3 - 0x4F
retlw b'01100110'
; 4 - 0x66
retlw b'01101101'
; 5 - 0x6D
retlw b'01111101'
; 6 - 0x7D
retlw b'00000111'
; 7 - 0x07
retlw b'01111111'
; 8 - 0x7F
retlw b'01101111' ; 9 - 0x6F
retlw b'01110111'
; A - 0x77
retlw b'01111100'
; b - 0x7C
retlw b'00111001'
; C - 0x39
retlw b'01011110'
; d - 0x5E
retlw b'01111001'
; E - 0x79
retlw b'01110001'
; F - 0x71 |
Su questa base realizziamo un piccolo programma di verifica.
Il suo flow chart è questo.
|
Dopo la necessaria inizializzazione del processore e del port
utilizzato per il controllo del display, trasferiamo allo stesso le
cifre che abbiamo tabellato, una dopo l' altra con un intervallo di
tempo tra ogni operazione tale da permetterne la visualizzazione.
Un contatore viene utilizzato per il codice del carattere da
presentare, partendo da 0, che. attraverso la tabella genererà la cifra
0.
Il contenuto del counter è incrementato ad ogni passo, producendo la
successione dei 16 caratteri tabellati.
Una pausa tra un carattere e il successivo ne permette la
visualizzazione.
Quando sono stati esauriti i caratteri disponibili, il ciclo riprende
dall' inizio.
La tabella di conversione per i segmenti va considerata un modulo
funzionale e quindi si utilizzerà la tecnica di
"passare" il dato in uscita attraverso l' accumulatore WREG.
Le routines di tempo sono quelle già viste in precedenza.
|
L' esercizio richiede le seguenti risorse:
- MPLAB IDE installato
- Pickit3 (o Pickit2)
- 28-40pin UniBoard con PIC18F2321
0 4321 (o 2221 o 4221 o altro hardware similare)
- il modulo DISP-1 oppure l' hardware che realizza lo schema
equivalente, presentato nella pagina seguente
Il listato sorgente è una estensione di quello già visto negli altri
esercizi e che fa da base per tutti i seguenti, aggiungendo gli elementi
necessari al nuovo lavoro.
Il template utilizzato contiene qualche elemento di diversità dai
precedenti, diventando leggermente più ricco di informazioni, come l'
assegnazione iniziale dei pin del processore.
Ed una area di parametri calcolati dall' Assembler, che, non utilizzati in
questa esercitazione, saranno invece pratici in altre occasioni.
;========================================================================
;= DEFINIZIONI =
;========================================================================
; CLOCK frequency = internal osc. - default al POR
XTAL_FREQ equ 1000000
; INTIO1 1 MHz
CLOCK equ XTAL_FREQ/4
; processor clock [Hz]
TCYC equ 1000000000/CLOCK ; cycle time
[ns] |
Anche l' assegnazione della RAM necessaria contiene un meccanismo che genera
un errore nel caso in cui l' area dichiarata superi la capacità dell' Access
RAM del processore.
;========================================================================
;= AREA ACCESS BANK - RAM =
;========================================================================
; bank 0 - 128 bytes
; -------------------
CBLOCK 0x00
d1
; 2 bytes per le routines di tempo
d2
counter
; contatore a 1 byte
end_Accbank0:0
; dummy for overrun check
ENDC
if
end_Accbank0
> 0x7F
; check for overrun
error "Access Bank 0 space overrun"
endif
|
Aggiungiamo anche una immagine grafica delle assegnazioni dei pin del port
utilizzato.
;========================================================================
;= I/O definitions =
;========================================================================
; Dive display 7 segmenti direttamente collegato a PORTC
; Common Kathode
;
; PORTC map
;| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
;|-----|-----|-----|-----|-----|-----|-----|-----|
;| dp | G | F | E | D | C | B | A |
;
#define SegmentTris TRISC
; direzione port
#define SegmentPort LATC
; latch uscita port |
Il programma vero e proprio parte con un salto, dopo il quale è posizionata
la tabella RETLW, il che ne garantisce la localizzazione all' inizio di una
pagina di memoria:
;========================================================================
;= PROGRAMMA =
;========================================================================
; vettore del reset
; ----------------
ORG 0x00
nop ; per ICD
goto Main
; vai al main
|
Segue dunque la tabella RETLW con i codici dei caratteri da visualizzare e l'
equivalente esadecimale.
;-----------------------------------------------------------------------
; 7 segments display code
;-----------------------------------------------------------------------
;***********************************************************************
; Table for 7 segment display
;* Displayport: 0 Segment A AAAAA
;*
1 Segment B F B
;*
2 Segment C F B
;*
3 Segment D GGGGG
;*
4 Segment E E C
;*
5 Segment F E C
;*
6 Segment G DDDDD
;*
7 Segment dp dp
GetDigitCode
andlw 0x0F
; 16 codes only
rlncf WREG, f
addwf PCL, f
; segments table for H drive
; 76543210
; pGFEDCBA
chr hex
retlw b'00111111'
; 0 - 0x3F
retlw b'00000110'
; 1 - 0x06
retlw b'01011011'
; 2 - 0x5B
retlw b'01001111'
; 3 - 0x4F
retlw b'01100110'
; 4 - 0x66
retlw b'01101101'
; 5 - 0x6D
retlw b'01111101'
; 6 - 0x7D
retlw b'00000111'
; 7 - 0x07
retlw b'01111111'
; 8 - 0x7F
retlw b'01101111'
; 9 - 0x6F
retlw b'01110111'
; A - 0x77
retlw b'01111100'
; B - 0x7C
retlw b'00111001'
; C - 0x39
retlw b'01011110'
; d - 0x5E
retlw b'01111001'
; E - 0x79
retlw b'01110001'
; F - 0x71 |
Vengono definiti 16 caratteri, con codici da 0h a Fh; i primi 10 sono le
cifre da 0 a 9, i rimanenti rappresentano alcune lettere.
Come al solito, il programma è costituito da un numero minimo di istruzioni,
dato che gran parte del lavoro viene effettuato dai moduli della tabella e della
subroutine di pausa.
;----------------------------------------------------------------------
; Main loop
;----------------------------------------------------------------------
Main:
; I/O setup
clrf SegmentPort
; preset a 0 latch uscita
clrf SegmentTris
; Port comando segmenti = Out
; loop primario
loop clrf counter
; parte con contatore = 0
; loop secondario
loop1 movf counter, W
; copia counter in W
rcall GetDigitCode
; richiama codice per 7 segmenti
movwf SegmentPort
; scrivi in uscita sul port
rcall Pausa
; pausa di visualizzazione
incf counter, f
; incrementa counter
btfss counter, 4
; counter > 0Fh ?
bra loop1
; no - altro loop
rcall Pausa
; si - breve pausa
bra loop
; riprendi dall' inizio
|
Il programma ricalca il flow chart visto prima.
Il carattere da presentare viene impostato in un counter a partire da 00h
(carattere 0) e ad ogni passo del loop viene incrementato, facendo puntare la
tabella al codice successivo.
Il test di fine dei codici disponibili si effettua sul valore del bit 4 del
counter:se, dopo un incremento, questo bit va a 1, significa che il numero
raggiunto (10h) supera il limite di valori codificati in tabella (0Fh).
Quindi il loop viene terminato e riprende il loop primario che ri azzera il
counter e riprende da capo.
Una pausa viene inserita ad ogni passo per visualizzare il carattere e una pausa
è introdotta anche alla fine del set di caratteri.
La subroutine di pausa è analoga a quelle fino ad ora utilizzate.
Il sorgente termina con lo statement END.
Se si incontrano errori nella compilazione è opportuno verificarli con la
lista di descrizione degli errori e correggere dove si è sbagliato.
Il file compresso di questa esercitazione è scaricabile dall'
area di download.
|