domingo, 5 de febrero de 2012

Interrupciones en SDCC - Parte 2, Uso de "signal.h"

En el anterior post de esta introducción a SDCC y el lenguaje C, se abordo el tema de las interrupciones, se hizo un comentario general de como se trabaja en la familia PIC18 y se explico su implementación recurriendo a las directivas del compilador. Ahora vamos a ver como lo podríamos lograr haciendo uso de la librería “signal.h”.
Al incluir dicha librería se nos provee de ciertas macros que nos simplifica la manipulación de las distintas fuentes de interrupciones. Las macros disponibles son:
  • DEF_INTHIGH(nombre) Se usa al inicio para la definición de las interrupciones definidas como de alta prioridad.
  • DEF_INTHIGH(nombre) De forma análoga que para el anterior, solo que para las de baja prioridad.
  • DEF_HANDLER(sig,handler) Define un handler para la señal sig. Esto nos permite tener un función especifica para cada interrupción sin tener que manualmente chequear los bits correspondientes para identificar la fuente.
  • END_DEF para terminar la definición de la tabla correspondiente.


Las señales que se encuentran definidas por defecto en “signal.h” son:

Nombre de la señal


Descripción


SIG_RB


Cambio en PORTB


SIG_INT0


Interrupción externa INT0


SIG_INT1


Interrupción externa INT1


SIG_INT2


Interrupción externa INT2


SIG__CCP


Interrupción del modulo CCP1


SIG_CCP2


Interrupción del modulo CCP2


SIG_TMR0


Overflow de timer TMR0


SIG_TMR1


Overflow de timer TMR1


SIG_TMR2


Para la interrupción de TMR2/PR2


SIG_TMR3


Overflow de timer TMR3


SIG_EE


EEMPROM/FLASH escritura completa


SIG_BCOL


Colisión en el bus


SIG__LVD


Detección de bajo voltaje


SIG_PSP


Interrupción de puerto paralelo esclavo


SIG_AD


Conversión AD completa


SIG_RC


Datos recibidos por USART


SIG_TX


Datos transmitidos por USART


SIG_MSSP


Datos recibidos/transmitidos por S


Tabla traducida del manual de SDCC ,pagina 76

Por ultimo tenemos dos macros mas, que son las que nos ayudan a definir la función que se encargara de controlar(handler) la señal:
  • SIGHANDLER(handler) declara la función para handler
  • SIGHANDLERNAKED(handler) declara una función naked para handler
Hay que recordar que cuando la función se declare como naked, se deberá hacer explicito el retorno desde la interrupción(retfie) y se deberán salvar los registros que se utilicen.

Declarando funciones naked de esta forma no tenemos el inconveniente de que se solapen las tablas de interrupciones ya que en el vector correspondiente se hacen las llamadas a las funciones las cuales se encuentran en el espacio de código del programa, no en el vector de interrupciones.Veamos un simple ejemplo de uso, en el que se usaran dos timers, ambos con la misma prioridad, y para comprobar el funcionamiento se harán parpadear dos leds.

El hardware es el siguiente:

Circuito led prende apaga interrupciones

 Y el código correspondiente:
 
//Definiciones para hacer mas legible el codigo

#define LED_TRIS1 TRISBbits.TRISB0
#define LED_PIN1 LATBbits.LATB0
#define LED_TRIS2 TRISBbits.TRISB1
#define LED_PIN2 LATBbits.LATB1

//Agrego la librería de los pic para cargar configuraciones y variables y se agrega la librería delay provista por el compilador.
#include "pic18fregs.h"
#include "signal.h"

static __code char __at __CONFIG1H conf1 = _OSC_INTOSC__INTOSC_CLK0_RA6___USB_EC_1H; // Activo oscilador interno
static __code char __at __CONFIG4L conf2 = _LVP_OFF_4L; // Desabilito LVP
static __code char __at __CONFIG2H conf3 = _WDT_DISABLED_CONTROLLED_2H; // Desabilito WDT

//Definiciones para le manejo de interrupciones
DEF_INTHIGH(high_int)
DEF_HANDLER(SIG_TMR0,tmr0_isr)
DEF_HANDLER(SIG_TMR1,tmr1_isr)
END_DEF

//Definicion de funcion para atender a al TRM0
SIGHANDLERNAKED (tmr0_isr){
INTCONbits.TMR0IF = 0;
LED_PIN2 ^= 1;
__asm
retfie
__endasm;
}
//Definicion de funcion para atender a al TRM1
SIGHANDLERNAKED (tmr1_isr){
PIR1bits.TMR1IF = 0;
LED_PIN1 ^= 1;
__asm
retfie
__endasm;
}


//Programa principal
void main(){
OSCCON = 0b11110011; //Configuro oscilador int en 8MHZ
TRISB &= 0xFC; //Configuro como salida
INTCONbits.GIE = 1; //Habilito interrupciones globales
INTCONbits.PEIE = 1; //Habilito interrupciones de perifericos(TMR1)

//Configuro TMR0
INTCONbits.TMR0IE = 1; //Habilito interrupcion de TMR0 por overflow
INTCONbits.TMR0IF = 0; //Limpio flag de interrupcion
T0CON = 0b10000100; //16bits,Ciclo de intruccion, Prescaler en 32
TMR0L = 0; //Pongo en 0 la cuenta
TMR0H = 0;

//Configuro TMR1
PIE1bits.TMR1IE = 1; //Habilito interrupcion de TMR1
PIR1bits.TMR1IF = 0; //Limpio flag de interrupcion
T1CON = 0b10110001; //16bits,Prescaler 8, Osc Timer1 des,Clock Fosc/4
TMR1L = 0; //Pongo en 0 la cuenta
TMR1H = 0;

while(1){
;
}
}

Es claro que estar trabajando de esta manera nos simplifica el código, haciéndolo también mas legible, si bien nos evita la verificación manual del bit de flag, estamos obligados a habilitar la interrupción y configurar su prioridad( si lo requiere), el usar las macros para identificar cada señal no nos evita este paso, es solo para saber a que vector corresponderá.
Comparte este articulo
  • Comparte con Facebook
  • Comparte con Twitter
  • Comparte con Google+
  • Comparte con Stumble Upon
  • Comparte con Evernote
  • Comparte con Blogger
  • Comparte con Email
  • Comparte con Yahoo Messenger
  • More...

0 comentarios:

Publicar un comentario