Utilizando el modulo TMR1 del PIC16F
Gracias por visitar este blog dedicado a la electrónica y en particular a la programación de microcontroladores PIC, En esta ocasión aprenderemos a manejar el modulo TMR1 o TIMER1 del microcontrolador PIC haciendo uso de los registros de configuracion SFR, con el objetivo de utilizar este modulo en la generación de rutinas para manejar el tiempo con gran precisión, y sobre todo hacer un uso adecuado de este recurso logrando integrar la funcionalidad que ofrece a nuestro programa principal.
Esta publicación es complementaria a la entrada <TMR0 Configuración y uso PIC16F>, por lo que te recomiendo puedas revisarlo para que tengas un mejor criterios en cuando a las diferencias que hay en ambos.
La programación se realizara utilizando MPLABX y el compilador XC8 ambos disponibles en la pagina de microchip. Aquí dejo los enlaces de las versiones utilizadas para nuestro ejemplo: <<MPLABX v6.20>> <<XC8 v2.45>>
Mencionar también que es necesario contar con conocimientos mínimos sobre programación en lenguaje C y el uso de microcontroladores PIC16.
Introducción al Modulo TMR1.
A menudo nos encontramos en la necesidad de manejar el tiempo con un mayor grado de precisión, para lo cual es ideal que un microcontrolador MCU incorpore recursos que ayuden con esta tarea, en el paso de un PIC este recurso se conoce denomina TIMER o TMR y puede cumplir la función de contar pulsos externos (Contador) que ingresan por un pin, o pulsos internos (Temporizador) que provienen del reloj principal.
En cualquier caso un registro contador incrementa su valor con los pulsos internos o externos hasta llegar a su limite, y posteriormente reiniciar la cuenta en 0, esta condición de reinicio se llama desbordamiento.Los microcontroladores PIC de gama media, integran por lo general tres modulo TIMER que están designados como:
- TMR0: Temporizador y Contador de 8 bits (Todos los modelos)
- TMR1: Temporizador y Contador de 16 bits (Todos los modelos)
- TMR2: Temporizador de 8 bits (Revisar modelo)
En este caso, dada la temática de esta publicación, nos enfocaremos en como utilizar el modulo TMR1, la
figura 1 corresponde al diagrama de bloques del módulo TMR1, que se describe en el <Manual de Referencia de los PIC16F>
Fig1. Diagrama del modulo TMR1 |
A diferencia del resto, el modulo TMR1 es un Temporizador y/o Contador de 16-bit debido al
ancho que tiene su registro de conteo TMR1, el funcionamiento de este modulo siguiendo a la secuencia que se observa en la figura 1, es la siguiente:
- El bit de configuracion TMR1CS permite seleccionar el modo de operación: Contador si los pulsos ingresar por el pin externo T1CKI; Temporizador si los pulsos provienen del oscilador principal, cuya frecuencia seria Fosc/4. Note que el periodo del Temporizador equivale a al ciclo de instrucción Tcy.
- Este punto corresponde al bloque de pre-escala programable, y se trata de un divisor de frecuencia con cuatro escalas seleccionables a través de los bits T1CKPS[1:0], las escalas posibles se muestran en la figura 2.
- El bit de configuracion T1SYNC, permite sincronizar la salida del divisor de frecuencia con la frecuencia del reloj principal Fosc/4, este requerimiento puede darse en modo contador, debido a que la señal externa T1CKI es asíncrona, es decir que el pulso puede darse en cualquier instante.
- El bit TMR1ON como puede ver en el diagrama, habilita la continuidad de los pulsos que salen del divisor de frecuencia, por lo tanto este bit arranca o paraliza la operación del TMR1. Es importante mencionar que las operaciones de lectura o escritura al registro TMR1, deben efectuarse cuando el modulo esta paralizado.
- Este punto corresponde al registro contador TMR1 que al ser de 16-bits, esta conformado por los registros TMR1H y TMR1L, entonces este par de registros TMR1H:TMR1L incrementa su valor con cada pulso que proviene del divisor de frecuencia, desde un valor 0 hasta 65535, posterior al limite final ocurre un desbordamiento donde TMR1H:TMR1L se reinician en 0.
- El bit TMR0IF indica la ocurrencia del desbordamiento en el registro contador TMR1, es decir cuando el par TMR1H:TMR1L pasa de FFFFh a 0000h. Esta bit de bandera deberá restablecerse por programa.
- Aunque es menos habitual hacer uso de este punto, es posible que el incremento de TMR1H:TMR1L con cada pulso del divisor de frecuencia, pueda ser controlado bajo las siguientes condicionales adicionales:
Si el bit TMR1GE se activa, entonces el incremento de TMR1H:TMR1L se controla con la función de puerta T1G, caso contrario TMR1H:TMR1L solo incrementa. La función de puerta T1G, posibilita controlar el incremento del contador TMR1 a través del pin externo T1G, en este caso el incremento puede ocurrir cuando este pin tenga nivel alto si el bit T1GINV=1 o nivel bajo si el bit T1GINV=0.
Los registros y bits utilizados para la configuracion del modulo TMR1 se describen en la figura 2, debe
tomar en cuenta los registros ya tiene valores por defecto, y en muchos casos podría no ser necesario
reconfigurarlos.
El modulo TMR1 posee otras particularidades relacionados con el modulo de captura, comparación y modulación CCP/ECCP que poseen muchos PIC de gama media, si desea conocer mejor las prestaciones de este recurso deberá consultar la hoja de datos.
Configuración de un Contador.
Al implementar un contador lo que se busca es capturar pulsos externos que ingresan por el pin T1CKI, que comparte función con la entrada RA4, los pulsos que ingresan serán contabilizados en el registro contador TMR1H:TMR1L y para este fin consideraremos el siguiente ejemplo:
Si nuestra aplicación necesita detectar 700 pulsos externos independientemente del tiempo en el que ocurran, entonces debemos considerar realizar lo siguiente:
- Configurar el pin T1CKI como entrada, es decir el bit TRISA4=1, en este punto si lo requiere puede considerar el uso de una resistencia pull-up.
- Seleccionar la operación de TMR1 como Contador, activando el bit TMR1CS=1, esta manera los pulsos ingresen por el pin de entrada T1CKI.
- Debe seleccionar una pre-escala para el divisor, y en este caso como solo se requiere contar 700 pulsos, la escala sera 1:1 T1CKPS=00, esto es así porque la capacidad del registro contador TMR1H:TMR1L puede llegar hasta los 65536 pulsos.
- Inicializar el valor del registro contador TRM1H:TMR1L, recuerde que el desbordamiento de este registro provoca el evento del modulo TMR1, entonces si el propósito es contar 700 pulsos, este registro debe ser cargado con el valor 65536 - 700.
- Limpiar la bandera TMR1IF del modulo TMR1, asegurando así el inicio de la espera para los 700 pulsos que ingresaran al Contador.
- Iniciar la operación del modulo TMR1, activando el bit TMR1ON = 1.
- La rutina deberá esperar hasta que la bandera TMR1 se active.
Ahora realizaremos la programación necesaria para los pasos descritos anteriormente.
T1CONbits.TMR1CS = 1; //Modo contador
T1CONbits.T1CKPS = 0b00; //Ajuste pre-escala 1:1
TMR1H = 0xFD; //65536-700 = 64836 o FD44h
TMR1L = 0x44;
PIR1bits.TMR1IF = 0; //Limpia la bandera
T1CONbits.TMR1ON = 1; //Arranca la captura
while(PIR1bits.TMR1IF == 0); //Espera hasta capturar los 700 pulsos
T1CONbits.TMR1ON = 0; //Para la captura
//** 700 Pulsos capturados **//
T1CONbits.T1CKPS = 0b00; //Ajuste pre-escala 1:1
TMR1H = 0xFD; //65536-700 = 64836 o FD44h
TMR1L = 0x44;
PIR1bits.TMR1IF = 0; //Limpia la bandera
T1CONbits.TMR1ON = 1; //Arranca la captura
while(PIR1bits.TMR1IF == 0); //Espera hasta capturar los 700 pulsos
T1CONbits.TMR1ON = 0; //Para la captura
//** 700 Pulsos capturados **//
Ahora si nuestra aplicación requiere contar 500.000 pulsos externos, notara que este valor excede el limite de 65535 en el registro contador, entonces la solución a este caso seria seleccionar una pre-escala del divisor que permita obtener un múltiplo exacto y dentro de los limites del registro contador, por ejemplo si dividimos los 5000000 entre 8 obtenemos como resultado 62500, entonces ajustaremos el registro contador al valor 65536 - 62500 utilizando una pre-escala 1:8 y nuestra aplicación detectara la captura de los 500000 pulsos cuando ocurran el desbordamiento, este calculo se resumen en la siguiente ecuación:
De forma generalizada la figura 3. muestra la ecuación de ajuste del registro contador TMR1H:TMR1L para una determinada cantidad de pulsos externos obedece al siguiente calculo donde la pre-escala deberá ajustarse para que el valor nunca exceda los 65535, en caso de no lograr un valor inferior la implementación debe apoyarse en variables en la programación.
Fig3. Ecuación del contador TMR1 |
Configuración de un Temporizador.
T1CONbits.TMR1CS = 0; //Modo temporizador
T1CONbits.T1CKPS = 0b10; //Ajuste pre-escala 1:4
TMR1H = 0x9E; //65536-2500 = 40536 o 9E58h
TMR1L = 0x58; //TMR1=65536-[(time*Fosc)/(pre*4)]
PIR1bits.TMR1IF = 0;; //Limpia la bandera
T1CONbits.TMR1ON = 1; //Arranca temporizador
while(PIR1bits.TMR1IF == 0); //Espera los 100ms
T1CONbits.TMR1ON = 0; //Para el temporizador
/** Tiempo transcurrido de 100ms */
TMR1 con interrupciones
Sin duda que unos de los recursos mas valiosos que posee el microcontrolador, son las interrupciones, gracias a ello es posible manejar los eventos asociados el modulo TMR1 con la mínima intervención del programa principal.
El objetivo final de implementar un temporizador es calcular con una gran precisión un lapso o periodo de tiempo transcurrido, brindando la posibilidad de utilizar retardos, pausas controladas, generar eventos controlados, alarmas y recordatorios para nuestra aplicación.
En este modo el modulo captura pulsos internos que poseen un tiempo de duración equivalente a un ciclo de maquina o instrucción Tcy, para determinar el valor adecuado del registro contador utilizaremos la siguiente ecuación que se muestra en la figura 4, donde n representa el tiempo que se requiere en segundos y la pre-escala es el valor de división seleccionado para que el resultado no supere el limite de 65535.
En este modo el modulo captura pulsos internos que poseen un tiempo de duración equivalente a un ciclo de maquina o instrucción Tcy, para determinar el valor adecuado del registro contador utilizaremos la siguiente ecuación que se muestra en la figura 4, donde n representa el tiempo que se requiere en segundos y la pre-escala es el valor de división seleccionado para que el resultado no supere el limite de 65535.
Fig4. Ecuación del temporizador TMR1 |
La configuracion de un temporizador sigue la misma secuencia que lo visto en el contador, siendo la únicas diferencia lo siguiente:
El el bit TMR1CS debe permanecer desactivado para que los pulsos ingresen del oscilador interno Fosc/4;
El calculo utilizando la ecuacion 4 requiere conocer la frecuencia del oscilador.
Por ejemplo si necesitamos generar un retardo de una décima de segundo, considerando Fosc=4MHz y una pre-escala 1:4, calculamos lo siguiente:
Veamos como implementar la programación de este ejemplo en las siguientes lineas.T1CONbits.TMR1CS = 0; //Modo temporizador
T1CONbits.T1CKPS = 0b10; //Ajuste pre-escala 1:4
TMR1H = 0x9E; //65536-2500 = 40536 o 9E58h
TMR1L = 0x58; //TMR1=65536-[(time*Fosc)/(pre*4)]
PIR1bits.TMR1IF = 0;; //Limpia la bandera
T1CONbits.TMR1ON = 1; //Arranca temporizador
while(PIR1bits.TMR1IF == 0); //Espera los 100ms
T1CONbits.TMR1ON = 0; //Para el temporizador
/** Tiempo transcurrido de 100ms */
TMR1 con interrupciones
Sin duda que unos de los recursos mas valiosos que posee el microcontrolador, son las interrupciones, gracias a ello es posible manejar los eventos asociados el modulo TMR1 con la mínima intervención del programa principal.
Los bits que deben activarse para esta funcionalidad son:
- TMR1IE Habilitador de la interrupción por desbordamiento del TMR1
- PEIE Habilitador de las interrupciones generadas por los periféricos.
- GIE Habilitador de las interrupciones en el PIC
En la rutina de interrupción se verificara la bandera correspondiente para identificar al evento, la bandera debe limpiarse antes de salir de la rutina, para evitar que la interrupción reingrese por la misma condición.
Como
ejemplo practico vamos hacer uso del temporizador utilizando la
interrupción del TMR1 para que nos permita destellar un LED1 conectado al pin RC0, con una frecuencia de un segundo. Note que se adicionara la rutina de
servicio a la interrupción ISR para atender el evento del temporizador que se configurara a 1ms considerando una Fosc=8MHz.
#pragma config FOSC = INTRCIO // Oscillator Selection
#pragma config WDTE = OFF // Watchdog Timer
#pragma config PWRTE = OFF // Power-up Timer
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function
#pragma config BOREN = OFF // Brown-out Detect
#pragma config CPD = OFF // Data EE Memory Code Protection
#pragma config CP = OFF // Flash Program Memory Code Protection
#include <xc.h>
#include <stdint.h>
volatile __bit tick1ms; //Bandera de ms
uint16_t cnt = 0; //Contador de ms
__bit led1st; //bandera de estado led
void setup(void);
void __interrupt() isr(void) //Rutina ISR
{
if(PIR1bits.TMR1IF) //Evento de desbordamiento TMR1
{
PIR1bits.TMR1IF = 0;
T1CONbits.TMR1ON = 0;
TMR1H = 0xF8; //TMR1=65536-[(time*Fosc)/(pre*4)]
TMR1L = 0x30; // 65536-63536 = 2000 o 7D0h
T1CONbits.TMR1ON = 1;
tick1ms = 1;
}
}
void main(void)
{
setup(); //Configura el PIC
while(1)
{
if(tick1ms) //Se valida cada 1ms
{
tick1ms = 0; //Limpia bandera de ms
cnt++; //incrementa variable contador
if(cnt > 499) //hasta llegar a 500 x 1ms
{
cnt = 0; //Reinicia contador de ms
led1st = !led1st; //Invierte el calor logico
PORTCbits.RC0 = led1st;
}
}
}
}
void setup(void) //Procedimiento de configuracion PIC
{
OSCCONbits.IRCF = 0b111; //Fosc=8MHz Tcy=0.5uS
while(OSCCONbits.HTS == 0){};
ANSEL = 0; //Desactiva los pines AN0:AN7
ANSELH = 0;//Desactiva los pines AN8:AN13
TRISCbits.TRISC0 = 0; //Salida LED1
TRISCbits.TRISC1 = 0; //Salida LED2
PORTC = 0; //Apaga LED1 y LED2
OPTION_REGbits.nRABPU = 0; //Habilita pullup A/B
/* CONFIGURACION TIMER1 1MS Fosc=8Mhz*/
T1CONbits.TMR1CS = 0; //Modo temporizador
T1CONbits.T1CKPS = 0b00; //Ajuste pre-escala 1:1
TMR1H = 0xF8; //TMR1=65536-[(time*Fosc)/(pre*4)]
TMR1L = 0x30; // 63536 F830h
PIR1bits.TMR1IF = 0;; //Limpia la bandera
T1CONbits.TMR1ON = 1; //Arranca temporizador
PIE1bits.TMR1IE = 1; //Activa interrupcion
/* CONFIGURA INTERRUPCIONES*/
INTCONbits.PEIE = 1;//Activa interrupcion de periferico
INTCONbits.GIE = 1; //Activador global ISR
}
#pragma config WDTE = OFF // Watchdog Timer
#pragma config PWRTE = OFF // Power-up Timer
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function
#pragma config BOREN = OFF // Brown-out Detect
#pragma config CPD = OFF // Data EE Memory Code Protection
#pragma config CP = OFF // Flash Program Memory Code Protection
#include <xc.h>
#include <stdint.h>
volatile __bit tick1ms; //Bandera de ms
uint16_t cnt = 0; //Contador de ms
__bit led1st; //bandera de estado led
void setup(void);
void __interrupt() isr(void) //Rutina ISR
{
if(PIR1bits.TMR1IF) //Evento de desbordamiento TMR1
{
PIR1bits.TMR1IF = 0;
T1CONbits.TMR1ON = 0;
TMR1H = 0xF8; //TMR1=65536-[(time*Fosc)/(pre*4)]
TMR1L = 0x30; // 65536-63536 = 2000 o 7D0h
T1CONbits.TMR1ON = 1;
tick1ms = 1;
}
}
void main(void)
{
setup(); //Configura el PIC
while(1)
{
if(tick1ms) //Se valida cada 1ms
{
tick1ms = 0; //Limpia bandera de ms
cnt++; //incrementa variable contador
if(cnt > 499) //hasta llegar a 500 x 1ms
{
cnt = 0; //Reinicia contador de ms
led1st = !led1st; //Invierte el calor logico
PORTCbits.RC0 = led1st;
}
}
}
}
void setup(void) //Procedimiento de configuracion PIC
{
OSCCONbits.IRCF = 0b111; //Fosc=8MHz Tcy=0.5uS
while(OSCCONbits.HTS == 0){};
ANSEL = 0; //Desactiva los pines AN0:AN7
ANSELH = 0;//Desactiva los pines AN8:AN13
TRISCbits.TRISC0 = 0; //Salida LED1
TRISCbits.TRISC1 = 0; //Salida LED2
PORTC = 0; //Apaga LED1 y LED2
OPTION_REGbits.nRABPU = 0; //Habilita pullup A/B
/* CONFIGURACION TIMER1 1MS Fosc=8Mhz*/
T1CONbits.TMR1CS = 0; //Modo temporizador
T1CONbits.T1CKPS = 0b00; //Ajuste pre-escala 1:1
TMR1H = 0xF8; //TMR1=65536-[(time*Fosc)/(pre*4)]
TMR1L = 0x30; // 63536 F830h
PIR1bits.TMR1IF = 0;; //Limpia la bandera
T1CONbits.TMR1ON = 1; //Arranca temporizador
PIE1bits.TMR1IE = 1; //Activa interrupcion
/* CONFIGURA INTERRUPCIONES*/
INTCONbits.PEIE = 1;//Activa interrupcion de periferico
INTCONbits.GIE = 1; //Activador global ISR
}
Conclusiones y Recomendaciones.
Debe considerar que el modulo TMR1 es un recurso que comparte funciones con otros módulos como el CMP, CCP/ECCP y el oscilador secundario, por lo su uso podría estar restringido si alguno de estos módulos esta en operación.
El concepto de utilizar una interrupción es detectar una condición y ejecutar una tarea en el menor tiempo posible, las funciones y procedimientos llevadas a cabo en la rutina ISR no deberían utilizar demasiados de ciclos de instrucción debido a que podrían generar inconvenientes en el programa principal, un aspecto importante a tomar en cuenta es que durante la lectura o escritura del registro par TMR1H:TMR1L puede ocurrir un desbordamiento y causar error, esto se debe a que leer o escribir el valor de este contador requiere mas de dos ciclos de reloj porque son dos registros referenciados en momentos diferentes, y un desbordamiento en medio del proceso haría que uno de estos registros sea 0, y con ello el valor final de 16-bit erróneo. La figura 5, extraida del manual de referencia, muestra este problema.
Debe considerar que el modulo TMR1 es un recurso que comparte funciones con otros módulos como el CMP, CCP/ECCP y el oscilador secundario, por lo su uso podría estar restringido si alguno de estos módulos esta en operación.
El concepto de utilizar una interrupción es detectar una condición y ejecutar una tarea en el menor tiempo posible, las funciones y procedimientos llevadas a cabo en la rutina ISR no deberían utilizar demasiados de ciclos de instrucción debido a que podrían generar inconvenientes en el programa principal, un aspecto importante a tomar en cuenta es que durante la lectura o escritura del registro par TMR1H:TMR1L puede ocurrir un desbordamiento y causar error, esto se debe a que leer o escribir el valor de este contador requiere mas de dos ciclos de reloj porque son dos registros referenciados en momentos diferentes, y un desbordamiento en medio del proceso haría que uno de estos registros sea 0, y con ello el valor final de 16-bit erróneo. La figura 5, extraida del manual de referencia, muestra este problema.
Fig5. Secuencia de error TMR1 |
Sin
mas que mencionar agradezco tu visita al blog y espero que el ejemplo
visto pueda ser útil en tu formación y el proyecto que desarrollas.
Atte. Pablo Zárate Arancibia
email: pablinza@me.com / pablinzte@gmail.com, @pablinzar
Santa Cruz de la Sierra - Bolivia
Atte. Pablo Zárate Arancibia
email: pablinza@me.com / pablinzte@gmail.com, @pablinzar
Santa Cruz de la Sierra - Bolivia
No hay comentarios.:
Publicar un comentario