TUTORIAL PIC 

 

PIC & C


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 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!

 

 

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