Corso programmazione PICMicro in C – Lezione 3 – Il primo programma in C: scrivere un semplice programma in C per PICMicro, impostare MPLAB e flashare il PICMicro con il PICKit2. Facciamo lampeggiare un LED!


Scritto da Giovanni Bernardo in data 18 agosto 2009

primo_programma_picmicro_in_c_flashare_con_pikcit2_circuitoFinalmente dopo le precedenti due lezioni, per lo più introduttive, eccoci alla parte pratica! In questa lezione scriveremo il nostro primo programma in C, lo compileremo e lo caricheremo sul PICMicro per vederlo in azione.

Ovviamente il primo programma è sempre qualcosa di molto molto semplice: faremo lampeggiare un diodo led. Certo non è un qualcosa di molto entusiasmante, ma bisogna partire a piccoli passi per comprendere a fondo tutto quello che c’è dietro ai programmi più complessi.

Ricordate lo schema elettrico del circuito di base presentato nella seconda lezione? Bene, andremo a collegare sul pin n° 19 (che rappresenta la porta RD0, ovvero la prima delle porte D) un diodo led, con in serie una resistenza da 330Ω (che ha la funzione di limitare la corrente che circola nel LED, in maniera tale da non farlo bruciare). Realizzeremo  quindi il seguente schema:

primo_programma_picmicro_in_c_mplab_schema_thumb

Quando andrete a montare il LED, tenete conto che il terminale più corto dei due è quello che va verso massa. Se lo montate al contrario non si accenderà. Fate una prova alimentandolo con 5 volt e con la resistenza da 330Ω: vedrete che in un senso si accenderà e dall’altro no. Anche se emette luce, si tratta pur sempre di un diodo ed è quindi capace di far circolare la corrente in un verso soltanto.

La porta RD0 è stata scelta a caso, avremmo potuto collegare il LED su qualsiasi altra porta indifferentemente: ci serve difatti unicamente la funzione di I/O digitale, che come abbiamo visto nelle lezioni precedenti, è a disposizione di qualsiasi porta.

Se state utilizzando per questi esperimenti la scheda Freedom di Mauro Laurenti, per poter utilizzare le varie porte per gli esperimenti, potete crearvi una piattina che da un lato si innesta nella scheda di sviluppo e dall’altro invece presenta uno strip da 10 contatti in linea che possiamo utilizzare facilmente per la connessione con una breadboard:

primo_programma_picmicro_in_c_adattatore_freedom_breadboard

Parte 1 -- Scriviamo il programma

Dopo aver montato tutto il circuito, possiamo procedere alla scrittura del nostro primo programma. Personalmente per scrivere il programma in C, utilizzo Notepad++ (mi piace perchè è freeware, evidenzia le parole chiave con colori diversi, ha il supporto della sintassi per molti linguaggi, la numerazione delle righe ecc). Voi ovviamente potete utilizzare ciò che più vi piace, anche il blocco note di windows (anche se non lo consiglio affatto, è buono avere un editor tipo Notepad++ che evidenzia la sintassi).

Questo è il primo programma che andremo a scrivere (sarà comunque possibile scaricarlo in fondo all’articolo per gli utenti iscritti: è gratis!), creiamo un file di testo vuoto, diamogli il nome “main.c” e incolliamoci all’interno il seguente codice:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//*************************************************
// CORSO PROGRAMMAZIONE PICMICRO
// www.settorezero.com
//
// modulo: main.c
// autore: Bernardo Giovanni
// data: 18/08/09
// descrizione: lampeggia un led su RD0
// picmicro: PIC16F877A
// clock: 20MHz
//
//*************************************************
 
#define  XTAL_FREQ 20MHZ // questo è utilizzato dalle routine di ritardo contenute in Delay.C
#include <pic.h> // contiene i nomi mnemonici di registri e porte
 
// Fuses di configurazione
__CONFIG (HS & WDTDIS & PWRTEN & BORDIS & LVPDIS & DUNPROT & WRTEN & DEBUGDIS & UNPROTECT);
 
#include "delay.c" // routine per ritardi
#define    LED    RD0 // invece di scrivere RD0, scriverò LED, così mi è più facile ricordare
 
// funzione principale, eseguita all'avvio del picmicro
void main(void)
 {
 
 // imposto i registri tristato in maniera tale che tutte le porte siano configurate come pin di uscita
 TRISA=0b00000000;
 TRISB=0b00000000;
 TRISC=0b00000000;
 TRISD=0b00000000;
 TRISE=0b00000000;
 
 while(1) // eseguo un ciclo finito
 {
 LED=LED^1;
 DelayMs(250);
 }// Fine ciclo continuo
 
 } // Fine main

Analizziamo quindi il programma. Quello che si trova dietro il doppio backslash (//) sono semplici annotazioni, verranno ignorate dal compilatore, servono soltanto al programmatore a “ricordarsi” cosa succede. E’ sempre buona norma mettere all’inizio del programma un’intestazione con le date ecc, e che specifica autore, scopo del programma e quant’altro vogliamo, se non altro ci aiuterà tra qualche anno quando dovremmo rimettere mano a un codice per modificarlo o migliorarlo.

14
#define  XTAL_FREQ 20MHZ

Le righe che iniziano con il cancelletto (#) sono direttive inviate al compilatore, ovvero non si tratta di vere e proprie istruzioni di programma, ma di istruzioni inviate al compilatore e che non occuperanno spazio in memoria programma. In questa riga in particolare, l’istruzione define dice al compilatore che quando all’interno del programma troverà la scritta “XTAL_FREQ”, la dovrà sostituire con il valore “20MHz”. Non si tratta affatto dell’assegnazione del valore ad una variabile, stiamo soltanto definendo un nome alternativo per un valore, il che ci aiuta molto a ricordare. Questa riga in particolare è utilizzata da una routine (che vedremo in seguito) che serve a generare ritardi.

15
#include <pic.h>

è un’altra direttiva (vedete? c’è il cancelletto): stiamo dicendo al compilatore di includere in quel punto, il file “pic.h”.  In pratica tale direttiva leggerà il contenuto di quel file e lo inserirà in quel punto. Questo è un buon modo per scrivere codice “portabile”: una volta scritta una porzione di codice riutilizzabile, non ci sarà bisogno di riscriverla sempre nei nostri programmi, basterà “includerla”. Quando tale direttiva include un file contenuto nella cartella “include” dell’installazione del compilatore, tale file va scritto tra < e >, quando invece il file da includere si trova in un altro percorso, allora va incluso tra virgolette ” ” (in particolare se tra virgolette indichiamo unicamente il nome del file, il compilatore assume che tale file da includere si trovi nello stesso percorso del file principale, ovvero main.c).

Il file pic.h in particolare, che già si trova nella cartella “include” di installazione dell’Hitec-C, contiene tutte le definizioni dei nomi delle porte e dei registri dei vari picmicro. In pratica non dovremo ricordarci tutti gli indirizzi di memoria (che sono numerici!) del nostro PICMicro: basterà ricordarsi i nomi “mnemonici”, molto più facili da ricordare perchè i loro nomi rispecchiano in un certo qual modo le funzioni dei registri. Impareremo man mano a utilizzare i nomi mnemonici quando ne avremo bisogno. In particolare il file pic.h includerà a sua volta un altro o più file con estensione .h a seconda del PICMicro che sceglieremo durante la compilazione del programma.

Quando programmerete in C, incontrerete essenzialmente due tipi di file di testo, con estensione .c (che  contengono funzioni, programmi principali) e con estensione .h. h sta per Header, ovvero “intestazione”, si tratta in genere di files ausiliari che contengono settaggi, prototipi di funzioni ecc, necessari per il funzionamento delle funzioni principali, si tratta in pratica di una convenzione e buona pratica di programmazione. Ovviamente noi potremmo scrivere tutto il nostro codice in un unico file, ma avere il file principale, soprattutto se si tratta di programmi complessi, suddiviso in più files c e h ci aiuterà molto a concentrarci su una parte di programma e ci permetterà di rendere il nostro programma, o le nostre funzioni, riutilizzabili in altri ambiti anche totalmente differenti da quelli per cui sono stati progettati.

Date una sbirciatina nella cartella “include” del compilatore (C:\Programmi\HI-TECH Software\PICC\PRO\9.65) : vedete? Ci sono un sacco di files .h, ognuno che contiene vari #define a seconda del picmicro che andremo a scegliere. Il file pic.h è quello generico, che si accorgerà di quale pic abbiamo scelto (durante l’operazione di compilazione che vedremo dopo) e che provvederà a includere il file .h adatto: in particolare per il PIC16F877 sarà incluso il file pic1687x.h.

Andiamo avanti con la spiegazione del programma:

18
__CONFIG (HS & WDTDIS & PWRTEN & BORDIS & LVPDIS & DUNPROT & WRTEN & DEBUGDIS & UNPROTECT);

Questa parte qui è un po’ più complicata… Si tratta in pratica di una funzione (chiamata appunto “__CONFIG” definita nel file pic.h) che serve a settare i FUSES (o fusibili) di configurazione del picmicro e di cui abbiamo già accennato qualcosina nelle precedenti lezioni. Ogni picmicro ha un suo registro di configurazione con i propri valori (vedete pag. 144 del datasheet del PIC16F877A) e che serve per impostare le modalità di funzionamento. I valori che si trovano tra parentesi (ovvero gli argomenti della funzione) sono nomi mnemonici (ogni parola, tipo HS è in realtà un valore numerico, definito da vari #define contenuti nei file h del picmicro scelto), che vengono combinati tramite AND logici per ottenere la word di configurazione adatta. In particolare questo settaggio dice:

HS (High Speed) = intendiamo utilizzare un oscillatore al quarzo ad alta frequenza.

WDTDIS (Watch Dog Timer DISabled) = disabilitiamo il Watch Dog Timer: si tratta di un timer che ha la funzione di resettare il pic se il programma si blocca e quindi far ripartire il programma daccapo, noi per ora (e forse mai) non utilizzeremo questo timer, dobbiamo programmare bene per fare in modo che il programma non si blocchi!! In realtà tale funzione a volte è utile, ma noi probabilmente non la useremo.

PWRTEN (PoWeRup Timer ENabled) = il pic aspetta alcuni microsecondi all’accensione prima di avviare il programma, in maniera tale da far stabilizzare la tensione e l’oscillatore.

BORDIS (Brown Out Reset DISabled) = il brown out è una funzione che permette di resettare il pic se la tensione di alimentazione scende al di sotto di un dato valore, tale funzione non ci serve, quindi la disattiviamo.

LVPDIS (LowVoltage Programming DISabled) = disabilitiamo la programmazione a bassa tensione (abbiamo visto nelle lezioni precedenti di cosa si tratta).

DUNPROT (Data UNPROTected) = la memoria dati non sarà protetta

WRTEN (WRite ENabled) = scrittura memoria flash abilitata

DEBUGDIS (DEBUG DISabled) = funzioni di debugging disabilitate, il debug non è disponibile con il pickit2

UNPROTECT = non protegge la memoria programma

Questi sono i settaggi ideali per gli esperimenti: proteggere la memoria dati, programma, eeprom, disattivare la scrittura, potrebbe difatti renderci impossibile riprogrammare il PIC una seconda volta, quindi se sbagliamo, il pic sarà da buttare! Quindi non proteggiamo assolutamente nulla!

20
#include "delay.c"

Stiamo includendo il file delay.c (scaricabile insieme a tutto il progetto in fondo all’articolo, per gli utenti iscritti) che si trova nello stesso percorso del file principale. Tale file include delle routine per creare ritardi (delay). Includendo questo file avremo a disposizione nel nostro programma due funzioni: DelayMs(n) che ci permetterà di eseguire ritardi di n millisecondi (con n da 0 a 255) e DelayUs(n) che ci permetterà di eseguire ritardi di n microsecondi. I ritardi sono funzioni molto importanti nei programmi.

21
#define    LED    RD0

Ancora un’altra direttiva! Stiamo dicendo al compilatore che quando leggerà la parola “LED” all’interno del programma, gli dovrà sostituire il valore RD0 (ricordate lo schema? Abbiamo collegato il led alla porta RD0, è più facile ricordarsi LED, sapendo che li cè collegato un led, piuttosto che RD0, vi pare?). La parola (o forse meglio il SIMBOLO) RD0 è a sua volta definito in uno di quei file h che abbiamo incluso all’inizio e contiene l’indirizzo di memoria che “mappa” la porta RD0, non ci dovremo preoccupare a quale indirizzo si trova la porta RD0, tanto abbiamo già tutti i nomi mnemonici già definiti per il nostro pic (definiti da pic.h, incluso all’inizio). Quindi come c’è RD0, ci sarà anche RA1, RB3 ecc ecc, comodo no?

Finalmente inizia il programma vero e proprio:

24
25
void main(void)
 {

Chiariamo innanzitutto una cosa, qui stiamo scrivendo una funzione, questa funzione si chiama “main” (scritto minuscolo!, ricordate che il C è case-sensitive! Ovvero fa distinzione tra maiuscole e minuscole!). La funzione chiamata main è quella che viene eseguita all’accensione del picmicro, per cui tutti i nostri programmi avranno sempre una funzione chiamata così.

Il “void” scritto all’inizio indica che tale funzione non restituisce valori (void=vuoto), il void scritto tra parentesi dopo main, indica che tale funzione non accetta valori in ingresso. La funzione main difatti non può nè deve accettare/restituire valori. Ci saranno altre funzioni che potranno accettare valori per eseguirci su dei calcoli e quindi restituire un valore, altre che restituiranno un valore ma non accetteranno niente in ingresso, altre ancora che accetteranno valori in ingresso ma non restituiranno niente.

Una cosa del genere ad esempio:

int somma(int a, int b)

definisce una funzione chiamata “somma”, che restituisce un numero intero (int), e accetta in ingresso due variabili (a e b) interi anch’essi. L’int messo davanti (al posto del void) specifica appunto un intero, int è uno specificatore del tipo di dato. Fate riferimento alla pagina 83 del file “manual.pdf” contenuto nella cartella docs del compilatore (C:\Programmi\HI-TECH Software\PICC\PRO\9.65\docs) per vedere quali sono i tipi di dato disponibili in C. Qui vi riporto un estratto:

tipi_di_dato_hitec_c

Ricordate inoltre: tutto ciò che deve essere eseguito dalla funzione, deve trovarsi tra parentesi graffe! Per cui: ricordiamoci di chiudere le parentesi dopo che le abbiamo aperte. E’ sempre buona norma mettere sempre prima entrambe le parentesi (aperta e chiusa) e quindi successivamente scriverci tutto all’interno.

Ovviamente qui qualcuno andrà nel pallone non trovando le parentesi graffe sulla tastiera. Le parentesi graffe si inseriscono tenendo premuto ALT (quello a sinistra, NO ALT GR) e quindi digitando in sequenza 1-2-3 sul tastierino numerico (ho detto sul tastierino! Non funziona con i numeri sopra alle lettere!) e quindi rilasciando il tasto ALT per la parentesi aperta, stessa cosa ma digitando 1-2-5 per la parentesi chiusa, e così anche per tutti gli altri caratteri che non trovano posto sulla tastiera.

Proseguiamo nella lettura del programma con qualcosa di più interessante:

28
29
30
31
32
TRISA=0b00000000;
TRISB=0b00000000;
TRISC=0b00000000;
TRISD=0b00000000;
TRISE=0b00000000;

I registri TRIS (regisatri tristato, ve ne ho accennato nella lezione 2) ci sono per ogni gruppo di porte (TRISA è il registro tristato delle porte A, e… TRISB ?). Notate innanzitutto che dopo l’uguale abbiamo uno 0b, questo zero-bi indica al compilatore che il numero che segue è in formato binario, se avessimo messo 0x (zero-ics) voleva dire che il numero che seguiva era scritto in esadecimale, invece senza notazione alcuna sarebbe stato scritto in decimale). Vi rimando ad un mio precedente articolo sulla numerazione binaria ed esadecimale, imparare queste cose non fa mai male. Sempre a pagina 83 del manuale dell’Hitec-C troviamo un’altra tabella con i prefissi da usare per specificare la base con cui scriviamo i numeri:

tipi_di_formato_numerico_hitec_c

Scrivendo

TRISA=0b00000000;

Stiamo dicendo che tutti i bit del registro tristato delle porte A, devono essere impostati su ZERO. Cosa significa? Impostare nel registro tristato un bit a zero equivale a impostare quella porta (quel pin) come USCITA (Output, per aiutarvi a memorizzarlo ricordate la somiglianza tra lo zero e la lettera O di Output…), impostare un bit a 1 equivale rendere quella porta (quel pin) un ingresso (Input, 1 somiglia alla lettera I, giusto? ;) ). In particolare il bit più significativo (ovvero l’ottavo bit) è quello più a sinistra: quello più vicino alla lettera b, andando verso destra troviamo il bit 7, 6 ecc fino ad arrivare al bit1 che viene detto bit meno significativo. Il bit1 mappa la porta 0, il bit2 mappa la porta 1 e così via. In pratica se noi scriviamo:

TRISA=0b00000010;

Diremo al PICMicro che la porta RA1 dovrà essere una porta di ingresso (abbiamo messo l’uno sul secondo bit, che mappa la seconda porta, ovvero la RA1) mentre tutte le restanti porte dovranno essere pin di uscita. Abbastanza semplice non trovate? Per questo motivo il settaggio delle porte lo scrivo in binario, così visivamente mi accorgo di quale porta sto impostando come ingresso e quale come uscita, ovviamente nulla mi vietava di scrivere 0x2 (notazione esadecimale) oppure 2 (notazione decimale) al posto di 0b00000010 … (usate la calcolatrice di windows in modalità scientifica per capire che ho detto se non ci siete arrivati…).

Ovviamente alcuni banchi di porte non disporranno di tutti e 8 i bit: ricordate l’immagine del datasheet del PIC16F877? Osservate: Le porte A sono soltanto 6 (da RA0 a RA5), quando vorremo settare questa porta, è buona norma per noi mettere comunque tutti e 8 i bit, ovviamente i bit 7 e 8 li metteremo a zero, tanto pure se li mettiamo a 1 non avranno effetto perchè le porte RA6 e RA7 su questo PIC non esistono… (per non parlare delle porte E che sono soltanto 3… Qui avremo a disposizione soltanto i bit 1, 2 e 3, gli altri bit da 4 a 8 li metteremo sempre a zero perchè tali porte non esistono).

Bene, abbiamo quindi settato tutte quante le porte del nostro PIC16F877 come uscita!

Quando nel nostro circuito rimarranno pin non utilizzati, è sempre bene impostare tali porte come uscite, perchè se le impostiamo come ingressi e non vengono utilizzate, potrebbero captare disturbi di natura elettromagnetica che dobbiamo sempre evitare come la peste.

Proseguiamo:

34
35
while(1) // eseguo un ciclo finito
 {

Qui abbiamo un ciclo while (vi rimando all’ottima lettura TrickyC presente nell’area risorse del sito, pagina 79, per capire come funzionano i 3 tipi di cicli disponibili in C) che verrà eseguito all’infinito perchè vi abbiamo messo 1 come condizione, che è sempre vera. All’interno di questo ciclo troviamo la parte interessante:

36
37
LED=LED^1;
DelayMs(250);

Qui stiamo accendendo e spegnendo il led!! Ma come? Iniziamo col dire che DelayMs(250) esegue una pausa di 250 millisecondi (ricordate le funzioni DelayMs incluse nel file delay.c di cui abbiamo parlato più in alto?), in pratica il led rimarrà acceso 250 ms e spento altri 250ms per poi ricominciare daccapo. L’accensione e lo spegnimento del led vengono eseguiti dall’istruzione LED=LED^1. Chiariamo innanzitutto che il simbolo ^ in C non è l’elevamento a potenza ma rappresenta la funzione di XOR, in pratica quando su un bit viene eseguito l’XOR con 1, viene invertito lo stato del bit: diventerà 1 se era 0 e viceversa.

Fate riferimento a questo nostro articolo per conoscere come si effettuano le operazioni di algebra booleana.

Scrivere LED=1 significa “dare tensione” a quel pin (ricordate? LED=RD0), quindi se scrivo LED=1 la porta RD0 si porterà in condizione di livello logico alto, ovvero da quel pin usciranno 5volt, per cui il led si accenderà, scrivere LED=0 porterà la porta RD0 in stato logico basso, per cui da quel pin usciranno zero volt (massa) e il led si spegnerà, scrivere LED=LED^1 significa semplicemente invertirne lo stato… Dal momento che dopo viene una pausa, si avrà l’effetto di vedere il led accendersi e spegnersi ogni 250ms (se avete la fortuna di avere un oscilloscopio, vedrete su tale pin un’onda quadra con la frequenza di 2Hz).

Difatti per portare un’uscita a livello logico alto (far uscire 5 volt), basta impostare tale porta a 1, per renderla bassa, si imposta a zero. Non dovrebbe risultare complicato.

Ci tengo a far notare che tutte le istruzioni devono terminare con un punto e virgola, altrimenti si verificheranno errori strani e incomprensibili durante la compilazione, e spesso gli errori vengono segnalati in una parte di programma che non c’entra nulla quando mancano il punto e virgola o non viene chiusa una funzione.

Abbiamo infine la chiusura del ciclo while e quindi della funzione main:

38
39
40
}// Fine ciclo continuo
 
} // Fine main

Ok… Abbiamo scritto il nostro primo programma (ok l’abbiamo copiato oppure scaricato!), abbiamo capito (si spera) come funziona… Non ci resta che compilarlo, caricarlo sul PICMicro e vedere come funziona!


Parte 2 -- Compiliamo il programma

Abbiamo questa situazione: in una cartella ci sono i files: main.c, delay.c e delay.h scaricati in fondo all’articolo. Avviamo MPLAB IDE, Selezioniamo Project -> Project Wizard

primo_programma_picmicro_in_c_mplab_01

Comparirà la finestra del wizard (ovvero la procedura guidata) :

primo_programma_picmicro_in_c_mplab_02

Clicchiamo su Avanti, ci verrà chiesto di selezionare il dispositivo (il PICMicro) che intendiamo utilizzare per questo progetto:

primo_programma_picmicro_in_c_mplab_03

Scorriamo la lista e troviamo PIC16F877A, premiamo quindi Avanti. Ci verrà mostrata la finestra nella quale dobbiamo scegliere quale suite di linguaggio intendiamo utilizzare:

primo_programma_picmicro_in_c_mplab_04

Dal menù Active Toolsuite selezioniamo HI-TECH Universal ToolSuite come nella figura (l’abbiamo installato nella lezione 2, ricordate?), premiamo quindi Avanti, dovremo dare un nome al nostro progetto:

primo_programma_picmicro_in_c_mplab_05

Premiamo sul tasto Browse e andiamo a trovare la cartella in cui abbiamo salvato main.c e tutto il resto, nella casella nome file mettiamo un nome facile da ricordare: lampeggia_led, come in figura:

primo_programma_picmicro_in_c_mplab_06

Premiamo quindi salva, ritorneremo nella finestra precedente:

primo_programma_picmicro_in_c_mplab_07

Clicchiamo su Avanti, ci verrà chiesto di aggiungere dei files al progetto:

primo_programma_picmicro_in_c_mplab_08

Selezioniamo soltanto main.c e premiamo sul tasto Add>> per aggiungerlo al progetto, premiamo quindi Avanti. Sarà mostrata una finestra di riepilogo:

primo_programma_picmicro_in_c_mplab_09

Possiamo quindi premere Fine, verremo riportati alla finestra principale di MPLab:

primo_programma_picmicro_in_c_mplab_10

Vedete sulla sinistra c’è una finestra più piccola in cui è evidenziato il nostro codice sorgente main.c, anche se non abbiamo incluso gli altri file nel progetto durante il wizard, funzionerà tutto lo stesso perchè saranno inclusi dal compilatore grazie alle direttive #include che abbiamo scritto nel programma. Possiamo passare a compilare il programma. Vedete nella barra degli strumenti ci sono due tasti: uno nero e uno rosso:

primo_programma_picmicro_in_c_mplab_11

Quello nero è per compilare la prima volta, quello rosso è per RI-compilare nel caso avessimo già compilato e modificato qualcosa (verranno cancellati dei file “di appoggio” e ricompilato tutto daccapo). Premiamo il tasto nero. La compilazione prenderà generalmente meno di un minuto (dipende da tanti fattori) e se tutto è andato per il meglio, sarà presentata una finestra di riepilogo con le operazioni di compilazione e una scritta BUILD SUCCESSFUL alla fine:

primo_programma_picmicro_in_c_mplab_12

Vedete in questa finestra ci sono molte informazioni: quanta memoria programma abbiamo utilizzato (lo 0.9%, ma ricordate che stiamo utilizzando la versione LITE che non ci permette di utilizzare tutta la memoria programma disponibile ma ha un limite di 2000h words, con una versione PRO lo spazio a disposizione è molto di più, inoltre con la versione pro viene abilitata una funzione, chiamata Omniscient Code Generation, che permette di risparmiare ulteriore spazio, fino al 52% in meno, dal momento che ottimizza ancor più il codice compilato).

Quando qualcosa andrà storto, ci sarà invece scritto BUILD FAILED, con l’elenco dei numeri di riga che hanno dato l’errore, in questo caso dovremo individuare l’errore, correggerlo e ricompilare (premendo il tasto rosso anzichè quello nero). Errori tipici dei principianti sono:

  • dimenticanza del punto e virgola alla fine di una riga che lo prevede
  • dimenticanza della chiusura di una o più parentesi graffe
  • dimenticanza che il C è case sensitive, per cui se abbiamo dichiarato una “VarIABILE” e la richiamiamo come “variabilE”, non sarà riconosciuta, stessa cosa per le funzioni e per tutto il resto: rispettate maiuscole e minuscole!

Andiamo a controllare la cartella dove avevamo messo main.c, vedete, ci sono un sacco di altri file, a noi ne interessa in particolar modo uno soltanto: lampeggia_led.hex, quello con estensione HEX! E’ questo qui che dobbiamo caricare nel PICMicro? Come?

Parte 3 -- Carichiamo il programma compilato nel PICMicro

Colleghiamo il PICKit2 alla porta USB del computer e innestiamolo nel connettore ICSP del circuito (ricordatevi di non tenere alimentato il circuito, anzi staccategli proprio i fili dell’alimentazione quando lo programmate).

Per sapere come va collegato il PICKit2 ai vari tipi di PICmicro (e anche alle memorie EEPROM), può esservi utile questo nostro articolo: Adattatore multizoccolo per PICKit2.

Avviamo il programma PICKit2:

primo_programma_picmicro_in_c_flashare_con_pikcit2_01

Dovrà essere presente il messaggio “PICkit2 found and connected” come in figura, dal menù “Device”, dove c’è scritto “-Select Part-”, scorriamo fino a scegliere PIC16F877A

Se PIC16F877A non è presente nell’elenco potrebbe essere necessario andare sul menù “Device Family” e selezionare Midrange -> Standard

primo_programma_picmicro_in_c_flashare_con_pikcit2_02

Come vedete cambia il valore di Configuration (che è il valore che sarà assegnato al registro di configurazione, non curiamoci di quello che esce scritto in questa fase, dal momento che il programma non l’abbiamo ancora caricato) e il valore di Checksum. Il Checksum in particolare è un valore che in un certo qualmodo identifica il programma che abbiamo caricato, quando faremo piccole o grandi modifiche a uno stesso programma, vedremo che tale valore cambierà, quindi due programmi perfettamente uguali avranno lo stesso checksum, è un buon modo per identificare un programma.

Andiamo avanti, dobbiamo caricare il nostro programma in memoria. Dal menù File selezioniamo “Import Hex”:

primo_programma_picmicro_in_c_flashare_con_pikcit2_03

Cerchiamo la cartella con il programma e selezioniamo “lampeggia_led.hex”:

primo_programma_picmicro_in_c_flashare_con_pikcit2_04

Premiamo quindi Apri, il file Hex sarà importato in memoria e cambieranno tutti i valori nell’interfaccia:

primo_programma_picmicro_in_c_flashare_con_pikcit2_05

In particolare cambierà il valore di configuration, che per i nostri esperimenti sarà sempre 2F02. Vedete che il campo program memory adesso contiene tanti valori, quei valori rappresentano appunto il nostro programma compilato: ovvero convertito da codice sorgente (comprensibile per gli esseri umani) a linguaggio macchina (comprensibile per il dispositivo).

Avete collegato il PICKit2 al circuito vero? Bene… Premiamo il tasto Write:

primo_programma_picmicro_in_c_flashare_con_pikcit2_06

Attendiamo un po’ che il nostro fido programmatore carichi nella memoria del PIC tutto il necessario: configurazione e programma. Dopodichè, se tutto va per il meglio, deve apparire la magica scritta:

primo_programma_picmicro_in_c_flashare_con_pikcit2_07

Ovviamente c’è un certo senso di soddisfazione: adesso il programma che abbiamo scritto è memorizzato all’interno del microcontrollore! Non ci resta che staccare il programmatore, e dare alimentazione al circuito. Il risultato che otterremo è questo:

Certo non è molto entusiasmante, ma avete visto quanto mi ci è voluto per insegnarvelo? Spero che abbiate capito tutto. Lasciate commenti e se potete, supportate il sito e chi ci lavora per passione. Nella prossima puntata cercheremo di fare di meglio ;)

Downloads

File di supporto alla terza lezione del corso di programmazione picmicro in C (589)

» Vai all’ indice delle lezioni e delle risorse del corso di programmazione

Articoli che potrebbero interessarti:

L'articolo ti è piaciuto o ti è stato utile per risolvere un problema? Supporta e mantieni in vita questo sito, ci basta soltanto un caffè o una birra

  1. #1 da Cristianoscr il 18 luglio 2010

    I LED sono giusti, ho provato a integrare il while e non andava ancora. Ho specificato MCLRDIS(ho provato così inizialmente perchè non avevo resistenze a portata di mano) ed è partito!!! Grazie Giovanni!!

    #include PIC.H (lo scritto cosi senno non visualizza sul sito)
    __CONFIG(UNPROTECT & BORDIS & MCLRDIS & PWRTEN & WDTDIS);

    void main(void)
    { while(1)
    {
    TRISIO = 0;
    GPIO1 = 1;
    GPIO2 = 1;
    }
    }

  2. #2 da Cristianoscr il 18 luglio 2010

    Bene, sono pure riuscito a pilotare il led tramite bottone e ha funzionato tutto a meraviglia! Mi manca solo il lampeggio, che sul mio 12F675 non riesco a fare. Come routine di delay utilizzo le tue, ora proverò con delle altre, intanto ecco il codice che ho scritto:

    #define XTAL_FREQ 4MHZ

    #include
    #include “delay.c”

    #define LED1 GPIO1 // led 1
    #define LED2 GPIO4 // led 2

    __CONFIG(UNPROTECT & BOREN & MCLRDIS & PWRTEN & WDTDIS & INTIO);

    void main(void)
    {
    TRISIO = 0; //attivo GP3 come input il resto output
    GPIO2 = 0;
    GPIO5 = 0;
    GPIO0 = 0;

    while(1) //inizio ciclo infinito
    {
    LED1=LED1^1; //inverto lo stato di led1
    DelayMs (150) ; // ritardo

    }
    }

    • #3 da Cristianoscr il 18 luglio 2010

      PS NEL TRISIO lascia perdere il commento era del programma per il pulsante

  3. #4 da Cristianoscr il 19 luglio 2010

    Ho risolto. Non so come mai, mai non andava con LED1=LED1^1. Neanche in altri codici che ho provato a scrivere… sai il perchè? è strano…
    Per cui ho dovuto scrivere.
    LED1=1;
    DelayMS(250);
    LED=0;

    Così andava! Anche se l’intervallo tra le accensioni è diverso da 250 Ms. Credo sia sui 350-400 Ms

    • #5 da Giovanni Bernardo il 19 luglio 2010

      Questo è strano… comunque l’architettura del pic12 è diversa dai pic16.. Io non ho mai provato a far lampeggiare un led coi pic12, uso sempre gli interrupt per fare queste cose.
      Riguardo al ritardo hai ragione: quelle routine di delay sono molto imprecise e provocano un ritardo di circa il doppio di quello che si va a scrivere, ne stavamo discutendo ultimamente in un forum perchè io e altri utenti ad occhio c’ervamo accorti di questa cosa, ho fatto delle prove con l’oscilloscopio ed effettivamente i ritardi sono proprio sballati, questo perchè pare che quelle routine siano state ottimizzate per quarzi da 2 MHz (almeno cosi mi è stato detto). Comunque sia per generare ritardi precisi o usi le routine enanched precision che si trovano su http://www.microchipc.com oppure ti affidi agli interrupt

  4. #6 da Cristianoscr il 19 luglio 2010

    Certo mi affiderò agli interrupt, e provero anche le routine di quel sito…
    Mi potresti chiarire un dubbio?
    I registri tristato devono essere sempre diciamo… dichiarati? Se non li dichiaro che succede?
    Nel 12F675 ho provato a mettere 2 interruttori. Uno che se premuto diminuiva la frequenza di accensione dei led e uno che l’aumentava. Altrimenti vi è una frequenza intermedia. LE PORTE del 12F675 sono VDD, GPIO5,4,3,2,1,0,e VSS.
    Funzionava tutto tranne il pulsante 2 che ho collegato al GPIO0. L’altro collegato al GPIO3 andava. Sul datasheet è possibile che abbia letto che solo il GPIO3 fosse una porta di input? O magari è impostata di default a input?
    Il tristato l’ho definito così, (non vorrei mai avere invertito i bit)
    TRISIO = 0b00001001
    Ma così non mi va il pulsante su GPIO0. Mentre anche se setto TRISIO = 0 mi il pulsante collegato alla GPIO3 rimane funzionale.
    A questo punto mi sono chiesto se stavo impostando i tristati, o qualcosa che non esiste :)

    • #7 da Giovanni Bernardo il 19 luglio 2010

      I registri tristato li devi sempre impostare altrimenti all’accensione si trovano in uno stato indefinito. Sul datasheet del 12F675 è comunque specificato che all’accensione, se non lo specifichi, si trovano tutti impostati come Ingressi (pagina 22: “value at POR”).

      Per usare i pulsanti ti conviene inoltre utilizzare le resistenze di pullup interne sfruttando il registro WPU (che a differenza con altri pic, questo ti permette di impostare le resistenze di pullup singolarmente), ovviamente ciò funziona solo se hai messo a zero il bit GPPU nel registro OPTION. Se abiliti solo le pullup, in automatico saranno presenti su tutti i pin configurati come ingressi, senza scomodare il registro WPU.

      Il GPIO0 è condiviso con il comparatore, per cui per utilizzarlo devi disattivare il modulo comparatore che di default è acceso. Il comparatore lo disattivi con:

      CMCON=7

      GPIO3 è condiviso con MCLR, per cui se nella word di configurazione metti MCLRDIS allora diventa normale Input, GPIO3 non può funzionare come output ma solo come input, quindi hai letto bene. Io nel tristato metterei comunque l’impostazione ad input ugualmente, quindi come hai fatto va bene.

      Comunque sia io non consiglio mai di utilizzare MCLR come I/O perchè la funzione di MCLR è importante, se proprio non se ne può fare a meno ok, ma se ci sono altri pin liberi… perchè utilizzare proprio MCLR?

      Se i pulsanti continuano a non funzionarti vuol dire che qualcosa non va o nel circuito o nel programma.

      Penso proprio che dovrò mettere delle note di utilizzo del 12F675 dato che vedo che in molti lo state utilizzando.

  5. #8 da Cristianoscr il 19 luglio 2010

    Domani farò le modifiche che mi hai consigliato e le prove del caso, ti dirò i risultati. Intanto ti ringrazio per la cortesia e per il tempo che mi dedichi.
    Complimenti per tutto.

  6. #9 da Cristianoscr il 20 luglio 2010

    __CONFIG(UNPROTECT & BORDIS & MCLRDIS & PWRTEN & WDTDIS & INTIO);

    void main(void)
    {
    CMCON = 7;
    GPPU = 0;
    WPU = 0b00000001;
    TRISIO =0b00001001; //attivo GPIO0 e GPIO3 come input il resto output
    GPIO2 = 0;
    GPIO5 = 0;

    Ecco le mie impostazioni, spero di averle settate correttamente.

  7. #10 da Gela il 21 luglio 2010

    La memoria programma occupata effettivamente nel pic è quella fisica che ha il fele .hex nel pc? Oppure basta solo fare riferimento ai risultati dell’Hi Tech C?
    Lo chiedo perchè ho scritto un programma simile alla lezione 11 dove uso un LM35 per fare un termostato, il cui peso è cira15k su disco, però in fase di compilazione l’Hi Tech C mi dava circa il59% del program space occupato.
    Posso continuare e scrivere il pic o è meglio migliorare il codice? se può servire, il pic che uso è un 16f690, secondo la microchip ha 7k di program space.

    Un grazie 1000 per i chiarimenti :)

    • #11 da Giovanni Bernardo il 21 luglio 2010

      Il peso del file è una cosa e il peso del programma ne è un’altra. Devi fare affidamento a quello che ti dice l’hitec-c. Tieni inoltre conto che quel 59% è calcolato non sullo spazio totale disponibile ma sul limite di 2000H words se stai usando la versione lite.

  8. #12 da Cristianoscr il 21 luglio 2010

    Ho un problema…mi sa che dovrò cambiare pic ma prima chiedo a te… il mio 12F675 programmato con pickit non si fa più riscrivere. Ho provato a fare erase, a riscriverlo ecc… ma mi da sempre checksum verify falied. Quando faccio verify mi da error in program memory. Mentre se faccio read. legge correttamente, tra l’altro il programma che fa lampeggiare i led in sequenza lo svolge, ma non riesco a cancellarlo. Nel CONFIG è sempre stato tutto unprotected. E’ possibile che dopo 80 – 90 riscritture si verifichi questo? Magari pacioccando ho sbagliato a spostare nel codice il config reg e si è protetto da scrittura inavvertitamente. Però non so…

  9. #14 da Cristianoscr il 21 luglio 2010

    Ahahaahaah va che son babbo ahahahah e pensare che l’avevo pure letto quell’articolo!!! Acciderbolina!! Grazie Giovanni!!

  10. #15 da pistacchio il 26 luglio 2010

    Un grazie a Gianni per la chiarezza e l’impegno.
    Il tuo corso mi ha stuzzicato e, siccome uso quasi sempre Linux, mi sono creato l’ambiente di “studio” sotto questo OS. Ho scelto Linux Mint (Gloria), ho acquistato Freedom II (ottima!!) e dopo aver navigato “qualche ora” in internet ho usato SDCC come compilatore. Ed ora sto “portando” le tue lezioni sotto linux e SDCC. Con un po’ di fatica ovviamente (vista la mia tarda eta’ e l’inesperienza) ma anche con gratificazione. Ho “tradotto” la lezione sul lampeggio led e quella sugli interrupts.
    E spero di continuare.
    Ciao e complimenti per l’impegno che metti nel sito.

    • #16 da Giovanni Bernardo il 26 luglio 2010

      Questa è una cosa ottima, se ti va di mandarmi le tue “traduzioni”, con qualche tuo appunto personale (tipo come si installa l’ambiente di sviluppo ecc ecc), faresti cosa gradita a un bel po di persone. Se vuoi puoi contattarmi via email: gianni [at] settorezero.com

  11. #17 da 10catgb il 27 luglio 2010

    Si può fare anche con il pic12f675?

    • #18 da Giovanni Bernardo il 27 luglio 2010

      Far lampeggiare un led? Penso proprio di si… Però questa routine di delay mi pare che coi 12F non va

  12. #19 da 10catgb il 27 luglio 2010

    a ok

  13. #20 da Cristianoscr il 29 luglio 2010

    Se ti interessa funziona come routine di delay anche sui 12F675, anche se non è proprio precisa sulle temporizzazioni.

    @ Giovanni, se ti interessa ho trovato un sito che offre un programma freeware per avere un oscilloscopio sul PC sfruttando l’entrata mini jack del microfono. Prova a darci un occhiata se ti va. Per le prove che ho fatto io funziona egregiamente. Ha una sensibilità nel mio di 1V alla -6. Non so se sia il rumore generato dai circuiti del pc oppure siano onde radio captate dal filo che vi ho collegato. In ogni caso questo è il link

    http://www.zeitnitz.de/Christian/scope_de

    • #21 da Giovanni Bernardo il 29 luglio 2010

      Io quelle routine di delay non le ho provate sui 12F, però qui alcuni utenti le hanno provate e hanno detto che non funzionano… Quindi probabilmente hanno sbagliato a fare qualcosa.. Che sono imprecise lo so, inducono un ritardo di circa il doppio di ciò che si va a scrivere, ho fatto le prove con l’oscilloscopio e appena ho tempo ci scrivo su due righe. Comunque di questo ne abbiamo discusso in altri commenti.

      Per l’oscilloscopio ti ringrazio, può essere utile a chi non ne ha uno. Questo sistema comunque è stato sviluppato tanti anni fa e ne esistono diverse varianti, c’è anche chi lo realizza su USB. L’unico problema è che la scheda audio non può campionare oltre i 20 / 40 KHz.. quindi come oscilloscopio è piuttosto limitato

  14. #22 da 10catgb il 2 agosto 2010

    Un pin di un pic che corrente da: Il + o il -?

    • #23 da Giovanni Bernardo il 2 agosto 2010

      Il pin di un pic può fornire tensione positiva (Vdd = tensione di alimentazione) o massa (GND = 0volt, riferimento) a seconda di come viene programmato.

  15. #25 da 10catgb il 2 agosto 2010

    nON RIESCO A CAPIRE DA DOVE SI ALIMENTA PERCHè C’è DUE “VSS” E DUE “VDD”

  16. #26 da 10catgb il 2 agosto 2010

    A me non mi si spegne il led, resta sempre acceso senza lampeggiare.

    • #27 da Luca il 4 agosto 2010

      I problemi sono sempre 3, la programmazione, il firmware e la piedinatura.
      Hai programmato correttamente il pic? (con la giusta config word) Il sorgente è corretto? (non parlo di errori di compilazione ma quanto errori funzionali, ad es: LED=LED^1; o ciclo while errato) Hai collegato correttamente il pic? (Il quarzo?il led è sul pin esatto?)

      Ci saranno due Vdd e due Vss per problemi di assorbimento, CREDO, in ogni caso devi collegarli entrambi.

      • #28 da 10catgb il 5 agosto 2010

        Perchè ci vuole un quarzo?
        Forse è questo perchè!!! Ma dove va collegato? :?

        • #29 da Luca il 5 agosto 2010

          Serve, nel circuito di clock, per generare un segnale preciso (in genere un quarzo ha un errore di 1ppm (di oscillazioni ovviamente), a differenza di reti rc che hanno errori spaventosi), nb.tutti i pic di recente produzione hanno un oscillatore integrato e quindi potresti non averne la necessità, anche se talvolta avere un quarzo interno complica le cose quando devi\vuoi calibrarlo. Ti consiglio vivamente di leggere le precedenti due lezioni se stai utilizzando l’877a, altrimenti potremmo stare qui a parlare per ore senza concludere nulla.

          • #30 da Giovanni Bernardo il 6 agosto 2010

            E’ inutile chiedere perchè ci sono due vdd e due vss, è inutile chiedere dove si collega il quarzo. Sono tutte cose che ho scritto chiaramente, se si è così pigri da non voler nemmeno leggere e da non voler addirittura seguire nemmeno le figure, vuol dire che non si è interessati a nulla oppure che si sta abusando della pazienza delle persone serie. Quindi un’altra domanda del genere e cancello.

  17. #31 da doghi81 il 14 agosto 2010

    Salve a tutti,
    prima di tutto complimenti per il sito e per la chiarezza delle lezioni.
    Ho un dubbio sulla possibilità di poter connettere direttamente il programmatore (pickit 2 o 3) con la tensione di alimentazione esterna del circuito attiva, perchè sui commenti della lezione 2 dici che non ci sono problemi, mentre, all’inizio della parte 3 di questa lezione dici di dover disalimentare il circuito dall’alimentazione esterna.

    • #32 da Giovanni Bernardo il 14 agosto 2010

      Puoi lasciare il circuito alimentato: il pickit se ne accorge e non fornisce lui l’alimentazione. basta che hai isolato opportunamente mclr col diodo o con una resistenza

  18. #33 da mrbitume il 20 agosto 2010

    ciao a tutti sto iniziando a muovere i primi passi con la programmazione, seguendo il tutorial ho un problema quando devo compilare il programma. sto usando la scheda freedom II e sono fermo a BUILD FAILED. Vi posto il problema:

    Executing: “C:\Programmi\HI-TECH Software\PICC-18\PRO\9.63\bin\picc18.exe” –pass1 “C:\Documents and Settings\Dealer\Desktop\programmazione PIC\main.c” -q –chip=18F4550 -P –runtime=default –opt=default -D__DEBUG=1 -g –asmlist “–errformat=Error [%n] %f; %l.%c %s” “–msgformat=Advisory[%n] %s” “–warnformat=Warning [%n] %f; %l.%c %s”
    Warning [171] C:\Documents and Settings\Dealer\Desktop\programmazione PIC\main.c; 18.90 wrong number of preprocessor macro arguments for “__CONFIG” (1 instead of 2)
    Warning [171] __CONFIG; 18.0 wrong number of preprocessor macro arguments for “___mkstr” (0 instead of 1)
    Warning [171] ___mkstr; 18.0 wrong number of preprocessor macro arguments for “___mkstr1″ (0 instead of 1)
    Error [311] C:\Documents and Settings\Dealer\Desktop\programmazione PIC\main.c; 18.112 closing quote expected
    Error [194] C:\Documents and Settings\Dealer\Desktop\programmazione PIC\delay.h; 50.1 “)” expected
    Error [312] C:\Documents and Settings\Dealer\Desktop\programmazione PIC\delay.h; 50.1 “;” expected

    • #34 da Giovanni Bernardo il 20 agosto 2010

      Se dicevi “sto usando un pic18F4550″ facevi prima.
      I pic18 hanno parecchi registri di configurazione, a dispetto dei 16 che ne hanno uno o due. Per cui non puoi usare la macro __config per configurarli. La soluzione? Io ti direi di utilizzare il C18.

      • #35 da mrbitume il 20 agosto 2010

        ok quindi cambio in pratica il modo di compilare? in questo modo posso lo stesso seguire il tuo tutorial? scusa l’ignoranza

  19. #37 da Fidus il 2 settembre 2010

    Ciao Giovanni, sto utilizzando un PIC16F876A, con un quarzo da 20 Mhz. Ho provato in tutti i modi a programmarlo ma non riesco neppure a fare lampeggiare un led! Potresti dirmi dove sbaglio? Premetto che il quarzo da 20 Mhz non l’ho collegato al condensatore. Ma non credo che sia il motivo principale del non funzionamento.. Ti allego il codice.

    //MAIN

    #define XTAL_FREQ 20MHZ
    #include
    #include

    __CONFIG (HS & WDTDIS & PWRTEN & BORDIS & UNPROTECT);

    TRISA=0b00000000;
    TRISB=0b00000000;
    TRISC=0b00000000;
    INTCON=0b00000000;

    void main(void)
    {
    while(1)
    {
    RB5=1;
    DelayMs(250);
    RB5=0;
    }
    }

    • #38 da Giovanni Bernardo il 2 settembre 2010

      Cosa vuoi dire con il fatto che non hai collegato il condensatore al quarzo? Il quarzo per poter oscillare ha bisogno dei due condensatori, altrimenti non oscilla e il pic non funziona.
      Poi ti manca l’include per il delay nel codice e devi mettere un altro ritardo dopo RB5=0; altrimenti cosi come sta ti passa subito da RB5=0 a RB5=1 e il led lo vedi sempre acceso

(non verrà pubblicata)
  1. Ancora nessun trackback

Fusion theme by digitalnature | Articoli (RSS) e Commenti (RSS) ^