Comunichiamo
Ora che sappiamo comandare le porte di I/O del PIC,
anche in interrupt, passiamo alla gestione della linea seriale, indispensabile
per comunicare con il modulo GSM TC35.
Come d'abitudine procederemo per gradi, iniziando a
controllare per ora solo la trasmissione dal PIC verso l'esterno.
Cosi' facendo potremo essere sicuri di aver
correttamente settato il baud rate e le linee TX / RX del PIC
Prepariamo come al solito l'ambiente del progetto inserendo il corretto Source
File e gli Header Files nella apposita finestra.
Dopo di cio' iniziamo ad analizzare le prime linee del programma che si
differenzia dal precedente per l'inclusione del file uart.h tramite la
direttiva #include <usart.h>
Questo file fa parte delle librerie standard del ed include tutte le
funzioni e le dichiarazioni utili per la gestione delle seriali.
Un invito e' quello di aprire anche solo per conoscenza questo file che si
trova in ../MCC18/h e magari anche il file di help delle librerie hlpC18Lib.chm
situato in ../MCC18/doc non tanto per reperire ora le necessarie informazioni
indispensabili alla comprensione del presente programma, quanto
per imparare dove e come accedere a quelle informazioni talvolta
indispensabili per superare difficolta' impreviste o per esplorare
possibili alternative piu' funzionali ed evolute.
Agli altri file di inclusione, gia' visti in precedenza sono state fatte solo
alcune modifiche estetiche.
Un discorso leggermente diverso merita invece il file di definizione delle
variabili di memoria ram.
Sino a questo momemto, vista l'esiguita' della nostra esigenza di variabili ,
non ci eravamo preoccupati di come e dove allocare lo spazio di memoria ram da
utilizzare, sicuri che il compilatore ci avrebbe pensato LUI autonomamente.
La cosa e' vera sino ad un certo punto!
Sebbene l'accessibilita' della memoria ram nei PIC18 non richieda il controllo
del banco con conseguente gestione del relativo registro, il compilatore
suddivide tale memoria in blocchi che vanno rispettati nella definizione delle
variabili, pena ricevere durante la compilazione una segnalazione di errore
del tipo:
Error - section '.udata_
(nome file) can not fit the section ecc.
lenth=0x00000104.
facendo pensare il programmatore ad una mancanza di memoria del PIC o a
chissa' quale errore commesso nella dichiarazione.
Nulla di tutto questo, il compilatore vuole che la memoria risulti segmentata
in banchi, tipicamente di 256 bytes e che delle variabili sia dichiarata
esplicitamente l'appartenenza ad uno specifico banco.
Per ogni processore esiste un file xxxxx.lkr che descrive come vanno allocate
queste risorse di memoria che ovviamente dipendono dall'hardware del
processore
i files sono disponibili nella directory ../MCC18/bin/LKR e
possono venir aperti da qualsiasi editor di testo o ad esempio dall'interno di
MPLAB con File -> Open ed andando poi a ricercare il file interessato.
Per comodita' riporto parzialmente il contenuto del file relativo al
processore PIC18F2620 di nostro interesse specifico.
DATABANK NAME=gpr0
START=0x80
END=0xFF
DATABANK NAME=gpr1
START=0x100
END=0x1FF
DATABANK NAME=gpr2
START=0x200
END=0x2FF
DATABANK NAME=gpr3
START=0x300
END=0x3FF
DATABANK NAME=gpr4
START=0x400
END=0x4FF
DATABANK NAME=gpr5
START=0x500
END=0x5FF
DATABANK NAME=gpr6
START=0x600
END=0x6FF
DATABANK NAME=gpr7
START=0x700
END=0x7FF
DATABANK NAME=gpr8
START=0x800
END=0x8FF
DATABANK NAME=gpr9
START=0x900
END=0x9FF
DATABANK NAME=gpr10 START=0xA00
END=0xAFF
DATABANK NAME=gpr11 START=0xB00
END=0xBFF
DATABANK NAME=gpr12 START=0xC00
END=0xCFF
DATABANK NAME=gpr13 START=0xD00
END=0xDFF
Si noti la disponibilita' di ben 14 banchi di cui 13 da 256 bytes ed uno il
banco 0 da 128 bypes, essendo i rimanenti 128 riservati ai registri speciali
del PIC.
La finestra qui sotto illustra come ad esempio vengono utilizzati tali banchi
nel nostro progetto
Per scegliere il banco da utilizzare per le variabili che seguiranno si
utilizza la direttiva
#pragma idata
gprN=0xN00 con N in
accordo alla tabella sopra esposta.
L'area delle variabili appartenenti a questo banco va conclusa con una linea
finale
#pragma idata
Si utilizza la parola chiave idata se
le variabili all'interno del blocco vanno inizializzate, ovvero devono
assumere uno specifico valore alla partenza del programma, in caso
contrario come fatto per i buffer di ricezione e trasmissione al posto di idata
( initialized data ) va usata la parola chiave udata
( uninitialized data ).
Nulla di particolare da segnalare invece per il file di inclusione dei
prototipi di funzioni che si arricchisce delle nuove funzioni di gestione
dell'USART
Ma veniamo al file principale PICcolino_3.c che nella funzione Init_HW_PIC()
si modifica leggermente rispetto alla precedente versione includendo la
parte relativa all'inizializzazione dell'hardware dell'USART e del buffer di
ricezione con la linea:
Rxdata[0] = 0 ;
Anche se in questo primo programma dedicato alla comunicazione seriale non si
fara' uso della linea di ricezione vorrei spiegare brevemente perche' quale
inizializzazione del buffer che conterra' i caratteri ricevuti viene inserito
un carattere = 0 ( carattere nullo ) nella prima posizione del buffer.
Il buffer lo ricordo e' stato definito come un array char
Rxdata[BUFSIZE+1]; nel file di inclusione.
Cio' significa che si e' riservato uno spazio di BUFSIZE+1
bytes indirizzabili come Rxdata[0]
... Rxdata[200] essendo BUFSIZE = 200.
Le funzioni standard del C che gestiscono le stringhe di caratteri di cui
avremo modo di discutere piu' approfonditamente in seguito, utilizzano in
carattere null per determinare
la fine della stringa stessa. Pertanto inserire un carattere null
nella prima posizione di un array di caratteri equivale a rendere
vuota tutta la stringa, indipendentemente dal contenuto delle restanti
posizioni.
Dunque Rxdata[0] = 0 ; corrisponde
ad annullare una stringa
Passiamo ora ad analizzare la funzione
Init_USART () che si occupa di predisporre l'USART con il
corretto baud rate e le altre caratteristiche di rice-trasmissione che
desideriamo impostare.
L'inizializzazione e' effettuata sfruttando le funzioni standard e le
definizioni che ci offre il file di inclusione usart.h
che consentono di programmare velocita' parita' numero di bit ecc in accordo
con quanto impostato nel nostro file di definizione delle costanti.
Da notare inoltre che per ora sia l'interrupt di trasmissione che quello di
ricezione abbinati all'USART risultano disabilitati
Cosi' l'interrupt verra' utilizzato solo da parte del timer TMR0 la cui
gestione e' gia' stata analizzata in passato.
Vorrei pero' porre in evidenza una piccola modifica relativa al tempo per il
lampeggio del LED 1, sempre presente anche in questo esempio:
L'incremento del tempo relativo tempo_led1 e'
effettuato tramite l'istruzione di preincremento ++tempo_led1
al posto di quella di postincremento tempo_led1++
.
Cio' rende piu' preciso il computo del tempo di lampeggio in quanto
l'incremento avviene prima del test sul valore finale permettendo di settare
la segnalazione tm_led1 esattamente
ogni 50 cicli, ovvero 0.5 sec.
La funzione di interrupt a bassa priorita' proveniente dall' USART risulta
ancora vuota e d'altra parte l'interrupt non risulta abilitato a livello di
registri hardware.
Ed ecco infine come risulta il programma principale o piu' precisamente la
funzione main()
Dopo l'inizializzazione dell'hardware si entra nel loop perpetuo while(
1 ) in cui ogni 0.5 sec viene commutato il led 1 quale segno di
attivita' e spediti sulla linea seriale due caratteri in successione A.
Prima della spedizione del carattere tramite
putcUSART('A'); viene testata la disponibilita' dell'USART ad
accettare un nuovo carattere nel buffer interno di spedizione.
Questo viene fatto dalla linea while(BusyUSART());
Occorre ben notare che il ; che apparentemente fa terminare la funzione
in realta' identifica una istruzione successiva NULLA in quanto il costrutto
while(....) non richiede di per se'
il ; Chiaro?
Dunque la linea inquestione, tradotta in linguaggio corrente si puo' leggere :
Sino a che l'USART e' occupata, NON FAR
NIENTE.
Del tutto analoga sembra la seconda parte del main che testa il
contatore tempo incrementato
dalla routine di interrupt per inviare ogni 5 sec. sulla linea seriale un
carattere B seguito dai
classici caratteri di Line-Feed e Carriage-Return.
Attenzione pero' che l'apparente semplice funzionamento di questa parte di
codice, che apparentemente poteva sembrare identica alla precedente racchiude
una problematica sottile che va ben tenuta in evidenza.
Anche se operiamo con un linguaggio di alto livello non dobbiamo mai
prescindere dall'harduare che stiamo utilizzando. Ricordiamoci dunque che
stiamo operando con un processore con parallelismo ad 8 bit e che nonostante
il C non ci faccia pesare questa limitazione nella gestione di variabili int
long ecc non puo' far altro che processare un byte alla volta.
Cio' premesso cerchiamo di spiegare la problematica nascosta in questo blocco.
Il test che vogliamo fare sulla variabile tempo
a 16 bit richiedera' sicuramente piu' di una istruzione elementare in
linguaggio macchina.
L'interrupt che per definizione interrompe il flusso ordinato delle operazioni
consente solo di completare l'istruzione corrente e puo' dunque capitare che a
meta' del test della variabile tempo
intervenga un interrupt a gestire la propria routine e solo alla fine il
controllo ritorni al punto di partenza con il completamento del test.
In genere nessun problema, tutti i registri usati dall'interrupt vengono
ripristinati al loro valore originario all'uscita ed il programma prosegue
come se nulla fosse successo, solo impiegando un piccolo tempo aggiuntivo.
Diverso e' invece il caso come il nostro in cui la variabile che si sta'
manipolando viene anche manipolata dalla routine di interrupt.
E' evidente in questo caso che si possono prendere fischi per fiaschi!
Che fare dunque per proteggersi? Semplice, evitare che l'interrupt possa
interromperci proprio in quel momento, rimandandolo a quando la sua azione non
diventa piu' pericolosa.
Il sistema piu' semplice ed immediato e' quello di disabilitare l'interrupt
prima del test e riabilitarlo poi non appena possibile.
Questo e' quello che e' stato fatto con le istruzioni
INTCONbits.GIEH = 0; e
INTCONbits.GIEH = 1;
Si, ma cio' e' estremamente improbabile obiettera' qualcuno!
Davvero? provare a togliere le istruzioni di disabilitazione ... e
vedere di nascosto l'effetto che fa ....
Certo in un programma piu' lungo ed articolato le probabilita' diminuiscono
fortemente, ma qui dove praticamente vom fa altro che questo test....
A proposito di ... vedere di nascosto l'effetto che fa .... per testare il
nostro programma occorrera' utilizzare uno qualsiasi tra i programmi di
monitor delle linee seriali da istallare sul PC.
Oltre al classico Hyperterminal, con una semplice ricerca in Internet se ne
potranno trovare parecchi a piacere.
Personalmente utilizzo Hercules ma a voi la scelta
Da ultimo accludo due foto che mostrano come effettuare sul prototipo del
PICcilino i ponticelli per dirigere la seriale del PIC verso l'esterno.
Ricordo che sulla scheda non e' inserito alcun adattatore TTL / RS232 in
quanto il modulo TC35 si interfaccia direttamente a livello TTL, occorrera'
pertanto utilizzare un adattatore esterno come ad esempio quello visibile
nella foto il cui progetto e' disponibile nella sezione Progetti del nostro
sito
Buona comunicazione!
|