INTERRUPCIONES CON EL C18
Como se puede apreciar en la gama 18FXXX existen 10 registros que interactúan para gestionar las interrupciones del PIC, estos son los siguientes:
- RCON
- INTCON, INTCON2, INTCON3
- PIR1, PIR2
- PIE1, PIE2
- IPR1, IPR2
Para cada interrupción existen 3 bits de control:
- Bit de Bandera (flag bit)
- Bit de Habilitación (enable bit)
- Bit de Prioridad (priority bit)
Cada interrupción tiene un bit de control de prioridad (priority bit) el cual determina si es de alta o baja prioridad dicha interrupción. Además, ésta característica de prioridad que tiene cada interrupción también puede ser deshabilitada y tratar a todas las interrupciones bajo una misma jerarquía (como en la gama 16FXXX), se trata del bit 7 del rgistro RCON:
DECLARACIÓN DE UNA INTERRUPCIÓNEn el C18 una interrupción tiene un formato de declaración definido por el propio compilador. Sin embargo tenemos que indicar al compilador donde esta ubicado el vector de interrupción correspondiente, y digo correspondiente porque tenemos dos prioridades de interrupción, una alta y otra baja:
Donde la interrupción de baja prioridad corresponde al vector de memoria 0X0018, y la de alta prioridad corresponde al vector de memoria 0X0008. Para efectuar el direccionamiento de los vectores utilizamos la directiva #pragma.
#pragmaLa directiva #pragma abarca aplicaciones tanto en la memoria de programa como en la memoria de datos, para efectos de entender bien lo de las interrupciones nos limitaremos (por ahora
) a su aplicación a nivel de secciones con instrucciones en la memoria de programa.
Con esta directiva #pragma lo que conseguimos es que el compilador ubique un bloque de código en una posición absoluta definida por nosotros. La sintaxis sería asi:
#pragma code [nombre_de_seccion]=[dirección absoluta de la seccion]
//aca va el bloque de código
.
.
.
#pragma code //se devuelve la posición de meoria por defecto al compilador
Entonces aplicándolo a los PIC, apuntamos al vector de alta prioridad de interrupción:
void interrupcion_por_timer0(void); funcion declarada prototipo
#pragma code prioridad_alta = 0X0008 //se apunta al vector de interrupcion de alta prioridad
void interrupcion_alta (void) //bloque de codigo, en el caso de interrupciones ubicamos una funcion
{
_asm
goto interrupcion_por_timer0 //indicamos al compilador donde esta la interrupcion
_endasm
}
#pragma code //fin de la interrupcion, como un retfie en el assembler
Ahora, apuntamos al vector de baja prioridad de interrupción:
void interrupcion_por_portb(void); funcion declarada prototipo
#pragma code prioridad_baja = 0X0018 //se apunta al vector de interrupcion de alta prioridad
void interrupcion_baja (void) //bloque de codigo, en el caso de interrupciones ubicamos una funcion
{
_asm
goto interrupcion_por_portb //indicamos al compilador donde esta la interrupcion
_endasm
}
#pragma code //fin de la interrupcion, como un retfie en el assembler
Observemos que en ambos casos existe una función interrupcion_alta e interrupción_baja, las cuales deben de ser declaradas como prototipos y no deben tener parámetro de entrada ni de salida por ser asincronas (interrupciones), por ello va el void.
Lo que nos falta es la otra directiva que transfiere el flujo del programa a la ISR (a la interrupcion
) para efectuar el codigo correspondiente. Me estoy refiriendo a la función que ha sido declarada en prototipo. Su sintaxis es la siguiente:
#pragma interrupt [nombre_de_función]
void [nombre_de_función](void)
{
//codigo de la interrupcion
}
Ejemplo:
void interrupcion_por_timer0(void); funcion declarada prototipo
//************************************************************************************
//************************************************************************************
#pragma code prioridad_alta = 0X0008 //se apunta al vector de interrupcion de alta prioridad
void interrupcion_alta (void) //bloque de codigo, en el caso de interrupciones ubicamos una funcion
{
_asm
goto interrupcion_por_timer0 //indicamos al compilador donde esta la interrupcion
_endasm
}
#pragma code //fin de la interrupcion, como un retfie en el assembler
//************************************************************************************
//************************************************************************************
#pragma interrupt interrupcion_por_timer0
void interrupcion_por_timer0(void)
{
if(INTCONbits.TMR0IF)
{
PORTB=~PORTB;
tiempo++;
INTCONbits.TMR0IF = 0;
}
}
//************************************************************************************
//************************************************************************************
void interrupcion_por_portb(void); funcion declarada prototipo
//************************************************************************************
//************************************************************************************
#pragma code prioridad_alta = 0X0008 //se apunta al vector de interrupcion de alta prioridad
void interrupcion_alta (void) //bloque de codigo, en el caso de interrupciones ubicamos una funcion
{
_asm
goto interrupcion_por_portb //indicamos al compilador donde esta la interrupcion
_endasm
}
#pragma code //fin de la interrupcion, como un retfie en el assembler
//************************************************************************************
//************************************************************************************
#pragma interrupt interrupcion_por_portb
void interrupcion_por_portb(void)
{
if(INTCONbits.RBIF)
{
PORTB=~PORTB;
tiempo++;
INTCONbits.RBIF = 0;
}
}
//************************************************************************************
//************************************************************************************
Ahora estudiaremos cada interrupción del PIC; tendré desahabilitada la opción de las prioridades en las interrupciones para los siguientes programas, luego activaremos las prioridades cuando dominemos cada interrupción.