Progetti - PIC

 

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:

  1. viene chiamata la subroutine set7segm
  2. questa carica in WREG il dato ricevuto
  3. 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 :

  1. 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.
  2. 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.


 

 

         

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 09/11/17.