Compartir:



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 blinking

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á.

Ariel Sperduti avatar

Full Stack web development, Ingeniero Electronico

Ir Arriba!