Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Los
timers
temporizadores
son
caractersticas
estndar
de
casi
todos
los
Cada timer tiene asociado un reloj que fija el paso que debe marcar y una o ms unidades
compradoras. El reloj del timer0 es derivado del reloj del CPU y puede tener dos fuentes
distintas: si se usa la fuente de reloj interna del CPU, el reloj del CPU pasa primero por un
divisor de frecuencia (prescaler) y la salida de este divisor va al timer, si se usa una fuente de
reloj externa por el pin T0, primero ser sincronizada con el reloj interno antes de pasar al
prescaler.
Los valores de divisin (prescaler) ya estn establecidos para cada timer, en el caso del timer0
la frecuencia del CPU puede ser dividida por un factor de 1, 8, 64, 256 o 1024, generando as
la frecuencia de conteo. Por cada unidad comparadora existe un pin asociado a ella,
normalmente marcado como OCx (Output Compare x) que es donde se puede generar una
frecuencia de salida por la unidad comparadora del timer.
El timer0 tiene 4 modos de funcionamiento que se pueden configurar programando sus
registros asociados:
Modo CTC: En este modo el timer0 es reiniciado a 0 cuando una comparacin entre el
timer y un valor determinado coincide. Opcional mente puede configurarse para que al haber
una coincidencia genera una interrupcin o cambie el estado de un pin.
Modo Fast PWM: Este modo permite generar una onda PWM de alta frecuencia. El
timer cuenta desde 0 a 255 y reinicia la cuenta. Con cada cuenta el valor del timer0 se
compara con un valor determinado que cuando coinciden cambia el estado de uno de los
pines de salida PWM, y cuando se reinicia el timer este pin vuelve a cambiar su estado.
Modo Phase Correct PWM: Este modo ofrece una onda PWM de alta resolucin, a
diferencia del modo Fast PWM, el timer cuenta hacia adelante y hacia atrs antes de hacer el
cambio de estado del pin PWM, es decir cuenta de 0 a 255 al llegar a 255 cuenta de 255 a 0,
obteniendo una salida PWM ms limpia pero de menor frecuencia.
COM01:0: Estos bits configuran el uso del pin OC0 con el modulo de
comparacin dependiendo del modo que se trabaje.
CS02:0: Con estos bits se selecciona la fuente del reloj para el timer, puede ser interna
con un prescaler o externa con una seal de reloj en el pin T0.
TCNT0:
(Timer/Counter
Register) Este
registro es
el
que
guarda
la
cuenta
del
Timer/Counter0 (0 a 255).
OCR0: (Output Compare Register) Aqu se guarda el valor a comparar con el registro
TCNT0. Cuando el valor del registro TCNT0 coincide con el valor guardado en este registro,
se realiza el evento programado para la coincidencia en comparacin, ya sea generar una
interrupcin o cambiar el estado del pin OC0.
Registro OCR0
TIMSK:
habilitacin de interrupciones para cada timer, a nosotros nos interesa el bit TOIE0(0)
y OCIE0(1) que son los correspondientes al timer0.
Registro TIFR
TOV0: Cuando este bit se pone a 1 se genera la interrupcin por desbordamiento del
Timer/Counter0. Este bit se limpia cuando cuando entra a la funcin que atiende la
interrupcin o escribiendo un 1 en este bit.
Como se mencion anteriormente, los timers son complicados, as que en este primer post
dedicado al Timer/Counter0, se ver su funcionamiento en modo Normal con ejemplos que
usen poleo e interrupciones y fuentes de reloj internas y externas. Se hace esto de explicar un
modo en cada post con el fin de profundizar y utilizar todas las caractersticas del timer.
Por lo que el periodo de la cuenta del timer sera el inverso a la frecuencia del timer:
Esto significa que el timer se incrementara cada Ttimer segundos, si queremos saber cada
cuanto tiempo se desbordar el timer, se multiplica el periodo del timer por la resolucin del
timer, que en este caso es de 8 bits (256):
Con la frmula anterior podemos saber cada cuanto tiempo se desborda el timer, pero si
queremos saber el que valor que debe de tener el timer para un determinado tiempo dentro
del rango Toverflow se aplica una sencilla regla de 3:
Por ejemplo: Supongamos que tenemos nuestro micro con una frecuencia de 8 Mhz y un
prescaler para el timer de 1024, la frecuencia del timer sera:
Ft = 8000000 Hz / 1024 = 7812.5 Hz
Tt = 1 /7812.5 Hz = 0.000128 segundos = 128 microsegundos
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//
#include <avr/io.h>
int main(void) {
// Timer0 modo normal, sin usar el pin OC0, prescaler de 1024
TCCR0 = _BV(CS00) | _BV(CS02);
// Puerto PB0 como salida.
DDRB = _BV(PB0);
PORTB = 0;
while(1) {
// si ya hubo desbordamiento del timer0:
if( (TIFR & _BV(TOV0)) ){
PORTB ^= _BV(PB0); // conmuta el led
TIFR |= _BV(TOV0); // limpia la bandera
}
}
//
return 0;
27
Primeramente el programa configura el timer y el puerto PB0 como salida y en el ciclo infinito
checa constantemente la bandera de interrupcin por desbordamiento del timer, si est en 1
quiere decir que el timer se ha desbordado (pas de 255 a 0) y se conmuta el estado del led
para despues limpiar la bandera de interrupcin.
Ejemplo2: Este ejemplo tiene el mismo objetivo que el anterior, cambiar el estado del led al
desbordarse el timer, solo que aqu en vez de estar checando la bandera de desbordamiento,
se usa la interrupcin por desbordamiento del timer0 que se ejecuta cada 32 ms, lo que libera
al CPU para ejecutar otras tareas.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void) {
// Timer0 modo normal, sin usar el pin OC0, prescaler de 1024
TCCR0 = _BV(CS00) | _BV(CS02);
// habilta interrupcin por desbordamiento de timer0
TIMSK = _BV(TOIE0);
// habilita interrupciones globales
sei();
// Puerto PB0 como salida.
DDRB = _BV(PB0);;
PORTB = 0;
while(1) {} // CPU libre
return 0;
}
// funcin para atender la interrupcin
ISR(TIMER0_OVF_vect){
// se conmuta el estado del led
PORTB ^= _BV(PB0);
}
//
32
Ejemplo3: En este ejemplo se usa el timer para contar intervalos de 10 milisegundos, que se
almacenarn en una variable para generar un retardo de 1 segundo para parpadear un led. Se
necesita saber cual es el valor del timer para 10 ms, se usa la regla de 3:
Cuenta del Timer = 10 ms * 256 / 32.8 + 1 = 79.4, cuando el timer cuente 79 pasos habran
transcurrido 10 ms.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//
#include <avr/io.h>
#define RETARDO 1000 // 1000 milisegundos - 1 segundo
int main(void) {
int milis=0;
// Timer en modo normal, sin salida por OC0 con prescaler de 1024
TCCR0 = _BV(CS00) | _BV(CS02);
// Puerto PB0 como salida.
DDRB = _BV(PB0);
PORTB = 0;
while(1) {
if(TCNT0 >= 79) // si ya pasaron 10 milisegundos
{
TCNT0=0;
if((milis+=10) >= RETARDO) // si ya pasaron 1000 milisegundos
{
// cambia el estado del led
PORTB ^= _BV(PB0);
// reinicia el contador de milisegundos
milis=0;
}
}
}
return 0;
}
//
Como se ve, se tiene que estar haciendo la comparacin del valor del timer constantemente
para saber si ya pasaron 10 ms, esto le toma tiempo al procesador. Para ahorrar ese tiempo
muerto se pueden emplear dos mtodos: el primero es haciendo que el timer se desborde
cada 10 ms, o sea, cada que pasen 79 incrementos y as hacer todo desde la funcin de
interrupcin por desbordamiento, y el segundo es usar la interrupcin por coincidencia en
comparacin con el timer0 y el registro OCR0, as el registro de comparacin se carga con el
valor 79 (para 10 ms) y cuando el valor del timer0 coincida con el valor del registro OCR0, se
ejecutar el cdigo en dicha interrupcin. Es necesario reiniciar el valor del timer a 0 en la
funcin de interrupcin para que vuelva a contar, de lo contrario el timer seguir su cuenta
hasta el desbordamiento y no se ejecutara la interrupcin en ese lapso.
Aqu pongo el cdigo de los dos programas:
Programa 1: Interrupcin por desbordamiento.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//
#define RETARDO 1000 // 1000 milisegundos - 1 segundo
#define F_CPU 8000000
#define LED PB0
#include <avr/io.h>
#include <avr/interrupt.h>
volatile int milis=0;
int main(void) {
// Timer en modo normal, sin salida por OC0 con preescaler de 1024
TCCR0 = _BV(CS00) | _BV(CS02);
// Activa interrupcin por debordamiento del timer0
TIMSK = _BV(TOV0);
// puerto PB0 como salida
DDRB = _BV(LED);
PORTB = 0;
// carga el timer con 177 para que se desborde a los 10ms
TCNT0 = 177;
// Avtiva interrupciones globales
sei();
while(1) {
}
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
return 0;
}
ISR(TIMER0_OVF_vect){
//
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//
#define RETARDO 1000 // 1000 milisegundos - 1 segundo
#define F_CPU 8000000
#define LED PB0
#include <avr/io.h>
#include <avr/interrupt.h>
volatile int milis=0;
int main(void) {
// Timer en modo normal, sin salida por OC0 con preescaler de 1024
TCCR0 = _BV(CS00) | _BV(CS02);
// Activa interrupcin por comparacin del timer0
TIMSK = _BV(OCIE0);
// puerto PB0 como salida
DDRB = _BV(LED);
PORTB = 0;
// carga registro comparador con 79
OCR0 = 79;
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
return 0;
ISR(TIMER0_COMP_vect){
// reinicia el timer
TCNT0 = 0;
if((milis += 10) >= RETARDO){
milis=0;
PORTB ^= 0xFF;
}
//
Como se ve en este segundo ejemplo, el timer se tiene que reiniciar cada que la comparacin
coincide, para evitar esto, se usa el modo CTC del timer, que automticamente limpia el timer
cuando coincide con el registro OCR0, este modo se explicar en el siguiente post relacionado
a el timer0 del ATmega16.
Espero la informacin les haya sido de utilidad, nos vemos la prxima!.