Generación del tick del sistema con el LPC2129

Hola amigos, en esta entrada voy a mostrar cómo generar una interrupción periódica (tick) que se puede utilizar como base de tiempo para generar retardos de duración exacta, o como base de tiempo para algún RTOS, o como periodo de muestreo para el convertidor A/D.

Aunque el título de la entrada dice “… para el LPC2129”, la verdad es que los módulos del timer son idénticos tanto en la familia LPC2000 (ARM7TDMI), como en la más reciente LPC1000 (Cortex Mx). Sin embargo, para esta última el núcleo ya incluye un timer dedicado para el tick, mientras que en los primeros tenemos que “sacrificar” uno de ellos.

El sacrificio viene del hecho de que el periodo del timer limitará la frecuencia máxima para otros usos de ese mismo timer. Es decir, si el periodo es de 1000 us, entonces en el caso de querer utilizarlo como PWM (además de tick) la frecuencia del PWM será de 1000 us. Pero no todo está perdido porque quizás podamos adaptar otras tareas a dicho periodo.

Generación del tick

El primer paso consiste en escoger un timer (casi siempre son 4). Me parece que lo más conveniente es escoger aquel que tenga menor resolución (16 bits), o menor cantidad pines de salida. Pero al final de cuentas todo depende de la aplicación en particular.

Luego hay que escoger un registro MATCH, es decir, uno de aquellos llamados MRx (donde ‘x’ es el número de registro, también normalmente son 4 para los timers de 32 bits, y 2 para los de 16).

La secuencia de eventos necesarios para generar el tick es así (la más general):

La frecuencia del cristal se multiplica en el PLL para obtener una frecuencia más alta. Lo más común es un cristal de 14.7 MHz multiplicado por 4 para tener 58.98 MHz. A esta frecuencia le llamaremos PCLK.

Luego, el PCLK se pasa por el prescaler del timer, esto es, los 58.98 MHz se van a dividir por una cantidad PR. En los timers de 32 bits el prescaler realmente no se requiere, pero vamos a utilizarlo porque esa es la práctica común. Para este ejemplo vamos a escoger un valor para PR de 59. Con esto tendremos una periodo de entrada al TC de casi 1 us.

El TC (timer counter) es un contador que cuenta los pulsos que le llegan del prescaler. Siguiendo con nuestro ejemplo, cada millón de pulsos que el TC cuente  representarán un segundo; esto es así porque del prescaler salen pulsos a 1 us.

Los registros MATCH, es decir, los MRx, se cargan con un cierto valor y se comparan continuamente contra el TC. Cuando el TC iguala el valor en un MR en particular se pueden tomar diversas acciones: no hacer nada, generar una interrupción, poner el TC a cero, o detener al timer (evitar que lleguen pulsos al TC).

Para generar el tick requerimos que cada vez que el TC llegue al valor en el MR (la cantidad de pulsos equivalente al periodo que deseamos) el TC se ponga a cero y se genere una interrupción (es necesario que TC se ponga a cero para que el ciclo se repita indefinidamente). En la ISR del timer tendremos algún contador para los retardos, o alguna señalización para el RTOS, o mandar la órden de iniciar una conversión en el ADC. Los usos son ilimitados.

Para el ejemplo que nos ocupa cargaremos un MR con el valor 1000; esto es, cada vez que el TC cuente 1000 pulsos (de 1 us cada uno) tendremos una interrupción cada 1 ms, y éste será nuestro tick. El periodo del tick va a depender de cada aplicación en particular. A mí me gusta utilizar 1 ms, aunque algunos recomiendan 10 ms si se utiliza un RTOS. Cuestión de gustos y necesidades.

Debo apuntar lo siguiente: las ISR no deben ser sobrecargadas con código. Una ISR debe ser simple y rápida. Por ejemplo, la ISR de nuestro tick sólo debería incrementar contadores y/o señalizar (semáforos). El trabajo útil de  los contadores o los semáforos debe ser hecho a nivel de tarea, y no a nivel de la ISR.

Un paso importante es el de la configuración de las interrupciones para el timer en cuestión. Si alguien no está familiarizado con la forma de utilizar las interrupciones en la familia LPC2000 puede darse una vuelta en esta entrada del blog.

A continuación presento un código que aterriza todo lo que he platicado:

 
volatile uint32_t tim0;
volatile uint32_t tim_delay;
...
void tick_isr(void)
{
 tim0++;
 if(tim_delay>0){
   tim_delay--;
 }

 T1IR=(1<<3);
}

/* El tick está implementado sobre T1MR3 */

void tick_Init(void)
{
 /*
 * detiene al timer
 */
 T1TCR=2;

/*
 * El TC se incrementa debido a PCLK
 */
 T1CTCR=0;

/*
 * Interrupción y reset en MAT1.3 (ancho de pulso)
 */
 T1MCR|=(3<<9);

/* 
 * Base de tiempo de 1us=58982400Mhz/1.0Mhz
 */
 T1PR=59;

/*
 * el tick está corriendo a 1ms=1us*1000
 */
 T1MR3=1000;

/*
 * Configura las interrupciones para el tick del sistema
 */
 VICIntSelect&=~(1<<5);         /* IRQ */
 VICIntEnable|=(1<<5);          /* En */ 
 VICVectCntl0=(5)|(1<<5);
 VICVectAddr0=(unsigned long) tick_isr;
/*
 * Limpia cualquier interrupción pendiente
 */
 T1IR=(1<<3);

/*
 * El timer arranca
 */
 T1TCR=1;
}

void pause(uint32_t d)
{
 tim_delay=d;
 while(tim_delay>0)
 ;
}

main()
{
  ...
  tick_Init();
  ...
  for(;;)
  {
    ...
    pause(500);  // retarde de 500 ms
  }
} // main

Por cierto, todas las variables cuyo valor cambie dentro de una ISR, o dentro de alguna función llamada por la ISR, deben ser declaradas como volatile. De no hacerlo así, cuando el compilador optimice el código hará cosas extrañas y su programa no funcionará como ustedes esperan. Pueden ver esta entrada de mi blog.

Espero que este mini-tutorial les haya sido de ayuda. Como siempre, todos sus comentarios, dudas y sugerencias son bienvenidos.

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s