Trasmettere a distanza più
segnali.
|
Un ampliamento della precedente puntata; con poche modifiche possiamo trasmettere una
segnalazione con display a 7 segmenti, usando una coppia di PIC.
Presupposti.
Ci occorre trasmettere a distanza una indicazione numerica su un display a 7 segmenti, ad
esempio per la posizione di un montacarichi o un ascensore, di un contatore o indicatore di uno
stato di funzionamento.
Come nel caso precedente vogliamo utilizzare una comunicazione seriale uni direzionale
(trasmissione asincrona), con un protocollo molto semplificato, su un solo conduttore tra una
coppia di PIC.
7 segmenti.
Sappiamo tutti di cosa si tratta quando parliamo di un display a 7
segmenti: sostanzialmente di un array di 7 LED con anodo o catodo in comune,
disposti fisicamente in modo da poter rappresentare con la loro accensione le
cifre da 0 a 9 e numerosi altri simboli alfabetici e non.
|
|
Se abbiamo, ad esempio 4 segnali, occorrerà disporre di
un cavo a 5 conduttori.
Se la distanza supera qualche metro, il costo e l' ingombro di questo
cavo possono essere inaccettabili.
Per convenzione si denominano i LED, ognuno dei quali
illumina un segmento, con le lettere tra a e g e
l'eventuale punto decimale con dp. |
Per ottenere la rappresentazione delle cifre si accenderanno
alcuni LED, lasciando spenti gli altri.
Possiamo stilare una tabella che mette in relazione la cifra presentata con
lo stato dei LED e completarla con una corrispondenza tra le cifre e la loro
codifica binaria (BCD)
Cifra |
Segmenti |
|
Codifica BCD |
g |
f |
e |
d |
c |
b |
a |
|
3 |
2 |
1 |
0 |
0 |
|
x |
x |
x |
x |
x |
x |
|
0 |
0 |
0 |
0 |
1 |
|
|
|
|
x |
x |
|
|
0 |
0 |
0 |
1 |
2 |
x |
|
x |
x |
|
x |
x |
|
0 |
0 |
1 |
0 |
3 |
x |
x |
|
x |
x |
x |
x |
|
0 |
0 |
1 |
1 |
4 |
x |
x |
|
|
x |
x |
|
|
0 |
1 |
0 |
0 |
5 |
x |
x |
|
x |
x |
|
x |
|
0 |
1 |
0 |
1 |
6 |
x |
x |
x |
x |
x |
|
x |
|
0 |
1 |
1 |
0 |
7 |
|
|
|
|
x |
x |
x |
|
0 |
1 |
1 |
1 |
8 |
x |
x |
x |
x |
x |
x |
x |
|
1 |
0 |
0 |
0 |
9 |
x |
x |
|
x |
x |
x |
x |
|
1 |
0 |
0 |
1 |
A |
x |
x |
x |
|
x |
x |
x |
|
1 |
0 |
1 |
0 |
b |
x |
x |
x |
x |
x |
|
|
|
1 |
0 |
1 |
1 |
C |
|
x |
x |
x |
|
x |
x |
|
1 |
1 |
0 |
0 |
d |
x |
|
x |
x |
x |
x |
|
|
1 |
1 |
0 |
1 |
E |
x |
x |
x |
x |
|
|
x |
|
1 |
1 |
1 |
0 |
F |
x |
x |
x |
|
|
|
x |
|
1 |
1 |
1 |
1 |
Osserviamo che sono sufficienti 4 bit per codificare le 16 cifre di un
conteggio esadecimale (da 0 a F). E queste cifre sono chiaramente presentabili
con i 7 segmenti del nostro display, come abbiamo visto nell' illustrazione
precedente.
Abbiamo già a disposizione un sistema che si basa sull' invio di 4 bit e
quindi basta adattarla alla nuova richiesta, con le poche modifiche
necessarie.
Il circuito.
Esistono componenti integrati che hanno la specifica funzione di convertire un
dato binario di 4 bit in una cifra sul display a 7 segmenti, ad esempio il noto
CD4511.
La soluzione più immediata, dunque, sarebbe quella di completare il ricevitore
con questa decodifica hardware.
I dati ricevuti vengono inviati agli ingressi del 4511, che pilota di
conseguenza i segmenti del display. La cosa è facilmente implementabile, dato
che non occorre alcuna modifica al firmware del ricevitore e si tratta
solamente di aggiungere la decodifica, il display e le resistenze d
limitazione della corrente nei segmenti.
Però, è tipica finalità della progettazione con microcontroller l'
evitare per quanto possibile (e sensato...) di aggiungere elementi esterni là
dove il microcontroller stesso possa svolgerne le funzioni.
Questo consente di ridurre il numero di componenti, la complessità e il
costo del prodotto, ma, per contro, aumenta complessità e costo del firmware.
Da notare altresì che per comandare il display occorrono 7 linee di I/O, una
per ogni LED, e che i chip usati in precedenza per il ricevitore sono
insufficienti, avendo disponibili solo 4 linee.
Occorre un microcontroller con un numero maggiore di I/O, a meno di usare
complessi algoritmi del genere charlieplexing, che permettono di comandare più
LED con un numero limitato di I/O; questo sistema, però, richiede un multiplex
delle uscite, con la corrispondente necessità di temporizzazioni che, su un
piccolo Baseline senza interrupt non sono di applicazione immediata.
Ne deriva che la soluzione scelta è quella di usare un PIC con un numero
maggiore di pin: questo non pone problemi, in quanto esistono chip con pin da
14, 18, 20, 28, 40 e più fino a oltre 100.
E, se restiamo nella famiglia Baseline, il costo di un 14 pin è poco più di
quello di un 8 pin : alla data odierna, Microchipdirect, 12F508 costa 0.52€ e
16F505 costa 0.96€, ovvero costa meno il PIC a 14 pin rispetto all' 8 pin + il
CMOS di decodifica.
Per inciso, nulla vieta di usare un 12F + 4511, se li avete in casa, e questo
consente di non modificare per nulla il firmware del ricevitore.
Però, noi vogliamo risolvere la cosa usando direttamente un PIC che svolga
la funzione di decodifica, anche se la cosa potrebbe sembrare problematica, dato
che si deve passare quanto già scritto per il ricevitore da un chip ad un'
altro, che appare del tutto differente, oltre ad aggiungere la parte di
programma che gestisce la conversione.
All' atto pratico questo non è così drammatico, in quanto dobbiamo considerare
che, operando all' interno di microcontroller aventi la stessa filosofia
costruttiva, il passaggio da un chip ad un altro può non essere per nulla
critico quando non si usano elementi specifici, come periferiche specializzate o
ampie quantità di memoria, restando invece nell' ambito delle minime risorse
comuni.
E non lo è in generale quando si resta all' interno di una stessa famiglia,
dove la situazione generale dei chip è piuttosto uniforme. Ed è questo il caso
corrente, in quanto intendiamo utilizzare sempre componenti a 8 bit,
possibilmente facenti parte degli economici Baseline.
Aiuta anche il fatto che il software è stato scritto senza utilizzare
particolari risorse, senza impiegare moduli funzione, come l'UART, senza
interrupt e con un consumo di memoria minimo, sia per il programma, scritto in
Assembly, sia per i dati in RAM.
Andiamo allora a selezionare dei PIC minimali per questo progetto. Componenti in
package a 14 pin sono lo step successivo a quelli da 8 pin e offrono 12 I/O, il
che è più che sufficiente per il nostro scopo.
L' immagine sopra presenta le funzioni assegnate ai pin di 16F505, che è il
più "essenziale" di questi Baseline a 14 pin. Sostanzialmente si
dispone di:
- due gruppi di I/O (port) denominati PORTB e PORTC,
ognuno con 6 bit
- il pin di reset MCLR può essere programmato come I/O (RB3)
in ingresso
- nonostante possa usare oscillatori esterni, dispone di un oscillatore
interno a 4MHz
Vediamo che si tratta di un componente strutturalmente del tutto analogo ai già visti componenti a 8pin, di cui sono essenzialmente una estensione; evidentemente il costruttore ha usato lo stesso core, con poche modifiche.
In particolare, 16F505 è parente così stretto di 12F508/509 da avere un foglio dati in comune!
Possiamo anche non limitarci a questo chip: nel progetto precedente sono stati accomunati nell' assemblaggio 12F508/509/510/519 e, osservando i rispettivi fogli dati, si rileva che, nuovamente, chip a 14 pin sono una "estensione" di chip a 8 pin. Così, hanno unico foglio dati 12F510 e 16F506, che possiamo aggiungere alla lista di candidati alla compilazione.
Altro chip a 14 pin che possiamo considerare è 16F526, analogo a 16F506, da cui si differenzia unicamente per la presenza di
EEPROM.
Per inciso, qualsiasi PIC a 14 pin sarà adeguato all' applicazione, in quanto hanno tutti lo stesso pinout. E pure saranno sovrapponibili i chip a 20 pin. Con questa "sovrapponibilità" si intende che tutti i chip a 14 pin hanno lo stesso pinout e così pure quelli a 20, ecc.
Con pinout si intende il rapporto tra il pin e le funzioni associate; questa situazione permette di realizzare hardware su cui potranno essere posti più tipi di PIC, senza dover variare i collegamenti.
Ma, ricordando quanto abbiamo detto in relazione al programma, nulla vieta di utilizzare qualsiasi altro PIC di qualsiasi genere, dal vetusto 16F84 al 18F95J94.
In base alla sovrapponibilità possiamo realizzare uno schema base che potrà accettare ogni chip a 14 pin.
Stiamo parlando ovviamente del ricevitore, in quanto il trasmettitore è lo stesso della puntata precedente e non richiede alcuna modifica.
Lo schema non presenta alcuna difficoltà, dato che comprende
essenzialmente solo il microcontroller e il display, pilotato direttamente
dagli I/O che hanno capacità di corrente di 25mA ciascuno, superiore a quella
richiesta da un 7 segmenti comune (2-20mA per segmento).
In questo senso è utile considerare che se il foglio dati del display indica
20mA come corrente tipica, a seconda della situazione della luminosità dell'
ambiente, si potranno probabilmente usare correnti molto minori, con un consumo
energetico ed uno stress ai componenti molto minore. In particolare, esistono
con facile reperibilità display a basso consumo, in grado di fornire una
luminosità elevata con soli 2mA per segmento.
Questa corrente deve essere limitata da resistenze in serie, anche perchè il
comando dei segmenti è del tutto "statico"e non è previsto PWM. Il
valore delle resistenze dipenderà dal display scelto e dalla luminosità voluta
e può variare tra 220 e 1k5 per una alimentazione a 5V.
|
Nello schema è evidenziato un display a catodo comune, in cui i LED, con il catodo alla Vss, sono alimentati e si accendono attraverso i pin di I/O quando programmati a livello alto, con una logica "diretta":
pin |
0 |
1 |
segmento |
spento |
acceso |
|
|
E' ovviamente possibile usare anche display ad anodo comune, collegandolo alla Vdd.
In questo caso occorrerà accendere i LED con una logica "inversa":
pin |
0 |
1 |
segmento |
acceso |
spento |
|
dato che la corrente fluirà quando il pin avrà un livello logico basso.
Da notare che, essendo i port composti da soli 6 bit, tutti i pin di PORTC, nell' ordine, sono collegati ai segmenti da
a a f, mentre il segmento g dipende da RB1.
RB3 è l' ingresso della linea.
RB0 il comando del LED che indica lo stato, con le stesse modalità viste nel progetto precedente. Si potrebbe risparmiare anche questo, usando il punto decimale del display: il circuito stampato prevede appunto un jumper da chiudere con un punto di saldatura per questo scopo.
Il solito condensatore è elemento fisso di stabilità tra i pin di alimentazione del chip.
I pin RB2 e RB4 non sono usati e possono essere impiegati per funzioni addizionali.
Hardware.
Data la super semplicità del circuito, anche lo stampato è semplice e non richiede commenti:
Software.
Il trasmettitore è lo stesso già descritto.
Vediamo invece il ricevitore.
Il "nuovo" ricevitore opera nello stesso identico modo del precedente, per quanto riguarda il "protocollo" di identificazione dei dati, della loro collezione ordinata e del l' esclusione di eventuali errori.
Quello che cambia, dal punto vista logico, è solamente l'aggiunta della gestione della conversione BCD-7 segmenti, che richiede alcune linee di istruzioni in più.
Il programma sorgente è scritto in Assembly per poter essere utilizzato immediatamente su 16F505/506/526.
E' possibile trasportarlo su altri chip 12F/16F/18F con cambi che interessano solamente il config e l' eliminazione di eventuali analogiche o comparatori dai pin di I/O usati, ovvero adattandolo alle risorse disponibili.
L' estrema semplicità e un dettagliato commento di ogni linea permettono di comprendere con facilità la sequenza delle istruzioni, ma può essere utile portare all'attenzione alcuni punti.
In primo luogo il meccanismo di scelta del processore, basato sulla funzione #ifdef e già descritto in precedenza:
;
scelta del processore
#ifdef
__16F505
LIST
p=16F505
#include
<p12F505.inc>
#endif
#ifdef
__16F506
LIST
p=16F506
#include
<p16F506.inc>
#define _Bigpic
#endif
#ifdef __16F526
LIST
p=16F526
#include
<p12F526.inc>
#define _Bigpic
#endif |
Osserviamo qui una piccola variazione, ovvero l' aggiunta della definizione di una label _Bigpic che viene attivata per due dei processori previsti.
Questa scappatoia si rende necessaria perchè, purtroppo, l' argomento di #ifdef deve essere singolo e non può essere una funzione, cosa che appesantirebbe le sezioni successive del sorgente dove è necessario ancora modificare le inizializzazioni a seconda delle risorse hardware dei chip.
In particolare facciamo riferimento al fatto che 16F506 e 16F526 dispongono di comparatori e di ADC.
Si nota immediatamente la presenza delle funzioni addizionali dalle label attribuite ai pin:
Pin |
16F505 |
16F506 |
16F526 |
13 |
RB0 |
RB0/C1IN+/AN0 |
12 |
RB1 |
RB1/C1IN-/AN1 |
11 |
RB2 |
RB2/C1OUT/AN2 |
10 |
RC0 |
RC0/C2IN+ |
9 |
RC1 |
RC1/C2IN- |
6 |
RC4 |
RC4/C2OUT |
Ricordiamo che, in base alla filosofia di progetto di Microchip (che tende al minimo consumo per default), queste funzioni analogiche hanno priorità su quelle digitali e vanno quindi escluse quando si vuole accede agli I/O digitali.
Quindi, per poter utilizzare su 16F506/526 i pin RB0/1/2 dobbiamo escludere sia i comparatori che l' ADC, mentre per utilizzare RC1/4 dobbiamo disabilitare i comparatori.
16F505, invece, non ha nessuna di queste funzioni analogiche e ne deriva che solamente per i primi chip due occorre aggiungere righe al sorgente per disabilitare analogica e i comparatori.
Ci troveremmo a dover scrivere:
#ifdef
__16F506
;
disabilita comparatore
movlw
b'11110111'
movwf
CM1CON0
movwf
CM2CON0
;
disabilita analogica
movlw
b'00000000'
movwf
ADCON0
#endif
#ifdef __16F526
;
disabilita comparatore
movlw
b'11110111'
movwf
CM1CON0
movwf
CM2CON0
;
disabilita analogica
movlw
b'00000000'
movwf
ADCON0
#endif |
e questo è dovuto al fatto prima citato di non poter usare una sequenza del genere:
#ifdef
__16F506
;
disabilita comparatore
movlw
b'11110111'
movwf
CM1CON0
movwf
CM2CON0
;
disabilita analogica
movlw
b'00000000'
movwf
ADCON0
#endif
#ifdef __16F526
;
disabilita comparatore
movlw
b'11110111'
movwf
CM1CON0
movwf
CM2CON0
;
disabilita analogica
movlw
b'00000000'
movwf
ADCON0
#endif |
dato che lo statement supporta solo argomenti singoli; la soluzione è usare la label "dummy" _Bigpic che identifica i due chip "diversi";
quindi:
#ifdef _Bigpic
;
disabilita comparatore
movlw
b'11110111'
movwf
CM1CON0
movwf
CM2CON0
;
disabilita analogica
movlw
b'00000000'
movwf
ADCON0
#endif |
Esistono altri "trucchi" per ottenere lo stesso risultato,ma questo è molto semplice e facilmente comprensibile.
E' da osservare che per l' Assembler MPASM una label valida può iniziare con una sottolineatura, ad esempio
_Bigpic ,ma che una doppia sottolineatura è riservata alla label stabilite dal compilatore, come
__16F506.
Anche utile notare che le funzioni #ifdef e #endif
non possono iniziare in prima colonna, mentre la #define
deve iniziare in prima colonna.
Sempre a riguardo delle colonne, l' allineamento è utile non solo per avere un listato ordinato, il che ne facilita la lettura, ma anche per avere una disposizione logica degli elementi, percepibile anche a vista, oltre al fatto di evitare errori di compilazione (le istruzioni non possono iniziare in prima colonna, dove vanno invece le label auto dichiaranti, ecc.).
Nell' ambito delle funzioni di default da "eliminare" per accedere agli I/O digitali, è da notare che la sezione:
;
Option_Reg - disabilita T0CKI e prescaler a Timer0
movlw
b'11010111'
; no pullup,
presc.256
OPTION |
valida per tutti e tre i chip, non serve solo a definire il prescaler di Timer0, ma anche a disabilitare l' ingresso T0CKI, che in questi PIC prevarrebbe per default sulla funzione RC5, impedendo di usare il pin come semplice I/O digitale.
Da notare anche l' istruzione OPTION che è tipica solo dei Baseline, dato che in questi PIC l' OPTION_REG è a sola scrittura, come pure i registri di TRIS.
Lookup Table
La parte "innovativa" del firmware del ricevitore riguarda la gestione del display.
La via più semplice per effettuare la conversione da una forma numerica ad un'altra, in questo caso da un dato a 4 bit a una maschera per i 7 segmenti del display, è attraverso una lookup table. Ecco come si presenta:
ORG
0x200
; tabella
allocata ad inizio pagina 1
; segment data table
segtbl andlw
0x0F ;
solo nibble basso
addwf
PCL,f ;
punta PC
retlw
b'00111111' ;
"0" -|F|E|D|C|B|A
retlw
b'00000110' ;
"1" -|-|-|-|C|B|-
retlw
b'01011011' ;
"2" G|-|E|D|-|B|A
retlw
b'01001111' ;
"3" G|-|-|D|C|B|A
retlw
b'01100110' ;
"4" G|F|-|-|C|B|-
retlw
b'01101101' ;
"5" G|F|-|D|C|-|A
retlw
b'01111101' ;
"6" G|F|E|D|C|-|A
retlw
b'00000111' ;
"7" -|-|-|-|C|B|A
retlw
b'01111111' ;
"8" G|F|E|D|C|B|A
retlw
b'01101111' ;
"9" G|F|-|D|C|B|A
retlw
b'01110111' ;
"A" G|F|E|-|C|B|A
retlw
b'01111100' ;
"b" G|F|E|D|C|-|-
retlw
b'00111001' ;
"C" -|F|E|D|-|-|A
retlw
b'01011110' ;
"d" G|-|E|D|C|B|-
retlw
b'01111001' ;
"E" G|F|E|D|-|-|A
retlw
b'01110001' ;
"F" G|F|E|-|-|-|A
set7segm:
movf
rxbuff,W ;
recupera dato
call
segtbl ;
lookup table
movwf
PORTC ;
segmenti a-f
movwf
d1 ;
temp.
btfss
d1,6 ;
aggiusta segmento g
goto
is0
bsf
segmg
retlw
0
is0 bcf
segmg
retlw
0 |
Per chi conosce la struttura delle lookup tables nei piccoli PIC, non ci sono
problemi. Per gli altri proviamo a chiarire la cosa, anche se non semplice, dato
che implica la conoscenza dei meccanismi interni del processore e in particolare
della funzione del Program Counter.
Innanzitutto la tabella solamente una lista di valori binari messi come
argomento alla istruzione retlw;
in questo caso si tratta di 16 righe, ognuna contenente una diversa maschera di
comando dei 7 segmenti.
L'istruzione retlw
una forma particolare di ritorno da subroutine con il registro WREG
caricato con il valore in oggetto.
Quindi, ad esempio,
retlw 7 il rientro
da una chiamata di subroutine (call)
con W=7.
E' chiaro quindi che l' accesso alla tabella deve avvenire attraverso una
chiamata a subroutine.
Questa esiste nella set7segm,
con la riga call
segtbl .
A sua volta questa riga preceduta da una istruzione movf
rxbuff,W che carica in WREG
il valore contenuto nel buffer di ricezione rxbuff; in questo caso si
tratta del dato ricevuto.
Dunque la sequenza è questa:
- viene chiamata la subroutine
set7segm
- questa carica in WREG
il dato ricevuto
- dopo di che chiama sua volta la tabella
segtbl
come se si trattasse di una
ulteriore subroutine
Per inciso, ricordiamo che questo è il "massimo" che si può
pretendere dai Baseline, che hanno uno stack di soli due livelli e quindi
non potrebbero supportare una ulteriore chiamata a subroutine.
Vediamo cosa succeda nella
segtbl
:
- il valore contenuto in
WREG
viene ripulito conservando solo i
4 bit bassi con un AND con 0Fh; questo è necessario non tanto nel caso
specifico, quanto in generale dove il registro potrebbe contenere valori
maggiori della lunghezza della tabella, che qui è di 16 elementi.
-
poi, il contenuto del registro WREG viene sommato alla parte bassa
PCL del Program Counter. Questo il meccanismo di funziomamento della
tabella.
Ricordiamo lo scopo del Program
Counter: esso il contatore che contiene l' indirizzo della prossima istruzione
che verr eseguita.
In questo momento contiene l' indirizzo dell' istruzione successiva alla riga addwf
PCL,f,
ovvero la riga retlw
b'00111111' .
Se WREG contenesse 0, la
sua somma con il PC non modificherebbe quest'ultimo e verrebbe eseguita la riga
successiva:
retlw b'00111111' ; "0"
-|F|E|D|C|B|A
Ma se WREG contiene un numero, questo, sommandosi al valore del PCL ,
lo modificherà in modo tale che la prossima riga eseguita non sarà quella
successiva, ma quella indicata dal risultato della somma. Ad esempio, se WREG
contiene Ah, il PC sarà puntato sulla decima riga successiva e si eseguirà l'
istruzione:
retlw b'01110111'
; "A"
G|F|E|-|C|B|A
Così, ognuno dei 16 valori possibili in WREG farà rientrare la
subroutine da una diversa posizione. I rientri sono istruzioni retlw e queste
caricano un diverso valore in WREG.
Di conseguenza otteniamo la conversione dato-7 segmenti richiesta.
Così è chiara anche la ragione per cui si è prima limitato il
contenuto di WREG al numero degli step della tabella, onde evitare che si
vada a puntare locazioni non previste.
Qui abbiamo la coincidenza già vista nella prima tabella tra il valore dei
bit di dato e l' accensione dei segmenti del display: essendo i bit in numero di
4, è possibile avere 16 combinazioni.
Se avessimo a disposizione un numero maggiore di bit, potremmo avere di
conseguenza un maggior numero di simboli in uscita.
Una lookup table del genere è espandibile al massimo valore contenibile nel WREG,
che è il puntatore, ovvero 256 step.
Dove avviene il rientro dalla "subroutine" costituita dalla
tabella? E' alla linea successiva alla sua chiamata, cioè alla riga:
movwf PORTC
; segmenti a-f
che semplicemente copia il contenuto del WREG sul PORTC.
Dato che il registro contiene la maschera di bit prelevati dalla tabella, questo
fa si che si accendano i LED che corrispondono ai bit posti a 1. Se
intendessimo usare un display ad anodo comune, basterebbe invertire questi
valori.
L' operazione di copia diretta sul port è possibile in quanto si modificano in
una sola volta tutti i bit di PORTC e quindi non c'è pericolo di errore
da R-M-W.
Da notare che, anche se i bit recuperati dalla tabella sono 8 e PORTC ha
solo 6 bit, collegati ai segmenti da a a f, la scrittura dei due bit più alti
non da origine ad alcun effetto.
Occorre, però, gestire anche il settimo bit, relativo al segmento g e per
questo prendiamo in prestito un pin di PORTB. Le istruzioni successive
servono a questo scopo: il bit relativo viene portato a 1 o a 0 a seconda del
valore del bit 7 estratto dalla tabella.
Esaurito il suo compito, la subroutine set7segm rientra con un retlw
0 alla chiamante.
retlw 0 e non return semplicemente perchè questo opcode non c'è nei
Baseline!!!
In ogni caso, se scriveste return invece di retlw 0
non
succederebbe niente: MPASM è abbastanza "furbo" da sostituire nella
compilazione il codice corretto e si limita a segnalare che ha operato questa
sostituzione.
Va però inteso bene che qui retlw 0 è solo il sostitutivo di
return;
il valore caricato in WREG è del tutto indifferente, dato che non
viene utilizzato. Invece i retlw xx della tabella sono essenziali, dato
che sono i vettori dei valori ottenuti dalla conversione.
Dove è stata chiamata inizialmente la tabella? La chiamata troviamo nel
sorgente piuttosto prima
;
dato ok - set out
pagesel
segtbl ;
cambio pagina
call
set7segm ;
e lookup table |
Vediamo che la call è
preceduta da una linea di pagesel .
Non sarebbe necessaria questa "finezza" nel presente sorgente, che è
di piccolissime dimensioni ed è completamente compreso nella pagina 0 della
memoria programma, ma è utile tenere presente le limitazioni dei Baseline e dei
PIC non-18F in generale.
Subroutines nei Baseline.
Questi limitazioni consistono proprio
nella divisione della memoria programma in pagine e della RAM in banchi, cosa
comune ai Midrange ed assai odiosa al programmatore Assembly.
|
In breve, la memoria programma dei Baseline, mediamente di
1k, è divisa in due banchi da 512 word.
La pagina 0 va dall' indirizzo 000h a 1FFh e la pagina 1 va da 200h a
3FFh.
Il Program Counter, che è composto da un PCL a 8 bit (che quindi
coprirebbe 512 word, pari a una pagina), per coprire tutta l' estensione
della memoria programma è completato da 1 (2 bit per i chip con 2K),
che arriva dallo STATUS.
|
|
Per le chiamate a subroutine, il PC può coprire un range
di indirizzi al massimo di 7 bit (l'ottavo è usato per l' opcode e il
nono è resettato a 0) e quindi non può accedere a locazioni oltre 256
indirizzi d all' inizio della pagina in cui si trova, area in cui deve
rientrare la subroutine. |
Se così non fosse si avrebbe un errore di page boundary con il PC che punta
a locazioni errate. Ne deriva che le subroutines (e le lookup tables) devono
essere possibilmente posizionate ad inizio pagina.
Ovvero, ad esempio, se da una riga di istruzione in pagina 0, chiamassi con una
call l'indirizzo 140h, il PC si porterebbe solo a 040h. Così pure se la routine
fosse posta a 240h; nel primo caso occorre che la routine non inizi oltre 7Fh e
nel secondo caso occorre aggiungere il bit di commutazione alla pagina 1.
Ora, mettere in pagina 0 le subroutine, dove si trova l' inizio del
programma, sarebbe ben possibile nel nostro caso, dato che l' ampiezza del
programma stesso non supera la pagina. Però, per programmi più complessi, la
situazione potrebbe essere diversa. Così pure per tabelle molto lunghe.
Allora, è buona norma utilizzare sempre una struttura che eviti ogni possibile
errore. Questa consiste nel posizionare la tabella in memoria programma all'
inizio pagina, in questo caso di pagina 1, soluzione preferibile dato che lascia
tutta la pagina 0 libera per il programma principale.
Il posizionamento all' inizio di pagina 1 lo otteniamo semplicemente con una
linea ORG 0x200, che modifica l' indirizzo a cui verranno
compilate le righe successive.
Però, per accedere al contenuto della pagina 1 dobbiamo uscire dalla pagina
0, dove si trova l' esecuzione del main. Lo switch di pagina è costituito da
uno (o due bit) dello STATUS, ma questo ci importa relativamente, dato che la
commutazione la lasciamo fare allo pseudo opcode pagesel di MPASM che si
occupa, in funzione del chip usato, di manovrare adeguatamente i bit di switch.
Possibili variazioni.
Volendo ottenere risultati diversi al
display, basta cambiare il contenuto della lookup table. Ad esempio, se vogliamo
presentare solo le cifre da 0 a 9, basta rendere nulli i successivi step: qui di
seguito, la tabella fa si che dati binari da 0 a 9 producano le relative cifre,
mentre dati da Ah a Fh hanno come risultato il display spento.
;
segment data table
segtbl andlw
0x0F
; solo nibble
basso
addwf
PCL,f ;
punta PC
retlw
b'00111111' ;
"0" -|F|E|D|C|B|A
retlw
b'00000110' ;
"1" -|-|-|-|C|B|-
retlw
b'01011011' ;
"2" G|-|E|D|-|B|A
retlw
b'01001111' ;
"3" G|-|-|D|C|B|A
retlw
b'01100110' ;
"4" G|F|-|-|C|B|-
retlw
b'01101101' ;
"5" G|F|-|D|C|-|A
retlw
b'01111101' ;
"6" G|F|E|D|C|-|A
retlw
b'00000111' ;
"7" -|-|-|-|C|B|A
retlw
b'01111111' ;
"8" G|F|E|D|C|B|A
retlw
b'01101111' ;
"9" G|F|-|D|C|B|A
retlw
b'00000000' ;
display spento
retlw
b'00000000'
retlw
b'00000000'
retlw
b'00000000'
retlw
b'00000000'
retlw
b'00000000' |
Oppure, si possono cambiare a piacere
i caratteri presentati; qui sono valide le cifre da 0 a 9, più una serie di
lettere e simboli:
;
segment data table
segtbl andlw
0x0F
; solo nibble
basso
addwf
PCL,f ;
punta PC
retlw
b'00111111' ;
"0" -|F|E|D|C|B|A
retlw
b'00000110' ;
"1" -|-|-|-|C|B|-
retlw
b'01011011' ;
"2" G|-|E|D|-|B|A
retlw
b'01001111' ;
"3" G|-|-|D|C|B|A
retlw
b'01100110' ;
"4" G|F|-|-|C|B|-
retlw
b'01101101' ;
"5" G|F|-|D|C|-|A
retlw
b'01111101' ;
"6" G|F|E|D|C|-|A
retlw
b'00000111' ;
"7" -|-|-|-|C|B|A
retlw
b'01111111' ;
"8" G|F|E|D|C|B|A
retlw
b'01101111' ;
"9" G|F|-|D|C|B|A
retlw
b'01110110' ;
"H" G|F|E|-|C|B|-
retlw
b'00111000' ;
"L" -|F|E|D|-|-|-
retlw
b'01110011' ;
"P" G|F|E|-|-|B|A
retlw
b'01000000' ;
"-" G|-|-|-|-|-|-
retlw
b'01100011' ;
"°" G|F|-|-|-|B|A
retlw
b'00000000' ;
display spento |
E così via, a seconda delle esigenze.
Con 7 segmenti possibile una
ampia quantità di combinazioni , nel nostro caso sempre con la
limitazione di un massimo di 16 elementi tabellabili, limite dovuto ai soli 4
bit trasmessi. Ecco qualche esempio:
Basterà adeguare la lookup table alle esigenze di accensione (1) o
spegnimento (0) dei segmenti.
Ovviamente se si usa un display ad anodo comune la logica sarà opposta. Si
potrà comunque operare anche senza invertire tutto il contenuto della tabella
semplicemente facendo un XOR con FFh del valore prelevato ed adeguando la
gestione del segmento g.
Se si vuole ampliare la gamma dei simboli visualizzabili occorrerà aumentare di
conseguenza il numero dei bit trasmessi.
Il rimanente del programma è identico a quello già visto in precedenza per il
ricevitore con uscita a 4 bit.
Da questo punto di vista può essere utile comparare il sorgente del ricevitore
con uscita a 4 bit paralleli e quello per il comando del 7 segmenti. Si noterà
che la struttura essenziale è del tutto identica: le uniche variazioni
riguardano le caratteristiche proprie dei vari chip, come configurazione e file
.inc e alle modalità per arrivare alla funzione digitale degli I/O,
disabilitando le altre. Oltre, ovviamente, alla gestione del display. Il resto,
potete osservare, è del tutto identico.
Così sarà altrettanto simile il passare da un 16F Baseline a un 16F Midrange o
a un PIC18F. I punti da curare saranno solo:
- l' inclusione del file .inc adeguato
- la configurazione corretta
- le azioni di eliminazione delle funzioni di default non richieste che sono
legate alla struttura dei moduli presenti nei vari chip
- l' allocazione della RAM all' indirizzo disponibile per quel dato chip
- la sostituzione di OPTION e TRIS nel caso in cui non si usi
un Baseline. Infatti, il resto delle istruzioni Baseline fa parte del
set dei processori superiori e non richiede modifiche.
La struttura della lookup table può essere riportata tale e quale anche su
PIC18F, semplicemente avendo l' accortezza in questo caso di moltiplicare per
due l' indice della tabella, dato che le istruzioni Baseline sono codificate su
un byte, mentre quelle 18F occupano due bytes.
Le routines di tempo sono le solite tratte dall'applet del sito http://www.golovchenko.org/cgi-bin/delay e funzionano su qualsiasi PIC, dipendendo solo dalla frequenza del clock.
Il sorgente Assembly del trasmettitore e ricevitore precedenti e del
ricevitore per il display a 7 segmenti sono scaricabili qui. E'
aggiunta anche una versione .pdf di queste pagine e delle precedenti.
Sono aggiunti i file hex per un trasmettitore con 12F509, un ricevitore a 4
bit con 12F510 e un ricevitore per 7 segmenti con 16F526.
Per chi volesse ottenere .hex per altri chip, senza passare attraverso un
progetto di MPLAB, la soluzione più semplice è utilizzare MPASMWIN, il
compilatore Assembly per Windows che può essere usato stand alone al di fuori
dell' ambiente MPLAB. Si trova nella directory C:\Programmi\Microchip\MPASM
Suite\ ottenuta scaricando e installando MPLAB-IDE.
Media.
Vale quanto detto in precedenza.
Un prototipo in cui è stato usato un collegamento diretto tra ricevitore e
trasmettitore, usando un cavetto FTP schermato:
- una una coppia per il segnale e la massa
- altre due coppie per la Vdd
- e le rimanenti due coppie e lo schermo per la Vss
ha permesso la remotazione del ricevitore compresa l' alimentazione a circa 30m. Sul ricevitore, tra Vss e Vdd è stato aggiunto un condensatore da 470uF.
Non è certamente una connessione di sicurezza, ma sufficiente per l' uso cui
era destinata.
In particolare, la remotazione dell' alimentazione può nascondere un
problema, di difficile ricerca delle cause.
Sappiamo che, certamente una lunga linea, sopratutto se non schermata, è
soggetta a induzione che può alterare il funzionamento del circuito, ma, una
volta utilizzato un cavo schermato, magari con la schermatura a terra su un
estremo, si limita alquanto questo problema e si finisce per considerare un
limite solo la caduta di tensione dovuta alla resistenza dei cavi.
Questa non è di per se un problema in quanto il ricevitore, usando un display a
basso consumo, assorbe sicuramente meno di 50mA e il circuito può funzionare
anche con 3V, mentre l' aggiunta di un elettrolitico da 500-1000uF in parallelo
all' alimentazione è garanzia di stabilità.
Però si trascura un aspetto non marginale del funzionamento del
microcontroller: il suo sistema di reset.
Nello schema usato, il pin MCLR viene configurato come RB3 e impiegato come
ingresso. Questo non influisce minimamente sul Power On Reset (POR), dato
che il pin MCLR riveste solo una funzione accessoria e che la circuiteria di
reset è interna al chip e dipende solo dalla Vdd.
Ma purtroppo dipende anche dal tempo di salita della Vdd stessa,
parametro questo (param. D004, tipicamente 0.05V/ms) indicato sui fogli dati. La
resistenza delle linea più la capacità finale costituiscono una rete RC che
rallenta la salita della tensione proporzionalmente al valore di R e di C. Ne
risulta che se il prodotto RC è troppo alto, la tensione ai capi del micro
salirà in modo tale da impedire un corretto operare del circuito di reset, il
che può rendere casuale il funzionamento del chip.
Oltre tutto, i Baseline non hanno nè un Power On Timer (PWRT) nè un Broun
Out Detector (BOR), ma solo un tempo fisso determinato da un clock interno.
Se ci troviamo nelle condizioni di dover alimentare il ricevitore con la
linea stessa e si verificano malfunzionamenti apparentemente casuali, è
pensabile che la causa sia proprio la salita della Vdd troppo lenta.
In tal caso la soluzione è semplice:
- ripristiniamo la funzione MCLR a RB3 e usiamo come ingresso uno dei
pin liberi (cambiando nel sorgente di conseguenza la corrispondenza della
label serin e la configurazione del TRIS relativo al pin
usato)
- colleghiamo a RB3 non alla classica, ma inutile resistenza +
condensatore, bensì a un voltage detector, genere MCP111 (uscita open drain)
o MCP112 (uscita push pull, non serve pullup) o simili, il quale terrà
bloccata a livello basso la linea MCLR fino al raggiungimento della sua
tensione di soglia, ad esempio 4.75V, per poi rilasciarla e avviare così il
micro in modo corretto
Un' altra soluzione che non modifica il firmware ed è forse più semplice
da implementare è quella di inviare in linea non la Vdd, ma la tensione da
cui la Vdd è derivata.
|
Solitamente si alimentano questi microcontroller con una
tensione continua (Vcc) tra 7.5 e 12V attraverso un regolatore lineare a
tre pin genere 7805.
Dunque, mandiamo sul cavo questa tensione continua e all' arrivo la
portiamo a 5V con un altro tre terminali. In questo modo è possibile
superare anche distanze considerevoli, dovendo ora considerare solamente
la caduta di tensione sul cavo per il suo valore.
|
Note.
Certamente è possibile raggiungere velocità di trasmissioni superiori: a
4MHz il ciclo di istruzione di un Baseline è 1us e quindi non ci sono problemi
a superare molti kHz. Usando poi chip dotati di USART, non particolarmente più
costosi di quelli qui indicati, si possono operare trasmissioni a 115 e più. E
con un PIC18F non abbiamo più il problema della memoria paginata e le lookup
tables sono gestite in modo più efficace.
Non è però questa la finalità del tutorial che prende l'esempio della
remotazione di semplici segnalazioni, dipendenti da ingressi lenti e che non
richiedono alte velocità di refresh, come base per dimostrare come una
determinata funzionalità e una certa sicurezza nella comunicazione sia
ottenibile con hardware e software veramente minimi.
Peraltro, più che da un punto di vista prestazionale, il tema che si cerca
di focalizzare è il fatto che non è poi così improponibile spostare un
programma, anche Assembly, da un chip ad un altro: basta porre la giusta
attenzione agli elementi chiave, che non sono poi così tanti.
Il che, però, richiede di avere almeno una idea dell' esistenza di una
filosofia di fondo che accomuna la produzione di un certo costruttore.
Vediamo ulteriori applicazioni.
|