Gestione dei ritardi Generatore di onde quadre Il generatore di onde quadre consiste di un treno di onde luminose caratterizzate da un periodo il cui 50% il livello logico è alto e il restante 50% è a livello logico basso. Fare ciò, significa gestire il tempo e quindi i ritardi sui pic Un metodo molto semplice e forse, un po’ banale, è quello di far lavorare la macchina avuoto senza far eseguire alcuna operazione. L’istruzione per non far eseguire operazioni è nop. La routine è la seguente: LIST p=16F628 include "P16F628.inc“ ; libreria dove sono definiti I registri del 16f628 org 0x0000 movlw 0x07 movwf CMCON ;tutti i comparatori sono disattivati. Le porte che sono multiplexate, fungono solo ;da I/O bsf STATUS, RP0 ; viene posto a 1 il bit RP0 del registro Staus per selezionare il bank1 movlw b'00000000' movwf TRISB ;portb come output movwf TRISA ;porta come output bcf STATUS, RP0 ; viene posto a 0 il bit RP0 del registro Staus per selezionare il bank0 Loop del programma Loop movlw 0xff ; viene caricato il numero ;255 in formato esadecimale che tradotto un binario è 11111111 movwf PORTA ;porta è a livello logico alto movwf PORTB ;portb è a livello logico alto nop ; viene inserito un ritardo di 1 microsecondo ;perchè ogni ciclo macchina ha frequenza 1 Mhz nop ; per 1 us la macchina non esegue operazioni movlw 0x00 ; viene caricato lo zero movwf PORTA ;porta e portb sono a livello logico basso movwf PORTB nop nop goto Loop end Nop Nop è l’istruzione che non fa eseguire alcuna operazione. A che cosa può servire? Fa lavorare il micro a vuoto per mantenere fisso lo stato di un microcontrollore e non far cambiare stato. Quanto può durare un ritardo? Se la frequenza di clock è di 4 MHz, il periodo sarà di 0,25 us e il tempo macchina è 4*Tclock=1us Ogni ciclo macchina dura quindi 1 us se la frequenza di clock è 4 MHz. Il ritardo ottenuto è quindi 2 us visto che sono state introdotte due righe con l’istruzione nop Senza libreria Se non avessimo inserito la libreria avremmo dovuto dare delle direttive al compilatore: PORTA EQU 0x05 PORTB EQU 0x06 TRISA EQU 0x05 TRISB EQU 0x06 STAUS EQU 0x03 RP0 EQU 0x05 ; bit 5 del registro status CMCON EQU 0x1F Riga programma Registro interessato Stato deI bit Registro MOVLW 0X07 accumulatore 0 0 0 0 0 1 1 1 MOVWF cmcon cmcon 0 0 0 0 0 1 1 1 Bsf status,rp0 5 bit registro status 0 0 1 0 0 0 0 0 Movlw 0x00 accumulatore 0 0 0 0 0 0 0 0 Movwf trisa Trisa del bank1 0 0 0 0 0 0 0 0 Movwf trisb Trisb del bank1 0 0 0 0 0 0 0 0 Bcf status, rp0 5 bit registro status 0 0 0 0 0 0 0 0 Movlw 0xff accumulatore 1 1 1 1 1 1 1 1 Movwf porta Porta del bank0 1 1 1 1 1 1 1 1 Movwf portb Portb del bank0 1 1 1 1 1 1 1 1 nop Nessun registro Movlw 0x00 accumulatore 0 0 0 0 0 0 0 0 Movwf porta Porta del bank0 0 0 0 0 0 0 0 0 Movwf portb Portb del bank0 0 0 0 0 0 0 0 0 Ritardo con una routine di ritardo • Con pochi microsecondi di ritardo non possiamo renderci conto della differenza tra i due livelli logici e, gestire un ritardo più grande, comporterebbe la scrittura di un numero enorme di istruzioni nop. • Un metodo più efficace è quello di inserire un valore in un registro GFR e, decrementarlo. • La macchina rimane nel suo stato fino a quando il registro non è zero • Si passa alla istruzione successiva se tutto è nullo. • Useremo quindi decfsz salta alla istruzione successiva se il risultato è nullo • Il programma è il seguente: LIST p=16F628 include "P16F628.inc“ count1 equ 0x0C ; registro generale count2 equ 0x0D org 0x00 Ritardo con una routine di ritardo loop: movlw movwf bsf movlw movwf movwf bcf clrf clrf call bsf call 0x07 CMCON ;pone I comparatori a 1 STATUS, RP0 ;seleziona bank1 b'00000000' TRISB TRISA STATUS, RP0 ;seleziona banco 0 PORTA ;porta non viene mai usata PORTB Delay PORTB, 7 ;pone il bit 7 a livello logico alto Delay Ritardo con una routine di ritardo: subroutine di ritardo Spegni: Delay: Loop1: Loop2: bcf portb,7 ;pone a livello logico basso il bit 7 goto loop movlw 0x0f movwf conta2 ;pone il valore 15 in un GFR movlw 0x0f movwf conta1 ;pone il valore 15 in un GFR decfsz conta1 ;decrementa il registro e salta se zero goto Loop1 decfsz conta2 ;decrementa il registro e salta se zero goto Loop2 return end Osservazioni Nel programma sono stati settati sia i registri porta e trisa che non servono; non serve nemmeno il settaggio del registro comcon che setta i comparatori. Se fosse stato utilizzato tutto il registro trisa, sarebbe stato utile settare i comparatori. Si noti che il ritardo sul cambio di stato di ogni porta è di 30 us perché il decremento di ciascun registro 0ch e 0dh dura 15 cicli macchina e, ogni ciclo dura 1 us; 15 cicli durano 15 us. Siccome sono due registri da decrementare, il ritardo sarà di 30 us Per fare un ritardo più grande, si possono settare a 1 al massimo 8 bit di ciascun registro per un numero di cicli massimi di decrementi pari a 255 us esercizio Scrivere un programma che faccia accendere e spegnere alternativamente 2 led e ciascuno con un ritardo di 200 us Osservazioni Quando si incontra l’istruzione goto oppure call subroutine, si ha un salto e si interrompe il flusso del programma per eseguire la linea programma o la subroutine. Nella memoria programmi c’è una parte riservata allo stack pointer in cui si inserisce l’indirizzo in cui è stato interrotto il programma. Terminate le istruzioni in cui il programma è saltato, lo stack pointer fornisce l’indirizzo della memoria programma dove ritornare Registri Riga programma Registri Bit Movlw 0x07 w 0 0 0 0 0 1 1 1 Movwf cmcon Registro comparatore 0 0 0 0 0 1 1 1 Bit 5 di status 0 0 0 1 Bsf status,rp0 0 0 0 0 Movlw 0x00 w 0 0 0 0 0 0 0 0 Movwf trisb trisb 0 0 0 0 0 0 0 0 Bcf status,rp0 Bit 5 di status 0 0 0 0 0 0 0 0 Clrf portb portb 0 0 0 0 0 0 0 0 Bit 7 portb 1 Call delay Bsf portb,7 Call delay Goto loop 0 0 0 0 0 0 0 Delay Supponiamo di inserire un ritardo molto più piccolo Riga programmi Registri Bit Movlw 0x02 w 0 0 0 0 0 0 1 0 Movwf conta1 Registro 0x0c 0 0 0 0 0 0 1 0 Movlw 0x02 W 0 0 0 0 0 0 1 0 Movwf conta2 Registro 0x0d 0 0 0 0 0 0 1 0 Decfsz conta1 0x0c 0 0 0 0 0 0 0 1 Decfsz conta1 0x0c 0 0 0 0 0 0 0 0 Decfsz conta2 0x0d 0 0 0 0 0 0 0 1 Decfsz conta2 0x0d 0 0 0 0 0 0 0 0 Goto loop1 Return Ritorna all’ultima chiamata di subroutine. Un tempo di ritardo più lungo Delay: Loop2: Loop1: movlw movwf movlw movwf decfsz goto decfsz goto return end 0x0f conta2 ;pone il valore 15 in un GFR 0x0f conta1 ;pone il valore 15 in un GFR conta1 ;decrementa il registro e salta se zero Loop1 conta2 ;decrementa il registro e salta se zero Loop2 Questa volta, per ogni decremento di conta2, si ritorna a loop2 Ogni decremento di conta2 genera 15 decrementi di conta1 I decrementi terminano quando conta2 è zero Il tempo di ritardo è 255 us=15*15 Sorgenti di segnali Ogni microcontrollore lavora secondo un certo sincronismo I segnali di sincronizzazione possono essere interni o esterni Se esterni, viene applicato un generatore di impulsi ad una certa frequenza su un determinato pin. Nel caso del pic16f628, il segnale viene applicato sul pin 3 Prescaler e interrupt Il pic 16f628 è dotato di un timer interno ad 8 bit. Il registro timer0, registro del bank0 all’indirzzo 01h, incrementa il proprio contenuto per ogni ciclo macchina L’oscillatore interno del pic genera una frequenza f=4 MHz; la frequenza di un qualsiasi segnale proveniente dal pic è quindi di f/4=1 Mhz, frequenza macchina. Il registro timer0 incrementa così il proprio contenuto con la frequenza di 1 MHz La frequenza di interruzione, frequenza di interrupt, si ha quando il timer inizia a contare daccapo, cioè quando passa da overflow a zero. Essendo il timer0 di 8 bit, l’interrupt si ha ogni 28 -1 cicli macchina+1=256 contando anche il passaggio da overflow a zero Frequenza di interrupt La frequenza fi di interrupt si calcola dividendo la frequenza macchina per 256: fi=f(osc)/(4*256) Si potrebbe cambiare la frequenza di interrupt con due metodi: Inizializzando il timer con un valore Nt diverso da 0. • È come accorciare la lunghezza del timer così impiegherà meno tempo per andare in overflow • La nuova frequenza fi di interrupt sarà: fi=f(osc)/4/(256-Nt) La frequenza di interrupt può cambiare se si inserisce un prescaler, cioè un divisore di frequenza. • Il prescaler è formato dai tre bit del registro option che si trova all’indirizzo 81h, è cioè un registro di bank1 Esercizi per impostare l’interrupt senza prescaler Calcolare il periodo di interrupt se Nt=0 2. Calcolare il periodo e la frequenza di interrupt se Nt=200 3. A quanto bisogna impostare Nt se si chiede che fi=300 Hz 1. Registro Option Bit 0 Bit 1 Bit 2 Bit 3 Bit 4 RBPU Disabilita o abilitai resistori interni di pull up 0 disabilita 1 abilita INTEDG Seleziona il fronte di salita di interrupt su RB0/INT 0 fronte salita TOCS Seleziona il fronte di segnale di clock del timer 0 clock interno Seleziona il fronte del segnale per il clock del timer su RA4/TOCKL 0 fronte di discesa Assegna il prescaler l timer TMR0 o al WDT 0 TMR0 TOSE PSA Bit 5 PS2 Bit 6 PS1 Bit 7 PS0 1 fronte discesa 1 clock esterno 1 fronte di salita 1 WDT Selezione il fattore di divisione per il TMR0 Un piccolo schema Prescaler Ps2 Ps1 Ps0 Divisore 0 0 0 2 0 0 1 4 0 1 0 8 0 1 1 16 1 0 0 32 1 0 1 64 1 1 0 128 1 1 1 256 Se si vuole cambiare la frequenza fi di interrupt con Np, il fattore di divisione di prescaler fi assumerà la seguente formula matematica: fi=fosc/4/Np Prescaler La frequenza di interrupt può essere cambiata anche utilizzando sia il prescaler che cambiando il punto di partenza del timerO. La frequenza fi può cambiare nel seguente modo: fi=fosc/4/Np*(256-Nt) Esempio con il prescaler • • • • • • • Supponiamo di voler generare un interrupt ogni 3 ms Ti=256*Tm=256*4*Tclock=256/fosc/4 Si inserisce il prescaler per cambiare Ti Ti=256*Np/f/4 Imponendo Ti=3 Np=Ti/256*fosc/4=11.72 Questo valore non è nelle tabelle del prescaler Si può far partire il conteggio del timer da un valore diverso da zero • Si deve allora calcolare il valore Nt da dove far partire il timer • Si può utilizzare la seguente formula per calcolare il periodo di interrupt Ti=Np*((256-Nt)/fosc/4 • Se si impone Ti=3 ms, Np=64, si calcola Nt=2563000/64=209,125=209 esempio Si vuole accendere e spegnere un led collegato su RA0 di un pic16f628 con periodo di 1 secondo. Si utilizza un clock di 4 MHz. Se si impone un divisore di prescaler pari a 64, la frequenza di interrupt diventa 15625 Con un semplice conto, si calcola che per avere 1 Hz basta far partire il timer da 125 Esercizi Se Nt=200 e Np=512, per un clock con frequenza 4 MHz, che valore assumeranno fi e ti? Se si vuole una frequenza fì pari a 300 con un clock di 4 MHz, come bisogna combinare Np ed Nt? Registro INTCON GIE EEIE TOIE INTE RBIE TOIF INTF RBIF Abilitazione di tutti gli interrupt 0 disabilitati 1 abilitati Segnala il completamento nella memoria EEPROM 0 Non completo 1 Completo Abilita l’interrupt per il superamento di capacità del timer0 0 Disabilitato 1 Abilitato Abilita l’interrupt su RB0/INT 0 Disabilita 1 Abilita Abilita l’interrupt per il cambio di livello su RB7RB4 0 Disabilita 1 abilita Segnala overflow su timer0 0 No Overflow 1 Overflow avvenuto 0 Nessuna richiesta 1 Richiesta avvenuta 0 Nessun cambio livello 1 Cambio di più livelli Segnala la richiesta di interrupt su RB0/INT Segnale di cambio di livello su RB7-RB4 Routine di interrupt senza prescaler La routine di interrupt va scritta a partire dalla locazione 004 della memoria programma Se si vuole utilizzare il timer senza inserire il prescaler, bisogna seguire questa sequenza di passaggi: Scrivere una routine di interrupt a partire dalla locazione 004. Nella routine bisogna dare le seguenti informazioni: • Si carica il valore dal quale bisogna far partire il timer • Azzerare il flag di avvenuto interupt, bit 2 del registro INTCON • Ritornare al programma principale tramite il comando RETFIE Nella routine principale bisogna scrivere: • Porre a zero il bit 5 del registro OPTION, cioè porre in modalità timer • Caricare il valore iniziale del timer • Abilitare l’interrupt ponendo a 1 il bit 5 di INTCON • Abilitare gli interrupt ponendo a 0 il bit 7 del registro INTCON Routine di interrupt con prescaler • Bisogna scrivere la routine di interrupt e partire dalla locazione 004 della memoria programma o ricaricare il valore iniziale del timer o azzerare il flag di avvenuto interrupt tramite il bit 2 del registro INTCON o terminare la routine di interrupt tramite l’istruzione RETFIE • Nel programma principale si scrivono le seguenti istruzioni: o assegnare il prescaler al timer ponendo a 0 il bit 3 del registro OPTION o selezionare il fattore di divisione del prescaler o assegnare il valore di inizio del timer o abilitare l’interrupt del timer ponendo a 1 il bit 5 di INTCON o abilitare tutti gli interrupt ponendo a 1 il bit 7 di INTCON Come settare il prescaler esempio Si vuol far accendere e spegnere un led su RA1 con frequenza 1 Hz. Si utilizzi un quarzo di 4 MHz f(clock)=4000000Hz; f(macchina)=1000000 Hz Si pone Np=(64) 10=combinazione prescaler 101 256-Nt=125 Lampeggio di un led con frequenza di 1 Hz list p=16f628 radix dec porta equ 5 portb equ 6 Timer equ 1 intcon equ 0x0b cont equ 0x0c flag equ 0x0d toif equ 0x02 toie equ 0x05 gie equ 0x07 goto start Lampeggio led: routine interrupt org 4 interrupt: incf movlw movwf bcf retfie cont,1 0x00 timer intcon, toif ;ritorno dall’interrupt Lampeggio di un led:routine principale start: movlw 0xd5 ; carica il valore 11010101 movwf option RBPU INTEDG TOCS TOSE PSA PS2 PS1 PS0 1 1 0 1 0 1 0 1 Divisore di prescaler=64 movlw 0xff tris portb movlw 0x00 tris porta Clock interno Prescaler al timer Lampeggio di un led movwf timer ;l’accumulatore è ancora azzerato movwf cont bsf intcon, toie ;abilita l’interrupt timer bsf intcon, gie ;abilita gli interrupt ancora: movlw 125 xorwf cont,0 ;esegue l’operazione logica XOR tra cont e W e pone il risultato in W skpz ; salta se il risultato è zero. Il conteggio si ferma a 125 goto ancora avanti: movwf cont comf flag,1 ;complementa flag e pone il risultatp in flag btfsc flag,0 ;testa il bit 0 e salta se 0 goto accendi bcf porta,0 goto ancora accendi: bsf porta,0 goto ancora end