Descrizione hardware
Hardware: Scheda Cedar "Pic Board - Studio"
Programma da usare: Microchip MPLAB X
Linguaggio da utilizzare: Assembly
Gestione eventi: microcontrollore in modalità sleep (dove possibile) in assenza di eventi da processare
Programmi utilizzati
- Microchip MPLAB X IDE
- Microchip Serial Bootloader AN1310
Descrizione software
Si realizzi un firmware che implementa un cronometro con risoluzione di un secondo e visualizza il tempo tramite porta seriale nel formato "mm:ss". Un pulsante permetta di arrestare e riattivare il cronometro, mentre un LED sia acceso quando il cronometro è attivo.
Svolgimento
Questo firmware vuole implementare un cronometro scrivendo un codice in Assembly. Per farlo viene usato il PIC16F887 con un clock interno a 4 MHz, installato in una scheda "Pic Board - Studio" prodotta dalla CEDAR Solutions. La scheda è dotata, in particolare, di un convertitore USB-seriale FTDI FT230X (U3) che permette un’agevole comunicazione tra il microcontrollore ed il computer. Il microcontrollore è fornito pre-programmato con il bootloader seriale AN1310 di Microchip; esso permette di programmare la memoria flash del microcontrollore senza disporre di un programmatore esterno. L’intero ciclo è gestito tramite polling, facendo sì che l’uso degli interrupt sia completamente assente.
Nella seguente facciata viene rappresentato il diagramma di flusso utile a vedere in maniera semplice ed immediata i passaggi che verranno svolti nel codice.
Codice
In questo paragrafo viene descritto in maniera dettagliata il codice utilizzato, andando a motivare ogni passaggio svolto.
Hardware usato
Si ha un generatore di clock esterno al quarzo a 32768 Hz, 1 pulsante collegato alla prima entrata del PORTB (RB0), 1 led collegato alla prima uscita del PORTD (RD0).
Tipo processore
Come prima cosa viene dichiarato il tipo di PIC utilizzato:
list p=16f887
Ogni micro dispone infatti di un set ben definito di istruzioni elementari, che sono naturalmente dedicate a quel singolo micro e variano proprio al variare del micro utilizzato. La direttiva successiva è la seguente:
#include <p16f887.inc>
Grazie alla quale posso semplificare in maniera sostanziale la scrittura del mio codice. Questo file contiene infatti le definizioni dei simboli, e mi permette di scrivere il nome dell’indirizzo invece che il suo numero, risultando così più semplice ed immediato. Ad esempio, se devo settare a 0 il bit 2 di STATUS, invece di scrivere bcf 03h, 2 – non proprio il modo più veloce per ricondursi al registro – scrivo direttamente bcf STATUS, Z.
Configuration bits
Questa operazione si effettua tramite la direttiva __CONFIG:
__CONFIG _CONFIG1, _INTRC_OSC_NOCLKOUT & _CP_OFF & _WDT_OFF & _BOR_OFF & _PWRTE_OFF &_LVP_OFF & _DEBUG_OFF & _CPD_OFF
I PIC dispongono di un registro di configurazione hardware che viene scritto una sola volta al momento della programmazione e che stabilisce il funzionamento di alcuni circuiti interni. In particolare per la realizzazione di questo firmware è stato disattivato il WatchDog_Timer (_WDT_OFF) ed è stato attivato il bit per il Low_Voltage_Programming (_LVP_ON).
Definizioni costanti
Questa operazione ha il vantaggio di rendere il codice più snello e leggibile e soprattutto permette di rendere molto più agevoli eventuali modifiche successive. In questo modo infatti, qualora fosse necessario andare a modificare i valori di alcuni parametri utilizzati, sarà sufficiente modificare il contenuto nella definizione della costante e non ogni singolo punto del programma in cui tale valore viene utilizzato. Con la direttiva EQU andiamo ad associare le costanti alle label che poi andremo ad utilizzare nel codice.
In questo firmware verranno utilizzate 2 costanti, quelle relative ai due timer usati, il TMR0 per il debouncing e il TMR1 per la risoluzione del cronometro.
tmr_20ms EQU (.256 - .78) tmr_1s EQU (.65536 - .32768)
Per quanto riguarda la prima scelta effettuata, essa viene svolta per risolvere il problema del bouncing: alla pressione e al rilascio di un pulsante possono verificarsi rimbalzi dei contatti meccanici (bouncing, appunto) che provocano la rilevazione di più eventi di pressione/rilascio da parte del microcontrollore, con conseguente esecuzione multipla delle operazioni associate. Esistono più soluzioni a questo problema. Una delle più semplici consiste nell’attesa di un tempo minimo tra due successive letture dello stato del pulsante, ed è questa che viene usata. Tuttavia, raddoppiando i soliti 10ms di attesa, si è riscontrato un annullamento di reset improvvisi, dovuti probabilmente al funzionamento non perfetto della scheda CEDAR. Il valore viene fatto partire da 256-78 = 178 poiché il periodo di un singolo incremento (tick) vale 256μs (il prescaler è stato impostato a 256 nel registro OPTION_REG, quindi il clock vale 3.90625 kHz), moltiplicando 256μs ∙ 78 ottengo proprio 20ms.
Il secondo timer è determinante nello svolgimento corretto del cronometro: esso infatti determina la risoluzione dello stesso, è se implementato in maniera errata, produrrà una discrepanza tra il tempo contato e quello effettivamente trascorso. Poiché il valore è consistente (1s), viene utilizzato il Timer1, la cui peculiarità è quella di avere 16 bit nel proprio registro; come settato nel registro T1CON, il prescaler utilizzato è settato a 1, la frequenza è 32768 Hz, tipica del quarzo esterno. Il timer viene fatto partire da 65536 – 32768= 32768, ovvero l’esatta metà. Infatti, valendo un singolo tick 30,518μs, ricavo con una semplice moltiplicazione che 30,518μs ∙ 32768 = 1s.
Definizioni variabili
Il passo successivo è definire le variabili che saranno poi necessarie per l’esecuzione del programma. Viene utilizzata la direttiva RES che permette di riservare il numero di byte desiderati e di associarli ad un nome a scelta del programmatore. Prima della direttiva RES si deve però specificare in quale punto della memoria RAM devono essere prese le locazioni di memoria da dedicare alle variabili. A questo scopo si utilizza la direttiva UDATA la quale indica appunto l’inizio di una sezione dati in cui è possibile riservare locazioni di memoria RAM da utilizzare come variabili. La direttiva UDATA può essere seguita da un indirizzo che specifica il punto preciso in cui far iniziare la sezione (codice non rilocabile) oppure, come nel nostro caso, se non viene specificato alcun indirizzo la sezione viene allocata automaticamente dal linker (codice rilocabile).
UDATA_SHR secondi_uni RES 1 secondi_dec RES 1 minuti_uni RES 1 minuti_dec RES 1
In questo modo riservo 1 byte di memoria ad ognuna di queste label, in maniera tale da poterle gestire successivamente all’interno del mio codice. La direttiva UDATA_SHR che qui viene usata è simile a UDATA con la differenza che in questo caso viene riservata una porzione di RAM condivisa tra tutti i banchi.
Definizione di reset
Nel codice è incluso il “vettore di reset”. Sarà il comando eseguito una volta che sulla scheda sarà attivato il pulsante di reset:
RST_VECTOR CODE 0x0000 pagesel start goto start
La direttiva “pagesel” si occupa proprio di generare automaticamente il codice necessario per commutare la pagina di ROM a quella in cui si trova l’indirizzo associato alla label indicata (in questo caso START).
Programma principale
Viene indicato l’inizio della porzione di codice che costituisce il corpo principale del programma; la direttiva CODE dichiara una sezione di codice da allocare in ROM. Con la label start comincia la stesura del codice; questo è inoltre il punto in cui riparte il codice dopo la pressione del tasto di reset.
start pagesel INIT_HW call INIT_HW
Andando così a chiamare immediatamente la subroutine INIT_HW, che andrà ad impostare tutti i dispositivi necessari al funzionamento del programma. Essendo una call, deve essere selezionata prima la pagina di memoria adeguata con “pagesel”.
INIT_HW
OPTION_REG
Il primo elemento ad essere impostato è il Timer0:
banksel OPTION_REG movlw B'00000111' movwf OPTION_REG
Per prima cosa vado a configurare il registro OPTION_REG che gestisce il Timer0 il quale verrà utilizzato per generare l’attesa di 20ms necessaria per il debouncing e varie altre funzionalità. Per poter andare a lavorare su un registro è necessario portarsi sul banco di memoria in cui si trova quel registro. Questa operazione viene eseguita grazie alla direttiva “banksel”. Movendo la literal scelta in OPTION_REG, imposto pull-up porta B abilitati (bit7=0), TMR0 incrementato da clock interno (1 MHz) (bit5=0), prescaler assegnato a TMR0 (bit3=0) e un prescaler a 256 (bit0-2=111).
INTCON
Concluso questo passaggio, si osserva che gli interrupt non verranno mai utilizzati in questo firmware, perciò è nell’inizializzazione che devono essere disattivati.
clrf INTCON
Porte I/O
Le varie porte A, C, B, E vengono settate come input digitali per tutti i pin, tranne RA0 settato come input analogico. Nella porta D i pin 0…3 sono settati come output (LED), mentre i pin 4…7 come input.
banksel TRISA movlw 0xFF movwf TRISA movwf TRISC movwf TRISB movwf TRISE movlw 0xF0 movwf TRISD
Anche qui, come già fatto in precedenza viene per prima cosa selezionato il banco di TRISA, lo stesso degli altri registri utilizzati in seguito (per questo motivo è necessario un solo banksel). Muovendo la literal FF (in binario ‘11111111’), si può soddisfare le richieste volute. Tuttavia copiando F0 in TRISD faccio in modo che solo i primi 4 bit della porta vengano impostati come output, 4 bit che coincidono con i 4 LED.
ANSELH
Di default, tutti i pin connessi all'ADC sono settati come input analogici, impedendone l'uso come I/O digitali. L'impostazione seguente rende I/O digitali i pin RB0...RB3:
banksel ANSELH clrf ANSELH
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
-
4 Esercitazioni Assembly Sistemi elettronici - No codice
-
Tesina di Architettura dei Calcolatori Elettronici
-
Tesina Architettura dei Calcolatori Elettronici
-
Tesina Architettura dei calcolatori elettronici