EL REGISTRO TMR0
Un temporizador especial.
Debido a la importancia de este registro, se ha optado por realizar un artículo en exclusiva para este registro. Se supone que el lector tiene unos conocimientos básicos para la comprensión y seguimiento del contenido de este artículo, sobre los microcontroladores y en especial sobre los microPICs. Esto es lo que nos hace emprender este proyecto. La medida constante del tiempo es en sí, una constante Universal del hombre, en todas sus facetas y esta no es una rama que escape a esta sentencia.
Una función muy habitual de los programas para controlar dispositivos consiste en contar intervalos de tiempo, el elemento que realiza esta función se llama temporizador (Timer), en ocasiones cuenta impulsos provenientes del mundo exterior, entonces el elemento destinado a tal fin se llama contador. Ahora bien, si las labores de temporizador o contador en un programa, las asignáramos al programa principal le robarían mucho tiempo al procesador en detrimento de actividades más importantes, es decir, en el tiempo que emplea en temporizar o contar, no puede atender otras rutinas. Por este motivo se diseñan recursos específicamente orientados a estas misiones. Este tipo de circuitos, se conocen con el nombre de RTCC (Real Time Clock Counter) y también como Timer/Counter (Temporizador/Contador).
Especificación Detallada para PIC16F84A. En este artículo, explicaré los datos específicos del registro TMR0 para este PIC.
La figura que sigue se muestra el temporizador (TMR0) y el temporizador perro guardián (WDT) en diagrama de bloques. Al pié de la figura (en amarillo) los bits relacionados, de cada registro que afecta el ajuste de los temporizadores.
El Preescaler que, está en el centro de la figura, puede ser usado por el TMR0 o por el WDT. La figura anterior muestra el preescaler conectado a TMR0. El bit PSA (bit 3) del OPTION_REGdetermina a cual de los dos es conectado el preescaler. El preescaler es un contador programable cuyo rango es determinado por los bits PS0, PS1, PS2 (bits 0, 1 y 2) de OPTION_REG.TMR0 es un contador binario de 8 bit que puede contar hasta 256. Cuando el contador rebasa la cuenta de 255 (FFh) a 0 (00h) ocurre una interrupción por desbordamiento y el bit T0IF (bit 2) del registro INTCON es puesto a 1. El hardware está diseñado tal que cuando ambos el GIE (bit 7) y TOIE (bit 5) del registro INTCON son H ("1") la interrupción ocurre y el PC (program counter) va la dirección 004h, para comenzar la operación de programa.
Una cuenta 256 de TMR0, a veces es corta. Por ejemplo, cuando el reloj interno es 20MHz, la frecuencia de entrada del contador es 5MHz. (Fosc/4). El período de este pulso de reloj es de 200ns (1 / 5Mhz = 0.2 µs.). Para desbordar el TMR0 se necesitan 0.2 µs. x 256 (51.2 µs.). Entonces para ampliar este período se usa el preescaler.
El preescaler se puede usar para dividir la entrada por 2, 4, 8, 16, 32, 64, 128, o 256. Por ejemplo, cuando el preescaler es puesto para dividir por 2, hay 1 pulso de salida por cada 2 pulsos de entrada al preescaler. Si ponemos a 256, habrá 1 pulso de salida por cada 256 pulsos entrada. Así que, el tiempo de desbordamiento de TMR0 se puede hacer más largo modificando el valor del preescaler. En el ejemplo anterior cuando el preescaler fue puesto a 256, el tiempo de desbordamiento se hizo 51.2 µs. x 256 = 13,107.2 µs. (aproximadamente 13 milisegundos).
La entrada al temporizador (TMR0) puede ser un reloj externo o el reloj interno. Para usar el reloj externo, el bit T0CS (bit 6) del registro OPTION_REG y bit 4 del registro TRISA debe ser puesto a "1". Esto pondrá el pin RA4/T0CKI en el modo entrada de reloj (TMR0 CLOCK IN). El borde de las transiciones de subida o de caída del pulso de reloj, también, se pueden seleccionar por el bit T0SE (bit 5) del registro OPTION_REG. Así, "0" para el borde creciente y "1" para el borde de caída.
En la entrada al temporizador (TMR0) hay un circuito de sincronización de reloj. Usando un reloj externo, el conteo del borde de subida y caída de reloj no se sincronizará con el reloj interno, esto afectaría la sincronización de la interrupción. Dicho circuito sincroniza la entrada de escritura de TMR0 con el reloj interno. La sincronización se alcanza en un máximo de 2 ciclos.
El oscilador del Temporizador Perro Guardián (WDT) es independiente del reloj de la CPU. La interrupción WDT se produce aproximadamente cada 18 ms.
Generalmente, para prevenir un tiempo mayor de la condición del WDT debe ser reiniciado de vez en cuando vía software, usando la instrucción CLRWDT. Ver imagen de la derecha. Si el Temporizador no es reiniciado antes de la interrupción, la CPU forzará un reinicio a la posición de dirección inmediatamente después del encendido (Power up).
El preescaler puede ser usado para ampliar el período de interrupción. En este caso, los valores de contador son diferentes del TMR0. El preescaler puede ser puesto a uno de los ocho valores 1, 2, 4, 8, 16, 32, 64 o 128. Cuando se pone a 128, la interrupción es aproximadamente 2 segundos (18msec x 128 = 2,304msec).
La función del temporizador perro guardián (WDT) debe prevenir una operación incorrecta del software. (Ej.: Ejecutar instrucciones que no son parte del programa. Lazo: Ejecutar la misma parte repetidamente). La función WDT no es siempre necesaria. Si hay un error en el programa, por lo general, puede reconocerse que hay un mal funcionamiento por el modo en el que éste se desarrolla (esto, no se realiza del modo que se esperaba). Si el WDT reinicia el PIC, no se puede ser capaz de entender, que hizo que, el programa funcionara mal.
En ocasiones es mejor, no usar el temporizador perro guardián. Para desactivar la operación del temporizador perro guardián, resetee el bit WDT de la palabra de configuración (2007) del programa de memoria a "0" cuando grabe el micro en su quemador de PICs.
Hemos visto las especificaciones generales del temporizador TMR0 y el WDT. Ahora, con más detalle, continuaremos con las características de los registros que intervienen en móduloTimer0, que nos permiten configurar su capacidad.
Los PICs poseen un registro llamado TMRO, es un temporizador/contador de 8 bits. El registro TMR0 es un temporizador especial del módulo Timer0. El módulo Timer0, tiene las características que se indican a continuación:
• Temporizador/contador de 8 bit
• Capacidad de Lectura/Escritura
• Software de 8 bits con Preescaler programable
• Selección de Reloj Interno o externo
• Interrupción por desbordamiento (al pasar de) FFH a 00H
• Selección del Borde (de subida o bajada) para el reloj externo
El registro TMR0 como se ha dicho, es un temporizador especial del módulo Timer0, es decir, es un contador de 8 bits cuyo contenido, se incrementa con una frecuencia constante en cada oscilación de su señal de reloj Ftmr0 programable por hardware. Por su estructura de 8 bits, el máximo de la cuenta está en 256 (podemos contar hasta 28 = 256 valores, entre 0 y 255).
Este registro TMR0, también puede usarse de modo que, permita contar eventos externos, según el valor del bit 5 (TOCS) del registro OPTION. Si este bit 5 está a 1, TMR0 cuenta pulsos de entrada por RA4 y se le llama Contador, por el contrario, si el bit 5 está a 0, TMR0 (como ya se ha descrito más arriba) cuenta pulsos internos de reloj de frecuencia constante (en modo temporizador) y se le llama Timer.
Si nos interesa, se puede insertar un divisor de frecuencia programable (preescaler). Como se ha dicho, este divisor puede ser utilizado indistintamente como preescaler del TMR0 o como postscaler del WDT (Watch Dog Timer), según lo programemos por software.
A diferencia de otros registros, el valor que contiene el registro TMR0 se incrementa continuamente. De modo que, si asignamos el valor 10, después de un ciclo de instrucción, el contenido del registro comienza a ser incrementado a 11, 12, 13 y así sucesivamente con una cadencia constante y totalmente independiente de la ejecución del resto del programa.
Una vez alcanzado el valor 255, en la cuenta siguiente, se desborda el registro TMR0, es decir, es puesto a cero automáticamente, cuando pasa de FFh a 00h, comenzando entonces a contar nuevamente desde cero y no desde el valor originalmente cargado. Además, en el momento de pasar por cero, se activa la bandera TOIF, bit 2 del registro INTCON por desbordamiento del TMR0, generándose la interrupción, sólo si el Control Global de Interrupciones está activado GIE = 1, INTCON bit 7. La frecuencia de conteo es directamente proporcional a la frecuencia de reloj aplicada al dispositivo que, como se ha dicho, puede ser modificada, programando adecuadamente los bits de configuración del preescaler.
La primera figura, es un diagrama simplificado de los bloques del módulo Timer0 y preescaler. La información adicional está disponible en el "Manual de Referencia de la Familia PIC ® de Gama Media MCU" (DS33023).
La tabla anterior debe despejar las posibles dudas al principiante, respecto del registro TMR0 y el módulo Timer0. El registro TMR0 está localizado en la dirección 01h y el módulo Timer0, es un módulo que tiene asociados los registros TMR0, OPTION_REG, TRISA e INTCON, como muestra la tabla.
En los PIC's de gama media, el módulo Timer0 es un módulo interno de los micros y el registro TMR0, como ya se ha mencionado, es un registro de 8 bits que interviene en las operaciones de interrupciones producidas por el módulo Timer0 que veremos con detalle.
El módulo Timer0 es un dispositivo que como se ha descrito (ver imagen), puede funcionar de dos formas: como contador de pulsos externos o como temporizador para calcular intervalos de tiempo.
El denominado módulo Timer0 en las hojas de datos (descrito más arriba), es un Temporizador/Contador de 8 bits habitual en los microcontroladores PIC16F84 (en otros modelos, es posible encontrar módulos adicionales de 8 ó 16 bits cuyo funcionamiento básico, es el mismo). Antes de explicar el funcionamiento y uso con ejemplos del Timer0, para evitar confusiones, debemos definir los tres conceptos siguientes:
Frecuencia de oscilación (Fosc): Es la frecuencia externa del PIC (mediante un cristal de cuarzo, un resonador, etc.).
Frecuencia interna (Fint): Es la frecuencia del reloj interno de instrucciones, generada a partir de la frecuencia de oscilación externa.
Frecuencia TMR0 (Ftmr0): Es la frecuencia constante, después del preescaler, a la entrada del TMR0.
Nota.- En los microcontroladores PIC, la frecuencia Fint difiere de la frecuencia Fosc, ya que para mantener la compatibilidad con los diseños originales es necesario dividirla por cuatro.
El tiempo empleado en una temporización se puede calcular a partir de un ciclo de instrucción (es decir, si estamos trabajando con un XT de 4 Mhz, es una instrucción por cada microsegundo, 1µs), según la formula:
Cuando los pulsos provengan del reloj interno (Fint), el Timer0 se utilizará para generar interrupciones periódicas mediante una cuenta programada. Puesto que conocemos la frecuencia de funcionamiento y en base a un valor que cargaremos en el contador del timer TMR0 podremos temporizar eventos.
Cuando dicha señal provenga de una fuente externa (patilla RA4/T0CKI) del microcontrolador (Fext), es especialmente útil para contar el número de pulsos que dicha señal genera en el tiempo ya que cada pulso de dicha señal incrementa el valor del TMR0.
El esquema simplificado del Timer0, se puede ver en la figura que sigue:
El Timer0, usado como temporizador, puede contar períodos de tiempo exactos, acumulándolos en un registro auxiliar, cada vez que ocurra un pulso de entrada podemos consultar el registro auxiliar y comparar el incremento acumulado desde el anterior pulso. Conocidos los períodos, podemos calcular el tiempo transcurrido.
Además como se describió más arriba, de las dos formas de reloj descritas, también podemos configurar que el disparo de la señal, sea por flanco ascendente o descendente. Esto lo podemos realizar con los siguientes bits de control:
T0SC (Timer0 Select Clock) (bit5) del registro OPTION: Indica el origen del reloj del contador, oscilador interno (1) o señal externa (0).
T0SE (Timer0 Set Edge) (bit4) del registro OPTION: Cuando se selecciona señal externa, indica el flanco activo que se usará (1 ascendente; 0 descendente).
Un circuito adicional en el Timer0, es el preescaler, este circuito, nos permite modificar la frecuencia del reloj de entrada del Timer0, dividiendo ésta y generando una nueva señal de menor frecuencia a su salida que, será la señal de reloj (Ftmr0) de entrada al registro TMR0. El preescaler, es una ayuda para cuando la señal de entrada es una frecuencia demasiado alta para nuestros propósitos y necesitamos reducirla.
El preescaler es un divisor de frecuencia programable que, se utiliza normalmente para lograr tiempos largos y se puede aplicar al TMR0 o al WDT, esto se configura en el bit PSA (bit3) del registro OPTION.
Para configurar el preescaler del registro TMR0, como veremos en la siguiente sección, usaremos 4 bits del registro OPTION, el PSA y tres bits que nos permiten dividir la frecuencia de una señal de entrada por 2, 4, 8, 16, 32, 64, 128 o 256. En caso de utilizar un divisor por 1, la señal de salida es la de entrada sin ningún cambio.
Por ejemplo, si usamos como oscilador externo del PIC un cristal de 4Mhz, entonces el reloj interno de instrucciones funciona a
Fint = 4Mhz /4 = 1 Mhz = 1 µs.
Si el Timer0 usa la señal del reloj interno y la pasamos por el preescaler configurado para una división por 4, la señal a la salida del preescaler será Fpresc = 250 Khz.
La configuración se realiza con los siguientes bits de control:
PSA (Post Scaler Assignament) (bit3) del registro OPTION: Indica si el postscaler es asignado "1" al WDT o "0" al Timer0.
PS2:0 (bit2:0) del registro OPTION: Indican el valor del divisor a utilizar en el postscaler (consultar tabla para los valores).
Nota de Microchip:
Al alimentar el PIC o después de un overflow por WDT, el postscaler esta asignado al WDT. Si se asigna el postscaler al Timer0, es posible que ocurra un reset por el WDT (incluso aun deshabilitado). Por esto se recomienda usar CLRWDT antes de reasignar el postscaler:
clrwdt ;borra postscaler y WDT movlw b'11110001' ;reloj externo por flanco de caida movwf OPTION_REG ;preescaler 1:4 asignado al Timer0
Registro OPTION
Para el cálculo del divisor de frecuencia en PS2 - PS1 - PS0, usaremos la tabla siguiente:
Aprovechamos este punto para insertar la tabla de los registros pertenecientes a INTCON, ya que nos ayudarán a comprender mejor los Bits implicados en el proceso de temporizadores con el TMR0.
Veamos en la practica, como hacer una temporización con el registro TMR0. Por lo descrito, el tiempo empleado en una temporización se puede calcular a partir de un ciclo de instrucción, es decir, si usamos un XT de 4 Mhz, 1 instrucción por cada microsegundo, necesitaremos el valor del divisor de frecuencia (el que se selecciona con los bit's PS2, PS1 y PS0) y también el complemento del valor cargado en TMR0 (es decir 255 - TMR0), la ecuación que nos permite realizar el cálculo es la que siguiente:
Temporización = Ciclo de instrucción * (255-TMR0) * Divisor de Frecuencia [1]
Generalizando: Retardo = 4 * Tosc (µs) * (256 - Valor cargado en TMR0) * (Rango del preescaler) . . . . [2]
1) Supóngase que necesitamos una temporización de 1s (1000 milisegundos), si usamos un cristal XT de 4 Mhz y a demás como divisor de frecuencia seleccionamos 8 (los bits PS2, PS1, PS0 = 0, 1, 0). Sabemos que, 1 seg. = 1000 ms = 1000000 µs y como 1 ciclos/µs es el tiempo empleado en ejecutarse una instrucción, aplicando la ecuación anterior, tenemos:
255 - TMR0 = Temporización (en microsegundos) / (1 ciclo/µS * Div. de Frec.) que sustituyendo: 255-TMR0 = 1000000 µs/(1 ciclo/µs * 8) 255-TMR0 = 1000000 /(8 ciclos) 255-TMR0 = 125000 ciclos 255-TMR0~= 125 ciclos
Por lo tanto TMR0 se debe cargar con: 255 - TMR0 = 125, que despejando nos proporciona el valor de TMR0 = 255 - 125 = 130 (82h).
El valor que se debe cargar en TMR0 es 82h. Entonces empezará a contar los 130 ciclos necesarios para desbordarse, produciendo así la interrupción. El tiempo empleado es el previsto, 1000000µs = 1µs.
2) Calculemos en este caso, el máximo retraso que es posible obtener con el módulo Timer0, usando la señal interna de reloj de un PIC genérico que usa un cristal de 4 Mhz.
El mayor retraso se puede obtener con el mayor divisor del preescaler ÷256 (PSA2,1,0 = 111), un valor 0 para el TMR0 (contará desde 0 a 255 antes del desbordamiento, es decir, 256 incrementos).
Como Fosc= 4 Mhz el retraso máximo es: Retardo - TMR0 = (256 * 256) * (4/ 4 Mhz) = 65,566 ms
3) En esta ocasión, utilizando el módulo Timer0 vamos a crear una función que genere un retardo de un milisegundo, el cual incrementa un contador cuyo valor se muestra por el PORTC de un PIC16F877.
Ahora, veamos como realizar una función de retardo. Debemos tener en cuenta que el reloj es de 20 Mhz, entonces según la formula [2],
1 (20x10^6) = 0.05 µs ; los ciclos para hacer 1 ms
1 ms = x * 0.05 ; y despejando x
x= 1000 / 0.05 = 20.000 ciclos.
Por lo tanto, necesitamos 20.000 ciclos para hacer 1 ms. Si, cada instrucción toma 4 ciclos en realizarse, las instrucciones que necesitamos son: 20.000/4 = 5.000 instrucciones para tener nuestro retardo de 1 ms (en el caso de contar ciclos de instrucción en lugar de los pulsos en RA4).
Un ciclo de instrucción = 4 * Fosc
Que, multiplicado por un numero X, obtendremos que,
Retardo = Un ciclo de instrucción * X luego:
Retardo_R = 4 * Fosc * TMR0
Según esta expresión, debemos considerar que el tiempo máximo sería:
Retardo_R = 4 * Fosc * 256 Ahora si tenemos un clock de 4MHz conectado al PIC tendríamos: 256 µs.
Además sabemos que el modulo timer 0 posee un preescaler que serviría para amplia el retardo. Si lo usamos debemos configurar el bit PSA a 0 . Si seteamos el bit PS2, PS1 y PS0 a 1 el preescaler tendría un valor de 256.
Retardo_T0_Pre = 4 * Tosc * TMR0 * Preescaler.
Retardo_T0_Pre = 4.0.25 µs * 256 * 256 = 65536 µs = 65.536 ms
Con esto no alcanzamos a generar un segundo. Sin embargo podemos usar un registro que sirva para efectuar varios bucles. En tal caso, podríamos lograr retardos mayores:
Retardo = Bucle * Retardo_T0_Pre
Retardo = Bucle * 65,536 ms ; Como el Retardo debe ser 1segundo
1000 ms = Bucle * 65,536 ;Bucle = 15.25
Como vemos generar el retardo es bastante fácil teniendo en cuenta que hemos de definir una rutina que se encargue de comparar o contar las veces que el TMR0 produce los desbordes hasta llegar a 256 cuentas.
Sabemos que, la bandera T0IF del registro INTCON (bit2) que se pone a 1 siempre que hay un desborde en el Registro TMR0 es decir cuando pasa de 255 a 0. Esta bandera puede servir nuestros intereses ya que una vez producido el desborde el bit T0IF permanece en "1" y es necesario que lo pongamos a "0".
Otro punto importante a recordar es que, debemos cargar el TMR0 con el valor apropiado para conseguir el retardo deseado. Supongamos que se necesita un retardo de 10 us. Entonces nuestro algoritmo sería:
• T0IF = 0 • Poner (256 - 246) 10 en el registro TMR0 • Comprobar cuando T0IF sea 1
Se ha cargado el TMR0 con 10 ciclos de instrucción (10 µs), lo que supone 10 cuentas de desbordamiento del TMR0, porque 10 cuentas = 1 T0IF. La que sigue podría ser una formula para el cálculo del retardo que necesitamos:
Retardo = Ciclo_instrucción * Valor_TMR0 * Valor_Preescaler * Bucle
Esto nos indica que puede haber otras soluciones.
Según lo descrito, el registro OPTION_REG debería configurarse así: OPTION_REG = 1100 0110 ;
Esto se puede traducir como:
MSB 1 Todas las resistencias de carga (Pull up) desconectadas 1 Flanco para la interrupción ascendente 0 Pulsos de reloj interno Fosc/4 0 Cada flanco ascendente incrementa TMR0 0 Divisor de frecuencia se asigna al TMR0 1 \ 1 } Valor del preescaler = 128 LSB 0 /
El programa que cumpla lo considerado, puede ser el siguiente:
list p=16f627A ;Comando que indica el Pic usado include "p16f628A.inc" ;Etiquetas genéricas para el Pic16F877 CONTA EQU 0x20 ;Variable CONTA en dirección 0x20 hexadecimal ORG 0x00 ;Inicio del programa en la posición cero de memoria inicio: BSF STATUS,RP0 ;Ir banco 1 BCF STATUS,RP1 CLRF TRISA ;PORTA salida MOVLW b'11000110' ;Configuración del modulo TMR0 MOVWF OPTION_REG ;Preescaler = 128 BCF STATUS,RP0 ;Ir banco 0 BCF STATUS,RP1 CLRF PORTC ;PORTC = 0 bucle: CALL retardo ;Llama la rutina de retardo INCF PORTC,F ;Incrementa el valor del PORTC GOTO bucle ;Ir bucle retardo: ;T = 4 * Tosc * Valor de TMR0 * Preescaler MOVLW d'64' ;Cargar el valor de CONTA para 1 segundo MOVWF CONTA espera1: CLRF INTCON ;Deshabilitar interrupciones MOVLW d'134' ;Cargar el valor de TMR0 para 122 cuentas MOVWF TMR0 ;(Complemento) espera: BTFSS INTCON,T0IF ;Esperar desborde del TMR0 GOTO espera DECFSZ CONTA,F ;Decrementar el registro CONTA hasta cero GOTO espera1 ;Si no es cero: ir a espera1 RETURN ;retorno de call END
Con esto, estoy convencido que se ha descrito con extensión y puede darse por cerrado el tema. Esta información se ha obtenido de distintas fuentes y de la red.