- Perché Timer quando abbiamo Delay ()?
- Timer microcontrollore PIC:
- Spiegazione di programmazione e funzionamento:
- Schema del circuito e simulazione di Proteus:
Questo sarà il quinto tutorial della nostra serie di tutorial PIC, che ti aiuterà a imparare e utilizzare i timer in PIC16F877A. Nei nostri tutorial precedenti, eravamo partiti con Introduzione a PIC e MPLABX IDE, poi abbiamo scritto il nostro primo programma PIC per far lampeggiare il LED usando PIC e poi abbiamo realizzato una sequenza di lampeggiamento LED usando la funzione di ritardo nel microcontrollore PIC. Ora usiamo la stessa sequenza di lampeggiamento LED che abbiamo usato nell'hardware del tutorial precedente e con questo impareremo come usare i timer nel nostro MCU PIC. Abbiamo appena aggiunto un altro pulsante nella scheda LED per questo tutorial. Segui il tutorial per saperne di più.
I timer sono uno dei cavalli di battaglia importanti per un programmatore integrato. Ogni applicazione che progettiamo coinvolgerà in qualche modo un'applicazione di temporizzazione, come l'attivazione o la disattivazione di qualcosa dopo un intervallo di tempo specificato. Ok, ma perché abbiamo bisogno di timer quando abbiamo già macro di ritardo (__delay_ms ()) che fanno la stessa cosa !!
Perché Timer quando abbiamo Delay ()?
Una macro Ritardo è chiamata ritardo "dump". Perché durante l'esecuzione della funzione Delay, l'MCU esegue il dump semplicemente creando un ritardo. Durante questo processo l'MCU non può ascoltare i suoi valori ADC o leggere nulla dai suoi registri. Pertanto non è consigliabile utilizzare le funzioni di ritardo tranne per applicazioni come il lampeggiamento del LED in cui il ritardo non deve essere preciso o lungo.
Le macro di ritardo hanno anche le seguenti carenze,
- Il valore di ritardo deve essere una costante per le macro di ritardo; non può essere modificato durante l'esecuzione del programma. Quindi rimane è definito dal programmatore.
- Il ritardo non sarà accurato rispetto all'utilizzo dei timer.
- Non è possibile creare valori più grandi di ritardi utilizzando le macro, ad esempio un ritardo di mezz'ora non può essere creato dalle macro di ritardo. Il ritardo massimo che può essere utilizzato si basa sull'oscillatore Crystal utilizzato.
Timer microcontrollore PIC:
Fisicamente, il timer è un registro il cui valore aumenta continuamente fino a 255, quindi ricomincia da capo: 0, 1, 2, 3, 4… 255…. 0, 1, 2, 3……eccetera.
L' MCU PIC PIC16F877A ha tre moduli timer. Sono nomi come Timer0, Timer1 e Timer2. Il timer 0 e il timer 2 sono timer a 8 bit e il timer 1 è un timer a 16 bit. In questo tutorial useremo il Timer 0 per la nostra applicazione. Una volta compreso il timer 0, sarà facile lavorare anche sul timer 1 e sul timer 2.
Il timer / contatore del modulo Timer0 ha le seguenti caratteristiche:
- Timer / contatore a 8 bit
- Leggibile e scrivibile
- Prescaler programmabile via software a 8 bit
- Selezione orologio interno o esterno
- Interruzione in caso di overflow da FFh a 00h
- Selezione bordo per clock esterno
Per iniziare a utilizzare un timer dovremmo comprendere alcuni termini fantasiosi come timer a 8 bit / 16 bit, Prescaler, Timer interrupts e Focs. Ora, vediamo cosa significa veramente ciascuno. Come detto in precedenza, nel nostro MCU PIC ci sono sia i timer a 8 bit che quelli a 16 bit, la differenza principale tra loro è che il timer a 16 bit ha una risoluzione molto migliore rispetto al timer a 8 bit.
Prescaler è un nome per la parte di un microcontrollore che divide l'orologio dell'oscillatore prima che raggiunga la logica che aumenta lo stato del timer. Il range del prescaler id va da 1 a 256 e il valore del Prescaler può essere impostato tramite il registro OPTION (lo stesso che abbiamo usato per le resistenze di pull up). Ad esempio, se il valore del prescaler è 64, allora per ogni 64esimo impulso il Timer verrà incrementato di 1.
Man mano che il timer aumenta e quando raggiunge il valore massimo di 255, attiverà un interrupt e si inizializzerà nuovamente a 0. Questo interrupt viene chiamato interrupt timer. Questa interruzione informa l'MCU che questo particolare tempo è trascorso.
Il Fosc sta per Frequenza dell'Oscillatore, è la frequenza del Cristallo utilizzato. Il tempo impiegato per il registro Timer dipende dal valore di Prescaler e dal valore di Fosc.
Spiegazione di programmazione e funzionamento:
In questo tutorial imposteremo due pulsanti come due ingressi e 8 LED come 8 uscite. Il primo pulsante verrà utilizzato per impostare il ritardo (500 ms per ogni pressione) e il secondo pulsante verrà utilizzato per avviare la sequenza del timer lampeggiante. Ad esempio, se il primo pulsante viene premuto tre volte (500 * 3 = 1500 ms), il ritardo verrà impostato per 1,5 secondi e quando il pulsante due viene premuto ogni LED si accenderà e spegnerà con il ritardo predefinito. Guarda il video dimostrativo alla fine di questo tutorial.
Ora, con queste basi in mente, diamo un'occhiata al nostro programma fornito alla fine nella sezione Codice.
Va bene se non hai ricevuto il programma, ma se l'hai fatto !! Datti un biscotto e scarica il programma per goderti il tuo output. Per altri suddividerò il programma in parti significative e vi spiegherò cosa sta succedendo in ogni blocco.
Come sempre le prime righe del codice sono le impostazioni di configurazione ei file di intestazione, non ho intenzione di spiegarlo poiché l'ho già fatto nei miei tutorial precedenti.
Successivamente, saltiamo tutte le righe e saltiamo direttamente nella funzione principale void, all'interno della quale abbiamo la configurazione PORT per il Timer0.
void main () {/ ***** Configurazione porta per timer ****** / OPTION_REG = 0b00000101; // Timer0 con freq esterna e 64 come prescalar // Abilita anche PULL UP TMR0 = 100; // Carica il valore temporale per 0.0019968s; delayValue può essere compreso tra 0 e 256 solo TMR0IE = 1; // Abilita il bit di interrupt del timer nel registro PIE1 GIE = 1; // Abilita interrupt globale PEIE = 1; // Abilita l'interruzione periferica / *********** ______ *********** /
Per capirlo dobbiamo guardare il registro OPTION nella nostra scheda tecnica PIC.
Come discusso nel tutorial precedente, il bit 7 viene utilizzato per abilitare il resistore pull up debole per il PORTB. Guardate la figura sopra, il bit 3 è impostato a 0 per indicare all'MCU che il seguente prescaler che viene impostato deve essere utilizzato per il timer e non per il WatchDogTimer (WDT). La modalità timer viene selezionata cancellando il bit 5 T0CS
(OPTION_REG <5>)
Ora, i bit2-0 vengono utilizzati per impostare il valore del prescaler per il timer. Come mostrato nella tabella sopra, per impostare un valore di prescaler di 64, i bit devono essere impostati come 101.
Successivamente, esaminiamo i registri associati a Timer0
Il timer inizierà ad aumentare una volta impostato e andrà in overflow dopo aver raggiunto un valore di 256, per abilitare l'interrupt del timer durante questo punto il registro TMR0IE deve essere impostato su alto. Poiché il timer 0 stesso è una periferica, dobbiamo abilitare l'interrupt periferico impostando PEIE = 1. Infine dobbiamo abilitare il Global Interrupt in modo che l'MCU venga avvisato dell'interruzione durante qualsiasi operazione, questo viene fatto rendendo GIE = 1.
Ritardo = ((256-REG_val) * (Prescal * 4)) / Fosc
La formula sopra viene utilizzata per calcolare il valore di Ritardo.
Dove
REG_val = 100;
Prescal = 64
Fosc = 20000000
Questo sul calcolo dà, Ritardo = 0,0019968 s
Il prossimo set di linee è quello di impostare le porte I / O.
/ ***** Configurazione porta per I / O ****** / TRISB0 = 1; // Indica all'MCU che il pin 0 di PORTB viene utilizzato come ingresso per il pulsante 1. TRISB1 = 1; // Indica all'MCU che il pin 1 PORTB viene utilizzato come ingresso per il pulsante 1. TRISD = 0x00; // Indica all'MCU che tutti i pin sulla PORTA D vengono emessi PORTD = 0x00; // Inizializza tutti i pin su 0 / *********** ______ *********** /
Questo è lo stesso del nostro tutorial precedente poiché stiamo usando lo stesso hardware. Tranne che abbiamo aggiunto un altro pulsante come input. Questo viene fatto dalla linea TRISB1 = 1.
Successivamente, al rovescio infinito ciclo while abbiamo due blocchi di codice. Uno viene utilizzato per ottenere l'input del timer dall'utente e l'altro per eseguire la sequenza di ritardo sui LED. Li ho spiegati usando commenti su ogni riga.
while (1) {count = 0; // Non eseguire il timer mentre si è nel loop principale // ******* Ottieni il ritardo del numero dall'utente **** ////// if (RB0 == 0 && flag == 0) // Quando input fornito {get_scnds + = 1; // get_scnds = get_scnds + http: // Increment variable flag = 1; } if (RB0 == 1) // Per impedire l'incremento continuo flag = 0; / *********** ______ *********** /
Una variabile chiamata get_scnds viene incrementata ogni volta che l'utente preme il pulsante 1. Una variabile flag (definita dal software) viene utilizzata per mantenere il processo di incremento finché l'utente non rimuove il dito dal pulsante.
// ******* Esegue la sequenza con ritardo **** ////// while (RB1 == 0) {PORTD = 0b00000001 <
Il blocco successivo entra in azione se viene premuto il pulsante due. Poiché l'utente ha già definito il ritardo di tempo richiesto utilizzando il pulsante uno ed è stato salvato nella variabile get_scnds. Usiamo una variabile chiamata hscnd, questa variabile è controllata dall'ISR (Interrupt service routine).
La routine del servizio di interrupt è un interrupt che verrà chiamato ogni volta che Timer0 è in overflow. Vediamo come viene controllato dall'ISR nel blocco successivo, come se volessimo aumentare il ritardo di mezzo secondo (0,5 secondi) alla pressione di ogni pulsante, quindi dobbiamo aumentare la variabile hscnd per ogni mezzo secondo. Poiché abbiamo programmato il nostro timer in overflow per ogni 0,0019968 s (~ 2 ms), per contare la variabile di conteggio di mezzo secondo dovrebbe essere 250 perché 250 * 2 ms = 0,5 secondi. Quindi, quando count ottiene 250 (250 * 2ms = 0,5 secondi), significa che è passato mezzo secondo, quindi incrementiamo hscnd di 1 e inizializziamo count a zero.
void interrupt timer_isr () {if (TMR0IF == 1) // Il flag del timer è stato attivato a causa dell'overflow del timer {TMR0 = 100; // Carica il timer Value TMR0IF = 0; // Cancella il conteggio dei flag di interrupt del timer ++; } if (count == 250) {hscnd + = 1; // hscnd verrà incrementato per ogni mezzo secondo count = 0; }}
Quindi usiamo questo valore e lo confrontiamo con il nostro hscnd e spostiamo il nostro LED in base al tempo definito dall'utente. È anche molto simile all'ultimo tutorial.
Ecco, abbiamo capito e funzionante il nostro programma.
Schema del circuito e simulazione di Proteus:
Come al solito, verifichiamo prima l'output usando Proteus, ho collegato qui i file schematici del Proteus.
Aggiungi un pulsante alla nostra precedente scheda LED e il nostro hardware è pronto per l'uso. Dovrebbe assomigliare a qualcosa di simile a questo:
Al termine della connessione, carica il codice e verifica l'output. In caso di problemi, utilizzare la sezione commenti. Controlla anche il video qui sotto per comprendere l'intero processo.