Scuola Politecnica e delle Scienze di Base Corso di Laurea in Ingegneria Informatica Elaborato finale in Misure per l’Automazione e la Produzione Industriale Generatore di forme d’onda arbitrarie basato su STM32F303 Anno Accademico 2015/2016 Candidato: Antonio Sorrentino matr. N46002022 [Dedica] Indice Indice.................................................................................................................................................. III Introduzione ......................................................................................................................................... 5 Capitolo 1: STM32............................................................................................................................... 7 1.1 STM32F3 ................................................................................................................................... 7 1.1.1 Board STM32F3 ................................................................................................................. 6 1.1.2 Architettura di sistema ........................................................................................................ 6 1.2 Sviluppo Software ...................................................................................................................... 8 1.2.1 Creazione di un nuovo Progetto .......................................................................................... 6 Capitolo 2: Periferiche ....................................................................................................................... 13 2.1 DAC ......................................................................................................................................... 13 2.1.1 Descrizione modalità di funzionamento singola ............................................................... 16 2.1.2 Conversione ...................................................................................................................... 17 2.1.3 Richieste DMA ................................................................................................................. 18 2.1.4 Registri del DAC............................................................................................................... 19 2.1.4.1 Registro DAC_CR .................................................................................................. 19 2.1.4.2 Registro DAC_DHR12R1....................................................................................... 20 2.1.4.3 Registro DAC_DOR1 ............................................................................................. 21 2.1.4.4 Registro DAC_SR ................................................................................................... 21 2.1.5 Configurazione del DAC .................................................................................................. 22 2.2 DMA ........................................................................................................................................ 13 2.2.1 Principali caratteristiche .................................................................................................... 22 2.2.2 Descrizione funzionale...................................................................................................... 24 2.2.2.1 Transazioni .............................................................................................................. 24 2.2.3 Arbiter ............................................................................................................................... 25 2.2.4 Mappa delle richieste DMA .............................................................................................. 26 2.2.5 Registri DMA.................................................................................................................... 27 2.2.5.1 DMA interrupt status register ................................................................................. 27 2.2.5.2 DMA flag clear register .......................................................................................... 28 2.2.5.3 DMA configuration register .................................................................................... 28 2.2.6 Configurazione del DMA.................................................................................................. 29 2.3 Timer ........................................................................................................................................ 31 2.3.1 Principali caratteristiche .................................................................................................... 31 2.3.2 Funzionamento di base...................................................................................................... 32 2.3.3 Configurazione del Timer ................................................................................................. 33 2.4 Gestione delle interruzioni ....................................................................................................... 33 2.4.1 Extended interrupts and events controller (EXTI) ............................................................ 35 2.4.2 Configurazione interruzione esterna ................................................................................. 37 Capitolo 3: Codice ............................................................................................................................. 38 Conclusioni ........................................................................................................................................ 40 Bibliografia ........................................................................................................................................ 41 Introduzione Un generatore di segnale è un dispositivo utilizzato per produrre forme d’onda con determinate caratteristiche scelte a priori, ovvero di generare un segnale periodico. Un generatore di segnali viene utilizzato principalmente per effettuare il testing di circuiti elettrici ed elettronici infatti nella fase di progetto e di produzione di dispositivi elettronici, debbano essere provati dei circuiti e capita che si abbia bisogno di segnali come quello generato da un componente che non è facilmente reperibile, per questo viene utilizzato un generatore di segnali, oppure può essere utilizzato come dispositivo di comando di processo, o come strumento campione. Scopo di questa tesi è la realizzazione di un generatore di forme d’onda elementari tramite componenti a basso costo. L’idea è quella di conservare in memoria un certo numero di campioni di un singolo periodo della forma d’onda desiderata, e poi trasmettere tali campioni a un DAC in modo che esso trasformi tali valori digitali nel relativo valore analogico avendo l’accortenza di inviarli a tale dispositivo a una velocità costante e facendo in modo di rispettare il valore di frequenza scelto. Waveform DAC Memory 5 Amp Nel nostro caso abbiamo scelto di generare tre tipi di forme d’onda: onda sinusoidale, onda triangolare e onda esponenziale (in quest’ultimo caso scegliendo di avere un esponenziale crescente nel primo semiperiodo e uno decrescente nel secondo). Ovviamente sarà possibile selezionare sia l’ampiezza che la frequenza del segnale. Scopo di questa tesi è la realizzazione di tale generatore di segnale tramite un microcontrollore dell’azienda STMicroelectronics, in particolare utilizzeremo la board F3 Discovery della famiglia STM32, di cui andremo ad analizzare nel dettaglio il DAC, i Timer e il DMA (Direct Memory Access). 6 Capitolo 1: STM32 STM32 rappresenta una famiglia di microcontrollori basati su core ARM, internamente ogni microcontrollore consiste di un processore, una memoria RAM statica, una memoria flash, una interfaccia per il debugging e varie periferiche. 1.1 STM32F3 La serie STM32F3 è basata sul core ARM Cortex M4F che opera a frequenze fino a massimo 72 MHz il quale incorpora al suo interno una FPU (Floating Point Unit), una MPU (Memory Protection Unit) e una ETM (Embedded Trace Macrocell). 1.1.1 Board STM32F3 Core ARM Cortex-M4 32-bit CPU con FPU (72 MHz max); Alimentazione attraverso ingresso esterno da 3V a 5V o tramite bus USB; Memoria : Flash 256 Kbytes, RAM 48 Kbytes; Quattro ADCs 0.20 μS con risoluzione di 12/10/8/6 bits; Due canali DAC da 12-bit; L3GD20, ST MEMS sensore di movimento, giroscopio digitale a 3 assi; LED1 (ROSSO) per accensione a 3.3 V; LED2(ROSSO/VERDE) per comunicazione USB; 7 Otto LED colorati Rosso (Led 3 e 10) Verde (Led 6 e 7) Arancione (Led 5 e 8) e Blu (Led 4 e 9) Due pulsanti (utente e reset) 1.1.2 Architettura di sistema L’architettura principale della serie STM32F3 è formata da un master (che può essere o il Cortex-M4 oppure uno dei due DMA) collegati a una Matrice di bus alla quale sono collegati la memoria Flash, la SRAM e le varie periferiche. Figura 1 Il core Cortex-M4 è collegato con la Matrice di bus con l’I-bus (Instruction bus) che viene usato dallo stesso per caricare le istruzioni di fetch ed è collegato con la memoria Flash, SRAM e CCM RAM. Il DCode bus (load e debug access) connette anch’esso il Cortex con la Matrice di bus ed è usato per caricare/salvare dati nel Code Space. Il S-Bus (System Bus) collega il Cortex con la Matrice di bus e viene usato per accedere ai dati nelle periferiche o nella SRAM. I DMA bus connettono il DMA con la Matrice di bus. La Matrice di bus 8 gestisce l’accesso dei Master tramite un meccanismo di arbitraggio, tale meccanismo usa un algoritmo del tipo Round Robin per permettere ai Master (Cortex oppure DMA di accedere agli Slave. I due ponti AHB\APB permettono la connessione tra il bus AHB e i due bus APB, APB1 limitato alla frequenza di 36 MHz, APB2 con massima frequenza 72 MHz. Una periferica prima di essere utilizzata dovrà esserne abilitato il relativo clock in uno dei registri RCC_AHBENR, RCC_APB1ENR oppure RCC_APB2ENR. 1.2 Sviluppo Software Il software che sarà utilizzato per lo sviluppo di questo progetto è IAR Embedded Workbench che ci darà la possibilità di programmare in C. 1.2.1 Creazione di un nuovo Progetto Per cominciare dobbiamo aprire l’ambiente di lavoro IAR, ci troveremo davanti la seguente schermata. A questo punto possiamo creare un nuovo workspace tramite il menu a tendina FILE, e poi creiamo un nuovo progetto tramite PROJECT->CREATE NEW PROJECT, appare la seguente schermata. 9 Scegliamo di creare un progetto vuoto in C con un main, e lo salviamo in una determinata cartella che andremo a scegliere. A questo punto dobbiamo configurare una serie di opzioni del nostro progetto, per fare ciò dobbiamo cliccare con il tasto destro del mouse sul nome del progetto e dal menù che appare selezionare la voce Options come mostrato nella figura. A questo punto dal menu che appare dalla categoria General Options nella scheda Target alla voce Processor Variant selezioniamo in Device la board che andremo ad utilizzare 10 ossia STM32F303XC così come mostrato di seguito. Una seconda cosa da fare è selezionare le cartelle a cui il compilatore deve fare riferimento, quindi andiamo nella categoria C/C++ Compiler nella scheda PreProcessor e clicchiamo sul pulsante in corrispondenza dell’opzione Additional include directories, e cliccando nella schermata che appare selezioniamo il percorso come di seguito in figura. 11 A questo punto nella categoria Debugger nella scheda Setup alla voce Driver selezioniamo ST-LINK. Sempre nella categoria Debugger spuntiamo l’opzione Use flash loader(s) nella scheda Download. L’ultima opzione da configurare si trova nella categoria ST-LINK bisogna spuntare 12 l’opzione SWD nella scheda Setup. Ora siamo pronti per iniziare a scrivere il nostro programma, prima però dobbiamo parlare dei componenti fondamentali della nostra Board che andremo ad utilizzare ossia il DAC (Digital to Analog Converter), i Timer, il DMA (Direct Memory Access) e la gestione delle interruzioni. 13 Capitolo 2: Periferiche Questo capitolo sarà completamente dedicato a descrivere le periferiche della board che sono state utilizzate per realizzare il progetto. In particolare abbiamo utilizzato: un DAC per convertire il segnale campionato tramite un’opportuna funzione generatrice in un segnale analogico, un DMA affinchè il trasferimento dei campioni dalla memoria (dove sono memorizzati in un vettore) al DAC avvenisse in modo automatico senza la necessità di gestirlo tramite software, un Timer utilizzato come trigger per effettuare la generazione con il DAC in un preciso istante di tempo in modo da soddisfare il valore di frequenza inserito dall’utente, inoltre è stata gestita anche un’interruzione esterna dovuta al fatto che si è scelto di cambiare la forma d’onda generata tramite la pressione del pulsante user (tasto blu sulla board). Di seguito riportiamo le varie specifiche delle periferiche. 2.1 DAC Il DAC (Digital to Analog Converter) è un componente elettronico che permette di trasformare un codice numerico in un segnale analogico tramite una determinata legge di conversione. Nel caso del STM32F3 abbiamo un DAC a 12 bit, ciò vuol dire che possiamo generare 212=4096 codici distinti. Il DAC di questa board genera tensioni comprese tra 0V e 3V, quindi al codice 0 corrisponderà la tensione 0V mentre al codice 4095 corrisponderà una tensione pari a 3V. La Risoluzione di questo dispositivo è pari a 3V/4096 =0,7mV ossia fra la tensione generata con un determinato codice e quella generata con il codice successivo c’è un salto pari proprio alla Risoluzione. 14 Le caratteristiche di questo dispositivo sono le seguenti: • Può essere configurato per essere utilizzato o nella modalità a 12 bit oppure nella modalità a 8 bit, nel caso della modalità a 12 bit questi possono essere allineati a destra oppure a sinistra; • La Board presenta un unico DAC con due canali di conversione che possono essere usati indipendentemente oppure simultaneamente (dual mode), il canale 1 è mappato sul pin PA4, mentre il canale 2 è mappato sul pin PA5; • Possibilità di generare onde di rumore e onde triangolari; • Utilizzo del servizio DMA per entrambi i canali; • Rilevazione di errori underrun nel caso di utilizzo del DMA; • Utilizzo di trigger esterno per iniziare la conversione; Di seguito si riporta il diagramma a blocchi del DAC integrato sulla board STM32F3. Figura 2 Il DAC per scelta progettuale integra un buffer di uscita che può essere usato per ridurre l’impedenza di uscita e per guidare carichi esterni senza la necessità di dover aggiungere un amplificatore operazionale. Tale buffer può essere anche disabilitato tramite il settaggio di 15 un bit specifico come vedremo in seguito, nel caso in cui il buffer sia abilitato però bisogna tenere in conto che il DAC non riuscirà a generare tutti i codici da 0 a 4096, come possiamo notare dalla seguente tabella presa dal Datasheet della board pag. 112 che riporta le specifiche elettriche del DAC, con il buffer abilitato la periferica genera tensioni a partire da 0.2V fino a 2.8V. Figura 3 Per quanto riguarda invece i tempi di conversione, sempre dalle caratteristiche elettriche della periferica si evince che questi sono tipicamente di 3µs quando i codici sono “lontani” 16 ossia se ad esempio vogliamo passare da una tensione di uscita del DAC di 0V a 3V e di 1 µs quando invece sono “vicini”. Figura 4 2.1.1 Descrizione modalità di funzionamento singola Per poter utilizzare questa periferica bisogna prima di tutto abilitarne il clock, per far ciò dobbiamo abilitare il relativo bit, consultando IL RCC register map p.132 del Reference Manual vediamo che tale bit si trova all’interno del registro RCC_APB1ENR e risulta essere precisamente il bit 29 di tale registro. Figura 5 17 Dopo questa operazione preliminare è possibile alimentare il canale del DAC scelto tramite il settaggio del relativo bit nel registro CR (Control Register). Il dispositivo può funzionare come un DAC a 8 bit oppure a 12 bit, il dato da caricare nel caso di utilizzo a 12 bit può essere allineato o a desta oppure a sinistra, nel caso di utilizzo a 8 bit può essere allineato solo a destra. Figura 6 2.1.2 Conversione Per caricare il dato da convertire non possiamo accedere direttamente al registro DOR (Data Output Register) del DAC ma abbiamo accesso al registro preliminare il DHR (Data Holding Register), ed è proprio questo registro che dobbiamo caricare con il dato da convertire nelle varianti 8 o 12 bit allineati a dx o sx come descritto in precedenza. Dobbiamo ora parlare di come poter trasferire il dato dal DHR al DOR per poter poi essere effettivamente convertito. In un particolare registro avremo un bit TEN (Trigger Enable) il quale quando posto a zero una volta caricato il DHR effettuerà la copia di quest’ultimo nel DOR dopo un ciclo di clock, quando invece il bit TEN è settato il trasferimento del dato dal registro preliminare avviene in seguito a un trigger, e precisamente il trasferimento del dato nel DOR avviene tre cicli di clock dopo il verificarsi del trigger. Quando il registro DOR è caricato con il dato da convertire, la corrispondente tensione analogica sarà disponibile dopo un tempo di assestamento che dipende dalla tensione di alimentazione e dal dalla tensione che era stata generata in precedenza. Il dispositivo effettua una conversione lineare tra 0V e 3V secondo la seguente legge: VOUT = 3V* (DOR/4095) 18 Come abbiamo già detto se il bit TEN è settato la copia del DHR nel DOR avviene tre cicli di clock dopo l’avvenimento di un trigger, con la board in uso per questo dispositivo ci sono otto possibili fonti distinte di eventi che possono essere selezionate tramite 3 bit definiti come TSEL (Trigger Selection), di seguito riportiamo la tabella 49 estratta dal Reference Manual della board pag.306 con i vari trigger che possono essere selezionati: Come si può notare ci sono vari trigger pilotati da TIMER, uno guidato da una linea esterna EXTI linea 9 e un Software Trigger per dare il comando di conversione tramite software. Dopo aver caricato il dato nel DHR, questo rimane in tale registro fin quando l’interfaccia del DAC non rileva un fronte di salita sul timer TRGO oppure sulla linea esterna e al verificarsi dell’evento trasferisce il dato nel DOR dopo tre cicli di clock, nel caso di Software Trigger invece il trasferimento del dato nel DOR prende solo un ciclo di clock. 2.1.3 Richieste DMA Entrambi i canali del DAC possono essere serviti da due canali DMA per trasferire i codici da convertire dalla memoria a uno dei registri DHR senza dover passare dalla CPU e soprattutto senza dover gestire questi trasferimenti da parte del software. Per abilitare tali richieste bisogna settare un bit specifico nel DAC il bit DMAEN (DMA Enable) nel registro 19 CR, inoltre bisognerà andare a configurare dei parametri per il DMA cosa di cui parleremo nel capitolo dedicato a tale periferica. Lato DAC una richiesta di servizio DMA viene effettuata quando occorre un trigger esterno, tranne che un software trigger, in tal caso il DHR viene caricato con il valore di un determinato codice in memoria e avviene la procedura di conversione come prima descritto. Nel caso in cui il DAC generi una nuova richiesta DMA prima che sia stato ricevuto l’ack per la richiesta precedente si genera una condizione detta di UNDERRUN, la seconda richiesta non viene accolta inoltre nessun altra viene rilasciata e viene settato un bit precisamente il DMAUDR (DMA Underrun Error) nel registro SR (Status Register) del DAC per segnalare la condizione di errore. Per ripristinare il funzionamento delle richieste DMA bisogna anzitutto pulire il bit DMAUDR via software (basta scriverci un uno), disabilitare il DMA e riconfigurare sia il DMA che il canale del DAC scelto facendo in modo o di diminuire la frequenza delle richieste al DMA o cercando di diminuire il carico di lavoro del DMA, dopo questo è possibile abilitare nuovamente il DMA e il trigger per la conversione. Nel caso in cui sia settato uno specifico bit è possibile generare un’interruzione per segnalare la condizione di underrun. 2.1.4 Registri del DAC Di seguito vengono riportati una serie di registri del DAC illustrandone le funzionalità. 2.1.4.1 Registro DAC_CR (DAC CONTROL REGISTER) Tale registro è a 32 bit in cui i bit 14-15-30-31 sono dei bit riservati, come dice il nome è un registro di controllo in cui i bit da 0 a 13 sono relativi al canale 1 del DAC e i bit da 16 a 29 sono i duali dei precedenti solo che si riferiscono al canale 2 del DAC. 20 Di seguito riportiamo il significato dei bit per quanto riguarda il canale 1: • Bit 0 EN1: DAC canale 1 bit abilitazione. Tale bit viene utilizzato per abilitare o disabilitare il canale 1 del DAC via software quando è settato il canale è abilitato. • Bit 1 BOFF1: DAC canale 1 buffer uscita disable. Tale bit è utilizzato per abilitare o disabilitare il buffer di uscita via software, quando è settato il buffer è disabilitato. • Bit 2 TEN1: DAC canale 1 trigger enable. Tale bit è utilizzato per abilitare l’utilizzo del trigger via software. Quando è settato il trigger è abilitato. • Bit 5:3 TSEL: DAC canale 1 trigger selection. Questi tre bit vengono utilizzati per selezionare via software la fonte di trigger esterno così come riportato nella tabella 49 del Reference Manual. • Bit 12 DMAEN1: DAC canale 1 DMA enable. Tale bit è utilizzato per abilitare\disabilitare la modalità DMA tramite software. Quando è settato l’utilizzo del DMA è abilitato. • Bit 13 DMAUDRIE: DAC canale 1 DMA Underrun Interrupt Enable. Tale bit è utilizzato per abilitare via software il servizio di interruzione in caso di underrun. 2.1.4.2 Registro DAC_DHR12R1 Questo è il registro DHR del DAC canale 1 a 12 bit allineati a destra. Ḗ un registro a 32 bit in cui viene inserito il codice da convertire nei bit da 0 a 11 mentre i restanti sono riservati. Figura 7 21 Questo è solo uno dei possibili registri DHR utilizzabili, nella modalità di utilizzo singolo ci sono anche il DAC_DHR12L1 (Registro DHR 12 bit allineati a sinistra) e DAC_DHR8R1 (Registro DHR 8 bit allineati a destra), ovviamente tali registri sono duplicati per il canale 2 e ce ne sono altri per il funzionamento in modalità duale. 2.1.4.3 Registro DAC_DOR1 Figura 8 Questo è il registro DOR del DAC canale 1. Ḗ un registro a 32 bit in cui i bit utilizzati sono dal bit 0 a 11 i restanti sono riservati, ovviamente ne esiste un altro per il canale 2. Ovviamente tali bit sono a sola lettura in quanto come abbiamo già detto non abbiamo accesso in scrittura a questo registro. 2.1.4.4 Registro DAC_SR (DAC Status Register) Questo registro è anch’esso a 32 bit, di questi abbiamo il bit 13 DMAUDR1 (DAC canale 1 DMA underrun flag) utilizzato per segnalare la condizione di underrun relative alle richieste DMA del canale 1 e il bit 29 DMAUDR2 (DAC canale 2 DMA underrun flag) che ha la stessa funzionalità ma per il canale 2 del DAC. Questi bit sono settati via hardware e puliti via software scrivendo un uno nel bit corrispondente. 22 Figura 9 2.1.5 Configurazione del DAC Nel nostro progetto come avremo modo di vedere inseguito nel codice ho utilizzato il canale 1 del DAC per generare il segnale analogico configurato in modo da avere il buffer di uscita disabilitato BOFF1 registro CR uguale a 0 in modo da poter generare segnali che possono variare da 0V a 3V, inoltre è stata abilitata la modalità di utilizzo con trigger bit TEN1 uguale a 1 e in particolare è stato utilizzato il trigger generato dall’update del Timer2 bit TSEL1 pari a 100 poi è stato settato l’utilizzo del servizio DMA tramite il settaggio del bit DMAEN1 per caricare il registro DHR, nel nostro caso è stato utilizzato il DHR12R1 (DHR canale 1 a 12 bit allineamento a destra). 2.2 DMA Il DMA (Direct Memory Access) è una periferica che permette lo spostamento di dati senza dover passare per la CPU lasciandola libera di poter effettuare altre operazioni. La board presenta due controller DMA completamente indipendenti ognuno dei quali serve più canali, ovviamente ogni controller gestisce più canali in base ad una priorità. 2.2.1 Principali caratteristiche • Il dispositivo presenta 12 possibili canali indipendenti; • Ogni canale è configurabile tramite software ed è connesso ad un hardware dedicato per le richieste DMA; • Le priorità tra richieste provenienti dai canali di un controller DMA sono 23 programmabili a livello software su 4 livelli (molto alta, alta , media, bassa), nel caso di richieste con uguale priorità software vengono servite le richieste con priorità hardware più bassa ossia le richieste provenienti dai canali con valori più bassi (canale 1 ha maggiore priorità rispetto a canale 2 e così via), questa suddivisione è stata fatta tenendo in conto gli aspetti di criticità del dato, infatti sui canali più bassi sono state mappate quelle periferiche time critical che bisogna servire per prime in caso di più richieste; • Diverse modalità di trasferimento, il controller permette di trasferimenti di 8-16-32 bit (byte, half word, word), per velocizzare è possibile impacchettare questi dati e spacchettarli alla destinazione, ovviamente bisogna accordare sorgente e destinazione sulla dimensione del dato. • Sono supportate due possibili modalità di utilizzo quella lineare chiedo n transazioni le effettuo e mi fermo, oppure la modalità buffer circolare, chiedo n transazioni le effettuo e dopo ritorno in testa e ricomincio. • Per la gestione il controller DMA prevede 3 flag: uno per indicare che è avvenuto mezzo trasferimento, uno per indicare che il trasferimento è terminato e un altro per indicare che è avvenuto un problema. • Possibilità di effettuare trasferimenti memoria-memoria, memoria-periferica, periferica-memoria, periferica-periferica. • Possibilità di eseguire fino a 65536 transazioni. Di seguito viene riportato lo schema a blocchi del DMA supportato dalla board in uso. 24 Figura 10 2.2.2 Descrizione funzionale Come si evince dallo schema a blocchi i due controller sono indipendenti dal core, ciò potrebbe creare un problema nel caso in cui DMA e Core volessero accedere allo stesso dato, il sistema è gestito in modo tale da rendere disponibile il dato al Core nel mezzo ciclo immediatamente successivo all’operazione del DMA, quindi il DMA ha priorità rispetto al Core. I due controller DMA supportano un certo numero di canali per effettuare delle richieste, in particolare DMA1 supporta fino a 7 canali, invece DMA2 supporta fino a 5 canali. 2.2.2.1 Transazioni Per iniziare una transazione la periferica in seguito a un evento invia segnale di richiesta al 25 controller DMA, questi in base alla priorità del canale serve la richiesta e invia un ACK alla periferica. Molta importanza riveste la configurazione del canale, di seguito vengono riportati i vari passi: • Impostazione del registro CPAR (Peripheral Address Register), sarà settato con l’indizzo del registro della periferica verso/dal quale saranno spostati i dati; • Impostazione del registro CMAR (Memory Address Register), dovrà essere settato con l’indirizzo della prima cella di memoria dalla quale o verso la quale si vogliono spostare i dati; • Impostazione del registro CNDTR con il numero totale di transazioni che si vogliono eseguire; • Configurazione della priorità software tramite il registro CCR; • Configurazione della direzione di trasferimento (memoria-memoria, memoriaperiferica, periferica-memoria, periferica-periferica) tramite il registro CCR; • Configurazione della modalità di utilizzo lineare o buffer circolare configurando il registro CCR; • Incremento del puntatore di memoria e periferica, questi puntatori possono essere incrementati o meno a seconda di come vengono impostati i bit PINC (Peripheral increment) e MINC (Memory Increment), ovviamente l’incremento del puntatore è commisurato alla size del dato (8-16-32 bit); • Impostazione della size del dato; • Generazione di interruzioni per segnalare mezzo trasferimento, trasferimento completato oppure un errore; 2.2.3 Arbiter L’arbitro si occupa di soddisfare le richieste in base alla loro priorità e quindi invia le sequenze di accesso alla periferica/memoria. Come già detto ci sono quattro livelli di priorità software a parità della quale viene servita la richiesta con priorità hardware più bassa a seconda del canale da cui arriva la richiesta, quindi se crediamo che una data richiesta sia critica ma si trova mappata in una posizione 26 scomoda come canale dobbiamo necessariamente dargli una priorità software alta per permettere che sia servita. 2.2.4 Mappa delle richieste DMA Di seguito si riporta la tabella 26 estratta dal Reference Manual della board pag.160 in cui si riportano per quanto riguarda il DMA1 come sono mappate le richieste sui vari canali. Di seguito invece riportiamo la tabella 27 estratta sempre dal Reference Manual pag.162 che invece riporta la mappatura delle richieste per quanto riguarda il controller DMA2. 27 Le richieste da parte delle periferiche su ogni singolo canale sono logicamente in OR prima di arrivare al controller DMA, quindi per ogni canale deve essere abilitata una sola richiesta per volta. Inoltre è prevista la possibilità per alcune periferiche di ricevere le richieste su un altro canale DMA come possiamo notare nel caso del controller DMA2 per ADC2, questo di base è mappato sul canale 1, ma può essere mappato anche su canale 3 basta effettuare una rimappatura del SYSCFG registro CFGR1. 2.2.5 Registri DMA Di seguito vengono riportati una serie di registri del DAC illustrandone le funzionalità. 2.2.5.1 DMA interrupt status register (DMA_ISR) Figura 11 Questo registro è a sola lettura in cui vengono riportate per ogni canale le seguenti condizioni: • TEIF:Transfer Error Interrupt Flag, viene settato via hardware quando avviene un’errore di trasmissione, per resettarlo bisogna settare il bit corrispondente nel registro IFCR. • HTIF:Half Transfer Interrupt Flag, tale bit viene settato via hardware quando è avvenuto mezzo trasferimento, anch’esso viene pulito scrivendo un uno nel corrispondente bit del registro IFCR. • TCIF:Transfer Complete Interrupt Flag, tale bit invece viene settato via hardware quando viene completato il trasferimento, per resettarlo basta settare il relativo bit nel registro IFCR. 28 • GIF:Global Interrupt Flag, tale bit viene settato via hardware quando avviene almeno una delle condizioni precedenti, per resettarlo basta settare il relativo bit nel registro IFCR. 2.2.5.2 DMA interrupt flag clear register (DMA_IFCR) Figura 12 Questo è un registro a 32 bit dove i bit 28-29-30-31 sono riservati, i restanti sono a sola scrittura e servono per resettare i bit corrispondenti del registro ISR dalle varie condizioni di errore che si sono verificate. 2.2.5.3 DMA canale x configuration register (DMA_CCR) Questo registro è a 32 bit, dove i bit dal 15 al 31 sono riservati, è un registro molto importante in quanto in esso vengono impostate le configurazioni del canale selezionato. Figura 13 29 • Bit 0 EN bit di abilitazione del canale; • Bit 1-2-3 vengono utilizzati per abilitare il servizio di interruzione relativamente agli eventi di trasferimento completato, mezzo trasferimento, errore di trasferimento; • Bit 4 DIR serve per impostare la direzione del trasferimento secondo la seguente modalità, quando il bit è a zero si legge dalla periferica, se invece è a 1 si legge dalla memoria; • Bit 5 CIRC quando viene settato si attiva la modalità circolare altrimenti è attiva quella lineare; • Bit 6-7 PINC (Peripheral Increment) e MINC (Memory Increment) sono utilizzati per abilitare rispettivamente l’incremento per la periferica e la memoria; • Bit 8-9 PSIZE (Peripheral Size) e bit 10-11 MSIZE (Memory Size) sono utilizzati per impostare la dimensione del dato (8-16-32 bit); • Bit 12-13 PL serve per impostare la priorità del canale (bassa, media, alta, molto alta); • Bit 14 MEM2MEM è utilizzato per abilitare i trasferimenti memoria-memoria; Altri registri che riservano importanza sono il registro DMA_CNDTR utilizzato per impostare il numero di transazioni da eseguire, il DMA_CPAR per impostare l’indirizzo base del registro della periferica sui cui effettuare la transazione e il DMA_CMAR in cui impostiamo l’indirizzo base della cella di memoria sui effettare la prima transazione. 2.2.6 Configurazione del DMA Per questo progetto ho utilizzato il canale 3 del DMA2 per inviare i codici da convertire al registro DHR 12 bit allineato a destra del DAC rispettando la tabella X. Per generare il nostro segnale periodico, quest’ultimo è stato campionato con un determinato numero di campioni che sono stati memorizzati in un vettore in memoria, poi tramite l’utilizzo del DMA questi sono stati fatti generare al DAC ottenendo il segnale voluto. In pratica ciò che abbiamo dovuto configurare per il nostro canale DMA sono stati i registri CPAR con l’indirizzo del DHR del DAC, CMAR con l’indirizzo della prima cella di memoria del 30 vettore in cui sono stati memorizzati i campioni, il registro CNDTR con il numero di transazioni da effettuare (ovvero pari al numero dei campioni scelto), poi tramite il registro CCR è stato configurato la priorità, la dimensione dei dati (entrambe a 16 bit), è stata impostata la modalità circolare (ovvero terminato di generare un periodo si ricomincia di nuovo per generarne uno nuovo in modo da avere un segnale periodico), incremento della memoria (in quanto i campioni sono memorizzati in celle di memoria adiacenti), e poi è stata impostata la direzione di trasferimento (in questo caso lettura dalla memoria). Ovviamente per far si che il segnale generato rispetti una determinata frequenza che viene da noi impostata dobbiamo far in modo di generare un nuovo campione in un istante di tempo prestabilito, per far ciò è stato utilizzato un Timer per guidare la generazione dei campioni. Nel prossimo capitolo diamo dei cenni sul funzionamento di questo dispositivo. 2.3 Timer La board STM32F3 presenta dei Timer hardware che possono essere usati per una varietà di scopi, come generare segnali a varie frequenze, generazione di PWM oppure essere usati da trigger per eventi a determinate frequenze, oppure per misurare il tempo trascorso tra due eventi o per effettuare conteggi. La board presenta fino a 10 timer che possono essere raggruppati in tre categorie, Basic Timers (TIM6-TIM7), General Purpose Timers (TIM2-TIM3-TIM4-TIM15-TIM16-TIM17), Advanced Timers (TIM1-TIM8). I Timer hanno tutti un’architettura comune, gli Advanced Timer hanno caratteristiche hardware aggiuntive. Tutti i Timers sono completamente indipendenti e non condividono alcuna risorsa, inoltre possono essere sincronizzati. 2.3.1 Principali Caratteristiche Timer General Purpose Di seguito si elencano le principali caratteristiche dei Timer General Purpose: • Up, down, up/down auto reload counter • Fino a 4 canali indipendenti per: Input capture, Output compare, PWM generation, One-pulse mode output 31 • Circuito di sincronizzazione per controllare il timer con segnali esterni o per interconnettere più timer Figura 14 2.3.2 Funzionamento di base Il contatore può essere configurato per effettuare un conteggio a incremento, decremento o centrale (incrementa e poi decrementa). Lo schema di base prevede tre registri, il counter register (CNT), prescaler register (PSC), auto-reload register (ARR). Il registro CNT è quello deputato a memorizzare il numero di conteggi effettuati, per misurare il tempo il timer conta il numero di impulsi di clock che si verificano dal quale conoscendo la frequenza di clock (72MHz) è possibile risalire al tempo trascorso. Il registo PSC viene utilizzato per dividere la frequenza del clock di un fattore compreso tra 1 e 65536. Il registro ARR invece contiene il numero di conteggi da effettuare, per leggere o scrivere su questo registro bisogna accedere al registro di preload. Il registro di preload modifica con il suo contenuto il valore del registro ARR in base al valore del bit ARPE (Auto Reload Preload Enable) nel 32 registro CR1, il particolare il registro ARR può essere modificato o in ogni istante dopo che il registro di preload è stato modificato oppure dopo che si è verificato un evento di update. L’evento di update si verifica quando il contatore raggiunge l’overflow (oppure underflow se stiamo contando a decremento). Inoltre è possibile scegliere una modalità di conteggio singola (faccio un unico conteggio e mi fermo) oppure continua (dopo aver terminato un conteggio azzero e riparto) configurando il bit OPM (One Pulse Mode) del registro CR1. Quando avviene un evento di update tutti i registri vengono aggiornati e viene settato il bit UIF (Update Interrupt Flag). L’evento di update può essere usato come trigger per guidare altre periferiche, per abilitare tale funzionalità bisogna settare i bit 4-5-6 del registro CR2 chiamati MMS (Master Mode Selection) con il valore 010. 2.3.3 Configurazione del Timer Per questo progetto ho utilizzato Timer 2 come trigger per la generazione dei campioni con il DAC. Il valore da inserire nel registro ARR del Timer viene calcolato in automatico in base alle scelte fatte dall’utente che inserisce la frequenza del segnale che si vuole generare. 2.4 Gestione delle Interruzioni La gestione delle interruzioni è demandata all’NVIC (Controllore del vettore delle interruzioni innestate), che gestisce 66 canali di interruzioni mascherabili, sedici linee di interruzione del core Cortex-M4. La priorità delle interruzioni è programmabile su 16 livelli. NVIC e il core sono fortemente accoppiati ciò permette di elaborare le interruzioni con una bassa latenza e di elaborare in modo efficiente interruzioni arrivate in ritardo. Tutte le interruzioni incluse le eccezioni del core sono gestite dall’NVIC come possiamo notare dalla tabella del vettore delle interruzioni a pag.183 del Reference Manual e di cui per comodità di seguito si riporta una parte. 33 Figura 15 Possiamo pensare alla memoria lineare come se fosse partizionata in varie zone, la prima è quella che corrisponde al vettore delle interruzioni, di questa zona una parte è dedicata al core, la successiva è quella dedicata alla gestione delle interruzioni generate dalle periferiche, tale servizio deve essere abilitato sia all’interno della periferica tramite un’opportuno bit, inoltre devo comunicare al core che il servizio delle interruzioni è abilitato, per questo il controllore del vettore delle interruzioni innestate (NVIC) ha dei registri ISER (Interrupt Service Enable Register) per abilitare il servizio della routine corrispondente, nel vettore delle interruzioni ho delle posizioni relative in cui a ogni posizione corrisponde una interruzione come si può notare dalla figura X, per abilitare il servizio di interruzione nel core della routine alla posizione x devo porre un uno nel registro NVIC->ISER alla posizione x. Ora dato che le posizioni nel vettore sono 92 e l’architettura del core è a 32 bit avremo 3 registri ISER uno che ricopre le posizioni da 0 a 31, il secondo 34 per le posizioni da 32 a 63 e l’ultimo le restanti. Dopo aver abilitato il servizio nel caso arrivi una richiesta di interruzione il processore salva il contesto corrente in uno stack, procede a servire l’interruzione saltando con il Program Counter alla relativa ISR terminata la quale riprende dallo stack il suo stato precedente e continua la sua esecuzione. 2.4.1 Extended interrupts and event controller (EXTI) EXTI gestisce eventi\interruzioni asincroni interni ed esterni, genera una richiesta di evento alla CPU\Controller delle interruzioni e una richiesta di risveglio al Power Manager. EXTI permette di manipolare 28 linee esterne e 8 linee interne, per le linee esterne il fronte attivo per l’interruzione può essere scelto in modo indipendente fra quello di salita, discesa oppure entrambi, mentre per le linee interne il fronte attivo è sempre quello di salita. Per la generazione di una interruzione nel caso di una linea esterna dobbiamo configurare la linea, dobbiamo scegliere il fronte che attiverà l’interruzione settando il bit relativo nel registro RTSR (Registro per la selezione del trigger sul fronte di salita) oppure FTSR (Duale del primo per quanto riguarda il fronte di discesa) oppure dobbiamo settare entrambi se vogliamo un’interruzione sia sul fronte di salita che su quello di discesa, poi bisogna abilitare la richiesta di interruzione nel registro IMR (Interrupt Mask Register) in quanto l’interruzione può essere anche mascherata e ovviamente abilitare il servizio nel core tramite l’NVIC come visto in precedenza. A questo punto quando si verifica il fronte selezionato viene generata una richiesta e viene settato un bit relativo a quella linea nel registro PR Registro di pending, quando serviamo la richiesta tale bit viene resettato scrivendoci un uno. Inoltre per quanto riguarda le richieste di interruzioni esterne possono essere emulate anche tramite software basta settare il bit relativo alla linea nel registro SWIER (Software Interrupt Event Register) infatti l’hardware di queste linee è multiplexato con i bit di quest’ultimo registro. La figura di seguito riporta il diagramma a blocchi di EXTI. 35 Figura 16 I PIN GPIO sono connessi su 16 linee esterne da EXTI0 a EXTI15 come di seguito: 36 Ogni PIN può essere selezionato configurando nel modo opportuno il registro EXTICR del SYSCFG. 2.4.2 Configurazione Interruzione esterna Per questo progetto è stato utilizzato il pulsante User della board per cambiare la forma d’onda generata. Dal datasheet tale pulsante è collegato al PIN 0 della porta A della board, e quando viene premuto porta tale pin al valore logico alto quando invece viene rilasciato lo porta al valore logico basso. Pertanto è stato abilitato il servizio di interruzione nel core della linea EXTI0, poi è stata smascherata la relativa linea nel registro IMR e selezionato il fronte di salita come fronte attivo. 37 Capitolo 3: Codice Di seguito si riporta il codice prodotto, opportunamente commentato e alcune foto relative a prove effettuate in laboratorio con uso dell’oscilloscopio. In questa prima parte di codice dapprima abbiamo trasformato il valore dell’ampiezza del segnale espresso in Volt nella corrispondente parola codice esprimibile con il DAC (riga 12), poi è stato trasformato il valore di frequenza inserito nel corrispondente periodo del segnale espresso in ms (riga 13). Inoltre abbiamo calcolato la costante di tempo del segnale esponenziale tau (riga 15) e il tempo di campionamento ossia il tempo che intercorre fra la generazione di un campione e quello successivo (riga 16). Poi è stato scelto di creare tre distinte LUT look-up table per i tre tipi di segnale che andremo a generare in ognuna delle quali vengono memorizzati i campioni del segnale relativi ad un singolo periodo, le righe 20-21 rappresentano il codice per la creazione della LUT relativa al segnale sinusoidale 38 (memorizzata nel vettore inputSIN), le righe da 25-29 si riferiscono al segnale triangolare (LUT memorizzata nel vettore inputTRIANGOLO), le righe 33-35 invece sono quelle relative al segnale esponenziale (LUT memorizzata nel vettore inputSIN) In questa seconda parte di codice dapprima è stato abilitato il clock delle varie periferiche utilizzate possiamo notare alle righe 39-40 che tramite i due registri dell’RCC AHBENR e APB1ENR è stato abilitato il clock della porta A (di cui utilizzeremo PA0 come fonte di interruzione esterna in quanto è collegato al tasto USER e PA4 sul quale è mappato l’uscita del DAC canale 1), il clock del DMA2 (infatti abbiamo utilizzato il canale 3 del DMA2 per scrivere i dati nel DHR del DAC), il clock del DAC (periferica utilizzata per effettuare la conversione), il clock di TIM2 (abbiamo utilizzato Timer2 per generare i campioni con il DAC in modo da rispettare i vincoli di frequenza del segnale da generare. Alla riga 41 invece abbiamo impostato PA4 (uscita del DAC) come analogico tramite l’utilizzo del registro MODER. Poi è stato configurato il canale 3 del DMA2 che come abbiamo già detto è stato utilizzato per inviare i dati al DAC, in particolare alla riga 45 è stato impostato il registro CPAR con l’indirizzo del DHR 12 bit allineamento a destra del DAC canale 1, invece alla riga 46 è stato configurato tale canale tramite il registro CCR, in particolare sono state impostate le dimensioni del dato a 16 bit, incremento della memoria ad ogni transazione e lettura dei dati dalla memoria, in seguito abbiamo impostato il DAC canale 1 (riga 51) tramite il registro CR è stato abilitato il servizio DMA, il funzionamento con trigger e in 39 particolare con il trigger generato dall’update di TIM2. Poi è stata eseguita l’impostazione del Timer 2 (righe 55-58), tale Timer è stato utilizzato come trigger per generare i campioni con il DAC e quindi è stato impostato in modo da misurare il tempo fra la generazione di un campione e il successivo rispettando il vincolo della frequenza del segnale da generare. In particolare è stato impostato il registro ARR con il valore del periodo/numero di campioni, poi è stato impostato il registro PSC paria a zero in quanto non è stata effettuata nessuna divisione del clock, poi tramite il registro CR2 è stato utilizzato l’evento di update come sorgente di trigger. Per ultimo è stata configurata l’interruzione esterna riguardante la pressione del tasto user (righe 62-66), notiamo che prima è stato abilitato il clock del SYSCFG tramite il registro APB2ENR dell’RCC, poi abbiamo impostato PA0 come fonte di interruzione esterna tramite il registro EXTICR[0] del SYSCFG (infatti il tasto user è collegato alla porta PA0), inoltre abbiamo smascherato l’interruzione sulla linea 0 tramite il registro IMR e poi abilitato l’interruzione sul fronte di salita tramite il registro RTSR, in ultimo abbiamo abilitato l’interruzione nell’NVIC tramite il registro ISER. In questa terza parte abbiamo invece la gestione dell’interruzione legata alla pressione del tasto user, possiamo notare che appena entriamo nella routine per prima cosa puliamo il bit di pending (riga 74). Notiamo che ad ogni pressione del tasto fermiamo il flusso da parte 40 del controller DMA (riga 78), poi in base al valore della variabile scelta terminiamo l’impostazione del canale DMA settando il registro CMAR che contiene l’indirizzo della prima cella di memoria del vettore in cui sono memorizzati i campioni da inviare al DAC. In particolare possiamo notare che quando scelta è uguale a 1 carichiamo CMAR con l’indirizzo della prima cella di memoria del vettore inputSIN, quando è pari a 2 con la prima cella di memoria del vettore inputTRIANGOLO, quando è pari a 3 con la prima cella di memoria di inputEXP. Poi impostiamo il registro CNDTR del canale 3 del DMA 2 con il numero di transazioni da eseguire che è pari al numero di campioni e abilitiamo il canale DMA (righe 102-103). A questo punto il codice del generatore è terminato, di seguito si riportano le misurazione eseguite in laboratorio con l’oscilloscopio nel caso di generazione del segnale con ampiezza pari a 3V e frequenza pari a 1KHz. Segnale Sinusoidale 41 Segnale Triangolare Segnale Esponenziale 42 Conclusioni Scopo di questo lavoro di tesi è stato la realizzazione di un generatore di forme d’onda arbitrarie basato su microcontrollore STM32F303, in particolare si è deciso di realizzare tre segnali periodici uno sinusoidale, uno triangolare e uno esponenziale di cui si può impostare ampiezza e frequenza. Per poter realizzare ciò si è pensato di costruire tre LUT (Look-UP Table) per contenere i campioni di un singolo periodo dei tre segnali, poi si è ottenuto il segnale desiderato tramite un DAC presente sulla board che ha trasformato tali campioni in segnali analogici. Ovviamente generando continuamente i campioni si è ottenuto il segnale sinusoidale; per far ciò è stato sfruttato l’utilizzo di un controller DMA anch’esso presente sulla board. Quindi siamo riusciti ad ottenere un generatore di segnali tramite l’utilizzo di un microcontrollore a basso costo e soprattutto con buone caratteristiche, infatti dal Datasheet della board fra le caratteristiche del DAC possiamo notare che ha un valore di INL (Integral non Linearity) ossia la differenza tra il valore misurato al codice I e il valore al codice I disegnato su una linea che congiunge il codice 0 e l’ultimo codice generabile con il DAC, tale differenza risulta essere ±4 LSB per un codice in ingresso a 12 bit. In conclusione i microcontrollori hanno avuto un grosso successo grazie alle numerose periferiche che incorporano, alla facilità di programmazione e soprattutto grazie al loro basso costo accoppiato alle elevate prestazioni. I microcontrollori sono presenti in svariati campi industriali, noi stessi li utilizziamo tutti i giorni senza nemmeno rendercene conto. 43 Bibliografia [1] A. Liccardo, Appunti del corso di Misure per l’Automazione e la Produzione Industriale, 2015 [2] STMicroelectronics, RM0316 Reference Manual [3] STMicroelectronics, STM32F302xx/STM32F303xx Datasheet [4] STMicroelectronics, UM1570 User Manual 44