Simulatore di logica booleana
|
Una semplice applicazione didattica che permette di verificare visivamente
le principali funzioni della logica binaria.
Nell' algebra di Boole abbiamo alcune funzioni fondamentali, ben note a
chiunque operi con la programmazione dei dispositivi digitali, che sono:
con i loro opposti (NAND, NOR, XNOR), determinati
dalla funzione invertente NOT. Possiamo tracciare per ognuna
di esse la "tabella della verità" che lega gli ingressi con lo
stato dell' uscita. Per due bit in ingresso abbiamo:
Possiamo realizzare un circuito che presenti su LED i risultati di queste
truth tables.
Utilizziamo un microcontroller a 14 della famiglia Baseline; dato che non ci
occorre alcuna funzione speciale possiamo puntare su un semplice 16F505, anche
se può essere utilizzato 16F506/526 o qualsiasi altro PIC con un numero
sufficiente di pin.
A e B sono due interruttori, che forniscono gli stati 1 e 0 di ingresso
alla logica; ognuno è associato ad un
LED che ne replica lo stato. Sette LED indicano il risultato delle varie funzioni.
Allo scopo di minimizzare il consumo e il numero dei componenti necessari, non
vengono utilizzati pull up esterni sugli ingressi, in quanto sono abilitati
quelli integrati, che risultano presenti su RB0,1 e 3.
RES è un pulsante collegato al pin MCLR; può essere anche non utilizzato
(vedere più avanti).
Il dispositivo può essere alimentato da 3 a 5V. Nella realizzazione del
prototipo viene usata una coppia di batterie AA.
Anche se è stato previsto un circuito stampato ad hoc, è comodo
utilizzare la LPCuB
per sviluppare il programma:
Essendoci solamente 8 LED nella fila superiore LED0-7, utilizziamo anche
uno dei LED relativi alla interfaccia di comunicazione.
Gli interruttori di ingresso sono simulati con i pulsanti PB3 e PB4. Non
occorre inserire pull up dato che vengono abilitati quelli interni al chip.
AVVERTENZA: dato che i pulsanti sono collegati a PB1:0 che
sono anche PGC e PGD della programmazione, occorre assicurarsi che
essi non siano premuti durante questa operazione che, altrimenti,
sarebbe impossibile.
Il prototipo finale, nella versione proposta, dispone di presa ISCP,
come visibile dallo schema elettrico: se si programma il chip on
board, occorre assicurarsi con certezza che gli interruttori collegati
a PB1:0 non siano premuti durante questa operazione, per non
danneggiare il Pickit. |
Volendo inserire una protezione, si useranno delle resistenze da 1k in
serie tra interruttore e pin, come è previsto sulla LPCuB. Queste, però, non
sono indispensabili se si cura quanto detto sopra.
Il programma
Il sorgente è estremamente semplice, dato che utilizziamo una "scorciatoia":
anche se nel set di istruzioni del microcontroller esistono opcodes che
realizzano le funzioni booleane (iorwf, xorwf,
andwf) è molto più semplice
utilizzare delle lookup table.
La scelta dei collegamenti è dettata da alcune considerazioni:
- i pin RB0,1 e 3 sono dotati di weak pull up integrati. Quindi sarà più
pratico usare questi come ingressi.
- inoltre la posizione iniziale nel byte dei due pin di ingresso della
logica è molto comoda per passare il loro stato direttamente come
indice di una tabella
La disposizione degli altri pin, che sono tutti dedicati al comando di LED,
è indifferente, dato che la costruzione delle tabelle potrà essere adeguata
con facilità alle più diverse combinazioni.
Il sorgente è estremamente semplice, dato che utilizziamo una "scorciatoia":
anche se nel set di istruzioni del microcontroller esistono opcodes che
realizzano le funzioni booleane (iorwf, xorwf,
andwf) è molto più semplice
utilizzare delle lookup table.
Per come è realizzato il circuito stampato e le indicazioni sul pannello, per
PORTC, tra i LED di indicazione del risultato e i
pin di I/O c'è la seguente equivalenza:
Bit |
RC5 |
RC4 |
RC3 |
RC2 |
RC1 |
RC0 |
LED |
!A |
AND |
NAND |
XOR |
NOR |
OR |
e per PORTB:
Bit |
RB5 |
RB4 |
RB3 |
RB2 |
RB1 |
RB0 |
LED |
LEDA |
LEDB |
- |
XNOR |
- |
- |
La lookup table mette in relazione lo stato dei pin di ingresso A e B
con le uscite sul PORTC:
|
|
|
RC5 |
RC4 |
RC3 |
RC2 |
RC1 |
RC0 |
B |
A |
|
!A |
AND |
NAND |
XOR |
NOR |
OR |
0 |
0 |
|
1 |
0 |
1 |
0 |
1 |
0 |
0 |
1 |
|
0 |
0 |
1 |
1 |
0 |
1 |
1 |
0 |
|
1 |
0 |
1 |
1 |
0 |
1 |
1 |
1 |
|
0 |
1 |
0 |
0 |
0 |
1 |
e sul PORTB:
|
|
|
RB5 |
RB4 |
RB3 |
RB2 |
RB1 |
RB0 |
B |
A |
|
LEDA |
LEDB |
- |
XNOR |
- |
- |
0 |
0 |
|
0 |
0 |
0 |
1 |
0 |
0 |
0 |
1 |
|
1 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
|
0 |
1 |
0 |
0 |
0 |
0 |
1 |
1 |
|
1 |
1 |
0 |
1 |
0 |
0 |
Dato che un port è composto da soli 6 bit, i due più alti (bit 7 e bit
6), non implementati, saranno a 0. Ne derivano le due lookup tables:
; Lookup table per PORTC
bltbl addwf PCL,f
; punta PC
; bit
; in 76 5 4 3 2 1 0
; BA 00 ! A N X N O
retlw b'00101010'
; 00 00 A N A O O R
retlw b'00001101'
; 01 00 D N R R
retlw b'00101101'
; 10 00 D
retlw b'00010001'
; 11 00
; Lookup table per PORTB
bltbl1 addwf PCL,f
; punta PC
; bit
; in 76 5 4 3 2 1 0
; BA 00 A B 0 X 0 0
retlw
b'00000100' ; 00 00
0 N 0 0
retlw
b'00100000' ; 01 00 0
O 0 0
retlw
b'00010000' ; 10 00 0
R 0 0
retlw
b'00110100' ; 11 00
|
Non importa il valore attribuito ai bit configurati come ingressi, dato che
la scrittura non avrà effetto.
Non serve neppure una limitazione del numero in ingresso alla tabella, dato
che è già limitato ai primi due bit dalle operazioni precedenti.
Ovviamente il contenuto delle tabelle dipende dal rapporto tra pin e LED;
se utilizzate un qualsiasi altro layout, basterà semplicemente adeguare le
tabelle.
Quindi, basta applicare il dato in uscita dalla tabella al relativo port:
mainlp movf lastport,w
; stato precedente
call
bltbl ; maschera per LED
movwf PORTC
movf
lastport,w
call
bltbl1
movwf PORTB
|
Con queste poche righe il programma è completato!
Però è possibile applicare alcuni miglioramenti.
Per prima cosa il sorgente è reso compilabile per diversi processori, col solito
sistema degli #ifdef.
Si ricorda, comunque, che non vengono usate periferiche speciali o interrupt,
per cui il sorgente, scritto per il set minimale dei Baseline, può essere
riportato senza problemi su qualsiasi altro PIC.
In secondo luogo, esiste un'altra questione: i LED accesi hanno un consumo che non è marginale per
le piccole
batterie AA; a 10mA per LED, abbiamo un massimo di 90mA, a cui va aggiunta la
corrente assorbita dal micro, con tutti i LED accesi.
Con LED a bassa corrente (2mA) la situazione sarebbe molto migliore, ma, per evitare
la ricerca e l' acquisto di questi componenti a volte poco reperibili, possiamo utilizzare LED comuni,
ricorrendo ad un PWM.
Il programma fa si che essi siano accesi per 5ms ogni 20ms: la corrente media si riduce a
circa 7mA,
senza presentare sfarfallio e con una luminosità visibile anche alla luce
del giorno.
Per ottenere questo, inseriamo una temporizzazione di circa 5ms per i LED on ed una di 15ms per lo stato
off:
timeon movlw time5ms ; attesa 5ms
movwf TMR0
tlp1 movfw TMR0
skpz
goto tlp1
DISPLAYOFF ; spegni display
; attesa 15 ms
movlw time15ms
movwf TMR0
tlp2 movfw TMR0
skpz
goto tlp2
; leggi stato ingressi ogni 20ms
|
Variando i tempi on/off, possiamo adattare il firmware a LED con differenti
caratteristiche; elementi a bassa luminosità necessiteranno di maggiore tempo
on.
Lo stato degli ingressi viene letto ogni 20ms circa allo scopo di
minimizzare l'influenza dei rimbalzi; da notare che, anche quando presenti, nell'applicazione non
hanno conseguenze sulla visualizzazione; quindi
non è implementata alcuna altra azione di debounce.
Una volta minimizzata la corrente a riposo, è possibile, volendo,
eliminare l'interruttore di accensione, sostituito dal solo pulsante che
agisce sul pin MCLR.
Alla prima applicazione della tensione di alimentazione (POR), il circuito resta attivo per 1 minuto; se non viene
premuto nessuno dei due interruttori, viene conteggiato un timeout allo
scadere del quale si passa in modalità sleep a
basso consumo (1uA o meno); ogni volta che viene cambiato lo stato degli
ingressi, il timeout viene resettato, permettendo di mantenere acceso con
continuità l'apparecchio. Dalla condizione di sleep, la pressione del tasto collegato a MCLR risveglia
il processore e riavvia il ciclo:
; verifica del timeout
ontime movf minutcntrl,f
skpz
decf
minutcntrh,f
decfsz minutcntrl,f
goto
mainlp ;
torna al loop
; timeout - va in sleep
sleepin DISPLAYOFF
sleep
|
Variando i valori pre caricati nei contatori, si potrà variare il timeout
a piacere.
Volendo, è possibile anche eliminare il tasto del reset ed utilizzare per
il wakeup la funzione di pin change, azionando uno dei due interruttori. In
tal caso occorre abilitare al funzione:
; setup OPTION: RB5, RBPU, Timer0 1:256
; b'11010111'
; 0------- RBWU abilitato
; -0------ RBPU abilitato
; --0----- clock interno
; ---1---- falling
; ----0--- prescaler al Timer0
; -----111 1:256
movlw b'00010111'
OPTION
|
Inoltre è necessario leggere lo stato del port prima di passare in sleep
per cancellare il flag relativo:
; timeout - va in sleep
sleepin DISPLAYOFF
movf
PORTB,w
sleep
|
Utilizzando lo stato di sleep è possibile realizzare un apparecchio tascabile alimentato
a battere, con una autonomia consistente.
Un prototipo.
Nelle immagini, come è stato realizzato un prototipo.
Il circuito è racchiuso in una scatoletta che contiene anche le due batterie
AA di alimentazione.
Con i LED
utilizzati, le resistenze in serie sono da 56-68 ohm.
Il circuito stampato prevede una connessione ICSP per poter programmare il
chip direttamente on circuit. Se non pensate di utilizzarla, potete
escluderla.
La realizzazione non è critica e può essere eseguita in qualsiasi altro modo
che risulti sufficientemente ordinato.
|