You are on page 1of 15

4.2 Temporizador/Contador

Los temporizadores/contadores son probablemente los periféricos complejos de mayor uso en los microcontroladores. Los temporizadores/contadores son altamente versátiles, se pueden emplear para medir periodos de tiempo, para determinar el ancho de un pulso, para medir una velocidad, para medir frecuencia, o para proporcionar señales de salida. Ejemplos de aplicaciones pueden incluir la medición de las revoluciones por minuto de un motor de automóvil, el periodo exacto de un tiempo específico, tal como el tiempo de trayectoria de una bala, producir tonos para crear música o para producir la chispa de una bujía de un sistema de ignición, o proveer el ancho especifico de un pulso u obtener frecuencias específicas para el control de la velocidad de un motor.

Un temporizador o timer, es un reloj especializado, que regularmente se ocupa para controlar la secuencia de un evento o proceso.

Un contador es en términos generales, un dispositivo que almacena el número de veces que un evento se lleva a cabo.

Una labor habitual de los controladores suele ser la determinación de

intervalos de tiempo concretos. Esto se hace a través de un elemento denominado Temporizador (Timer).

Un temporizador básicamente es un registro de n-bits que se incrementa de

manera automática en cada ciclo de instrucción o cuando ocurre un evento externo. Si la fuente de temporización es externa, se le conoce como contador de eventos.

El registro puede ser pre-cargado para iniciar su conteo a partir de un valor determinado.

Cuando ocurre un desbordamiento en el registro (una transición de 1’s a 0’s)

se genera alguna señalización. En el caso de los microcontroladores AVR, pueden generarse

4.2.1 Configuración y programación como temporizador.

La operación del Timer0 se controla a través del registro OPTION_REG . En el modo temporizador (T0CS=0), se produce un incremento del registro TMR0 cada ciclo de instrucción (prescaler asignado al perro guardián WDT). Si se escribe en el registro TMR0, no se produce el incremento durante los dos siguientes ciclos de instrucción; este hecho debe tenerse muy en cuenta por parte del usuario y, de ser necesario, ajustar el valor escrito en TMR0.

4.2.1 Configuración y programación como temporizador. La operación del Timer0 se controla a través del registro

El TMR0 tiene las siguientes características:

se puede leer y escribir

puede trabajar con reloj externo o interno

detecta el flanco de subida o bajada del reloj externo

tiene un prescaler de 8 bit´s

tiene una interrupción por desborde de la cuenta de 255 a 0

los registros asociados a el son el TMR0, INTCON y el OPTION_REG El bit T0CS (OPTION 2) a 1 trabaja como temporizador, 0 trabaja como contador

El

bit T0SE

(OPTION 4) en modo

contador a 1 flanco descendente, 0 flanco

ascendente

 

El bit PSA (OPTION 3) 0 prescaler al temporizador, 1 prescaler al perro guardian WDT

Cuando se desborda el TMR0, se activa el bit 2 de INTCON (T0IF) y si el T0IE esta activado el T0IF generara interrupción y se debe borrar por software cuando se atienda la interrupción

Para calcular el tiempo de temporización

El TMR0 tiene las siguientes características:  se puede leer y escribir  puede trabajar con

Configurators el OPTION_REG BANKSEL OPTION_REG MOVLW B'00000000' ANALICEMOS

BIT 7 Pull up PORTB Disable

BTI 6 interrupcion por flaco de subida en el INT Pin

BIT 5 fuente del clock para el TMR0, internal 1 ciclo ed intruccion (1/Fosc)

BIT 4 Flanco de bajada para el incremento del TMR0

BIT 3

Prescaler asignado al TMR0

BIT 2-0 Division del prescaler

Configurators el OPTION_REG BANKSEL OPTION_REG MOVLW B'00000000' ANALICEMOS  BIT 7 Pull up PORTB Disable 

Configuramos el INTCON

Registro donde activamos las interrupciones globales y la del TMR0 MOVLW B'10100000' MOVWF OPTION_REG

BIT 7 Activamos las interrupciones Globales

BIT 6 interrupciones de otros perifericos desactivadas

BIT 5 Habilitamos la interrupcion por desborde en el TMR0

BIT 4 desabilitamos interrupcion externa INT

BIT 3

desabilitamos interrupcion por cambio en el PORTB

BIT 2 bandera interrupcion en TMR0

BIT 1 bandera interrupcion en INT

BIT 0 bandera interrupcion por cambio en PORTB

4.2.2 Configuración y programación como contador. En el modo contador (T0CS=1), se produce un incremento por

4.2.2 Configuración y programación como contador.

En el modo contador (T0CS=1), se produce un incremento por cada transición ascendente (T0SE=0) o descendente (T0SE=1) en el pin RA4 del Timer0.

Para manejar la TMR0, el PIC utiliza 2 registros, estos son:

• TMR0 (01h): Este es el contador propiamente dicho y puede ser leído y escrito

en cualquier momento.

• OPTION (81h): De este registro, utilizaremos los siguientes bits:

T0CS: Indica cual es el origen de los pulsos de clk para el timer; si es 1

el origen es la señal introducida por el pin RA4/T0CKI, si es 0 el origen

será el ciclo de instrucción (CLKOUT = FrecuenciaXtal/4).

T0SE: Indica el flanco por el que se incrementará la cuenta del contador

TMR0. Si vale 0 se hará por el flanco de bajada, si es 1 se hará por el

flanco de subida.

PSA: El chip tienen internamente un divisor, este se puede asignar

mediante este bit al WDT con un 1 o al TMR0 con un 0.

PS2:PS0: Este es el valor de la preescala del divisor, según la siguiente

tabla de valores.

 T0CS: Indica cual es el origen de los pulsos de clk para el timer; si

El TMR0 puede funcionar de dos maneras; como contador de eventos o como temporizador. Para seleccionar de que manera queremos que funciones utilizamos el bit T0CS del registro OPTION_REG.

Cuando seleccionamos TMR0 como contador de eventos, mediante el pin T0SE indicamos porque flanco, de subida o bajada, en la señal aplicada en el pin RA4/T0CKI, queremos que se incremente el contador.

La señal que lleva al TMR0 la podemos preescalar mediante un divisor, este divisor lo podemos asignar tanto al WDT como al TMR0 mediante el bit PSA del

OPTION_REG. El valor del rango del divisor viene determinado por los bit PS2:PS0 según la tabla anterior.

El TMR0 es capaz de realizar una interrupción cada vez que pasa de 255 a 0, para permitirla hay que poner a 1 el bit T0IE del registro INTCON y el flag de la interrupción es el T0IF del registro INTCON.

OPTION_REG. El valor del rango del divisor viene determinado por los bit PS2:PS0 según la tabla
OPTION_REG. El valor del rango del divisor viene determinado por los bit PS2:PS0 según la tabla

El valor de TMR0 se inicializa con ValIni (250), con lo que cada 6 pulsaciones en la tecla conectada al pin RA4/T0CKI, el TMR0 pasara de 255 a 0, generando una interrupción que incrementara el valor del contador formado por los leds conectados al PORTA.

Lo único a destacar es la programación del OPTION_REG para configurar el TMRO. Veamos dos ejemplos:

OPTION_REG = b’11111000 : Con este valor hacemos T0CS=1 (

pone el timer en modo contador de eventos), T0SE=1 ( el contador se incrementará con el flanco de bajada de la señal el RA4/T0CKI), PSA=1 ( el divisor esta asignado al WDT por lo que el ratio de la señal será 1:1). Con esta configuración se necesitarán 6 pulsos para incrementar el valor de los leds.

• OPTION_REG = b’11110000 : Con este valor hacemos T0CS=1 (

pone el timer en modo contador de eventos), T0SE=1 ( el contador se incrementará con el flanco de bajada de la señal el RA4/T0CKI), PSA=0 ( el divisor esta asignado al TMR0 por lo que el ratio de la señal vendrá determinado por el valor de PS2:PS0, como es 000 el rango será 1:2). Con esta configuración se necesitarán 12 pulsos para incrementar el valor de los leds. Puedes probar diferentes valores de OPTION_REG, para estudiar más en profundidad el funcionamiento

del TMR0.

Si utilizamos el TMR0, junto con el divisor, como temporizador podremos medir periodos de tiempo muy precisos, hacer periodos de espera o generar señales de tipo PWM.

4.2.3 Desarrollo de aplicaciones

Timer0_1.c: Cada vez que se actúe sobre el pulsador conectado en el pin RA4 se incrementa un contador que se visualiza en el LCD.

Cuando este módulo trabaja como temporizador cuenta los ciclos de instrucción (sin prescaler) o los ciclos

Cuando este módulo trabaja como temporizador cuenta los ciclos de instrucción (sin prescaler) o los ciclos que recibe del prescaler. Como es un contador ascendente el TMR0 debe ser cargado con el valor de los ciclos que se desean contar restados de 256 que es el valor de desbordamiento. Por ejemplo, para contar 28 ciclos (de instrucción/prescaler), se carga el TMR0 con 228 (256-28). El ciclo de instrucción tiene una duración de 4us para una frecuencia de oscilador de 1MHz (PIC16F88). Sin prescaler mediría un tiempo de 28x4x1us = 112us. Con un prescaler 1:8, el tiempo medido sería 28x4x8us = 896us. De manera general, el intervalo de temporización T se puede calcular con la siguiente fórmula:

T=N x TCI x n

Donde:

N = número de ciclos de instrucción/prescaler TCI = período del ciclo de instrucción n = valor del prescaler

Mientras que el valor de carga Q del TMR0 se calcula así:

Q=256-N

Para medir 500us, con un prescaler 1:1 (prescaler asignado al perro guardián WDT) y un TCI = 4us se necesitan 500/4 = 125 ciclos de instrucción. El valor inicial del

Ejemplo 2

TMR0 debe ser 131 (256-125). T0CON: el más importante de cara a la configuración del timer:

T0CON.TMR0ON (bit 7) -> arranca(1) o para (0) el TIMER T0CON.T08bit (bit 6) -> selecciona modo 8 bits (1) o 16 bits (0) T0CON.T0CS (bit 5) -> selecciona modo TIMER (0) o contador externo (1)

T0CON.T0SE

(bit 4)

-> en caso de contador externo decide si cuenta en

flanco subida (0) o bajada (1). T0CON.PSA (bit 3) -> uso (0) o no (1) de un divisor (prescaler) previo.

T0CON.PS0-2

(bits 0-2) -> bits que definen el valor del divisor previo, desde 1:2

(000) hasta 1:256 (111)

TMR0L y TMR0H: permiten acceder (lectura/escritura) al valor del contador (TMR0L para el byte menos significativo y TMR0H para el más significativo).

INTCON: bits para activar la interrupción asociada al Timer0 (ver la entrada sobre interrupciones).

Las decisiones importantes de cara a la configuración del timer son:

· Uso de contador de 8 o 16 bits. Si se escoge 8 bits solo se usa TMR0L como contador y obviamente se reseteará cada 256 incrementos. Esto es importante también de cara a las posibles interrupciones asociadas, ya que la interrupción del TIMER0 se produce al pasar por 0 el contador.

· Si queremos hacer más lento el contador podemos usar el prescaler, que no es más que un divisor previo que hace que sólo se cuenten 1 de cada N ciclos. N puede ser 1 (sin prescaler), 2, 4, 8, etc. En ese caso hay que poner a 0 el bit PSA (usar PRESCALER) y poner el valor correspondiente en los bits asociados. La fórmula es simplemente que divisor N deseado será = 2^(bits_PS+1).

Una vez en marcha el timer podemos consultar su valor accediendo a los registros TMR0L y TMR0H. El byte alto TMR0H no es el verdadero byte alto del contador sino un buffer de dicho valor. La razón de esto es asegurar la consistencia al hacer lecturas/escrituras de un contador de 16 bits en un procesador de 8 bits. Siempre habría la

posibilidad de que entre la lectura de TMR0L y la de TRMR0H el contador se actualizase invalidando la lectura.

La solución es hacer que una lectura de TMR0L cause una copia simultánea del byte alto del contador a TMR0H. De la misma forma una escritura de TMR0L causa la escritura simultánea de TMR0H al byte alto del contador.

¿Consecuencias para nosotros? Si queremos leer el valor del

timer (16 bits)

debemos siempre leer primero TMR0L y luego TMR0H. En la escritura

es

lo

contrario, primero escribimos TMR0H (lo que no tiene ningún efecto sobre el contador, ya que TMR0H es un buffer) y luego TMR0L (momento en el que TMR0L y lo que tuviéramos en TMR0H se vuelcan al contador. Podríamos definir una macro o función para no tener que estar pendientes. Por ejemplo, para escribir el valor de TMR0 (16 bits) haríamos:

contrario, primero escribimos TMR0H (lo que no tiene ningún efecto sobre el contador, ya que TMR0H

Como vemos primero fijamos TMR0H (con la parte alta de x) y luego TMR0L. Igualmente podríamos definir algunas macros para arrancar/parar el timer sin tener que acordarnos de los bits involucrados como hicimos con las interrupciones:

contrario, primero escribimos TMR0H (lo que no tiene ningún efecto sobre el contador, ya que TMR0H

En el main(), configuraremos el timer en modo 16 bits con un prescaler de 256, lo que supone que se incrementa cada 256 x 0.2 usec (@20 Mhz), esto es, cada 51.2 usec más o menos. Con la macro set_TMR0 inicializamos el contador y con start_TMR0 arrancamos el reloj. Durante el bucle (cada 10,000 ciclos = 2 msec) mostramos el contenido de TMR0L y TMR0H en PORTB y PORTC respectivamente.

contrario, primero escribimos TMR0H (lo que no tiene ningún efecto sobre el contador, ya que TMR0H

Al ejecutarse PORTB se ve estático, ya que cambia demasiado rápido (50 usec) para poder ser apreciado. En cambio en PORTC (el byte alto del timer) si se aprecia el incremento. PORTC se incrementa cada 256 x 51.2 usec, es decir, unos 13 msec. Cada vez que se actualiza los datos (2 msec) PORTC habrá cambiado en unas 6/7 unidades.

Trás un rato (65536 x 2 msec = 128 sec) las lucecitas se detienen, ya que el contador cont da la vuelta completa y la condición establecida (cont==0) detiene el timer.

Como otros compiladores, C18 tiene algunas rutinas para facilitar el manejo de los timers:

OpenTimer0 -> da valores a T0CON a través de mascaras predefinidas. También pone a 0 el contador y lo pone en marcha.

CloseTimer0 -> desactiva el contador TMR0 y su interrupción asociada.

WriteTimer0 -> equivalente a la macro set_TMR0()

Abajo se lista el mismo programa usando estas rutinas. Algunos comentarios sobre los cambios:

Es preciso incluir <timers.h> para tener acceso a las declaraciones de las funciones usadas

No es necesario resetear el contador ni arrancarlo explícitamente

OpenTimer0)

(lo

hace

Las máscaras de configuración se combinan con AND. Es posible combinarlas con OR definiendo previamente #define USE_OR_MASKS en el programa. Esto afecta a las opciones que se establecen por defecto. En el modo AND el valor por defecto de los bits es 1 (modo 8 bits, contador de pulsos en un pin, etc.). En modo OR el valor por defecto es 0 (16 bits, contador de reloj).

Las máscaras de configuración se combinan con AND. Es posible combinarlas con OR definiendo previamente #define

Obviamente es más cómodo y legible usar OpenTimer que dar valores directamente a T0CON. Sin embargo conocer los detalles siempre es interesante por si lo que queremos hacer no se puede hacer exactamente con las rutinas suministradas. Por ejemplo, closeTimer0() no sólo para el contador sino que desactiva la interrupción asociada, por lo que no es exactamente equivalente a nuestra macro stop_TMR0. Las llamadas a funciones son más costosas que una macro, por lo que en ciertas aplicaciones críticas son preferibles, etc. Como siempre es una cuestión de usar lo más conveniente en cada caso (pero para ello hay que conocer las posibilidades).