Recursos del PIC - Uso del TMR0 como temporizador

El uso del temporizador TMR0 es una de las características más útiles y versátiles de los microcontroladores PIC. En este artículo, exploraremos cómo utilizar el TMR0 como temporizador en el PIC16F8X utilizando el compilador CCS.

Biblioman

3/26/200912 min read

En esta práctica vamos a ver cómo utilizar el Timer0 con el compilador CCS.

Pero antes vamos a ver un poco de teoría:

El Timer0 es un temporizador/contador ascendente de 8 bits, cuando trabaja con el reloj del PIC se le suele llama temporizador y cundo los pulsos los recibe de una fuente externa a través de la patilla RA4/TOCKI se le llama contador, pero digamos que es el mismo perro con dos collares diferentes. Para no liarnos con las patillas y el nombre de los registros voy a mostrar los registros y patillas implicadas solo en el TMR0 utilizando el PIC16f84, aunque podría ser otro cualquiera ya que este temporizador viene incorporado en todos los PIC.

Patillaje:

RA4/TOCKI: cuando el temporizador trabaje como contador, los pulsos externos los recibirá a través de esta patilla.
OSC1/CLKIN y OSC2/CLKOUT: son para conectar el oscilador que nos determinará a la frecuencia que va a trabajar nuestro PIC, los dos tipos de osciladores más usados son el XT (cristal de cuarzo) y el RC (resistencia y condensador).

Nota: cuando las patillas traen más de una función separadas por la barra del siete quiere decir que según la configuración de los registros SFR que se muestran más abajo se utilizarán de una manera o de otra.

Banco de registros específicos (SFR) del PIC 16f84A:

Como podemos ver solo están marcados de un color diferente los que están implicados en el funcionamiento del TMR0, vamos a ver la función que tienen:

TMR0: es un registro del que podemos leer el valor del contador en tiempo real, pero también podemos escribir en él y alterar el valor de conteo.

OPTION: los bits que están en color verde son los que están implicados en la configuración del TIMER0

  • PS0, PS1 y PS2: Configuración del preescaler. El preescaler es un divisor de pulsos que está a la entrada del Timer-contador. El prescaler divide el número de pulsos que le entran al timer-contador o al Wachtdog. El factor de división es el siguiente (según los valores de PS2, PS1 y PS0 respectivamente

  • PSA: Bit de asignación de prescaler. Si está a "1" el prescaler se asigna a WDT (Wachtdog), si está a "0" se asigna al TMR0.

  • TOSE: Bit de selección del tipo de flanco para el TMR0. A "1" se incrementa TMR0 por flanco descendente de RA4, a "0" se incrementa TMR0 por flanco ascendente de RA4.

  • TOCS: Selecciona la entrada de reloj de TMR0. A "1" la entrada de reloj de TMR0 es por flanco de la patilla RA4, a "0" la entrada de reloj de TMR0 es por ciclo de reloj interno.

Pero el PIC también permite que se produzca una interrupción por desbordamiento del TMR0.

Cuando se produce una interrupción el programa abandona temporalmente lo que estaba haciendo para atender la subrutina de interrupción, pero antes guarda en una región especial de la memoria llamada pila la dirección de la siguiente instrucción de programa, para que cuando acabe la subrutina de interrupción pueda seguir ejecutando el programa por donde se había quedado.

El registro que configura todas las interrupciones es el INTCON, como veis está mapeado en los dos bancos, una cosa muy útil cuando se trabaja en ensamblador para no tener que cambiar de banco constantemente para su configuración, pero que en C no es necesario saber ya que es el compilador el que se encarga de gestionar esto de una manera transparente para el programador.

Pues bien vamos a ver lo que significa cada uno de los bits que están implicados en la interrupción por el TMR0:

  • TOIF: solo se puede leer su valor, es un Flag o bandera que se pone a “1” cuando se produce un desbordamiento del TMR0, (este o no este configurado para producir una interrupción). Cuando se trabaja en Ensamblador, este bit hay que ponerlo a "0" por programa, con CCS no tenemos que preocuparnos por ello. (je, je,.. otra cosa que nos ahorramos)

  • TOIE (Habilita la interrupción por desbordamiento de TMR0). Si este bit esta a "1" la interrupción por desbordamiento de TMR0 es posible.

  • GIE (Habilita las interrupciones globalmente). Este bit permite que cualquier interrupción sea posible. Para poder usar cualquier interrupción hay que habilitarla globalmente e individualmente.

Bueno como veis hay varias variables que podemos utilizar a nuestro antojo para utilizar este recurso del PIC, la fórmula que las relaciona es la siguiente:

Y para ver como funciona todo esto con el compilador CCS vamos hacer el siguiente ejemplo:

Se trata de usar el Timer0 para generar una interrupción cada 32 ms utilizando el reloj interno del microcontrolador. La función de interrupción se utilizará para hacer parpadear un Led en la patilla RB7 del PIC.

Para ello vamos a utilizar en el circuito un cristal de cuarzo de 4 MHz.

Si nos fijamos en la formula anterior tenemos Interrupt_TMR0= 32 ms que es el valor que nos da el ejemplo, también tenemos F.Oscilador = 4 MHz y nos quedan dos valores para tener resueltas todas las variables de la ecuación el valor TMR0 que ya hemos dicho que se puede sobrescribir su valor en cualquier momento durante la ejecución del programa y el Prescaler, pues le damos el valor que queramos al Prescaler por ejemplo 256 y calculamos en la fórmula el valor que tenemos que escribir en el registro TMR0 para obtener los 32 ms:

Si despejamos el valor del TMR0 nos sale un valor de 131 que en hexadecimal es 0x83.

Pues bien ya solo queda implementar todo esto en C, para ello en este ejemplo (y de paso vemos una nueva funcionalidad que nos presenta este entorno de programación de PIC) vamos a utilizar el Wizard o asistente para generar proyectos. Quiero aclarar primero que este asistente está todavía en desarrollo por lo que es recomendable revisar el código que genera antes de compilar, pues casi siempre tendremos que corregir algún error de sintaxis en el código generado, pero suele ser una gran ayuda para el que empieza a trabajar con este compilador por que te genera un código que es como una plantilla o esqueleto donde están ya incluidas las funciones para manejar los recursos del PIC que hemos seleccionado con el asistente, para ello hacemos clic con el ratón en el icono que pone PIC Wizard (si, si en el sombrero del mago), nos saldrá un cuadro de dialogo de Windows para que guardemos nuestro proyecto donde queramos, una vez hecho esto nos aparecerá la siguiente ventana:

En ella podemos ver que a la izquierda podemos seleccionar los diferentes recursos que tienen los PIC y a la derecha tenemos dos pestañas: en Options configuramos el recurso seleccionado y en la pestaña Code visualizamos el código que nos genera el asistente para ese recurso, para el ejemplo que estamos haciendo en General, lo dejamos tal y como se muestra en la figura de arriba. Si ahora hacemos clic en la pestaña code vemos el código generado por el asistente para esta opción.

Inserted into .h file: no pertenece al código que se va a compilar, simplemente es una nota informativa que nos está diciendo que el código que hay mas abajo se va a insertar en el archivo de cabecera con extensión .h.

#FUSES NOWDT, XT, NOPROTECT: fuses cuya tradución literaria equivaldría a "fusibles" es una directiva que establece unas configuraciones para el PIC que son necesarias a la hora de grabar el programa en el, se pueden incluir aquí o configurarlas en el programa de grabación que utilicemos, se guardan en la memoria de programa y según la versión de PIC que utilicemos tendremos mas o menos opciones, las que aquí aparecen tienen el significado siguiente:

NOWDT --> No se va a utilizar el Wachtdog.

XT --> Se va a utilizar un cristal de cuarzo para la base de tiempos del reloj del PIC.

NOPUT -->( Power Up Timer), cuando se configura como PUT al alimentar el circuito el Microcontrolador espera un tiempo determinado para que se estabilicen las tensiones antes de ejecutar el programa.

NOPROTECT --> El código grabado en el PIC no está protegido.

Hay que decir que cuando creamos un proyecto por medio del Wizard, este nos genera tres archivos, que son los que se muestran en la figura de abajo:

Uno con extensión .c que es donde estará el grueso de nuestra aplicación, otro con extensión .h que es donde estarán los archivos de cabecera y otro con extensión .pjt que es el proyecto en si y que nos sirve para abrir y editarlo con el IDE del compilador.

Cuando compilemos y generemos nuestra aplicación aparecerán mas archivos pero estos que hemos mencionado aquí son los que necesitamos para editar nuestro código fuente.

Sigamos, cuando seleccionemos la pestaña Communications debemos desmarcar la opción de comunicación con el puerto serie RS232, ya que no la vamos a utilizar.

Configuración de los timmers:

En las opciones de este recurso desmarcamos la casilla Not used del Wachtdog por que no lo vamos a utilizar, en el Timer 0 en Source seleccionamos Internal ya que vamos a utilizar los pulsos de reloj del microcontrolador y en Resolution marcamos 256 us, que es el preescaler que habíamos decidido utilizar. Vemos que las opciones del Timer 1 y el Timer 2 están deshabilitadas, ya que PIC 16f84A solo tiene el temporizador timer 0.
Bien si ahora hacemos clic sobre la pestaña Code vemos el código generado por el asistente:

La primera línea como antes nos dice donde se va a insertar el código, en este caso en el archivo .C y dentro de la función principal main(). La siguiente línea es una llamada a la función setup_timer_0( parametro1|parametro2|…), que ya está declarada y definida dentro de las librerías del compilador y es utilizada para configurar el registro OPTION del PIC, como vemos acepta parámetros, con el primero RTCC_INTERNAL, le estamos diciendo que vamos a utilizar el reloj interno, lo que viene después aunque en la figura no se vea muy bien es una barra vertical “|” y es el símbolo que hay que utilizar para separar los diferentes parámetros que utilicemos, el segundo parámetro que nos ha puesto es RTCC_DIV_1);); y como veis aparece doble el cierre del paréntesis y el punto y coma, ¡¡error del asistente!!, lo que realmente tenemos que poner es esto: RTCC_DIV_256, ya que este segundo argumento le pasa a la función el valor del preescaler y queremos que sea 256. Bueno esto lo que me ha generado a mí, lo mismo a vosotros os va bien. Vemos que no podemos corregir el código aquí, lo haremos después en los archivos fuente que nos genera.
Por ultimo nos queda configurar el registro INTCON, para ello seleccionamos Interrups y en Options marcamos la casilla Timer0 overflow (usando como nombre de la interrupción TIMER0).

El código generado será el siguiente:

Como vemos la rutina de interrupción va antes de la función main y consta de la directiva del preprocesador #int_TIMER0, con esta directiva el programa sabe a que dirección de programa debe de ir cuando se dispare esta interrupción (podemos tener otras), y de la función de interrupción propiamente dicha, donde tenemos que incluir nuestro código, veámoslo aquí debajo de una forma más clara:

#int_TIMER0

Void TIMER0_isr(void)
{
//Nuestro código de interrupción.
}

Dentro de la función main incluye lo siguiente:

enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);

La primera instrucción habilita la interrupción particular del Timer 0 por desbordamiento y la otra habilita las interrupciones globalmente, como dijimos al principio no hay que preocuparse por resetear el flag de interrupción TOIF, ni de desactivar las interrupciones globales mientras se este dentro del código de interrupción por si se produce otra simultáneamente, como hay que hacer cuando se trabaja en ensamblador, estas funciones lo hacen también pero de una manera transparente al programador, por eso es un lenguaje de alto nivel.

Después de terminar con la configuración de los recursos pulsamos el botón de aceptar y el asistente nos creará los archivos correspondientes:

Archivo de cabecera:

Archivo .C principal:

Pues bien este es el esqueleto de nuestra aplicación y sin escribir una línea de código. Ahora sobre esta plantilla tenemos que escribir nuestro código para hacer nuestra aplicación funcional.

En el archivo de cabecera incluiremos la línea que esta en negrita:

#include <16F84A.h>
#use delay(clock=4000000)
#bit RB7=0x6.7 //definimos RB7 como el pin 7 de la puerta B


Con la directiva #bit le podemos asignar la dirección de memoria del bit de un registro a una variable que podremos utilizar después para referirnos a ese bit.

En el archivo principal TMR0.C añadiremos también las siguientes líneas marcadas en negrita:

#include "C:\Prácticas PIC C\TMR0\TMR0.h"
#int_TIMER0
void TIMER0_isr(void)
{
if (RB7==0)
{
RB7=1;
set_TIMER0(0x83); //inicializa el timer0
}
else
{
RB7=0;
set_TIMER0(0x83); //inicializa el timer0
}

}

void main()
{

set_tris_b(0b01111111); //configura RB7 como salida el resto como entrada
RB7=0; //inicializo el bit RB7 a 0;

setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);

set_TIMER0(0x83); //inicializa el timer0
//el bucle infinito es necesario ya que si el micro entra en sleep
//desactiva la interrupción del TMR0
while(true);

}


Con la directiva #include lo que se hace es incluir un archivo dentro de otro, si queremos tener todo el código fuente en un mismo archivo lo único que tenemos que hacer es sustituir esta línea por el contenido del archivo de cabecera, pero es recomendable separar los archivos de cabecera del resto del programa. C es un lenguaje estructurado y así es como trabaja, cuanto antes nos acostumbremos a ello mejor.

¡ojo¡ si utilizamos los dos archivos y copiamos el código fuente y lo pegamos en un directorio diferente tendremos que modificar la ruta actual al archivo de cabecera si no al compilar dará un error de archivo no encontrado.

El funcionamiento del programa es muy simple, después de inicializar el puerto B inicializamos el TMR0 por medio de la función set_TIMER0, el valor que recibe como parámetro esta función es el valor que se escribirá en el TMR0 que es 0x86, valor que habíamos obtenido de la fórmula anteriormente, como está activada la interrupción por desbordamiento del contador este empieza a contar a partir de 0x86 ó 131 en decimal hasta 255 que equivale a 11111111 en binario como es un contador de 8 bits al siguiente pulso de reloj se produce el desbordamiento y el contador de programa va a la dirección de memoria donde está el vector de interrupción del TMRO y ejecuta las instrucciones que están dentro de su función. La función de interrupción lo único que hace es a través de una sentencia de control if–else encender o apagar el led, fijaros que en cada caso se vuelve a cargar el valor del TMR0 para que el contador vuelva a empezar a contar por 131 y no por cero, de esta manera la interrupción se producirá cada 32 ms que es lo que queríamos.
Bueno a hora toca construir nuestro circuito virtual y simularlo con Proteus para comprobar que efectivamente es así.

Hemos agregado una sonda de tensión y para poder ver los valores que toma en el tiempo incluimos una grafica en simulación digital. Si ampliamos la gráfica veremos que cada intervalo tiene una duración de 32 ms. Como siempre el DSN y el código fuente os lo podéis bajar desde la sección Descargas –> Apuntes -> Electrónica -> Microcontroladores -> Ejemplos prácticos programación PIC en C.

Bueno yo creo que mas mascado no puede estar, para aquellos que no conozcan el lenguaje C y les suene a chino lo que es una función y lo de pasar parámetros a una función. “No problem” en el curso de teoría se verá detenidamente.