Resumen del Timer2.             



Para generar la señal de control requerida por el microcontrolador del miniz se va a utilizar la interrupción el timer2 al igual que se hizo mediante ensamblador al estudiar el funcionamiento de su electrónica, de esta forma nos aseguramos que la señal se entregue en el tiempo esperado. Debido a que el programa final será bastante largo y requiere de muchas operaciones se va a utilizar C en lugar de asm siempre que se pueda, como microcontrolador el PIC18f452 y compilador el C18 de Microchip del que se puede descargar una versión estudiante de manera gratuita.

Esto es un pequeño resumen del funcionamiento del timer2 de uso personal para acordarme lo que he de hacer y no perderlo al tenerlo aquí... Soy muy malo en esto de programar, así que mejor no seguir leyendo =) (dicho ésto ya puedo poner cualquier barbaridad XD).

La primera diferencia la tenemos en cuanto a las interrupciones, y es que en los 18 nos encontramos con interrupciones de dos prioridades y dos vectores de interrupción, uno en el 000008h y en 000018h, es decir dependiendo de la prioridad de la interrupción nuestro programa saltará a una de estas dos direcciones. Cada fuente de interrupción tiene 3 bits para su control::

- Un flag que nos indica cuando una interrupción ha ocurrido, deberemos de limpiarle por soft cuando atendamos a la interrupción y también nos vale para detectar que interrupción ha saltado cuando tenemos más de una.

- Un bit de enable que habilita la interrupción.

- Y un bit que selecciona si la interrupción es de prioridad alta o baja.

La característica de interrupciones con dos prioridades se puede activar o desactivar, cuando la desactivamos las interrupciones son compatibles con las de los 16 y  a no ser que requiera de otras interrupciones así se va a hacer al sólo tener una, para ello hay que poner el bit IPEN del registro RCON a cero, de esta forma los bits de prioridad para cada interrupción no tienen ningún efecto y todas las interrupciones apuntarán al 000008h. El bit 7 del registro INTCON, GIE se encarga de activar o desactivar todas las fuentes de interrupción y cuando una interrupción ocurre éste se deshabilita hasta que sale de ella para evitar otras posibles interrupciones, lo único que debemos hacer es limpiar el bit de flag de la interrupción por soft dentro de ésta.

Los bits para el funcionamiento del timer2 y su interrupción son los siguientes:



Considerando IPEN=0, no tenemos prioridades.

INTCON.GIE. Global Interrupt Enable bit. Habilita todas las interrupciones que no se encuentren desactivadas cuando vale 1, si su valor es 0 deshabilita todas las interrupciones.
INTCON.PEIE. Peripheral Interrupt Enable bit. Igual que el anterior pero para las interrupciones de los periféricos (incluidas en el anterior).
PIR1.TMR2IF. El bit de flag que nos indica con 1 que ha ocurrido una interrupción, debe ser limpiado en software.
PIE1.TMR2IE. Habilita la interrupción del timer2 que ocurre cuando el registro TMR2 es igual a PR2.
IPR1.TMR2IP. Establece la prioridad de la interrupción, 1 de prioridad alta, 0 de prioridad baja.
TMR2. Registro en el que llevamos la cuenta.
PR2. Valors hasta que TMR2 debe de alcanzar para activar el flag de interrupción.
T2CON. Registro de control del timer2, con los bits del 6 al 3 seleccionamos el valor del postscale, con el bit 2 encendemos o apagamos el timer2, y con el bit 0 y 1 seleccionamos el valor del prescale.

Para tener la interrupción del timer2 funcionando el valor inicial de estos bits debería ser: GIE=1, PEIE=1, TMR2IF=0  (se pone a 1 sólo), TMR2IE=1, TMR2IP no lo usamos al tener IPEN=0, PR2 con un valor para el periódo deseado, T2CON -xxxx1xx.

La lógica es la siguiente:



Para configurar los bits debemos de escribir las siguientes líneas de código:

    OpenTimer2 (TIMER_INT_ON & T2_PS_1_16 & T2_POST_1_16); 
    RCONbits.IPEN = 0;
    INTCONbits.GIE = 1;
    INTCONbits.PEIE = 1;
    PR2 = 250;

Para poner un bit de un registro a uno o cero se hace de la manera siguiente: NOMBRE_REGISTRObits.NOMBRE_BIT = 1; por lo que de esta forma daremos valor a los bits IPEN, GIE, PEIE.

Si buscamos la función OpenTimer2 encontramos el siguiente código.

#include <p18cxxx.h>
#include <timers.h>

/********************************************************************
*    Function Name:  OpenTimer2                                     *
*    Return Value:   void                                           *
*    Parameters:     config: bit definitions to configure Timer2    *
*    Description:    This routine first resets the Timer2 regs      *
*                    to the POR state and then configures the       *
*                    interrupt and clock source.                    *
*    Notes:          The bit definitions for config can be found    *
*                    in the timers.h file.                          *
********************************************************************/
#if defined (TMR_V2) || defined (TMR_V3) || defined (TMR_V4) || defined (TMR_V5)
void OpenTimer2(unsigned char config)
{

  T2CON = (0xfb & config);  // Set all configuration values, but
                            // don't start timer yet

  TMR2 = 0;                 // Clear Timer2
  PIR1bits.TMR2IF = 0;

  if(config & 0x80)         // Enable timer interrupts?
    PIE1bits.TMR2IE = 1;
  else
    PIE1bits.TMR2IE = 0;

  T2CONbits.TMR2ON = 1; // Turn on Timer2
}

#endif

En función de los parámetros que le pasemos se encarga de poner el registro que lleva la cuenta TMR2 a cero y limpia el flag de interrupción TMR
2IF. Si en la función hemos indicado que la interrupción esté activa se encarga de habilitarla mediante el bit TMR2IE poniendole a 1 y activa el timer poniendo TMR2ON a 1. Por último se encarga de poner los bits del postscale y prescale en T2CON al valor que indicamos.

Los valores del prescale y postscale junto al valor de PR2 y la frecuencia del reloj nos dará la duración de la cuenta del timer2, es decir el periódo de nuestra interrupción. Para el miniz necesitamos mandarle una señal cada 16 ms, por lo que nuestro timer2 se encargará de llevar la cuenta cada 16 ms, y cuando los alcance activar la interrupción lanzando la ISR que se encargue de generar la señal deseada para el control del coche. La explicación de los valores está aquí, para este caso con un reloj de 16 MHz para alcanzar un período de 16 ms, debemos dar al pre y post un valor de 1:16 y al PR2 el valor de 250.

Con estas líneas tenemos el timer2 configurado, sólo falta colocar el código en su parte correspondiente de memoria para atender la interrupción, para ello se usa la directiva #pragma que es similar al ORG de ensamblador, mediante #pragma indicamos en que parte de la memoria es situado el código. En el 0008h colocaremos la dirección de la rutina que va a atender la interrupción. También mediante la directiva #pragma diferenciaremos el ISR de una función normal, ya que una rutina de atención a la interrupción implica ciertas acciones que no se realizan en salto a la función, como deshabilitar las interrupciones y salvar ciertos registros.

Para ello debemos escribir el siguiente código:

#pragma code high_vector=0x08 // high interrupt vector en 0008h
void interrupt (void)
{
    _asm GOTO timer2_isr _endasm // Salta a la ISR
}
#pragma code // default code section

Esto colocaría en el 0008h la instrucción en ensamblador GOTO timer2_isr, es decir el programa saltaría a esta función.

Y para distinguir el ISR de una función normal.

#pragma interrupt timer2_isr save=DelayCounter1  // interrupción, salvamos la variable de los delays

void timer2_isr ( void)

En este caso indicamos mediante #pragma interrupt que el ISR es de prioridad alta, salvando el registro de STATUS, WREG, y BSR (Bank Select Register) y mediante save=DelayCounter1 salvamos el valor de esta variable para luego restablecerla al salir de la interrupción, ya que es una variable que se usa en el programa principal debiendo conservar su valor aquí fuera y modificamos también al usarla en el ISR.

Un código de ejemplo para generar el TIC es el siguiente:


/******************************************************************************************
*******************************************************************************************
**  Programa para probar la electrónica del miniz, generando el tic con timer2.
**    Pic18f452
**    Cristal 16 MHz
**    28/11/2008 www.jmnlab.com
*******************************************************************************************
******************************************************************************************/
 
#include <p18f452.h>
#include <delays.h>
#include <timers.h>

#pragma config WDT=OFF, LVP=OFF, OSC=HS, OSCS=OFF, PWRT=ON, BOR=OFF, STVR=ON

void inicializar (void);
void reseteo (void);
void timer2_isr (void);

#pragma code high_vector=0x08 // high interrupt vector en 0008h
void interrupt (void)
{
    _asm GOTO timer2_isr _endasm // Salta a la ISR
}
#pragma code // default code section


#define LED1 PORTEbits.RE0 // ambar
#define LED2 PORTEbits.RE1 // rojo
#define MINIZ PORTBbits.RB4

//Variables
int DelayCounter1;

//Programa principal
void main (void)
    {
        inicializar();
        reseteo();

        while(1)
        {
            LED1 = 1;
            Delay10KTCYx(200);
            LED1 = 0;
            Delay10KTCYx(200);
        }
    }

//******************************FUNCIONES*******************************************
void inicializar (void)
{
      TRISA = 0b11111111;     // Configuración de los puertos
      TRISB = 0b11101111;        //
      TRISC = 0b11111111;        //
    TRISD = 0b11111111;        //
      TRISE = 0b00000000;      // Puerto E como salidas.
    ADCON1 = 0b00001111;     // Todo puerto A como digitales.
    PORTB = 0;
    PORTE = 0;
    OpenTimer2 (TIMER_INT_ON & T2_PS_1_16 & T2_POST_1_16); //TMR2IF=0 TMR2IE=1 TMR2ON=1 TMR2=0
    RCONbits.IPEN = 0;
    INTCONbits.GIE = 1;
    INTCONbits.PEIE = 1;
    PR2 = 250;
}

void reseteo (void)
{
    INTCONbits.GIE = 0;
    LED2 = 1;                  //3 parpaedos para detectar los posibles resets del pic
    LED1 = 0;
    Delay10KTCYx(200);
    LED2 = 0;
    Delay10KTCYx(200);
    LED2 = 1;
    Delay10KTCYx(200);
    LED2 = 0;
    Delay10KTCYx(200);
    LED2 = 1;
    Delay10KTCYx(200);
    LED2 = 0;
    INTCONbits.GIE = 1;
}

#pragma interrupt timer2_isr save=DelayCounter1  // interrupción, salvamos la variable de los delays

void timer2_isr ( void)
{
    PIR1bits.TMR2IF = 0;  //Limpia el flag de interrupción del timer2
    MINIZ = 1;
    Delay100TCYx(16);   
    MINIZ = 0;
    Delay100TCYx(44);
    MINIZ = 1;
    Delay100TCYx(16);   
    MINIZ = 0;
    Delay100TCYx(44);   
    MINIZ = 1;
    Delay100TCYx(16);   
    MINIZ = 0;   
}







En el osciloscopio obtenemos la siguiente señal en el pin que iría al miniz (hay que meter el divisor resistivo):








Video funcionamiento.



Para cualquier duda, corrección.. pls..