EJEMPLO 7Es turno del Timer/Counter0, este es un módulo interno del microcontrolador que sirve para realizar temporizaciones internas en el micro o bien, conteos de pulsos externos, Es un registro de 8 bits del micro que puede leerse y escribirse. El conteo o temporización será siempre ascendente.
El Timer/Counter0 posee un preescaler que sirve de divisor de frecuencia, para retardar o mantener el incremento de este registro a la velocidad del Oscilador/Cristal usado para el micro, cuando el timer/counter0 trabaja con reloj interno.
Dependiendo el valor que necesitemos temporizar en segundos, ms ó us se usará uno u otro valor de preescaler para llegar a nuestro objetivo.
Usando el temporizador con reloj externo, pasa a modo contador ya que contará los pulsos que le ingresen, en este caso el preescaler siempre estará en 1:1 ya que no existe división de la frecuencia cuando el timer/counter0 está configurado de esta manera.
Teniendo estos conceptos claros, podemos ver a detalle los registros involucrados con el manejo del timer/counter0, que son 4, de los cuales en algunos registros no se usan todos los bits para configuraciones del timer/counter0 sino sirven también para configuraciones de los otros timer/counter, estos registros para el Timer/Counter0 son:
- TCCR0 – Timer/Counter Control Register
- TCNT0 – Timer/Counter Register
- TIMSK – Timer/Counter Interrupt Mask Register
- TIFR – Timer/Counter Interrupt Flag Registe
TCCR0 – Timer/Counter Control RegisterEn este registro se configura el Preescaler, si por ejemplo seleccionamos la segunda opción, clkI/O/(No prescaling), significa que el timer/counter0 se incrementará a la velocidad del oscilador que se tiene para el micro, si por el contrario configuramos con otro valor como clkI/O/256 (From prescaler), significa que cada 256 pulsos del oscilador que tenemos configurado al micro incrementarán solo en 1 al valor del Timer/Counter0. De esta manera encontramos ese retardo o división de frecuencia para el conteo del Timer.
La primera opción sirve para desactivar el funcionamiento del Timer/Counter0, es decir éste no se incrementará, permanecerá detenido.
TCNT0 – Timer/Counter RegisterEste es el registro de lectura y escritura de 8 bits que lleva la cuenta del Temporizador/Contador, puede comenzar la cuenta en caulquier valor que nosotros le carguemos y puede ser leído en cualquier momento.
TIMSK – Timer/Counter Interrupt Mask RegisterComo el timer/counter0 es un registro de 8 bits que se irá incrementando a una frecuencia determinada, sucederá un caso muy particular y este es cuando el temporizador llegue a su valor máximo que es 255, como más allá de 255 en un registro de 8 bits no se puede hacer nuestro timer volverá a 0 para continuar su conteo indefinido hasta que el micro se quede sin energía. Este paso de 255 a 0 se llama desborde o en inglés
overflow, este desborde puede generar una interrupción al micro.
Mediante el Bit 0 de este registro, TOIE0, podemos activar o desactivar esta interrupción por desborde de nuestro Timer/Counter0. Con un 1 queda activada la interrupción y con un 0 desactivada.
TIFR – Timer/Counter Interrupt Flag RegisteEl bit0, TOV0, es el que interesa para el timer/counter0. Es una bandera que indica que ha sucedido el desborde el timer/counter0, se coloca a 1 en ese caso, y se vuelve a 0 automáticamente cuando el timer/counter0 toma el valor de 1. El bit de bandera de desborde se coloca a 1 en el momento que el timer/counter está en 0 previamente de haber venido de 255.
Si se usa la interrupción por desborde del timer, no se debe olvidar colocar en 1 la bandera de interrupciones globales.
Ahora nuestro ejemplo, configuraremos nuestro timer0 para contar 1 seg, ya que a un segundo encenderemos un led, y al siguiente lo apagaremos, haremos lo mismo del ejemplo 1 pero utilizando el Timer/Counter0.
Para ello configuraremos el preescaler en 256 con reloj interno, usaremos la interrupción por desborde, y siempre precargaremos al timer0 con el valor de 130 para solamente contar 125 estados del timer y a la vez contaremos 125 interrupciones para poder recién contar el segundo que buscamos. Eso salió del siguiente cálculo y una búsqueda algo paciente porque no encontré un buen programa para hacer ello, el
avrcalc no lo logré entender

. Este mi cálculo:
Uso 4MHz de Oscilador, entonces mi periodo es 0.25us, necesitaré 4'000.000 incrementos en el timer0 si el preescaler estaría en 1, usaré un preescaler de 256 por tanto solo necesitaré 4'000.000/256=15625 de incrementos en el timer0.
Si contamos los 256 valores (incrementos) del registro del timer0 tendríamos: 15625/256=61.03515625 interrupciones que contar, lo que no es "exacto" para nuestro caso, se podría quizá adoptar directamente ese número ya que sería aproximado el valor temporizado al valor que necesitamos.
Pero como nos gusta ser un poquitín más precisos, encontramos un numero divisible entre 15625, pero este número debe ser entre 0 y 255 pues son los valores que el timer0 de 8 bits puede soportar.
Ese número divisible que elegí es 15625/
125=
125, por tanto debemos contar
125 interrupciones y además debemos reducir la resolución de los 8 bits del timer, por tanto debemos precargar al timer con un valor para que solamente se cuente
125 incrementos antes de desbordar.
Nuestro valor de precarga será: 255-
125=130.
Con esto podemos ver también nuestra tan conocida formula:
Tiempo=Preescaler*(255-Precarga)*interrupciones*Tosc donde Tosc es el periodo de nuestro Oscilador
Tiempo = 256*125*125*0.25u = 1seg.
Ahora queda plasmarlo en código:
;--------------------------------------------------------------------------------
; Programa: Led on/off con Timer/Counter0
; Version: 0.0
;
; Dispositivo: ATmega8 Compilador: AVRASM2
; Entorno IDE: AVR Studio4.15 Simulador: Proteus 7.5sp3
;
; Notas: Este programa hace parpadear un led conectado al pin RB0 del puerto
; B durante aprox. 1seg. utilizando el timer0 para esto con su interrupcion
;
; Registros: r0 -> sirve para el parpadeo del led al aplicarle un eor
; r16 -> On/Off led, r17 para precargar al timer/counter0,
; r18 contador de interrupciones, r19 flag indicación de que paso un segundo
;
; Conexiones: B1 -> Led
;--------------------------------------------------------------------------------
;DIRECTIVAS EN ENSAMBLADOR
.include "m8def.inc" ;ATmega8
;DEFINICION DE REGISTROS
.def led_cambio=r0 ;registro para parpadeo del led
.def count=r18
.def flag=r19
;reset-vector address $0000
.org $0000
rjmp main ;ve al main
.org $009
rjmp int_tmr0 ;va a la interrupcion por desborde del timer0
;PROGRAMA PRINCIPAL
main:
ldi r16,$04 ;Configuracion de...
out sph,r16
ldi r16,$5F
out spl,r16 ;...la pila: Stack: $045F=RAMEND
ser r16 ;r16 <- $FF
out ddrb,r16 ;portB salida
clr flag ;ponemos a cero la bandera para indicar que ha pasado 1seg.
clr count ;ponemos a cero el contador de interrupciones
ldi r16,4 ;r16 <- 4
out tccr0,r16 ;configuramos el Timer/Counter0 con reloj interno preescaler 256
ldi r16,130 ;r16 <- 130
out tcnt0,r16 ;valor de precarga para contar solamente 125 estados del timer
ldi r16,1 ;r16 <- 1
out timsk,r16 ;se habilita la interrupción por desborde del Timer/Counter0
ldi r16,$01 ;r16 <- $01 -> Iluminar led conectado pin RB0
mov led_cambio,r16 ;led_cambio <- r16 :variable para el parpadeo
sei ;se habilita las interrupciones globales
loop:
cpi flag,255 ;pasó 1seg?
breq toggle ;saltamos a cambiar estado
rjmp loop ;bucle infinito
toggle:
out portb,r16 ;actuamos sobre el led(conectado al pin RB0)
eor r16,led_cambio ;cambiamos estado on/off
clr flag ;limpiamos la bandera de indicación de 1seg pasado
rjmp loop ;bucle infinito
;RUTINA DE INTERRUPCION
int_tmr0:
inc count ;incrementar contador de interrupciones
cpi count,125 ;se han producido 125 interrupciones
brne salir ;si no han pasado 125 interrupciones, salimos de la rutina
ser flag ;pasaron las 125 interrupciones = 1 seg, activamos bandera
clr count ;reiniciamos contador de interrupciones
salir:
ldi r17,130 ;r17 <- 130
out tcnt0,r17 ;valor de precarga para contar solamente 125 estados del timer
reti ;fin de rutina de interrupción
El esquema del circuito:
La foto del circuito montado:
Y por último el video:
hasta la próxima...
