Visualizzazione post con etichetta ASF. Mostra tutti i post
Visualizzazione post con etichetta ASF. Mostra tutti i post

sabato 15 febbraio 2014

Introduzione ad ASF (ottava parte) – SPI

Al livello più basso nei microcontrollori XMega per utilizzare il supporto hardware ad SPI abbiamo a disposizione per ogni porta SPI i seguenti registri: Control (CTRL), Interrupt Control (INTCTRL), STATUS e DATA, tutti adeguatamente documentati nel datasheet del microcontrollore.

Possiamo accedere ad esempio ai registri della porta SPI C tramite SPIC_CTRL, SPIC_INTCTRL, SPIC_STATUS ed SPIC_DATA o tramite la comoda struttura SPIC (il suffisso C indica la porta SPI) che espone i registri come membri della struttura (es. SPIC.CTRL). Mantenendo un puntatore alla struttura la sintassi diventa (&SPIC)->CTRL.

Come operazione preliminare configuriamo i pin che utilizzeremo tramite SPI (dove nel codice i vari *_PIN sono pin creati ad esempio con l’istruzione IOPORT_CREATE_PIN come visto negli articoli precedenti).

config pin

Nella modalità Master è necessario mantenere il pin SS ad un livello logico alto per evitare che il modulo pensi che un altro master voglia prendere il controllo del bus (bus contention).

Se lavorassimo a basso livello come prima cosa dovremmo configurare il registro CTRL del modulo SPI.

ASF per la sua configurazione ci mette a disposizione diverse funzioni: spi_master_init, spi_master_setup_device ed spi_enable.

config code

spi_master_init abilita il clock al modulo SPI e imposta la modalità SPI a Master Mode

spi_master_setup_device imposta la modalità SPI scelta (convenzioni di comunicazione come ad esempio MODE_0 elencate nel datasheet) e la velocità di clock della comunicazione (nell’esempio 1 MHz).

Nota Bene: Da notare che nell’XMega utilizzato la struttura spi_device_conf passata per riferimento non viene di fatto utilizzata, così come l’ultimo parametro select_id che viene semplicemente impostato a zero, probabilmente per compatibilità con versioni precedenti della libreria la funzione ha mantenuto questi parametri.

spi_enable abilita il modulo SPI, è il passaggio finale della configurazione.

Attenzione: Inizialmente avere tre metodi diversi per configurare un solo registro (CTRL), con tanto di parametri inutilizzati, può rendere difficile comprendere l’utilizzo del modulo SPI di ASF.

Una volta configurato il modulo è possibile inviare dei dati al dispositivo slave con le funzioni: spi_select_device, spi_write_packet e spi_deselect_device

write

Dove spi_device_conf è la struttura creata in precedenza, che ha come unico scopo il mantenimento del pin di abilitazione del dispositivo slave.

spi_select_device porta a low tale pin per abilitare l’effettiva ricezione da parte del dispositivo slave

spi_write_packet invia una sequenza di bytes (nell’esempio 3 bytes: 1, 2 e 3) occupandosi di attendere il completamento della trasmissione di ogni byte prima di procedere all’invio del successivo (a differenza di spi_write_single che invia un singolo byte ma non attende il completamento dell’invio). Sostanzialmente l’invio di un byte corrisponde alla scrittura del registro DATA del modulo SPI e l’attesa nel controllo del registro STATUS.

spi_deselect_device porta ad high il pin di abilitazione del dispositivo slave per la sua disabilitazione.

Oltre alla sola istruzione spi_write_packet possono essere inserite anche ulteriori letture o scritture prima di deselezionare il dispositivo. Nel datasheet del dispositivo slave è in genere possibile trovare i dettagli richiesti per l’utilizzo delle sue funzionalità.

Analogamente la lettura di dati dal dispositivo slave avviene tramite la funzione spi_read_packet

read

Dove nell’esempio vengono letti 3 byte dal dispositivo slave, come abbiamo visto scrivendo in effetti dei dati fittizi sul dispositivo slave e attendendo la completa scrittura del byte prima della presentazione del byte successivo. La lettura di un byte comporta la lettura del registro DATA del modulo SPI.

Oltre alla documentazione anche per questo articolo è stato analizzato il codice sorgente del modulo ASF per trarre maggiori informazioni sul funzionamento del modulo di cui sono state riportate le più rilevanti per sopperire alle mancanze della documentazione.

Spero che in questi due articoli si siano chiariti alcuni dubbi, senza la pretesa però di spiegare tutto nei minimi dettagli.

domenica 26 gennaio 2014

Introduzione ad ASF (settima parte) – Introduzione ad SPI

Analizziamo in questo articolo l’utilizzo dell’interfaccia Serial Peripheral Interface (SPI)

SPI permette una comunicazione sincrona ad alta velocità tra un dispositivo Master ed uno o più dispositivi Slave.
Ogni dispositivo contiene un registro a scorrimento tipicamente ad 8 bit (1 byte) il cui scorrimento è basato sul segnale di clock fornito dal Master.

Prendiamo in considerazione una comunicazione tra un dispositivo Master ed un solo dispositivo Slave

spi

I registri a scorrimento sono collegati tra loro tramite i segnali MISO (Master Input - Slave Output) e MOSI (Master Output – Salve Input), lo scorrimento dei bit tra i due registri permette quindi uno scambio di dati tra i due dispositivi.

Questa architettura porta ad alcune importanti conseguenze da tenere sempre presenti:

- Il Master deve inviare dei dati per leggere i dati dello Slave, i bit del registro a scorrimento dello Slave verranno copiati in quello del Master ma al tempo stesso i bit del Master saranno copiati nel registro dello Slave.

- Nuovi byte non possono essere scritti nel registro prima che i vecchi siano stati trasferiti tramite lo scorrimento

- I dati devono essere letti prima dell’arrivo dei successivi che faranno scorrere il buffer e sovrascriveranno quindi i vecchi dati che saranno così persi.

Iniziamo a vedere adesso dopo questa breve panoramica su SPI come interfacciarci tramite XMega (Master) ad un dispositivo esterno (Slave)

Creiamo un nuovo progetto tramite il template EWS ATXmega32A4U come visto negli articoli precedenti

UPDATE: Template aggiornato per Microchip Studio ed ASF 3.52 disponibile qui

Aggiungiamo tramite ASF Wizard il modulo SPI – Serial Peripheral Interface Master (Common API) scegliendo standard_spi, utilizzeremo il modulo hardware SPI del micro. Un’altra opzione che non vedremo in questo articolo è utilizzare il flessibile modulo hardware USART del micro o addirittura entrambi.

spi standard

Dopo aver aggiunto il modulo notiamo come nel progetto siano stati inseriti i file spi.c ed spi.h, i driver di basso livello di SPI ed i file spi_master.c ed spi_master.h, servizi a livello più elevato

sol explorer

Il Driver SPI è semplicemente un file C contenente le funzioni sotto elencate che permettono una gestione a basso livello del modulo hardware di cui il micro è dotato, queste funzioni non contengono molta logica e sostanzialmente espongono sotto nomi più amichevoli i registri interni del micro. L’unica funzione contenente un poco di logica è la funzione spi_xmega_set_baud_div che effettua una serie di controlli sui dati in ingresso prima di impostare il clock in modo da avvicinarsi al baudrate desiderato.

spi_driver

Non chiameremo direttamente nessuna funzione del Driver SPI ma utilizzeremo invece il servizio SPI Master che oltre ad alcune definizioni e strutture dati offre le seguenti funzioni che ci permettono di strutturare in termini di pacchetti di byte il nostro Firmware

spi_service

Nella prossima puntata vedremo come funzionano nella pratica le funzioni che ASF ci offre, alla prossima.

lunedì 12 agosto 2013

Introduzione ad ASF (sesta parte) – USB

Indice degli articoli su ASF

Microcontrollori come AVR UC3 ed XMega hanno in alcuni modelli un modulo hardware per la comunicazione USB, la scrittura del software per gestire il protocollo non è però banale e molto articolata, la libreria ASF ci viene in aiuto tramite uno stack USB che può risparmiarci molti mesi di lavoro permettendo di focalizzarsi su cosa rende unica la nostra applicazione.

xmega_usb

Non analizziamo in questo articolo nel dettaglio tutto lo stack USB che offre molteplici possibilità ma vediamo in modo semplice come sostituire la vecchia comunicazione seriale con la più moderna USB presente su tutti i dispositivi di ultima generazione.

Utilizzeremo la classe USB CDC (Communication Devices Class), non una classe del mondo della programmazione ad oggetti ma un insieme di specifiche a cui attenerci che ci esonera dallo scrivere uno specifico driver per l’Host USB (il sistema operativo lato pc ad esempio) in quanto già presente per generici dispositivi USB CDC ed utilizzabile sotto Windows con un semplice file .inf. La comunicazione verrà semplicemente vista come una porta seriale virtuale utilizzabile con semplicità da un software sul sistema operativo.

Esistono anche altre classi USB ed è possibile crearne personalizzate, sotto una tabella riassuntiva (non completa) per rendere meglio l’idea di cosa possono offrire alcune classi USB ed alcuni vantaggi/svantaggi.

usb_class

Lo stack USB di Atmel è composto da tre moduli: UDC, UDI ed UDD

usb_stack

UDC (USB Device Controller) fornisce un’astrazione di alto livello del dispositivo USB, presenta metodi come udc_start(), udc_stop() per avviare e fermare lo stack USB, udc_attach(void), udc_detach() per collegare il dispositivo al bus quando la tensione è idonea e scollegarlo dal bus rimuovendo le resistenze interne di pull-up sulle linee D- e D+.

UDI (USB Device Interface) il modulo è distinto per ogni classe di dispositivo che si vuole utilizzare, utilizzeremo UDI for CDC che offre metodi per controllare la linea seriale virtuale e gestire il trasferimento dati

UDD (USB Device Driver) fornisce un’astrazione di basso livello del dispositivo USB, gestisce eventi provenienti dall’hardware come interruzioni chiamando a sua volta funzioni dei moduli UDC e UDI, non utilizzeremo direttamente questo modulo.

Il funzionamento dello stack USB si basa sulle interruzioni, questa soluzione garantisce bassa latenza e non richiede cicli di attesa, bisogna porre però particolare attenzione. Le interruzioni USB hanno bassa priorità e possono essere bloccate da interruzioni con priorità maggiore o sezioni critiche di codice.

Le specifiche USB impongono un tempo massimo di 2ms per assegnare l’indirizzo USB, procedura di basso livello che si svolge durante l’enumerazione, al collegamento del dispositivo.

Gli host USB usano poi un timeout, non previsto dalle specifiche USB, per rilevare e resettare un dispositivo che non risponde ai messaggi di controllo, sotto una tabella di esempio dei tempi di timeout

timeout
CBW: Command Block Wrapper, ZLP: Zero Lenght Packet, CSW: Command Status Wrapper

Tralasciando le varie sigle che si riferiscono a meccanismi di basso livello del protocollo USB di cui non parleremo in questo articolo, è possibile notare che per avere compatibilità con i principali sistemi operativi la MCU deve rispondere entro 5 secondi a delle richieste particolari ricevute tramite USB.

L’utilizzo delle funzionalità di Debug (es. Debug con AVR Dragon) può interferire con il funzionamento del modulo hardware USB del microcontrollore, con la modalità USB OCD break mode disabilitata eventuali breakpoint saranno immediatamente soddisfatti ma il comportamento USB sarà imprevedibile mentre con la modalità abilitata le transazioni USB verranno completate prima di fermare l’esecuzione del programma e il micro non accetterà ulteriori transazioni e risponderà con un messaggio NACK ad eventuali richieste dell’host.

Nota: Seppur non specificato nel datasheet o nel manuale del programmatore la modalità USB OCD break mode è automaticamente configurata dal debugger e non richiede intervento manuale. Fonte: supporto tecnico Atmel

Le funzioni più interessanti sono nel modulo UDI con singola interfaccia CDC, sotto riportate per comodità:

udi_cdc

Aggiungiamo prima di tutto al nostro progetto HelloASF creato negli articoli precedenti il modulo USB Device tramite il menù ASF / ASF Wizard come visto negli articoli precedenti per gli altri moduli, scegliamo la variante cdc_stdio dal menù a discesa come mostrato in figura e premiamo Apply 

asf_module

cdc_stdio fornisce al nostro progetto un’interfaccia Standard I/O (stdio) che implementa dietro le quinte la classe USB CDC permettendo così di utilizzare metodi come printf(), scanf(), getc(), puts(), etc.. le funzioni del modulo UDI for CDC rimangono però estremamente utili per inviare e ricevere byte o per attendere la ricezione di dati dall’host USB.

Verrà automaticamente aggiunto al nostro progetto tutto quello che serve per utilizzare l’USB con la classe CDC tramite stdio, notiamo dall’ASF Explorer l’aggiunta dei vari moduli dello stack USB.

asf_explorer

La prima operazione sarà adesso impostare il clock del modulo USB, nel file conf_clock.h eliminiamo i commenti ai #define relativi al clock usb come nell’immagine seguente

usb_clk

Per il modulo USB verrà quindi utilizzato l’oscillatore RC interno da 32 MHz con la calibrazione di fabbrica a 48 MHz, non sarà però utilizzabile direttamente per la CPU se perlomeno si vuole rimanere entro le specifiche raccomandate (32 MHz) ed evitare l’overclock della CPU.

Una possibilità è utilizzare i prescaler per ridurre la frequenze di clock a 24 MHz, è però anche possibile utilizzare un oscillatore esterno o l’oscillatore RC interno da 2 MHz e tramite il PLL moltiplicare per 16 la frequenza ed ottenere così 32 MHz, seguiremo quest’ultima strada:

clock_pll

Il file conf_usb.h contiene le impostazioni di configurazione per l’USB CDC, è obbligatorio modificarne il Vendor ID con un identificativo rilasciato dietro pagamento dal consorzio USB per applicazioni commerciali.

Per utilizzare stdio_usb è necessario impostare alcuni #define a funzioni del modulo stdio_usb come mostrato di seguito

USB_conf

Da notare l’ #include finale al file <stdio_usb.h> per poter risolvere correttamente le funzioni aggiunte.

Dal file è possibile specificare tramite USB_DEVICE_ATTR se l’alimentazione è prelevata dal bus USB oppure no ed un eventuale risveglio remoto del dispositivo.

Nota per i dispositivi alimentati indipendentemente dal bus USB: E’ necessario un modo per rilevare lo stato della connessione USB, la scheda EWS utilizzata come è possibile vedere dallo schema sul pin PA7 presenta un divisore di tensione 1:3 per questo scopo, 5v saranno rilevati circa come 1,67 e sarà possibile misurarli senza problemi tramite l’ADC per esempio. Sarà poi necessario modificare la funzione stdio_usb_init() nel file stdio_usb.c mostrata di seguito

stdio_usb_init

Sostituendo il valore true del parametro di stdio_usb_vbus_event() con l’effettivo valore del bus USB.

Alimentando continuamente da bus USB il micro questa operazione non sarà però necessaria.

Le impostazioni di comunicazione infine permettono di impostare il data rate e gli altri parametri tipici di una comunicazione seriale che devono coincidere col programma sull’host usb.

Modifichiamo adesso il file main.c col seguente codice

main_code

Dopo l’inizializzazione del servizio sysclk notiamo che vengono abilitati gli interrupts, passaggio fondamentale dato che tutto lo stack USB si basa su essi per il funzionamento.

Con l’istruzione stdio_usb_init() inizializziamo il modulo ASF stdio_usb che si occuperò di avviare lo stack USB ed impostare stdin ed stdout all’implementazione USB CDC.

All’interno del ciclo while è stata utilizzata semplicemente l’istruzione printf() per inviare tramite USB una stringa di testo alla porta seriale virtuale.

Il micro è pronto, non rimane adesso che modificare il file .inf che il Wizard ha aggiunto al progetto con le informazioni corrette sul nostro dispositivo (VID, PID) e che dovremo indicare a Windows per l’installazione del driver. Nel progetto allegato, scaricabile alla fine dell’articolo è possibile trovare un esempio di come modificare il file.

Nell’immagine seguente il terminale PuTTY collegato al micro XMega.

terminale

Anche per oggi è tutto, la libreria ASF permette di nascondere le complessità dietro al protocollo USB e ci fornisce strumenti semplici da utilizzare per concentrarci sulla nostra applicazione e scambiare senza problemi dati ad esempio tra micro e pc. Spesso il supporto USB è un fattore rilevante e permette di abbattere il BOM (Bill of Materials) evitando l’utilizzo di un IC dedicato.

Scarica il progetto dell’articolo

mercoledì 3 luglio 2013

Introduzione ad ASF (quinta parte) - Il sistema di clock

Indice degli articoli su ASF

Scopriamo in questo articolo il flessibile sistema di clock dell’AVR XMega A4U che ricordiamo essere molto simile agli altri XMega e vediamo come configurarlo con semplicità grazie al modulo ASF System Clock Control che abbiamo aggiunto al nostro progetto ed analizzato nelle precedenti puntate.

Per chi volesse scaricare il progetto Atmel Studio creato fino ad ora: progetto_introduzione_ASF_3

Come detto nell’articolo precedente, dopo il reset (quindi all’avvio) il dispositivo partirà sempre dall’oscillatore interno da 2 MHz dopodiché sarà possibile cambiare a runtime attraverso il nostro firmware i vari clock di sistema.

Vediamo adesso uno schema tratto dal datasheet del microcontrollore che riassume bene cosa ci offre relativamente ai clock:

clock

Inizialmente si può essere spaventati da tante opzioni e sigle, una domanda spesso posta dagli esordienti è “ma non bastava un semplice clock generale?”

Il microcontrollore dispone di molte periferiche che lavorano tipicamente a velocità diverse dalla CPU:

- il modulo Brown-out che rileva eventuali cali di alimentazione azionando il reset ed il Timer Watchdog che permette la rilevazione di blocchi del firmware lavorano ad 1 kHz generato a partire da un clock a 32 kHz

- il Contatore Real Time (RTC) che permette di tenere traccia del tempo lavora tipicamente a 1024 kHz ma può lavorare con maggiore risoluzione (maggiore di 1ms) a 32.768 kHz

- il modulo USB richiede un clock minimo di 6 MHz per lavorare in low speed ed almeno 48 MHz per la modalità full speed

- le periferiche come il controller DMA, il sistema di eventi, il controller delle interruzioni e la RAM hanno un clock impostabile chiamato clkPER

- il modulo EBI (External Bus Interface) usato per collegare memorie esterne come SRAM e SDRAM o periferiche come display LCD mappati in memoria lavora ad un clock clkPER2 veloce fino al doppio rispetto alle periferiche. Il modulo è presente solo su alcuni package, in generale con un elevato numero di pin, non è presente sulla nostra scheda di sviluppo EWS ATXMega32A4U.

- il modulo Hi-Res ha clock clkPER4 fino al quadruplo rispetto alle periferiche, è utilizzato per generare forme d’onda ad alta risoluzione

Adesso il perché il micro ha diversi clock configurabili può risultare più chiaro, in alcuni casi la scelta permetterà di privilegiare la velocità e la precisione in altri il risparmio energetico e minori possibilità di interferenze elettromagnetiche (EMI). Questa panoramica ci ha dato inoltre un’idea di cosa fanno alcune periferiche del micro.

I clock delle periferiche clkPER4, clkPER2, clkPER e della CPU e memoria EEPROM non volatile clkCPU sono generati a partire da clkSYS che è selezionato direttamente da una delle varie sorgenti di clock, dopodiché come è possibile vedere dal seguente schema può essere diviso diverse volte da vari Prescaler configurabili per generare le altre frequenze.

prescaler

Dopo questa introduzione al sistema di clock comprendere il file di configurazione conf_clock.h (sotto riportato) del modulo ASF System Clock Control sarà senz’altro più semplice:

xosc

Tralasciamo per il momento le voci relative al PLL che vedremo in un prossimo articolo.

Impostiamo adesso come sorgente del clock di sistema (clkSYS) l’oscillatore esterno da 12 MHz presente sulla scheda, per fare ciò dobbiamo commentare la prima riga (o comunque le altre definizioni di CONFIG_SYSCLK_SOURCE) e de-commentare

#define CONFIG_SYSCLK_SOURCE        SYSCLK_SRC_XOSC

Per specificare la frequenza è necessario de-commentare

#define CONFIG_XOSC_RANGE XOSC_RANGE_12TO16

Se avessimo avuto un oscillatore non compreso tra 12 e 16 MHz avremmo dovuto scegliere la corrispondente definizione.


Possiamo notare che il Prescaler A ed il Prescaler B e C sono impostati ad 1 tramite le righe

#define CONFIG_SYSCLK_PSADIV          SYSCLK_PSADIV_1
#define CONFIG_SYSCLK_PSBCDIV         SYSCLK_PSBCDIV_1_1

In particolare la prima definizione si occupa di impostare il Prescaler A e la seconda i Prescaler B e C, questo poiché i Prescaler B e C sono dipendenti tra loro nella scelta dei valori di divisione.


Nel file di intestazione asf\common\services\clock\xmega\sysclk.h sono presenti i valori che possiamo assegnare alle due definizioni


prescalerDefine


Per raggiungere questo file ricordo che possiamo andare alla definizione ad esempio di SYSCLK_PSADIV_1 e tramite il comando Goto implementation accessibile tramite click destro sulla parola verrà aperto il file interessato che contiene le nostre definizioni.


Con questa configurazione il nostro micro, la cpu e tutte le sue periferiche, viaggeranno a 12 MHz.


Impostando invece ad esempio

#define CONFIG_SYSCLK_PSBCDIV         SYSCLK_PSBCDIV_4_1

Avremmo avuto il Prescaler B a 4 e gli altri ad 1 avendo così: clkSYS e clkPER4 a 12 MHz ma clkPER2, clkPER e clkCPU a 3 MHz, in parole povere il modulo Hi-Res ad una frequenza 4 volte superiore rispetto alla CPU ed alle periferiche.

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.

mercoledì 26 giugno 2013

Introduzione ad ASF (terza parte)

Indice degli articoli su ASF
Vediamo in questo articolo come aggiungere al nostro programma HelloASF nuovi moduli ASF, aggiungeremo per la precisione il servizio Delay routines che ci fornirà nuovi metodi per inserire dei brevi ritardi temporali nel nostro codice.
Apriamo il nostro progetto HelloASF creato negli articoli precedenti e scegliamo dal menù ASF la voce ASF Wizard
menu
Apparirà una schermata da cui potremmo scegliere i moduli da aggiungere al nostro progetto, selezioniamo la voce Delay routines (service) dall’elenco di sinistra e premiamo il pulsante Add,il modulo apparirà anche nell’elenco di destra.
asfwizard
Possiamo aggiungere in un colpo solo anche più moduli ma per il momento ci basterà il modulo che abbiamo scelto, premiamo il pulsante Apply, una finestra riepilogativa ci informerà delle operazioni che verranno compiute.
summary
E’ possibile notare che oltre al servizio scelto verranno aggiunti altri servizi, questo perché Delay routines dipende da altri servizi e driver che dovranno essere inclusi per il corretto funzionamento.
Facciamo click su OK e aspettiamo che Atmel Studio compia le operazioni indicate
tree
Tramite il Solution Explorer possiamo notare che sono state aggiunte al progetto diverse nuove cartelle e file. L’aggiunta di un modulo è proprio la copia di questi file.
In particolare è stato aggiunto anche il servizio System Clock Control che è fondamentale e ci permetterà di cambiare il clock del nostro microcontrollore che all’avvio è sempre di 2 MHz da un’oscillatore RC interno.
Rispetto ai micro ATTiny ed ATMega si ha un grande cambiamento, il clock non viene più impostato tramite i fuse ma a runtime. Questo può evitare condizioni particolarmente spiacevoli come l’inutilizzabilità di un micro saldato su una scheda senza oscillatore se per sbaglio si è impostato il rispettivo fuse per il clock da cristallo esterno. Permette di cambiare inoltre in modo dinamico al frequenza operativa per ottenere il massimo risparmio energetico o la massima velocità a seconda delle necessità
Il servizio Delay routines necessita del servizio System Clock Control per sapere la frequenza attuale del microcontrollore, a sua volta il servizio sysclk (abbreviazione per System Clock Control da ora in poi) ricava questa informazione dal file di configurazione conf_clock.h che dovrà essere impostato correttamente dal programmatore oltre ad offrire utili funzioni per la gestione del clock.
Iniziamo ad intravedere una stessa struttura logica coerente, i moduli spesso richiedono dei parametri tramite un file conf_X.h (dove X è il nome del modulo) nella cartella config mentre i file sorgenti dei moduli sono inseriti nelle cartelle common ed xmega in base alla loro tipologia (servizi o driver).
E’ da notare che non avviene nessun “collegamento” a librerie esterne ma i sorgenti vengono copiati nel progetto, sia i file di intestazione (.h) sia i file di codice (.c). Questo fatto comporta vantaggi ma anche svantaggi, non è consigliato apportare modifiche ai file di codice sorgente perché eventuali aggiornamenti a nuove versioni di ASF sovrascriveranno i file e le nostre modifiche andranno perse e riapplicate manualmente.
Modifichiamo ora il nostro programma nel seguente modo:
code
Abbiamo inserito la chiamata alla funzione sysclk_init() che in base al file conf_clock.h imposterà il clock di sistema. Come il modulo IOPORT e BOARD ritroviamo dunque che la prima cosa in genere da fare è chiamare la funzione _init. Per il momento non apportiamo nessuna modifica al file conf_clock.h, diamo però una rapida occhiata alla prima parte di questo file:
conf_clock
Notiamo che è composto esclusivamente da #define e le righe commentate forniscono una sorta di documentazione che può evitarci l’apertura della guida, per cambiare il clock di sistema dai 2 MHz di default ai 32 MHz sarà sufficiente commentare la prima riga e togliere il commento dalla seconda.
Vedremo maggiori dettagli sul servizio sysclk in un successivo articolo, per il momento ci basta sapere che non “toccando” nulla avremo un clock di 2 MHz.
Torniamo adesso al nostro codice, riportato sotto per comodità di lettura:
code
Il servizio Delay routines ha la funzione delay_init() deprecata, questo significa che non è consigliato chiamarla ed è presente solamente per compatibilità. Non è infatti più necessaria per il funzionamento del servizio nelle nuove versioni di ASF. Nel codice è presente commentata proprio per ricordare questo fatto.
Nel ciclo infinito while sono presenti nuove istruzioni che faranno lampeggiare il primo led (quello rosso), abbiamo già incontrato l’istruzione ioport_set_pin_level che imposta il livello logico di un pin, sotto notiamo l’istruzione delay_s il cui scopo è fermare l’esecuzione per i secondi indicati come parametro. Avremo quindi un ciclo ON/OFF di due secondi che ci permetterà di osservare il lampeggio del led.
Il segnale che controllerà il led visto dall’oscilloscopio sarà un’onda quadra
oscilloscope
Il servizio Delay routines ci offre anche altri due metodi per generare ritardi più brevi, delay_ms() per ritardi in millisecondi e delay_us() se necessitiamo di brevissimi ritardi in microsecondi.

domenica 23 giugno 2013

Introduzione ad ASF (seconda parte)

Dopo aver visto una panoramica dei file creati dal template apriamo il file main.c

main

La funzione main come consueto sarà il punto d’ingresso per il nostro programma, notiamo le prime due istruzioni con suffisso _init, avremo spesso a che fare con funzioni di inizializzazione che spesso andranno semplicemente chiamate come in questo caso, diciamo che è una convenzione dei moduli ASF.

La prima istruzione ioport_init() inizializza il servizio IOPORT e va necessariamente chiamata prima di utilizzare qualsiasi altra funzione di questo servizio che permette di gestire i pin GPIO (General Purpose Input/Output)

La seconda funzione board_init() in realtà chiama una nostra funzione, nel senso non appartenente dal principio ad ASF ma scritta durante la stesura del template.

Una domanda che è lecito chiedersi è come fa il compilatore a sapere dove sono le due funzioni che abbiamo visto, il trucco è presto svelato, nell’header asf.h sono presenti le inclusioni ai file header (es. ioport.h, board.h) che contengono le definizioni delle funzioni ed il compilatore è già normalmente impostato per includere i percorsi che gli serviranno per recuperare tali file.

compiler

La funzione board_init() è all’interno del file asf\common\boards\user_board\init.c presente nel nostro progetto, qui sotto riportato

init

La funzione utilizza a sua volta due funzioni del servizio IOPORT che quindi è stato necessario chiamare prima di board_init.

Il codice è molto semplice, il suo funzionamento si basa su due funzioni del modulo IOPORT, il codice di ASF è sostanzialmente C quindi non ci sono classi complesse da istanziare e risulta di semplice comprensione:

ioport_set_pin_dir: imposta la direzione per una singolo pin specificato dal primo parametro che potrà quindi essere un pin di ingresso (IOPORT_DIR_INPUT) o di uscita (IOPORT_DIR_OUTPUT) in base a quanto specificato nel secondo parametro

ioport_set_pin_mode: imposta la modalità per un singolo pin specificato dal primo parametro, in questo caso le modalità specificate nel secondo parametro posso essere molteplici

ioportmode

Il codice imposta i pin relativi ai 3 led presenti sulla scheda come uscite ed il pulsante come ingresso con modalità pull-up, ovvero il pin sarà collegato tramite una resistenza interna al micro alla linea di alimentazione VCC ed alla pressione del pulsante si instaurerà un collegamento verso GND ma non un cortocircuito grazie proprio alla resistenza di pull-up di diverse decine di kilo-ohm che limiterà la corrente e leggeremo uno stato logico basso. Quando il pulsante non sarà premuto l’ingresso non rimarrà volante (floating) e quindi soggetto a disturbi ma avrà una connessione a VCC e leggeremo quindi uno stato logico alto. La logica sarà quindi invertita, ricapitolando: pulsante premuto –> ingresso basso, pulsante non premuto –> ingresso alto. Il punto è proprio quello di prevenire i disturbi che generano false pressioni dovute all’ingresso floating (o detto anche in alta impedenza).

Nel codice è possibile notare come i pin sono specificati tramite dei nomi facilmente comprensibili (es. LED0_GPIO, GPIO_PUSH_BUTTON_0), questi “nomi” sono definiti nel file user_board.h:

user_board

Come è possibile vedere le definizioni avvengono tramite una funzione IOPORT_CREATE_PIN che in realtà non è una vera e propria funzione ma una funzione MACRO che viene sostituita nel codice dal preprocessore per ottenere la massima velocità.

ioportcreate

La funzione macro converte la porta prima in un nome es. IOPORT_PORTB che ha una definizione che l’associa ad un numero (in questo caso 1 come è possibile notare dal codice sopra) che viene poi moltiplicato per 8 e viene sommato al numero del pin.

In sostanza la funzione converte ogni pin in un numero univoco che sarà utilizzato per identificare il pin senza possibilità di errori ed utilizzato dalle altre funzioni del servizio IOPORT.

Nel file sono presenti alcune definizioni legati all’oscillatore presente sulla scheda, nel nostro caso un cristallo da 12 MHz, queste informazioni saranno utilizzate col modulo System Clock Control che vedremo prossimamente.

Tornando adesso al nostro file main.c (riportato sotto per semplicità di lettura) dopo aver dilagato sugli internals delle prime due istruzioni e aver visto qualcosa sotto al cofano

main

Notiamo che dopo l’inizializzazione viene utilizzata una nuova funzione del servizio IOPORT che non abbiamo ancora visto:

ioport_set_pin_level: imposta un pin specificato dal primo parametro al livello logico specificato dal secondo parametro. Il livello può essere IOPORT_PIN_LEVEL_HIGH oppure IOPORT_PIN_LEVEL_LOW, il tipo del secondo parametro è un bool quindi potremmo passare equivalentemente anche true oppure false ma a mio parere l’utilizzo dell’enumerativo (es. IOPORT_PIN_LEVEL_HIGH) rende il codice di intenti più chiari.

Vengono quindi accesi tutti e tre i led della nostra scheda dopodiché notiamo un while(true) che è semplicemente un ciclo infinito dove dovremmo inserire il nostro codice che sarà ripetuto fino ad un reset o allo spegnimento del micro.

venerdì 21 giugno 2013

Introduzione ad ASF (Atmel Software Framework)

Scopriamo in una serie di articoli il framework di Atmel per la programmazione dei microcontrollori

asf_structure

ASF è un framework organizzato in strati a livelli di astrazione diversi, facciamone una panoramica:

Boards rappresenta i dettagli della scheda fisica es. pin, led, pulsanti, etc..

Drivers offrono funzionalità di basso livello per interfacciarsi con le periferiche, esempi di Drivers sono ADC, DAC, DMA, SPI, TC (Timer Counter), IOPORT etc.. non hanno nulla a che fare coi Driver di Windows, nonostante il nome sono in genere semplici da usare, sono normali funzioni C

Components come HX8347A Display Controller Low Level driver offrono funzionalità per componenti esterni come display LCD, sensori, etc..

Services come SPI, PWM, FIFO, FatFS, etc.. che spesso offrono funzionalità a più alto livello dei Drivers. Ad esempio il driver SPI offre funzionalità come spi_get ed spi_put per leggere o scrivere un byte sull’interfaccia mentre il servizio SPI offre funzionalità come spi_write_packet che permettono di scrivere pacchetti (sequenze di byte). Un altro esempio è il servizio

Applications sono semplicemente gli esempi da cui è possibile partire scegliendo New Example Project dalla schermata iniziale

Utilities sono funzioni utilizzate trasversalmente da tutti gli strati

Your Code sarà il codice che scriveremo noi

A mio parere personale ASF è più propriamente una libreria piuttosto che un framework in quanto il nostro codice chiama la libreria al posto di essere chiamato dal framework. Ma diciamo che è una sottile distinzione e visto che la sigla contiene la parola framework lo utilizzerò intercambiabilmente con entrambi i termini.

atxmega32a4u_breakout_board-500x500Utilizzeremo nel nostro viaggio la scheda EWS ATXmega32A4U Breakout Board (link produttore, schema) che sostanzialmente espone con semplicità tutti i pin del micro ATXMEGA32A4U ed è precaricata col bootloader Atmel Flip che permette di caricare i nostri programmi tramite USB senza l’acquisto di un programmatore aggiuntivo.

Gli XMega sono una “via di mezzo” tra i micro ad 8 bit e i micro a 32 bit e permetteranno di esplorare parecchi moduli della libreria ASF che non sarebbero utilizzabili con degli ATTiny o ATMega.

Per semplificare l’utilizzo della scheda ho creato un template per creare rapidamente nuovi progetti: scarica il template

Per installare il template una volta aperto Atmel Studio andare su File/Import/Project Template e selezionare il file .zip scaricato

0.importtemplate

Al riavvio di Atmel Studio scegliendo New Project sarà disponibile un nuovo tipo di progetto EWS ATXMEGA32A4U

1newproject

Diamo il nome HelloASF al progetto e scegliamo OK, verrà creata una serie di file che serviranno come infrastruttura per il nostro firmware e l’utilizzo della framework ASF.

Iniziamo con l’analizzare la struttura di file creata col Solution Explorer che troviamo di default sulla barra di destra, dentro la cartella src è presente:

solexpl- cartella asf
relativa ad ASF, contiene a sua volta diverse cartelle

     - cartella common
contiene dei file interessanti nella cartella boards (illustrati sotto), la cartella services relativa ai vari moduli che si aggiungeranno e sarà popolata automaticamente, la cartella utils relativa a file che in genere non vanno modificati e sono di supporto alla programmazione

     - cartella xmega
codice di supporto per i micro XMega, è possibile ignorarla in quanto difficilmente da modificare

- cartella config
è la cartella dove verranno inseriti tutti i file di configurazione per i moduli ASF scelti, ad esempio il servizio System Clock Control aggiungerà il file conf_clock.h che permetterà di impostare la frequenza del clock da utilizzare.

- asf.h  
file automaticamente generato, include tutti gli header necessari per utilizzare i moduli ASF selezionati, non va modificato manualmente

- main.c  
il file iniziale del nostro firmware, contiene la funzione main e un’include alla libreria asf

 

Vediamo adesso alcuni file particolarmente interessanti che consiglio di leggere mentre i rimanenti file delle cartelle services, utils e xmega possono essere ignorati e visti come file di supporto.

asf/common/boards/user_board/init.c contiene una funzione per inizializzare la scheda, vengono impostati i pin dei led come uscite e dei pulsanti come ingressi

asf/common/boards/user_board/user_board.h
contiene #define che danno ai pin dei led e dei pulsanti nomi mnemonici, contiene impostazioni sull’oscillatore presente sulla scheda

asf/common/boards/board.h
sostanzialmente il file non è da modificare, contiene degli include condizionali in base a dei #define definiti nelle opzioni del compilatore

config/conf_board.h
aggiunge se non presente #define CONF_BOARD_H che però non è utilizzato, è il posto dove inserire dei propri #define per personalizzare la configurazione della scheda

Per il momento è tutto, lascio un utile link: http://asf.atmel.com che contiene la documentazione ufficiale ASF e segnalo il canale video di Atmel http://www.youtube.com/user/AtmelCorporation che contiene numerosi video tra cui alcuni dedicati ad ASF

Prossimamente analizzeremo nel dettaglio il file main.c e vedremo come aggiungere nuovi moduli al nostro progetto