Una de las limitaciones que tiene cualquier tipo de procesador mono-core es que “solo” podemos seguir un hilo de ejecución, es decir, en cada ciclo de instrucción solo podemos ejecutar una instrucción, con lo que nos es imposible ejecutar 2 acciones a la vez. Ojo! tan solo hablo de los procesos en los que interviene el procesador, si por ejemplo tenemos un modulo CCP en nuestro micro, este se encarga el solo de generar la señal PWM sin que el procesador tenga nada que ver, al igual que ocurre con los canales DMA de los PIC24 y PIC32. Por razones de eficiencia y, al fin y al cabo, coste de la aplicación, en aplicaciones en las que necesitamos mucha velocidad y el tratamiento de grandes volúmenes de datos, como por ejemplo en el control embebido, no nos podemos permitir implementar un sistema multitarea como un PC industrial, por lo que debemos utilizar las interrupciones para, de alguna forma, “dividir” la atención del procesador entre todos los módulos que requieren su atención. Un dato curioso es que a pesar de que en los microcontroladores, las interrupciones son algo que está a la orden del día, no olvidemos que el lenguaje C está pensado para ordenadores, por lo que, nativamente, no lleva incorporado el concepto de interrupción.
Las interrupciones, como ya expliqué en otra entrada, son eventos internos o externos, asíncronos, que requieren la atención rápida de la CPU. la arquitectura PIC32 puede manejar hasta 64 fuentes de interrupción diferentes, y cada una de estas, si fuera necesario, le corresponden unas líneas de código que se conocen como Interrupt Service Routine (ISR) o Interrupt Handler. Las fuentes de interrupción se pueden consultar en el documento PIC32 Reference Manual. Los registros que utilizamos para configurar las interrupciones son en primer lugar el registro de Interrupt Enable, que nos servirá para habilitar la interrupción, y lo identificamos por el sufijo –IE, por ejemplo TMR0IE, otro de los registros de las interrupciones es el registro de flag, que se pondrá a 1 cuando salte la interrupción. En este caso lo identificamos por el sufijo –IF, por ejemplo TMR0IF. Estos dos registros lo encontramos de forma idéntica en los micros de 8 bits, ahora vienen las diferencias . El siguiente registro es el de la prioridad. En los micros de 8 bits, las interrupciones se pueden configurar como alta prioridad, o, baja prioridad. En el caso de PIC32 eso se extiende hasta 7 prioridades diferentes, siendo las de prioridad 7 las mas prioritarias. ESto lo hacemos mediante los registros con el sufijo –IP, por ejemplo TMR2IP, que son 3 bits dentro del registro IPCx. Pero no solo esto, sino que además nos ermite crear una sub-prioridad entre las 7 prioridades, es decir, si saltan 2 interrupciones con la misma prioridad, será esta la que decida quien va primero, en este caso los registros tienen el sufijo –IS, y los encontramos, como en el caso anterior, en los registros IPCx.
Una vez explicado el funcionamiento y los registros correspondientes a las interrupciones, queda saber como declararlas, es decir, definir el propio manejador de la interrupción, que en el caso de C32 nos deja hacerlo de varias maneras, aunque la más sencilla es utilizando la sintaxis #pragma interrupt. Quedaría de la siguiente manera:
#pragma interrupt InterruptHandler single
void InterruptHandler( void){// código manejador interrupciones aquí
}
Como veis, al final de la declaración pone single, esto es porque estamos utilizando una vectorización de las interrupciones simple (single vectored), que es de la que os voy a hablar en esta entrada. Para aumentar la eficiencia del procesador, en pic32 existe otro tipo de vectorización que es multiple (multi vectored), de forma que existe un manejador diferente para cada fuente de interrupción, lo que hace mucho más rápido su tratamiento. En el caso de single vectored, tan solo tenemos un manejador de interrupciones y somos nosotros los que por software decidimos la prioridad de estas.
Otro asunto interesante de las interrupciones en C32 es la libreria int.h, que podemos encontrar dentro de la libreria de periféricos plib.h. Esta librería nos facilita instrucciones en las que normalmente accedemos a un registro, por ejemplo, para borrar el flag de la interrupción del Timer 2, normalmente lo haríamos de la siguiente manera:
IFS0bits.T2IF = 0;
...
mT2ClearIntFlag();
...
Lo único que nos ahorramos es saber en que registro está. A continuación tenéis un código completo de un programa que realiza una interrupción cada segundo y otra cada medio segundo, un programa muy sencillo pero que sirve como ejemplo de utilización:
#include <p32xxxx.h>
#include <plib.h> //incluimos la libreria plib que //lleva metida la libreria int.h
// Bits de configuración #pragma config POSCMOD = XT, FNOSC = PRIPLL, FSOSCEN = ON
#pragma config FPLLIDIV = DIV_2, FPLLMUL = MUL_20, FPLLODIV = DIV_1, FPBDIV = DIV_8#pragma config OSCIOFNC = ON, CP = OFF, BWP = OFF, PWP = OFF
#define LED1_IO _LATG6
#define LED2_IO _LATD6#define LED1_TRIS _TRISG6
#define LED2_TRIS _TRISD6#define GetSystemClock() 80000000u
#define GetPeripheralClock() (GetSystemClock()/(OSCCONbits.PBDIV)) //80MHz / 8 = 10MHz
#define TMR_CONF 0x8070 //TON, 1:256, Internal, 16 bit
#pragma interrupt InterruptHandler single
void InterruptHandler( void){LED1_IO = 1;
if ( mT3GetIntFlag()){ //IFS0bits.T3IF == 1 //código de interrupción de T3mT3ClearIntFlag(); //IFS0bits.T3IF = 0;
}else if ( mT2GetIntFlag()){ //IFS0bits.T2IF == 1
//código de interrupción de T2
mT2ClearIntFlag(); //IFS0bits.T2IF = 0;}}
void main()
{ AD1PCFG = 0xFFFF; // Configuramos todos lo pines como digitales
LED1_TRIS = 0;LED2_TRIS = 0;
T2CON = TMR_CONF;mT2IntEnable( 1); // IEC0bits.T2IE = 1;
PR2 = 39063; //1 segundo
mT2SetIntPriority( 1); //IPC2bits.T2IP = 1; asignamos la prioridadT3CON = TMR_CONF;
mT3IntEnable( 1); // IEC0bits.T3IE = 1;
PR3 = 19531; //0,5 segundos mT3SetIntPriority( 3); //IPC3bits.T3IP = 3; asignamos la prioridad
INTEnableSystemSingleVectoredInt(); //le decimos que vamos a utilizar
//vectorización simple while(1);
}
Por último os quiero hacer una pregunta, ¿Que pasa si salta una interrupción de prioridad 1, y mientras esta está siendo tratada salta otra de prioridad 3?, lo normal sería pensar que, el micro deja de lado la interrupción de baja prioridad y se ponga con la de alta prioridad, pero esto no es así. En la próxima entrada os explicaré como resolver este problema y os hablaré de la vectorización múltiple.
Los ordenadores utilizan las interrupciones igual que los microcontroladores, es la formar por la que se accede a los periféricos. Se pueden incluso asignar mediante la BIOS, normalmente las gestiona el sistema operativo y con librerías de por medio no vemos que pasa, pero a nadie le impide que con un programa escrito en C acceda a las interrupciones, se trata de leer un registro. Lo que posiblemente no tenga C es una manejador de interrupciones que facilite la tarea, para eso ya tenemos el SO.
ResponderEliminarUna de las cosas que no me gusta de programar en los "C" que se inventa PIC es exactamente eso, que no utilicen el estándar y tengas que recurrir a sentencias que lee "su" precompilador, como el famoso #pragma...
Un saludo
Las interrupciones de los ordenadores son diferentes a las del micro, además que no se llaman interrupciones, sino excepciones. La principal diferencia es que los ordenadores llevan un controlador de excepciones hardware, y las excepciones que se manejan suelen ser errores en la CPU. Los PIC32, al estar basados en procesadores MIPS32 su sistema admite exceciones, y dentro de esas excepciones, están las interrupciones.
ResponderEliminarrespecto a lo otro yo pienso que el estándar C está obsoleto, de hecho pocas aplicaciones están programas en C, la mayoría utilizan C++, que ya admite muchas más funciones, pero como he dicho, C es para ordenadores, por tanto hay que adaptarlo a los micros. Lo que estaría bien es que todos los micros de 32 bits, por ejemplo, utilizaran el mismo C, y pudieras utilizar un micro de Texas o de ST y programarlo igual que uno de Microchip, aunque si eso no pasa en Texas y ST que utilizan la misma arquitectura, Microchip que utiliza arquitectura diferente ya me dirás tu....
Un saludo!
Interrupciones y excepciones son dos cosas distintas, como bien dices las excepciones son errores en la CPU, pero las interrupciones son eventos asíncronos externos a la CPU, como cuando pincho con el ratón o escribo con el teclado.
ResponderEliminarPuedes ver todas las que hay aquí: http://es.wikipedia.org/wiki/Interrupci%C3%B3n
Respecto a C, no creo que esté obsoleto y mucho menos en micros, ANSI C tiene todas las características necesarias para hacer funcionar cualquier tipo de micro. Obviamente si quieres utilizar un C estándar para programar micros no puedes irte al C que te dan las compañías que lo fabrican, pero hay otras opciones libres como SDCC para micros pequeños o GCC con sus respectivos ports para cada micro. El único problema son la falta de librerías para muchas cosas. Respecto a las arquitecturas de microchip, no voy ni ha comentar...cualquiera que haya visto algo de ensamblador el PIC ya sabrá. :D
Muy útil, gracias
ResponderEliminarme alegra que te resulte útil. Gracias por pasarte!
EliminarGracias! Me están ayudando mucho todas tus entradas! Un saludo
ResponderEliminar