IL TIMER DEL PIC16F877 E I COMPILATORI CCS
La periferica timer è una delle periferiche base messe a disposizione da un microcontrollore. Per i PIC, i compilatori CCS forniscono funzioni per la gestione di ritardi, per monitorare un tempo trascorso ed altre utilità legate appunto al timer del microcontrollore. Il PIC16F877 dispone di ben tre periferiche timer note come Timer0, Timer1 e Timer2. Questi sono sostanzialmente dei contatori incrementati automaticamente in base alla frequenza del clock secondo un eventuale fattore di scala impostato mediante prescaler. Timer0 è ad 8 bit mentre gli altri sono a 16 bit. Con i compilatori CCS i singoli timers possno essere abilitati mediante le macro set_timer0, set_timer1 e set_timer2 rispettivamente. Ogni qualvolta un timer giunge a fine conteggio, viene automaticamente azzerato e contemporaneamente viene generata una interruzione. Il legame tra il valore corrente del timer ed il tempo trascorso dall’ultimo azzeramento è dato dalla seguente espressione:
Ritardo[ms]=valore x 4 x prescaler x 1000/Clk
Dove valore è il valore corrente assunto dal timer, prescaler è il fattore di scala impostato e Clk è la frequenza del clock. Usando quindi un clock a 20MHz ed un prescaler pari a 8, se il valore corrente del timer è di 6250 (decimale) significa che sono trascorsi 6250 x 4 x 8 x 1000/20000000=10ms. Il listato 1 mostra come inizializzare il timer1 ed utilizzarlo per determinare il tempo di esecuzione di un particolare task.
long delay; /* Imposta il prescaler a 8 per il Timer1 */ setup_timer_1(T1_INTERNAL | T1_DIV_BY_8); /* Azzeramento del Timer1. */ set_timer1(0); /* Task di cui si deve calcolare il tempo di esecuz. */ printf(“Hello, world!”); /* Calcolo del tempo in ms. */ delay = get_timer1() / 625;
Listato 1. |
Ovviamente è necessario che il task non abbia un tempo di esecuzione superiore al massimo misurabile altrimenti la misura risulterebbe incorretta. Usando ancora il compilatore CCS, il listato 2 riporta un ulteriore esempio in cui l’esecuzione del programma viene sospesa per un determinato numero di millisecondi.
/* Imposta il prescaler a 8 per il Timer1 */ setup_timer_1(T1_INTERNAL | T1_DIV_BY_8); /* Azzeramento del Timer1. */ set_timer1(0); for ( ; get_timer1() < 3125; ) ;3125 è il valore che dovrà raggiungere il timer /* ritardo introdotto. */
Listato 2. |
L’accorgimento utilizzato in questo caso è sfruttare un ciclo for vuoto in cui la condizione di uscita è che Timer1 abbia raggiunto il valore prefissato (e dipendente dal ritardo che si intende introdurre).
#include <16f877.h> #ORG 0x1F00,0x1FFF {} /* Reserve memory for bootloader for the 8k 16F876/7 */ #device PIC16F877 *=16 /* Allow RAM to expand beyond 256 bytes */ #device adc=10 /* Make sure that we are sampling on 10 bits. This directive is required for compiler version 2.7. */ /* Set the clock speed */ #use delay(clock=20000000) #fuses HS,NOPROTECT,NOWDT,BROWNOUT,PUT,NOLVP /* Directive for RS-232 interface */ #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7) #include “input.c” #define VARIABLE_PERIOD 1 // If this symbol is defined, let the user select the // period and prescaler, otherwise, // if VARIABLE_PERIOD = 0, use fixed values. main() { char selection; byte duty, period; byte prescale; // We use serial input to capture PWM parameters to make // an easy demo. setup_ccp1(CCP_PWM); // Configure CCP1 as a PWM setup_ccp2(CCP_PWM); // Configure CCP1 as a PWM #if VARIABLE_PERIOD // This is default for ( ; ; ) { // Select value for the period (100% duty cycle) printf(“Period (100%% duty cycle): “); period = gethex(); printf(“\r\n”); // Set the prescaler do { // Select prescaler (t2div) printf(“\r\nSelect prescaler:\r\n”); printf(“ 1: Prescaler = 1\r\n”); printf(“ 2: Prescaler = 4\r\n”); printf(“ 3: Prescaler = 16\r\n”); printf(“Selection: “); selection = getc(); putc(selection); printf(“\r\n”); } while((selection < ‘1’) || (selection > ‘3’)); // The cycle time will be (1 / clock) * 4 * t2div * (period + 1) // In this program, if period is 0x80 (or 128 decimal), with // a clock of 20000000: // For the three possible prescaler selections the cycle time is: // (1/20000000) * 4 * 1 * 128 = 25.6 us or 39 khz // (1/20000000) * 4 * 4 * 128 = 102.4 us or 9.8 khz // (1/20000000) * 4 * 16 * 128 = 409.6 us or 2.4 khz switch(selection) { case ‘1’: prescale = 1; setup_timer_2(T2_DIV_BY_1, period, 1); break; case ‘2’: prescale = 4; setup_timer_2(T2_DIV_BY_4, period, 1); break; case ‘3’: prescale = 16; setup_timer_2(T2_DIV_BY_16, period, 1); break; } #else period = 0x80; prescale = 4; setup_timer_2(T2_DIV_BY_4, period, 1); #endif printf(“Frequency = %ld kHz\r\n”, 5000 / period / prescale); while(TRUE) { printf(“Enter duty cycle: “); duty = gethex(); printf(“\r\n”); set_pwm1_duty(duty); set_pwm2_duty(duty); // This sets the time the pulse is // high each cycle. // If period is 128 (or ‘80’ hex),a value of 64 (or ‘40’ hex) will set // the duty cycle to 50%, i.e. // the pulse is high 50% of time. // WARNING: A value too high or low will prevent the output from // changing. A value too high will make the CCPx high // at all times. if (duty == 0x10) { // If ‘10’ is entered for duty, exit loop to be able to break; } } } }
Listato 3. |

Un timer può essere utilizzato sia come contatore sia come temporizzatore. Il micro può essere impostato in modo tale che possa produrre un interrupt quando raggiunge il conteggio massimo (overflow). Ottima rappresentazione del case per la scelta del prescaler.