Você está na página 1de 15

AVR PROGRAMACIN EN C

TIMER/COUNTER 0
Los timers o temporizadores son caractersticas estndar de casi todos los microcontroladores,
as que es muy importante aprender su uso. Los microcontrolador AVR tiene temporizadores
muy poderosos y multifuncionales, pueden medir tiempo, generar frecuencias, contar eventos
externos, hacer comparaciones, sincronizar tareas, generar una onda PWM, etc.
El tema de los timers es un tanto complicado y extenso, en esta serie de post se trata de dar
una breve explicacin del funcionamiento y programacin del timer0 en el ATmega16.
El timer0 del ATmega16 es un contador/temporizador de 8 bits (puede contar hasta 255)
sncrono, es decir, que depende del reloj del CPU (slo su reloj, no el CPU) para funcionar, es
un mdulo independiente del ncleo del AVR lo que reduce la carga del micro.
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 Normal: El timer cuenta desde 0 a 255 y se desborda reiniciando la cuenta.
Puede generar interrupcin al desbordarse o cuando la comparacin del conteo
concuerde con un valor determinado.
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.

AVR PROGRAMACIN EN C
REGISTROS DEL TIMER/COUNTER 0

TCCR0: (Timer/Counter Control Register) Este registro configura la frecuencia a la que


trabajar el timer, el modo de trabajo y si el timer controlar la salida del pin asociado a el, en
este caso el pin OC0.

FOC0: Escribiendo un 1 en este bit, fuerza a que se realice una coincidencia de


comparacin y acta sobre el pin OC0 si se usa.
WGM01:0: Configura el modo de funcionamiento del timer0.

COM01:0: Estos bits configuran el uso del pin OC0 con el modulo de
comparacin dependiendo del modo que se trabaje.

AVR PROGRAMACIN EN C
Salida del comparador en modo NO-PWM (Normal CTC)

Salida del comparador en modo FAST PWM

Salida del comparador en modo PHASE CORRECT PWM

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.

AVR PROGRAMACIN EN C
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.

TIMSK: (Timer Interrup Mask Register) En este registro se encuentran los bits de habilitacin
de interrupciones para cada timer, a nosotros nos interesa el bit TOIE0(0) y OCIE0(1) que son
los correspondientes al timer0.

TOIE0: Escribir a 1 en este bit habilita la interrupcin por desbordamiento del


TimerCounter0 (Timer/Counter Overflow Interrupt Enable 0) si tambin se habilitan las
interrupciones globales con el bit I en SREG.
OCIE0: Escribir a 1 en este bit habilita la interrupcin por coincidencia en la
comparacin del Timer/Counter0con el registro OCR0 (Output Compare Interrupt
Enable 0), si tambin se habilitan las interrupciones globales con el bit I en SREG.
TIFR: (Timer Interrup Flag Register) En este registro se encuentran las banderas de
interrupciones para cada timer, a nosotros nos interesa el bit TOV0(0) y OCF0(1) que son los
correspondientes al timer0.

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.

AVR PROGRAMACIN EN C
OCF0: Cuando este bit se pone a 1 se genera la interrupcin por coincidencia en la
comparacin del Timer/Counter0 con el registro OCR0. 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.

Timer/Counter0 en Modo Normal


En este modo el timer0 cuenta desde BOTTOM hasta TOP (BOTTOM = 0, TOP = 255) y al pasar
la cuenta empieza de nuevo desde BOTTOM. El timer puede generar interrupciones de
desbordamiento (al pasar de TOP a BOTTOM) y de coincidencia en comparacin con el registro
OCR0.
En estos primero ejemplos se usara el reloj del CPU como fuente de reloj para el timer,
descartando el pin T0.
Para saber a que frecuencia y con que periodo trabajar el timer, es necesario saber algunas
frmulas para calcular tiempos con exactitud.
Para calcular la frecuencia del timer, se divide la frecuencia del CPU entre el prescaler
deseado:

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:

AVR PROGRAMACIN EN C

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
Tovf = 128 microsegundos * 256 = 32768 microsegundos = 32.8 ms
Con estos valores el timer se desbordara cada 32.8 milisegundos, si queremos medir en
intervalos de 10 milisegundos, aplicamos la regla de 3 para saber que valor tendr el timer
cuando hayan transcurrido 10 ms:
Cuenta del Timer = 10 ms * 256 / 32.8 + 1 = 79.4
Esto quiere decir que cuando el timer haya contado 79 pasos habrn transcurrido
aproximadamente 10 ms.

AVR PROGRAMACIN EN C
EJEMPLOS
Ejemplo1: configuraremos el timer0 con un prescaler de 1024 con una frecuencia de CPU =
8Mhz para que el timer se desborde cada 32ms, y una vez que se desborde cambaremos el
estado de un led en el puerto PB0:
//
#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;
}
//

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.

AVR PROGRAMACIN EN C
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.
//
#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);
}
//

AVR PROGRAMACIN EN C
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.
//
#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.

AVR PROGRAMACIN EN C
Programa 1: Interrupcin por desbordamiento.
//
#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) {
}
return 0;
}
ISR(TIMER0_OVF_vect){
// El timer se desborda cada 10ms
// recarga el timer con 177 de nuevo
TCNT0 = 177;
if((milis += 10) >= RETARDO){
milis=0;
PORTB ^= _BV(LED);
}
}
//

AVR PROGRAMACIN EN C
Programa 2: Interrupcin por Coincidencia en comparacin.
//
#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;
// Avtiva interrupciones globales
sei();
while(1) {
}
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.

AVR PROGRAMACIN EN C
Como se vio en la parte 1, el timer/counter0 tiene distintos modos de trabajo, en esa parte se
vio el modo normal, que solo cuenta de 0 a 255 de manera ascendente y puede generar
interrupcin al desbordarse o al coincidir con el registro OCR0. En esta parte veremos el modo
CTC (Clear Timer on Compare) que reinicia el timer0 cuando los valores del registro OCR0 y el
registro TMR0 coinciden, y este evento puede generar una interrupcin o cambiar el estado
de un pin del microcontrolador.
Este modo del timer es til para generar una salida de onda cuadrada por el pin OC0 (Output
Compare 0), que es el pin asociado al mdulo comparador del timer0, ya sea dejando el
periodo definido en el registro OCR0 o cambiando este periodo al atender la interrupcin que
se genera al coincidir la comparacin del registro OCR0 con el TMR0. Estos registros se
describen ms detalladamente en la primera parte de los tutoriasl del Timer/Counter0.
La mejor manera de aprender es con el ejemplo, y mas si el ejemplo tiene aplicacin, y como
se menciono antes, este modo sirve para generar una salida de onda cuadrada de frecuencia
ms o menos variable, y que adems de todo puede controlar el estado de el pin OC0,
haremos un programa que simule un control remoto emisor.
La gran mayora de los controles remotos funcionan transmitiendo seales de luz infrarroja en
forma de pulsos a una frecuencia de 38Khz, aunque tambin se utilizan frecuencias de 30, 36 y
56 KHz. Los receptores infrarrojos reciben la luz modulada a esta frecuencia y la convierten en
niveles lgicos de 1s y 0s, un 0 para la presencia de luz y un 1 para la ausencia de luz, por lo
que se podra decir que los receptores trabajan con lgica negada. Los modelos ms comunes
de receptores son el TSOP1738 y el VS1838, que funcionan a una frecuencia de 38kHz, y es el
que usaremos.
Entonces, lo que tendremos que hacer, es un circuito que emita pulsos infrarrojos a 38khz para
que el receptor los pueda recibir y decodificar.
En este ejemplo lo que se quiere es ejemplificar el uso del timer0 como generador de
frecuencia, por lo que solo veremos el transmisor, ya en otro post se ver como hacer el
receptor y as poder dotar fcilmente a nuestros proyectos de comunicacin infrarroja con un
protocolo propio.
El circuito no es ms que un led infrarrojo conectado al pin OC0 y un push-button conectado a
cualquier otro pin. El funcionamiento seria que cuando se presione el push-button el Led
infrarrojo se encienda a una frecuencia de 38khz y cuando se suelte se apague. Para generar la
frecuencia de 38 kHz usaremos el timer0 en modo CTC, que conmuta el estado del pin OC0
cada que la cuenta del timer coincide con el registro OCR0, generando una seal de onda
cuadrada. Para encender y apagar el Led, se usa un push button conectado a pin INT0, pata
atender al botn en una interrupcin. La manera de encender el Led es cambiando la direccin
del pin, si el pin es de salida encender el led con la seal enviada por el modulo CTC, si el pin
es de entrada esta seal no tiene efecto en el pin manteniendo al led apagado.

AVR PROGRAMACIN EN C

Para establecer al timer0 en modo CTC, se deben programar los bits WMG00 y WMG01 del
registro TCCR0 con 0 y 1 respectivamente.
Para que cambie el estado del pin OC0 en cada comparacin se programan los bits COM00 y
COM01 con 1 y 0 respectivamente.
Para establecer el prescaler de 8 para el timer se programan los bits CS0, CS1 y CS2 con 0, 1 y 0
respectivamente.
Para calcular la frecuencia de la onda que genera el timer en modo CTC, el datasheet tiene la
siguiente frmula:

Donde Focn es la frecuencia a calcular, Fclk es la frecuencia del microcontrolador, N es el


prescaler del timer ( 1, 8, 64, 256 o 1024) y OCRn es el valor del registro OCR0. Como lo que se
desea saber es el valor del registro OCR0, de la frmula anterior solo hay que despejar el valor
OCRn, quedando:

AVR PROGRAMACIN EN C
Ahora slo se sustituye la Focn en la frmula con la frecuencia deseada y obtendremos el valor
que hay que cargar en el registro OCR0, que para este caso lo deseado es 38kh (38000 Hz). Con
un microcontrolador funcionando a 8Mhz y con un prescaler de 8 para el timer, el valor del
registro OCR0 seria:

El valor que se tiene que escribir en el registro OCR0 es 12 para generar una frecuencia de
38khz. El programa propuesto para realizar esta tarea sera el siguiente:
//
#define F_CPU 8000000
#define BUTTON PORTD2 // Push Button conectado a PD2
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
void InitiSystem(){
DDRD &= ~_BV(BUTTON); //Puerto PD2 como entrada.
PORTD |= _BV(BUTTON); // Activa Pull-Up de PD2
// Timer0 en modo CTC, prescaler de 8 y cambio de estado de pin
OC0
// cada coincidencia del registro OCR0 y TCNT0
TCCR0 = _BV(WGM01) | _BV(COM00) | _BV(CS01);
// Registro OCR0 con 12 para generar una seal de 38khz
OCR0 = 12;
// Habilita interrupcion en INT0
GICR = _BV(INT0);
// Configura interrupcion por flanco de bajada en pin INT0
MCUCR = _BV(ISC01);
// Habilita interrupciones globales.
sei();
}
int main(void) {
InitiSystem();
while(1) {
}
return 0;
}

AVR PROGRAMACIN EN C
// Funcin para atender la interrupcin INT0 del push button
ISR(INT0_vect){
// Cambia la direccin del puerto OC0, si es de entrada
// lo pone como salida (enciende led) si es de salida
// lo pone como entrada (apaga el led)
DDRB ^= _BV(PB3);
// pausa para eliminar rebotes.
_delay_ms(30);
}
//

Você também pode gostar