Esercitazioni ASM - PIC18

 


ESERCITAZIONE # 4


LED lampeggiante - routine di tempo

 

L' operazione di fare lampeggiare un LED è di per se semplice, ma richiede l' inserimento di un un ulteriore elemento: il tempo.

Infatti cosa vuol dire lampeggiare ? Come nella barzelletta "ora va, ora non va, ora va,...".

Ovvero un lampeggio è la sequenza di istanti in cui il LED è acceso, intervallati da altri in cui è spento.
Si potrebbe essere tentati inizialmente di impostare il programma come segue:

E potrebbe parere corretto, ma non lo è, in quanto viene trascurato l' elemento "tempo". Certamente il LED "lampeggia", ma a noi parrebbe sempre acceso !

Perchè il LED appaia illuminato alla vista occorre che sia acceso per un certo tempo, necessario alla retina per cogliere l' immagine.
E per apparire spento dovrà esserlo per un tempo adeguato.

Se facciamo svolgere l' azione di spegnere e accendere il LED in successione dalle istruzioni del processore, dobbiamo ricordare che queste, con un clock di 1 MHz impiagano ognuna tipicamente 250 us.

Avremmo il LED acceso per qualche centinaio di uS, poi spento per altrettanto e così via. Il risultato per l' occhio sarebbe quello di percepire il LED costantemente acceso ad una luminosità non proprio massima.

Dovrà quindi essere un obbligo l' inserire nel flusso delle istruzioni una qualche procedura che temporizzi questi stati di on e off in modo tale da far apparire allo sguardo il LED come lampeggiante.

 

Per cui sarà necessario modificare il flow chart così:

Adesso la sequenza è composta dagli elementi sufficienti alla visione del lampeggio.

Il led viene acceso, dopo di che questo stato viene prolungato per il tempo che si ritiene adeguato.

Poi il led viene spento e anche qui si attende un tempo adegato prima di riprendere il loop.

Dato che i blocchi di attesa sono creati con istruzioni, posiamo dare loro la durata che più ci aggrada, ottenendo così ogni genere di lampeggio, veloce o lento e con tempi on e off determinabili separatamente.

Esistono vari modi per creare queste routines di attesa, ma qui utilizziamo la più semplice, ovvero quella detta waste time (tempo perso), dato che il processore non ha alcuna altra attività da svolgere nel frattempo.

Una trattazione dettagliata del modo waste time la trovate qui.

In particolare, onde evitare per ora calcoli sui tempi di ciclo delle istruzioni, utilizziamo il calcolatore disponibile in rete per la creazione di ritardi di tempo, il noto Delay Generator di Nicolai Golovchenko.

Il nostro esercizio prevede di collegare un LED al bit 0 di PORTC: esso lampeggerà alla frequenza di 2 impulsi al secondo, determinati dai cicli di attesa e dalla frequenza del clock del processore.

Va ricordato che stiamo utilizzando il clock interno in modalità INTIO1; il default di questo modo imposta un oscillatore interno a 1 MHz, il che corrisponde ad un ciclo istruzione di 4 us.

Ovviamente, dovendo visualizzare il risultato, il progetto imposta Pickit3 come debugger e viene utilizzata la Uniboard, direttamente alimentata a 5V dal Pickit.

Si dovranno semplicemente collegare il pin PC0 ad un dei LED della scheda.
Questo pulsante può essere benissimo quello del RESET, che non viene usato durante l' esercizio.

Il Pickit è inserito direttamente nella spina ICSP/ICD e permette un debug passo passo delle istruzioni.

Ovviamente sarà possibile utilizzare Pickit2 senza variazioni se non la diversa impostazione nell' ambiente MPLAB; oppure anche REAL ICE o ICD, sempre con il semplice cambio nel setup di MPLAB, tenendo presente che non tutti i tools sono in  grado di alimentare l' hardaware.
Inoltre è sempre possibile utilizzare SIM senza alcun hardware o debugger collegato.

Una descrizione particolareggiata delle funzioni dei PORT è consultabile qui.


Per chi non avesse chiare le connessioni, qui trova di seguito una pagina dedicata.

L' esercizio richiede le seguenti risorse:

  • MPLAB IDE installato
  • Pickit3 (o Pickit2)
  • 28-40pin UniBoard con PIC18F2321 0 4321 (o 2221 o 4221 o altro hardware similare)
  • un cavetto jumper da 14-15 cm

Il listato sorgente è una estensione di quello già visto negli altri esercizi e che fa da base per tutti i seguenti, aggiungendo gli elementi necessari al nuovo lavoro.

Da un punto di vista strutturale il programma agisce in questo modo:

  1. Azzeramento dei latch del PORTC. Questa operazione preliminare serve per pre determinare il livello che i pin assumeranno  nel momento in cui saranno configurati come uscite. In questo caso il LED collegato a PC0 si troverà a livello basso, quindi sarà spento.
  2. Imposizione della direzione uscita per i bit 0 dei PORTC.
  3. Accensione del LED.
  4. Attesa con il LED acceso
  5. Spegnimento del LED
  6. Attesa con il LED spento
  7. Loop infinito al punto 3. 

Vediamo il listato nei dettagli.

Come inizio, è sempre presente una testata di descrizione del programma e delle sue funzioni e la già descritta definizione del processore usato, a cui segue la sezione di configurazione.


;*****************************************************************
; Esercitazioni PIC18 - Esercitazione # 4
;*****************************************************************
; Lampeggio di un LED - routine di tempo
; Author  : afg
; Version : E18_4_00
; Date    : 09/10/2010
;-----------------------------------------------------------------
; Descrizione: Il programma fa lampeggiare un LED collegato a PC0.
;-----------------------------------------------------------------
; Note : Processore PIC18F2321
; Oscillatore interno a 1 MHz (clock interno 250 us)
; senza la necessità di componenti esterni
; Debug con MPLAB IDE e PickKit3
; Previsto per funzionare con tutti gli enhanced
;*****************************************************************

         LIST P=18F2321 ; Utilizziamo il PIC18F2321
         radix dec      ; con base decimale per le operazioni
                        ; matematiche


         #include "P18F2321.INC" ; Include l' header file

; questo header è fornito dallo stesso Assembler e non richiede 
; alcun file addizionale

;---------------------------------------------------------------------
; Configurazione minima dei debug del processore
;
; Disabilita analogica dal PORTB
 
CONFIG PBADEN = DIG 
; Background debugger enabled su RB6 e RB7 per il debug con il 

; Pickit o altro, attraverso ICSP/ICD
  CONFIG DEBUG = ON 
; Single-Supply ICSP disabled 
  CONFIG LVP = OFF 
; Uso dell' oscillatore interno, port su RA7 e FOSC/4 su RA6

  CONFIG OSC = INTIO1 
; PWRT disabled per il debug 
  CONFIG PWRT = OFF
; Brown-out in hardware only 
  CONFIG BOR = ON 
; Soglia BOR 4.2V 
  CONFIG BORV = 1
; Funzione del pin MCLR abilitata 
  CONFIG MCLRE = ON
; WDT disabilitato 
  CONFIG WDT = OFF 

 


Assegniamo un paio di locazioni in RAM con la direttiva CBLOCK/ENDC.
Nel caso di questo programma, i due registri serviranno come supporto alle routines di attesa.

;----------------------------------------------------------------------
;Assegna registri di memoria RAM


 CBLOCK 0x00 ; blocco di RAM a partire da 0x00
   d1        ; riserva 2 bytes per un contatore
   d2
 ENDC       
; fine blocco RAM

Nell' area degli Equates inseriamo alcune definizioni che ci serviranno durante il programma.

Da notare che utilizziamo il registro LATC al posto di PORTC dato che è più adeguato e che gli enhanced prevedono questa possibilità per eliminare il problema dell' R-M-W.

Possiamo anche cerare due macro per il comando del LED, con la finalità di rendere più leggibile il listato.

;----------------------------------------------------------------------
; Equates 
;

; assegnazioni per il LED
#define LED  LATC,0 ; LED collegato a RC0

;----------------------------------------------------------------------
; Macros
;
LedOn   MACRO
     
bsf   LATC,0
       
ENDM

LedOff  MACRO
     
bcf   LATC,0
       
ENDM     

 


Ecco il "programma" vero e proprio, con ampi commenti che descrivono le funzioni svolte da ogni riga.

Per i salti viene usata l' istruzione bra (BRanch Always) tipica degli enhanced.

;=====================================================================
; Inizio programma
; Il programma:
; - imposta i pin RA0, RB0, RC0 come uscite
; - porta questi pin a livello alto
; se ad essi c'è collegato un LED verso massa, esso verrà acceso



   ORG 0x00         ; Programma inizia a 0x00 - vettore del reset

Start  nop          ; dummy - linea utile solo ai fini del debug

; inizializza a 0 i latch di uscita dei PORTC
; questo serve per avere subito a livello 0 i pin che saranno programmati come uscite
; non ha effetto sui pin programmati come ingressi.

       clrf   LATC

; inizializza PORTC,0 come output
; agendo sul registro di direzione
       bcf    TRISC,0 

; Ciclo di lampeggio
loop   LedOn               ; LED acceso
       call   Attesa       ; Attesa .25 s
       LedOff              ; LED spento
       call   Attesa       ; Attesa .25 s

; il programma entra in un loop infinito sul 
; test pulsante - azionamento LED

       bra    loop    

Dato che utilizziamo la stessa sequenza di attesa più volte diventa sensato formularla come una subroutine e richiamarla con l' istruzione call o rcall.

Le subroutines sono procedure che vengono usate in un programma principale, diciamo dei sotto-programmi, che svolgono una funzione che viene ripetuta più volte o che viene tratta da una libreria in cui è conservata dopo essere stata provata.
Questa struttura consente di avere listati leggibili senza essere appesantiti da ripetizioni inutili, mentre la subroutine può essere un elemento ri utilizzabile in altre situazioni (per questo collezionabile in una libreria o raccolta di funzioni).

Solitamente è più pratico porre le subroutines in coda la programma; questo perchè sono funzioni definite di per se e già verificate funzionanti e solitamente non occorre la loro analisi durante il debug del programma principale. Vengono utilizzate come black box dei quali interessa il dato in ingresso e quello in uscita e non in particolare come esso venga elaborato.

Qui viene usata una procedura waste time generata dal sito di Nicolai Golovchenko e modificata per l' uso con il set di istruzioni enhanced.
Si tratta semplicemente di caricare due registri di RAM (d1 e d2) con valori esadecimali calcolati e decrementarli fino a portarli a zero. Siccome ogni istruzione a 1 MHz impiega 4 us per essere eseguita, creando un loop che "spreca" 62500 cicli si ottiene una attesa di .25 s.

=====================================================================
; Subroutines

; Attesa .25 s
; Procedura ricavata dal Delay Generator di Golovchenko
; Sorgente modificato per l' uso con il set di istruzioni enhanced
; Delay = 0.25 seconds
; Clock frequency = 1 MHz
; Actual delay = 0.25 seconds = 62500 cycles
; Error = 0 %

Attesa			;62493 cicli
	movlw	0xD2
	movwf	d1
	movlw	0x31
	movwf	d2
Delay_0
	decfsz	d1, f
	 goto	$+6 (2)
	decfsz	d2, f
	goto	Delay_0
			;3 cicli
	goto	$+2 (1)
	nop
			;4 cicli (includendo la call)
	return

;********************************************************************
; Direttiva di fine sorgente

   END

La fine del sorgente è determinata come al solito dalla direttiva END

Pagine di informazione sulle routine di tempo come quelle usate ora è disponibile qui.

Da notare che il programma vero e proprio, invece, non termina, dato che permane nel loop che fa lampeggiare il LED


Avviando lo step-by-step  potremo seguire il flusso delle istruzioni ed osservare nella finestra degli SFR lo stato  e l' accensione di LED, mentre nella finestra della RAM è possibile visualizzare lo stato dei contatori d1 e d2.

Si ricorda che, in step by step, per saltare le routine di tempo, che sono loop ripetitivi di decremento di due contatori, quindi ben poco significativi per il resto del programma, il tasto Step Over di MPLAB permette l' esecuzione delle subroutines, ma senza visualizzazione, salvando una quota notevole di tempo durante il debug.

Se si incontrano errori nella compilazione è opportuno verificarli con la lista di descrizione degli errori e correggere dove si è sbagliato.


Il file compresso di questa esercitazione è scaricabile dall'  area di download.

 

 

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 04/04/11.