Programmare in mikroBasic
 
  Box
Passare tipi di dato errati
nei parametri delle procedure

Per le informazioni riferite a questo argomento e per compilare i programmi è stata usata la vers. 7.0.0.2 di mikroBasic.
Altre versioni del compilatore potrebbero avere comportamenti diversi o non funzionare.

Certe volte, scrivendo un programma in mikroBasic, si possono commettere degli errori che, però, il compilatore non segnala. La compilazione verrà tranquillamente portata a termine e verrà creato il file .hex finale.
Quando però, dopo aver inserito il firmware nel micro, proviamo il nostro circuito, questo non funziona come ci aspettavamo.

Con questo esempio, si vuol far vedere come si può usare il simulatore-debugger, per indagare e capire cosa succede quando si commette qualche errore di programmazione.

Creiamo un nuovo progetto e inseriamo il listato del riquadro sottostante.
Per creare il progetto, potremmo scegliere qualsiasi PIC e clock, dal momento che, essendo solo una simulazione di istruzioni software, non andremo a coinvolgere nessun registro dei PIC (portX, adcon, ecc.).
In questo esempio, però, per fare in modo che possiate riprodurre la prova, come l'ho eseguita io, è consigliabile scegliere un 16F877A e clock 8MHz. Altra cosa da dire, è quella di non modificare nulla del listato, altrimenti i risultati saranno diversi da quelli spiegati.

program prova_param

dim gatto as byte
dim txt1 as byte
dim alfa as byte
dim txt2 as string[3]
dim cane as byte

Main:
    txt1=45
    alfa=21
    txt2="CIP"
    cane=35

    bytetostr(cane,txt1) 'param. di tipo sbagliato

    gatto=1
    txt2="pic"
    bytetostr(cane,txt2) 'invece con parametro giusto
    alfa=5
    wordtostr(alfa,txt2) 'param. di tipo sbagliato
    goto main

End.
 
(1) Clickiamo sull'icona "Buid Project".

(2) Il compilatore si fermerà e apparirà un messaggio di errore con evidenziato il punto dell'errore.
La procedura ByteToStr richiede che il primo parametro sia di tipo byte e il secondo di tipo string.
La variabile txt1 è di tipo byte e non di tipo string, perciò, il compilatore, si rifiuta di eseguire la compilazione.

(3) Mettiamo un apostrofo, all'inizio della riga, per trasformarla in commento ed escluderla dalla prossima compilazione.
(4) Ricompiliamo di nuovo il nostro programma e speriamo che, stavolta, vada tutto bene.

Attendiamo un'attimo e, finalmente, la compilazione va a buon fine senza nessuna segnalazione di errore.

Adesso proviamo il programma col simulatore-debugger e controlliamo se tutto funziona come dovrebbe.

(5) Clickiamo sull'icona "Start Debugger",

(6) verrà visualizzata la finestra "Watch"

(7) ed il simulatore evidenzierà la riga di programma, pronta per essere eseguita.

(8) Nella finestra "Watch", clickiamo su "Select variable from list" per vedere tutti i registri e variabili coinvolte.

   

(9) Qui vediamo le variabili che terremo sotto controllo in questo esempio

(10) ed i loro indirizzi di allocazione nella memoria RAM (in formato esadecimale).

(11) Cliccare sulla variab. txt1

(12) e poi su "Add" per aggiungerla alla lista delle variabili da tenere sotto controllo.

(13) Facciamo la stessa cosa con le altre 4 variabili.
(14) Clickiamo sul +, per vedere tutti i bytes della variabile e poi su txt2.

(15) Clickiamo su "..."

(16) e si aprirà la finestra "Edit Value".
I valori sono visualizzati in formato decimale, ma txt2 è di tipo string, quindi , vogliamo vederli in formato carattere (char).
Nella lista delle variabili ottenuta, vediamo, nella prima colonna, il nome della variabile, nella seconda, il valore contenuto nella variabile e, nella terza, l'indirizzo della stessa.

Ogni cella di memoria RAM contiene un byte ed è individuata tramite un'indirizzo. Le nostre variabili sono memorizzate, nella memoria RAM, a partire dall'indirizzo esadecimale 0x0020 fino al 0x0027.

txt1, alfa, cane, gatto
sono di tipo byte ed occupano un byte.

txt2
è di tipo string[3], cioè una stringa di 3 caratteri ed occupa 1 byte per ogni carattere + il carattere di fine stringa (NUL). In totale occupa, quindi 4 bytes (dall'indirizzo 0x0022 fino al 0x0025).

Prima di proseguire, è consigliabile aprire la finestra "ASCII Chart" e tenerla pronta per vedere i codici dei caratteri quando serve. Per fare questo

(17) clickiamo su "Tools -> Ascii Chart", apparirà la finestra "Ascii Chart" (18),

(19) la riduciamo a icona e la teniamo pronta (20).



 
(21) I valori delle variabili sono tutti messi a 0 ed i caratteri della stringa
(22) sono tutti caratteri (") NUL cioè di valore 0 (vedere tabella ASCII).

Siamo pronti per iniziare il debugging ed allora

(23) clickiamo su "Step Over" oppure premiamo il tasto F8 sulla tastiera per eseguire la prima istruzione. Step Over perchè, così, evitiamo di eseguire, una ad una, le istruzioni dentro le procedure, perdendo un sacco di tempo. Saranno eseguite con un solo click.
(24) Eseguiamo, una ad una, le righe di programma, e fermiamoci in questo punto.

Notiamo che i valori, modificati dall'ultima operazione (txt2), sono segnalati col colore rosso.

Continuiamo ed eseguiamo anche l'istruzione di questa riga. Ricordatevi di usare solo Step Over o tasto F8, altrimenti ci introduciamo dentro alla procedura.
(25) Notiamo che la variabile "cane", con valore 35, è stata convertita in una stringa, nella variabile txt2, composta dai caratteri "spazio", "3", "5" e NUL per fine stringa.

(26) Se proviamo a vedere i valori della txt2, in formato decimale, vedremo:
32 - 51 - 53 - 0.


Codice del carattere
Spazio


Codice del carattere
"3"


Codice del carattere
"5"


Codice del carattere
NUL
Proseguiamo con un'altra riga e vediamo il valore della variabile alfa, modificato e pronto per essere convertito in stringa nella variabile txt2, dalla prossima istruzione.

(27) Eseguiamo l'istruzione e vediamo il risultato nella variabile txt2.

Ma... che cosa è successo?
Perchè, nella variabile txt2, non mi trovo il carattere "5" che è il valore di alfa?
Mi ritrovo solo 4 caratteri spazio...

E poi, è stato modificato anche

(28) il valore di cane

(29) e anche quello di gatto.

Il valore di cane è il codice del carattere "5", come mai?
E quello di gatto è il codice di NUL, perchè mai?
Per trovare la spiegazione, dobbiamo

(31) clickare sul TAB "QHelp" e, nella casella "Find:" scrivere le prime lettere della parola da cercare (in questo caso "bytetostr").

(32) La risposta è immediata e ci ritroviamo la parola cercata. Facciamo un doppio click sulla parola e si aprirà l'aiuto in linea, alla posizione di ByteToStr.
   

Perchè il compilatore ha eseguito la compilazione senza segnalare nessun errore?
Il compilatore, quando ha analizzato l'istruzione WordToStr(alfa, txt2), ha controllato che i parametri passati fossero di tipo compatibile con quelli dichiarati nel prototipo della procedura.

(33) Nel prototipo della procedura, il primo parametro è di tipo word, mentre alfa è di tipo byte. Il tipo word occupa, in memoria, 2 bytes, mentre il tipo byte occupa 1 solo byte. Il compilatore ha, perciò, fatto una conversione implicita automatica (da byte a word), visto che, il valore di 1 byte, ci sta benissimo in un tipo word, e non ha segnalato errori.

(34) Il secondo parametro, nel prototipo della procedura, è di tipo string e, quindi, txt2 va bene perchè è anch'esso di tipo string. Nessuna segnalazione di errore, anche se l'errore si trova proprio qua. Perchè?
La risposta sta nella parola chiave byref presente nella dichiarazione del secondo parametro.

(35) Nell'istruzione ByteToStr(cane, txt2) è andato tutto bene perchè la variabile txt2 aveva una dimensione [3] uguale a quella della dichiarazione nel prototipo della procedura.

(36) Nell'istruzione WordToStr(alfa, txt2), invece, la variabile txt2 ha sempre la dimensione [3] che non è uguale a quella della dichiarazione nel prototipo della procedura [5]. Ma il compilatore non se ne accorge?

Il motivo, perchè non se ne accorge, sta nella parola chiave byref.
Con la parola chiave byref, si dice al compilatore che, quella variabile, non la passiamo col suo valore, ma diamo un riferimento all'indirizzo di memoria dove questa inizia. Quello che verrà modificato sarà, quindi, il valore che si trova a quell'indirizzo.
Il compilatore controlla solo che, a quell'indirizzo, corrisponda il tipo della variabile specificato, ma non controlla quanti bytes andranno scritti.

Spieghiamo, allora, quello che è successo al punto (30).
Abbiamo passato l'indirizzo di una stringa di 3 caratteri ma sono stati scritti, invece, 5 caratteri.
Se controllate con la tabella codici ASCII, vedrete che è stata scritta la stringa "    5" (4 spazi, il carattere "5" ed il NUL), andando ad "inquinare" le caselle di memoria successive.

Provate a giocherellare col debugger, cambiando anche di posto le variabili nel programma, e scoprirete molte cose interessanti e, soprattutto, imparerete dagli errori... che non farete più.

Bibliografia:
Manuale mikroBasic

Ultima modifica  

 
Privacy Policy Cookie Policy