giovedì 27 giugno 2013

Introduzione ad ASF (quarta parte)

Indice degli articoli su ASF

Atmel Studio tramite il menù ASF ci offre lo strumento ASF Explorer che permette di visualizzare i moduli ASF presenti nel nostro progetto ed un collegamento diretto alla documentazione ed ai file header di ogni modulo aggiunto. Vengono mostrati anche i moduli dipendenti dal principale come sotto-voci.

asfexplorer

E’ proprio la documentazione a mettere spesso in difficoltà i programmatori che si avvicinano ad ASF per la prima volta, il codice sorgente rappresenta infatti un complemento indispensabile per il programmatore per compensare descrizioni certe volte minimali e non sufficienti per l’utilizzo corretto.

Facciamo adesso un esempio concreto di utilizzo della guida e del codice sorgente, apriamo la documentazione del modulo Delay routines facendo doppio click su API Documentation, si aprirà la pagina http://asf.atmel.com/docs/3.5.1/xmegaau/html/group__group__common__services__delay.html che riportiamo sotto per comodità:

doc

Notiamo che è presente una descrizione del modulo ed il contenuto che in questo caso è una serie di funzioni con il prototipo ed una breve descrizione. Cliccando sulle varie funzioni è possibile leggere qualche informazione aggiuntiva, ad esempio la pagina della funzione delay_init è sotto riportata:

delayinit

Che ci informa per esempio che il metodo è deprecato come abbiamo visto nell’articolo precedente. In questo caso, vista anche la semplicità e intuitività delle funzioni non sono presenti esempi di utilizzo. Per altri moduli è presente oltre alla voce API Documentation una utile voce Quick Start Guide con esempi ben descritti da cui consiglio sempre di partire.

Nell’immagine precedente possiamo notare nell’intestazione a piè pagina che la pagina è stata generata automaticamente tramite doxygen, in poche parole la documentazione è all’interno del codice sorgente, proprio tra il codice, ed un programma automatizzato ha provveduto a generare le pagine web di documentazione.

Così come sono state presentate le funzioni potremmo credere di poter utilizzare all’interno del nostro codice l’istruzione

delay_us(1)

Senza particolari problemi, compilando e caricando il programma utilizzante questa istruzione scopriamo amaramente che a tale istruzione il programma si blocca.


E’ normale in questa situazione avere dell’amaro in bocca, anche perché si potrebbe scoprire che non funziona con un clock di default di 2 MHz ma funziona con un clock impostato a 32 MHz. Si rimane addirittura confusi.


In situazioni del genere se una breve ricerca su internet non porta a risultati interessanti che ci possono aiutare e la documentazione non accenna a problemi del genere, non specifica infatti in questo caso dei limiti temporali a cui restringere l’utilizzo della funzione, la strada da seguire è lo studio del codice sorgente della funzione.


Lo studio del sorgente richiederà uno sforzo in più, non sempre è facile leggere codice scritto da altri, non preoccupatevi quindi se non capite completamente quanto descritto di seguito, anche se è segno che avete una confidenza col linguaggio C che probabilmente necessita di una risploverata.


Apriamo dunque il file delay.h (sotto riportato senza commenti per brevità):


delayh


Notiamo che in realtà esistono dietro le quinte due funzioni: cpu_delay_ms ed cpu_delay_us, la funzione in secondi non fa altro che convertire in millisecondi il ritardo e chiamare la relativa funzione. La definizione F_CPU grazie ad una funzione di un altro modulo da cui questo dipende, come visto nello scorso articolo, recupera la frequenza della CPU.


A questo punto per seguire la funzione facciamo click destro sul nome della funzione e scegliamo Goto implementation, possiamo in alternativa premere la combinazione Alt+G


goto


Verrà aperto il file cycle_counter.h che riportiamo:


cycle_counter


Notiamo dalle ultime due righe che in realtà le due funzioni sono dei #define che chiamano una ulteriore definizione chiamata delay_cycles che intuitivamente ritarderà di un certo numero di cicli l’esecuzione del programma. Il numero di cicli dipenderà dalla frequenza del microcontrollore. Il parametro passato a delay_cycles è quindi la conversione da us (nel caso in esame da cui siamo partiti, la funzione delay_us) a cicli da ritardare. Notiamo che questa conversione avviene tramite la definizione cpu_us_2_cy .


L’utilizzo di definizioni (#define) al posto di normali funzioni avviene per motivi prestazionali ed è pratica usuale nella libreria ASF, a complicare la lettura spesso il fatto che alcune definizioni dipendono da altre definizioni. In questo caso alcune definizioni dipendono dalla presenza della definizione __DELAY_CYCLE_INTRINSICS__ che non è definita. Il codice da considerare è quindi solamente quello dentro ai rettangoli rossi.


Analizziamo ora cpu_us_2_cy definita sopra nel rettangolo rosso nel mezzo, e poniamo attenzione al fatto che coi nostri parametri vengono eseguiti alcuni calcoli che possiamo credere corretti anche senza approfondire la loro natura.


Analiticamente: numero_cicli = ((microsecondi * frequenza_cpu) + 999.999) / 6.000.000


Il risultato verrà passato alla funzione delay_cycles che altro non è che la funzione __portable_avr_delay_cycles nel rettangolo rosso sopra. La funzione accetta in ingresso un numero N senza segno intero che viene decrementato prima dell’utilizzo.


A questo punto si iniziano ad intravedere le situazioni limite, il parametro N non dovrà mai essere minore di 1, se fosse zero il decremento lo porterebbe a 4294967295 visto che il numero non può essere negativo, è infatti unsigned long, e quindi il ritardo diventerebbe molto grosso, il massimo permesso per la precisione ed il programma sembrerebbe a prima vista bloccato.


Qual’è la condizione per cui il numero dei cicli è sempre maggiore di zero? Per rispondere a questa domanda risolviamo rispetto l’espressione analitica che abbiamo visto sopra imponendola >= 1 supponendo che nell’incertezza di come venga arrotondata la divisione sia sempre meglio raggiungere almeno il primo numero intero, troviamo:


((microsecondi * frequenza_cpu) + 999.999) / 6.000.000 >= 1


microsecondi * frequenza_cpu >= 6.000.000 - 999.999


Il prodotto del ritardo in microsecondi per la frequenza in Hertz del microcontrollore deve quindi essere maggiore o uguale a 5.000.0001


Con una frequenza di 2 MHz ed un ritardo di 1 us avremmo: 1 * 2.000.000 < 5.000.0001 che non soddisfa la relazione trovata. La soluzione può essere quindi aumentare il ritardo o la frequenza del microcontrollore, con 32 MHz il ritardo funzionerà correttamente.


Abbiamo quindi trovato un’importante relazione che limita il ritardo minimo che possiamo apportare in base alla frequenza, purtroppo di questa relazione non c’è traccia nella documentazione ed il codice della funzione ASF non gestisce il caso in cui il ritardo sia così piccolo da generare un problema, il codice di ASF però ci permette di capire in profondità il funzionamento della libreria e risolvere eventuali problemi come abbiamo visto.


Concludo l’articolo riportando l’analoga relazione per la funzione delay_ms() deducibile nello stesso modo che abbiamo visto per la funzione delay_us():


millisecondi * frequenza_cpu >= 5001


E ricordando che le funzioni di ritardo ammetteranno sicuramente un limite superiore legato al tipo di dati utilizzato.

Nessun commento:

Posta un commento