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:
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
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 TMR2IF.
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
*******************************************************************************************
******************************************************************************************/