# Interrupciones en XC8



## nimio (Nov 4, 2012)

Hola, alguien podría decirme la forma correcta de hacer una interrupción con el compilador XC8 en un 16F84A??

Quiero pulsar un botón (sin usar RB0/INT) y que el programa me cambie de secuencia de leds al pulsar.

Voy averiguando algo pero doy palos de ciego y estoy más que mareado, os lo agradecería.

Si de paso me podéis explicar también como hacerlo con TIMER0 y RB0/INT sería genial, pero me urge el poderlo hacer desde cualquier puerto (si lo permite el 16F84A).

No entiendo como no se anima más gente a usar el compilador de Microchip (apenas hay información en castellano), yo no tengo ni idea de programación y voy avanzando poco a poco y para empezar con "sucedáneos" creo que es mejor usar lo "oficial".

Saludos


----------



## carferper (Nov 4, 2012)

hola, te dejo un ejemplo usando RA0 como entrada y todo el puerto B como salida.
utiliza el timer 0 para retrasos de tiempo y control de rebotes.  puse dos secuencias sencillas como ejemplo y las conexiones son las de el adjunto.


```
#include <xc.h>
#include <stdint.h>

#define retraso(x) TMR0 = 0; while(TMR0 < x);
#pragma config FOSC = XT, WDTE = OFF, PWRTE = OFF, CP = OFF

bit estado_boton = 0b0;
uint8_t caso_cnt = 0;
uint8_t cnt = 0;
uint8_t sOUT1 = 0, sOUT2 = 0, sOUT3 = 0;

int main(void){
    PORTB = 0x00;
    TRISA = 0b11111;
    TRISB = 0x00;
    OPTION_REG = 0b11011111;

    while (1){
        if (!PORTAbits.RA0){
            retraso(150);
            if (!PORTAbits.RA0)  estado_boton = 0b1;
        }
        if (PORTAbits.RA0){
            retraso(150);
            if (PORTAbits.RA0 && estado_boton){
                    caso_cnt++;
                    estado_boton = 0b0;
                 }
        }
        switch (caso_cnt){
            case 0 : PORTB = 0x00; break;
            case 1 :
                sOUT1 = 0b10000000;
                for (cnt = 0; cnt < 20; cnt++){
                    PORTB = (sOUT1 | sOUT2 | sOUT3);
                    sOUT3 = sOUT2;
                    sOUT2 = sOUT1;
                    sOUT1 = (cnt < 10) ? sOUT1 >> 1: sOUT1 << 1;
                    if (cnt == 10) sOUT1 = 0b00000001;
                    retraso(250);
                }
                break;
            case 2 :   
                PORTB = 0x00;
                retraso(250);
                PORTB = 0xFF;
                retraso(250);
                break;
            default: caso_cnt = 0;
        }
    }
}
```

Saludos


----------



## nimio (Nov 5, 2012)

Hola carferper, gracias por el interés.

Hubiera agradecido comentarios en el programa para comprenderlo ya que se me escapan algunas cosas. Aún así, no se si es lo que busco.

Yo simplemente quiero que el pic detecte la activación de un pulsador en RB5 y que cambie de una secuencia de luces a otra y al finalizar vuelva a la primera secuencia de luces hasta que no se vuelva a pulsar el botón.

Mi código hace la primera secuencia de leds en cuanto se programa el chip y luego pasa automáticamente a la segunda sin salir de ahí (aunque pulse el botón de reset), tanto pulse como si no el pulsador en RB5... no hace nada.

El código que llevo hecho es el siguiente:


```
#include <xc.h> //Librería del compilador de Microchip, en este caso el XC8.
//#include <stdint.h> // Librería interrupciones ¿?

#define _XTAL_FREQ 4000000 //Frecuencia del Cristal a 4MHz.

#pragma config FOSC=XT, WDTE=OFF, PWRTE=ON, CP=OFF // Definición Fuses del PIC:
                                                   // Osc. tipo XT, P.Guardián OFF,
                                                   // PWRTE ON para dar tiempo a que se estabilice la tensión,
                                                   // Protección de Código OFF
//#pragma interrupt_level 1
//
//
//

void interrupt pulsador(void) {  // Función llamada "pulsador" de interrupción.
                                 // Si no se especifica es high_priority.

    if (RBIF=1) {       // Si detecta cambio en RB4-RB7.

            __delay_ms(150);  // Retardo para antirrebote para el pulsador.

            if (RBIF=1) {

                __delay_ms(1000); // Retardo
                PORTB=0b00011111;
                __delay_ms(1000); // Retardo
                PORTB=0b00011011;
                __delay_ms(1000); // Retardo
                PORTB=0b00000100;
                __delay_ms(1000); // Retardo
                PORTB=0b00011000;
                __delay_ms(1000); // Retardo
                PORTB=0b00001100;
                __delay_ms(1000); // Retardo
                PORTB=0b00000110;
                __delay_ms(1000); // Retardo
                PORTB=0b00000011;
                __delay_ms(1000); // Retardo
                PORTB=0b00001010;
                __delay_ms(1000); // Retardo
                PORTB=0b00011111;
                __delay_ms(200); // Retardo
                PORTB=0b00000000;
                __delay_ms(200); // Retardo
                PORTB=0b00011111;
                __delay_ms(200); // Retardo

        INTCONbits.RBIF=0; // Se inicializa a 0 el Flag de interrupción en PUERTO B.
        
            }
        
        }
        return; // necesario ¿?
        
}



void main(void) {

    
    TRISB=0b00100000;   // Definición PUERTO B: Todos salidas menos RB5 que es entrada.
    PORTB=0;            // Salidas PUERTO B a 0 voltios.
    
    

    //HABILITAR INTERRUPCIONES:

    INTCONbits.GIE=1;   // Habilita TODAS las interrupciones. Para permitir interrupciones hay q habilitarlas
                        // tanto Globalmente como... Individualmente ¿?.

    //CONFIGURACIÓN INTERRUPCIÓN EXTERNA:

    
    //OPTION_REGbits.INTEDG=0; // Se activa la interrupción por flanco: 0=descendente, 1=ascendente.
                             // Para RB0/INT.

    INTCONbits.RBIE=1; // Se habilita detección de interrupción en PUERTO B.
                       // No se si con configurar un pin del Puerto B como entrada ya se autoconfigura.
    

    //ei ();              // Habilita las interrupciones Globales.

    //di();             // Deshabilita todas las interrupciones.
    //INTCON=0b10001001;    // El Bit 3 me parece que es para habilitar cualquier interrupción en RB
    
    while (1) {

        __delay_ms(800); // Retardo de 600 milisegundos.
        PORTB=0b00000001; // RB0 a 5 voltios y el resto a 0 voltios.
        __delay_ms(800); // Retardo de 600 milisegundos.
        PORTB=0b00000010; // RB1 a 5 voltios y el resto a 0 voltios.
        __delay_ms(800); // Retardo de 600 milisegundos.
        PORTB=0b00000100; // RB2 a 5 voltios y el resto a 0 voltios.
        __delay_ms(800); // Retardo a 600 milisegundos.
        PORTB=0b00001000; // RB3 a 5 voltios y el resto a 0 voltios.
        __delay_ms(800); // Retardo de 600 milisegundos.
        PORTB=0b00010000; // RB4 a 5 voltios y el resto a 0 voltios.
        __delay_ms(800); // Retardo de 600 milisegundos.
        PORTB=0b00011111; // RB0-RB4 a 5 voltios y el resto a 0 voltios.
        __delay_ms(800); // Retardo de 600 milisegundos.
        PORTB=0b00010001; // RB0 Y RB4 a 5 voltios y el resto a 0 voltios.
        __delay_ms(800); // Retardo de 600 milisegundos.
        PORTB=0b00001010; // RB1 y RB3 a 5 voltios y el resto a 0 voltios.
        __delay_ms(800); // Retardo a 600 milisegundos.
        PORTB=0b00000100; // RB2 a 5 voltios y el resto a 0 voltios.
        __delay_ms(800); // Retardo de 600 milisegundos.
        PORTB=0b00010101; //RB0, RB2 y RB4  5 voltios y el resto a 0 voltios.
        __delay_ms(800); // Retardo de 600 milisegundos.

        
    }


}
```

Comentar que no se si tengo que llamar a la función de interrupción (que he llamado pulsador) o si al ser una función de interrupción se invoca sola.

A ver si veis que tengo mal o que me falta.

Saludos!!


----------



## carferper (Nov 5, 2012)

hola, te aclaro algo del codigo que puse antes:

la libreria <stdint.h> hace que se pueda declarar las variables uint8_t que son parte del estandar de C99 y que se recomienda usar por motivos de portabilidad. En este caso significa "unsigned integer 8 bits".

#define retraso(x) TMR0 = 0; while(TMR0 < x);

es un macro que hace mas facil escribir el programa, en realidad hace que se escriban menos lineas de codigo. y trabaja similarmente al __delay_ms(). La diferencia es que no utiliza espacio en la memoria sino mas bien usa el Timer 0 para control de tiempos.

El Timer 0 se configura con: OPTION_REG = 0b11011111;

El programa empieza con todos los LEDs apagados. Al pulsar el boton conectado al pin RA0 se ejecuta una secuencia de encendido/apagado. Si pulsas nuevamente, se ejecuta otra secuencia. Y asi sucesivamente.  Estas secuencias puedes cambiarlas segun tus requerimientos.

Este segmento:

```
if (!PORTAbits.RA0){ 
            retraso(150); 
            if (!PORTAbits.RA0)  estado_boton = 0b1; 
        } 
        if (PORTAbits.RA0){ 
            retraso(150); 
            if (PORTAbits.RA0 && estado_boton){ 
                    caso_cnt++; 
                    estado_boton = 0b0; 
                 } 
        }
```
elimina rebotes y determina que secuencia se ejecuta. Esto se hace simplemente incrementando el contador "caso_cnt".

Finalmente, la estructura switch es como sigue:

```
switch (caso_cnt){ 
            case 0 : 			/* no pulsaciones */
		/*secuencia 0 */
		 break; 
            case 1 : 			/* 1 pulsacion */
		/*secuencia 1 */ 
		break; 
            case 2 :   			/* otra pulsacion */ 
                /*secuencia 2 */ 
		break; 
            default: caso_cnt = 0;      /* repite todo */ 
        }
```
Como ves este programa no utiliza interrupciones. Si los tiempos de espera entre encendido y apagado de LEDs en una secuencia es muy grande, va a presentar problemas y necesariamente te conviene usar interrupciones.

Lo mas facil es usar RB0 como entrada y generar una interrupcion por flanco de subida. Por ejemplo y en forma muy simplificada se puede escribir:


```
#include <xc.h>
#include <stdint.h>

#define _XTAL_FREQ 4000000
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))

#pragma config FOSC = XT, WDTE = OFF, PWRTE = OFF, CP = OFF

uint8_t caso_cnt = 0;

void main(void) {
    TRISB = 0b00000001;
    PORTB = 0;

    OPTION_REGbits.nRBPU = 0b0;   // activa "weak pull-ups"
    OPTION_REGbits.INTEDG = 0b1;  // flanco de subida en RB0
    
    INTCONbits.INTE = 0b1;  // habilita interrupciones en RB0
    ei();	// habilita interrupciones 

    while (1) {
       switch (caso_cnt){
            case 0 : 
                PORTB = 0x00;  // primera secuencia 
                break;
            case 1 :
                PORTB = ~PORTB;  // segunda secuencia
                __delay_ms(1000);
                break;
            case 2:
                PORTB = 0xFF;  // tercera secuencia
                break;
           case 3:
               caso_cnt = 0;  // repite secuencias desde case 0
       }
    }
}

void interrupt isr(void){  // rutina de servicio de interrupciones
    INTCONbits.INTF = 0b0;  // habilita interrupciones RB0 nuevamente
    caso_cnt++;             // determina que secuencia se ejecuta
    __delay_ms(10);         // retraso para esperar rebotes
}
```
Para interrupciones usando cambios en RB4 a RB7, debes hacer el control de rebotes mediante hardware y el pulso debe ser al menos uno o dos ciclos. Es mas complejo por eso rara vez se usa con botones pulsadores.

Saludos


----------



## nimio (Nov 6, 2012)

carferper, gracias nuevamente por la explicación y por los criterios de programación que me comentas. 

He podido entenderlo algo mejor, ten en cuenta que justo estoy empezando. Aún así tu programa es mucho más de lo que necesito.

Finalmente me di cuenta que tenía un error tonto de MPLAB y no compilaba lo que tenía que compilar jeje.

Finalmente he conseguido crear interrupciones externas por RB0/INT y por puerto B (RB4-RB7), concretamente por RB5.

Adjunto el programa con interrupción externa en RB5 por si pudiera servir de utilidad/comprensión:



```
/* 
 * File:   main.c
 * Author: nimio
 *
 * Programa para encender 2 secuencias de 5 Leds en el Puerto B (RB0-RB4)
 * Con pulsador haciendo uso de las interrupciones en RB5.
 *
 * Created on 3 de noviembre de 2012, 21:20
 */

#include <xc.h> //Librería del compilador de Microchip, en este caso el XC8.

#define _XTAL_FREQ 4000000 //Frecuencia del Cristal a 4MHz.

#pragma config FOSC=XT, WDTE=OFF, PWRTE=ON, CP=OFF // Definición Fuses del PIC:
                                                   // Osc. tipo XT, P.Guardián OFF,
                                                   // PWRTE ON para dar tiempo a que se estabilice la tensión,
                                                   // Protección de Código OFF
//
//
//
//

void interrupt pulsador(void) {  // Función llamada "pulsador" de interrupción.
                                 // Si no se especifica es high_priority.
    //INTCONbits.GIE=0; // Se deshabilitan las interrupciones Globales para que no se
                        // produzca otra interrupción mientras se atiende esta.

    if (RBIF) {       // Si detecta cambio en RB4-RB7.

            __delay_ms(150);  // Retardo para antirrebote para el pulsador.

            if (RBIF) {

                __delay_ms(2000); // Retardo
                PORTB=0b00011111;
                __delay_ms(2000); // Retardo
                PORTB=0b00000000;
                __delay_ms(100); // Retardo
                PORTB=0b00000001;
                __delay_ms(100); // Retardo
                PORTB=0b00000010;
                __delay_ms(100); // Retardo
                PORTB=0b00000100;
                __delay_ms(100); // Retardo
                PORTB=0b00001000;
                __delay_ms(100); // Retardo
                PORTB=0b00010000;
                __delay_ms(100); // Retardo
                PORTB=0b00000000;
                __delay_ms(100); // Retardo
                PORTB=0b00011111;
                __delay_ms(200); // Retardo
                PORTB=0b00000000;
                __delay_ms(200); // Retardo
                PORTB=0b00011111;
                __delay_ms(200); // Retardo

                INTCONbits.RBIF=0; // Se inicializa a 0 el Flag de interrupción en PUERTO B.
        
            }
        
        }      
}



void main(void) {

    
    TRISB=0b00100000;   // Definición PUERTO B: Todos salidas menos RB5 que es entrada.
    PORTB=0;            // Salidas PUERTO B a 0 voltios.

    INTCONbits.RBIE=1; // Se habilita detección de interrupción en PUERTO B.
                       // No se si con configurar un pin del Puerto B como entrada ya se autoconfigura.
    INTCONbits.RBIF=0; // Se pone a 0 el Flag indicador de interrupción en RB4-RB7.
                       // Es conveniente ponerlo a 0 ya que RB4-RB7 sólo detecta cambio de estado
                       // y puede dar problemas si no se pone.

    INTCONbits.GIE=1;   // Habilita TODAS las interrupciones. Para permitir interrupciones hay q habilitarlas
                        // tanto Globalmente como... Individualmente ¿?.
    
    while (1) {

        __delay_ms(800); // Retardo
        PORTB=0b00000000; 
        __delay_ms(800); // Retardo
        PORTB=0b00011111; 
        __delay_ms(800); // Retardo
        PORTB=0b00000000;
        __delay_ms(800); // Retardo
        PORTB=0b00011111;
        __delay_ms(800); // Retardo 
        PORTB=0b00000000; 
        __delay_ms(800); // Retardo
        PORTB=0b00011111; 
        __delay_ms(800); // Retardo
        PORTB=0b00000000;
        __delay_ms(800); // Retardo
        PORTB=0b00011111;
        __delay_ms(800); // Retardo 
        PORTB=0b00000000; 
        __delay_ms(800); // Retardo
        PORTB=0b00011111;
        __delay_ms(800); // Retardo

        
    }


}
```

Ahora supongo que probaré suerte con el TIMER0

Muy agradecido por todo.

Saludos


----------



## ilcapo (Dic 24, 2013)

Hola en todos los ejemplos que encuentro por san google solo veo que usen una sola interrupcion para el MPLAB XC8 y se usa el formato : 

void interrupt ISR(void) {
                                       ......
                                 }

en el caso de tener que hacer 2 interrupciones como se llama a cada una ?  se le puede agregar un nombre ? por ejemplo si quisiera hacer 2 interrupciones una por TMR0 y otra Externa se puede poner asi ? 

void TMR0_interrupt ISR(void) {
                                                .....
                                          }


void EXT_interrupt ISR(void) {
                                               .......
                                         }

 a esto te lo reconoce el compilador ? o no se puede darle otro nombre y tenemos que luego de realizada la interrupcion determinar cual fue ? 

gracias


----------



## ByAxel (Dic 24, 2013)

Hola.
void *interrupt*... es un calificador (palabra privada) por lo que no puedes poner otros textos por delante.
Es simple.
Para los PIC que tienen un vector de interrupción como los 16F, solo es necesario una función de interrupción e internamente se determina verificando los flags...

Ejemplo de la documentación del XC8.

```
void interrupt myIsr(void)
{
// only process timer-triggered interrupts
if([B]INTCONbits.TMR0IE[/B] && [B]INTCONbits.TMR0IF[/B]) {
portValue++;
INTCONbits.TMR0IF = 0; // clear this interrupt condition
}
}
```

Para PICs de dos a más vectores de interrupción, se determina la dirección de interrupción (generalmente por defeto pero se puede modificar) y las prioridades de interrupción (por cada interrupión que se quiera usar).

Ejemplo 18F, interrupción de baja prioridad, vector 0x18 por defecto 'creo'.

```
void [B]interrupt low_priority[/B] tc_clr(void) {
if ([B]TMR1IE && TMR1IF[/B]) {
TMR1IF=0;
tick_count = 0;
return;
}
// process any other low priority sources here
}
```

Saludos


----------



## ilcapo (Dic 24, 2013)

gracias by Axel tenia la duda porque en el comppilador CCS le ponia diferentes nombres a las interrupciones y entraba a la adecuada, aca parece que hay que chequear los flags   , me parece un punto a favor para seguir usando el CCS, aunque me han dicho que el MPLAB XC8 es mejor


----------



## ByAxel (Dic 24, 2013)

Al final es lo mismo, los vectores de interrupción son los mismos solo que CCS tiene esa verificación internamente, de ese modo cuando corresponde una función de interrupción declarado por el usuario, internamente compara y salta al correspondiente.
XC8 es totalmente bajo el estandard ANSI C, así no hay nada que hacer... y en cierto modo se tiene mayor control del PIC... claro eso equivale a escribir un poco más  .


----------



## ilcapo (Dic 24, 2013)

hola expertos ! 

estuve buscando por el foro y por sanGoogle algun ejemplo para programar el ADC con interrupciones en MPLAB XC8 y lo unico que encontre fueron ejemplos pero sin interrupciones 
si alguien tiene algun ejemplo que me pueda salvar el pellejo? 

sobre todo para el pic 16f887 que es con el que estoy aprendiendo a usar el XC8, gracias!


----------



## Galix (Oct 28, 2014)

Buenas amigos,

vengo con un problema en un ejemplo sencillo de lo que sería el uso de las interrupciones utilizando el compilador XC8 en el entorno MPLABX de Microchip. Estoy utilizando el PIC16F876A también de Microchip.

Pues bien, lo que pretendo es cambiar el estado de un LED (D1) cada vez que se produzca una interrupción externa (RB0/INT). Mientras tanto el en el programa principal un LED (D2) se prende de manera intermitente. De esta forma cuando se produzca una interrupción D1 pasará a estado bajo si se encontraba en estado alto y a alto si se encontraba en estado bajo. Gracias al uso de la interrupción en ningún momento el diodo D2 dejará de parpadear puesto que la rutina de la interrupción se realiza de manera independiente a la rutina del programa principal.

Aquí os dejo el código del programa, y a continuación comentaré los resultados que obtengo:


```
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include "FUSES.h"
#include <stdint.h>

#define _XTAL_FREQ 4000000



int main (void)
{
   
    TRISB = 0b00000001;
    PORTB =0;
    TRISA = 0;
    


    INTCON = 0;                         // Limpiamos el registro INTCON
    INTCONbits.INTE = 1;            // Habilitamos la Int. externa hablitando el bit INTE
    INTCONbits.GIE = 1;             // Hablitamos la Int. global habilitando el bit GIE
    OPTION_REGbits.INTEDG = 0;     // Configuramos el flanco de alto a bajo para la interrupción externa


    while(1)
    {
        PORTAbits.RA1=0;
        __delay_ms(500);
        PORTAbits.RA1=1;
        __delay_ms(500);
    }

    return 0;
}

void interrupt isr (void)
{
  
    if(INTCONbits.INTF)
    {
        if(PORTAbits.RA0)
        {
            PORTAbits.RA0=0;
        }
        else PORTAbits.RA0 = 1;
    }

    INTCONbits.INTF=0;
    __delay_ms(10);

}
```

Pues bien, simulando este código en el circuito de la imagen adjunta con PROTEUS, observo como efectivamente D2 parpadea intermitentemente pero al pulsar el botón y forzar así la interrupción por RB0 el LED D1 sólo se prende si D2 está apagado y no mantiene el estado, es decir, una vez se enciende D2, D1 se apaga. 

Me gustaría que le echaran un vistazo al código y me dijeran si ven algún fallo que a mi se me escapa.

Otra duda que tiene que ver con el compilador XC8:
He observado en foros y videos de youtube como gente utiliza comandos en XC8 para prender un led de manera intermitente y les funciona perfectamente, como por ejemplo:


```
PORTAbits.RA0 =! PORTAbits.RA0;
PORTAbits.RA0 = ~PORTAbits.RA0;
```

Pues bien, si you utilizo estos comandos el programa compila pero no realiza lo que quiero, es decir, no prende el led de manera intermitente y tengo que usar la manera tradicional.

Algo similar me ocurre con los registros:


```
ANSEL = 0;
ANSELH =0;
```
El programa me da error al compilar.

No se si todos estos errores se deben a nuevas actualizaciones o son error mío por algo que se me escapa. Me gustaría que me ayudaran tanto en el problema principal que os he expuesto y en las dudas que tengo acerca de dichos comandos. Gracias y un saludo a todos.


----------



## Ardogan (Oct 28, 2014)

Galix dijo:
			
		

> ... El programa me da error al compilar.



¿Que error? (copia y pega la salida de la ventana de compilación).

PORTAbits.RA0 =! PORTAbits.RA0;

Me parece medio raro, ! es el operador lógico not,  "!=" o "!", sí se usa en if para comparar por distinto:

if(variable!= 1)
  //hacer algo si variable no es igual a 1
else
  //hacer otra cosa si variable = 1 

Lo anterior es operativamente igual a

if(!(variable ==1))

PORTAbits.RA0 = ~PORTAbits.RA0;  

~ es el operador complemento. Invierte unos por ceros y viceversa.
Dada una variable a = 0b01010011 será ~a = 0b10101100

El operador ~ puede andar para variables enteras, pero desconozco si se puede aplicar a campos de bits (creo que eso esta fuera del C estandar). Podes probar haciendo una xor:
PORTAbits.RA0 ^= PORTAbits.RA0; 

Por ese tipo de dilemas hace tiempo uso máscara de bits en vez de campos de bits...

Otra cosa: no se como manejará las cosas elmplabx ide, pero ¿no debería haber arriba de todo un

#include <PIC16F876A.h>

o algo así?. ¿O al configurar el proyecto ya se le indica el micro utilizado?


----------



## Galix (Oct 28, 2014)

He metido estos comando en el programa que tengo problemas para que veas la salida de error que me da pero estos comandos creo que no son necesarios en mi programa. De todas formas en el problema principal que tengo el programa compila perfectamente solo que no funciona correctamente.


----------



## Miembro eliminado 356005 (Oct 28, 2014)

El microcontrolador PIC16F876A no tiene el registro ANSEL ni ANSELH.

Te recomiendo que quites

__delay_ms(10)

de la interrupción, mientras haces pruebas. Sé que está puesto para evitar rebotes, pero para hacer pruebas con el Proteus, te vale.


----------



## Galix (Oct 28, 2014)

Muchas gracias por tu respuesta Ardogan y JoaquinFerrero, ahora entiendo por qué no me funcionaban esos comandos. Tengo una pregunta Ardogan: A qué te refieres con máscaras de bits en lugar de campos de bits?
y en referencia a tu duda acerca del #include<16...>, el PIC ya se elige al crear el proyecto y no hace falta incluirlo.

JoaquinFerrero, he quitado el delay pero no se ha solucionado el problema. No sé exactamente el por qué la interrupción no funciona correctamente. Cuando fuerzo la interrupción el LED no cambia su estado permanentemente, sólo de forma momentánea y nunca coinciden los dos encendidos a la vez.


----------



## Miembro eliminado 356005 (Oct 28, 2014)

Según he leído en los foros de Microchip, hay que indicar que el PORTA sea digital, no analógico. Eso podría influir en que una patilla afecta a la otra (no sé por qué, pero eso es lo que dice).

PORTAbits.RA1 = !PORTAbits.RA1;

Lo que hace esa línea es aplicar el operador !, que es la negación lógica del argumento, así que lo que hace es, efectivamente, invertir el bit de RA1, y asignar el resultado al propio RA1.


----------



## Galix (Oct 28, 2014)

JoaquinFerrero, efectivamente el problema venía de no declarar que el puerto A era digital. He cambiado de puerto al C y funciona perfectamente, tanto el programa como el comando:

PORTCbits.RC0 = !PORTCbits.RC0;

Sabed que si quereis usar dicho comando en el puerto A debéis declarar que el puerto es digital, si no, tendréis problemas. Muchas gracias por vuestra ayuda. Un saludo a todos.


----------



## Ardogan (Oct 29, 2014)

Ups... respondí mal lo anterior, con
PORTAbits.RA0 ^= PORTAbits.RA0; 
no sirve para conmutar la salida (más bien siempre va a fijarla en cero).

Y ese error lo cometí por pensar en máscara de bits.
Máscara  de bits es una forma alternativa de trabajar con bits individuales sin  definir estructuras. Antiguamente un compilador al encontrarse con una  línea como la de arriba generaba una cantidad de código innecesariamente  grande (en assembler hace un shift, luego máscara, luego escribe...)
¿Entonces, como sería conmutar la salida con máscara de bits?. En algún lugar se define
#define BIT0 1 //o 0x01, o 0b00000001
#define BIT1 2 //o 0x02, o 0b00000010
#define BIT2 4 //o 0x04, o 0b00000100
...
ahora "está de moda" poner
#define BIT0 (1U<<0)
#define BIT1 (1U<<1)
#define BIT2 (1U<<2)
...

Luego en el programa se puede escribir
#define RA0 BIT0
#define RA1 BIT1
#define RA2 BIT2

PORTA |= RA0; //salida RA0 = 1, los otros bits no se alteran
PORTA &= ~RA0; //salida RA0 = 0
PORTA ^= RA0; // RA0 = ~RA0 conmutar

¿Por que algunos preferimos máscara de bits y no campos de bits? (bitmask vs bitfield).


Porque las operaciones sobre campo de bits no están bien definidas en C estandar. ¿Cual estandar? no recuerdo, pero cuando lo busqué decía que era "implementation defined" eso significa que cada compilar lo interpreta a su manera, con lo que el código escrito corre riesgo de no ser portable
Con máscara de bits puede operar sobre múltiples pines a la vez. Si quiero conmutar tres salidas al mismo tiempo hago:
PORTA ^= (RA0 + RA1 + RA2);
en vez de 
PORTAbits.RA0 = ~PORTAbits.RA0;
PORTAbits.RA1 = ~PORTAbits.RA1;
PORTAbits.RA2 = ~PORTAbits.RA2;
Código más eficiente: depende... se supone que si el compilador te lo da el fabricante entonces tiene la inteligencia para optimizar las 3 líneas de arriba como 1 sola... pero eso depende del fabricante
Puede que no pase al principio por costumbre, pero al usar máscara de bits el código me parece más amigable y breve => subjetivo, cada cual dirá


----------



## Galix (Oct 29, 2014)

Muchas gracias Ardogan, no tenía ni idea de todo este tema de la máscara de bits.
 Voy a investigar sobre el tema y lo empezaré a usar a partir de ahora en mis proyectos. Gracias!


----------



## LucheXx (Dic 25, 2019)

Saludos. Hice algo similar solo que no me funciona ninguna de las interrupciones.
Mi intención es que con "salida" se inicie el led verde y el motor1, con "entrada" la luz roja 1 y el motor 2, y con "paro" crear un reinicio de todo el sistema que está en RB0 mientras prende la luz roja 2 antes de apagarse.
Mi código es este:

```
#pragma config FOSC = EXTRC     // Oscillator Selection bits (RC oscillator)

#pragma config WDTE = OFF       // Watchdog Timer (WDT disabled)

#pragma config PWRTE = OFF      // Power-up Timer Enable bit (Power-up Timer is disabled)

#pragma config CP = OFF         // Code Protection bit (Code protection disabled)

#include <xc.h>

#include <stdio.h>

#include <stdlib.h>

#include <stdint.h>

#include <pic16f84a.h>

#define _XTAL_FREQ 4000000

#define INT_TRCC

#define cont(x) TMR0 = 0; while(TMR0 < x);

bit salida = 0b0;

bit entrada = 0b0;

bit paro = 0b0;

uint8_t cont=0;

uint8_t cas_cont=0;

uint8_t sOUT1=0, sOUT2=0, sOUT3=0, sOut3=0, sOUT4=0, sOUT5=0;


void main ()

{

PORTA = 0b00011;

TRISA = 1;

PORTB = 0b00000001;

TRISB = 0;

OPTION_REG=0b11100111;

OPTION_REGbits.T0CS=0;

OPTION_REGbits.INTEDG =1;

ei();

INTCONbits.GIE=1;

INTCONbits.TMR0IE=1;

INTCONbits.TMR0IF=0;

INTCONbits.INTE =1;

while (1)

  {

      if (PORTAbits.RA0 == 1)

      {

        cont(229);

        salida = 0b1;

      }

      if (PORTAbits.RA0 && salida)

      {

                    cas_cont++;

                    salida = 0b0;

      }

      if(PORTAbits.RA1 == 1)

      {

        cont(229);

        entrada = 0b1;

      }

      if (PORTAbits.RA1 && entrada)

      {

                    cas_cont++;

                    entrada = 0b0;

      }

     if(PORTBbits.RB0 == 1)

      {

       PORTBbits.RB5 = 1;

       cont(229);

       paro = 0b1;

      }

      if (PORTBbits.RB0 && paro)

      {

                   cas_cont++;

                   paro = 0b0;

      }

      switch (cas_cont)

      {

          case 0: PORTB = 0;

          break;

          case 1 :pORTBbits.RB2 = 1;

                  cont(240);

                  PORTBbits.RB3 = 1;

                  cont(240);

                  break;

          case 2 :pORTBbits.RB1 = 1;

                  cont=(240);

                  PORTBbits.RB4 = 1;

                  cont=(240);

                  break;

          default: cas_cont = 0;       

                

      }   

  }

}

void interrupt isr(void)

{

    INTCONbits.INTF = 0b0;

    cas_cont++;           

    __delay_ms(10);   

}
```


----------



## D@rkbytes (Dic 26, 2019)

Veo varios errores.
El primero en la selección del oscilador. #pragma config FOSC = EXTRC es para un oscilador externo RC
Si usas un cristal de 4 MHz. el fuse debería ser XT
El segundo es que estás configurando todo el puerto B como salidas con TRISB = 0;
Si usas la interrupción por RB0, entonces debería ser entrada. TRISB = 0b00000001; o TRISB = 1;
Habría que definir el flanco de interrupción si es que es necesario. (Ver hoja de datos)
El tercero está en el esquema. Los pines de entrada están flotando, o sea, no tienen una referencia lógica.

Lo demás, como esto, es redundante:

```
OPTION_REG=0b11100111;
    OPTION_REGbits.T0CS=0;
    OPTION_REGbits.INTEDG =1;
```
No usas las librerías stdio.h ni stdlib.h pero las tienes incluidas.
Y el servicio de interrupciones no tiene una estructura adecuada.

PD:
Esto debe generar un error de compilación:
pORTBbits.RB2


----------



## LucheXx (Dic 27, 2019)

Gracias por responder. Tenía esas librerías porque estuve tratando de otras maneras, lo volví a cambiar:

```
#pragma config FOSC = XT        // Oscillator Selection bits (XT oscillator)

#pragma config WDTE = OFF       // Watchdog Timer (WDT disabled)

#pragma config PWRTE = OFF      // Power-up Timer Enable bit (Power-up Timer is disabled)

#pragma config CP = OFF         // Code Protection bit (Code protection disabled)

#include <xc.h>

#include <pic16f84a.h>

#define _XTAL_FREQ 4000000

#define INT_TRCC

#define cont(x) TMR0 = 0; while(TMR0 < x);

bit salida = 0b0;

bit entrada = 0b0;

bit paro = 0b0;

void main ()

{

PORTA = 0b00011;

TRISA = 1;

PORTB = 0b00000001;

TRISB = 0b00000001;

ei();

OPTION_REG=0b11100111;

INTCONbits.GIE=1;

INTCONbits.TMR0IE=1;

INTCONbits.TMR0IF=0;

INTCONbits.INTE =1;

while (1)

  {

      if (PORTAbits.RA0 == 1)

      {

            PORTBbits.RB2 = 1;

            cont(150);

            PORTBbits.RB3 = 1;

            cont(150);

          

       }

      else

      {

       PORTBbits.RB2 = 0;

       PORTBbits.RB3 = 0;

      }

      if(PORTBbits.RB0 == 1)

      {

          PORTBbits.RB2 = 0;

        

          PORTBbits.RB3 = 0;

        

      }

      else

      {

       PORTBbits.RB5 = 1;

       cont(150);

      }

  }

}

void interrupt   tc_int  (void)       


{

        if(INTCONbits.T0IF && INTCONbits.T0IE)

    {                                   

                TMR0 -= 250;             

                INTCONbits.T0IF = 0;               

    }


}
```


----------



## D@rkbytes (Dic 27, 2019)

LucheXx dijo:


> lo volví a cambiar


Supongo que funcionó.


----------



## LucheXx (Dic 27, 2019)

No, no entiendo por qué no hace nada ahora.

```
#pragma config FOSC = XT        // Oscillator Selection bits (XT oscillator)

#pragma config WDTE = OFF       // Watchdog Timer (WDT disabled)

#pragma config PWRTE = OFF      // Power-up Timer Enable bit (Power-up Timer is disabled)

#pragma config CP = OFF         // Code Protection bit (Code protection disabled)

#include <xc.h>

#include <pic16f84a.h>

#define _XTAL_FREQ 4000000

#define INT_TRCC

#define cont(x) TMR0 = 0; while(TMR0 < x);


int main ()

{

TRISA = 1;

TRISB = 0b00000001;

ei();

OPTION_REG=0b11100111;

INTCONbits.GIE=1;

INTCONbits.TMR0IE=1;

INTCONbits.TMR0IF=0;

INTCONbits.INTE =1;

while (1)

  {

      if (RA0 == 1)

      {

            RB2 = 1;

            RB3 = 1;

            cont(150);

          

       }

    

      if(RB0 == 1)

      {

          RB2 = 0;

          RB3 = 0;

          cont(150);

      }

      else

      {

       RB5 = 0;

       RB1 = 0;

       RB2 = 0;

       RB3 = 0;

       RB4 = 0;

       cont(150);

      }

  }

return 0;

}

void interrupt   tc_int  (void)       


{

        if(INTCONbits.T0IF && INTCONbits.T0IE)

    {                                   

                TMR0 = 255;             

                INTCONbits.T0IF = 0;               

    }


}
```



D@rkbytes dijo:


> Supongo que funcionó.


Pues se prende aunque no pulse el pulsador:

```
#pragma config FOSC = XT        // Oscillator Selection bits (XT oscillator)

#pragma config WDTE = OFF       // Watchdog Timer (WDT disabled)

#pragma config PWRTE = OFF      // Power-up Timer Enable bit (Power-up Timer is disabled)

#pragma config CP = OFF         // Code Protection bit (Code protection disabled)

#include <xc.h>

#include <pic16f84a.h>

#define _XTAL_FREQ 4000000

#define INT_TRCC

#define cont(x) TMR0 = 0; while(TMR0 < x);


int main ()

{

TRISA = 1;

TRISB = 0b00000001;

ei();

OPTION_REG=0b11100111;

INTCONbits.GIE=1;

INTCONbits.TMR0IE=1;

INTCONbits.TMR0IF=0;

INTCONbits.INTE =1;

while (1)

  {

      if (RA0 == 1)

      {   

            __delay_ms(100);

            RB2 = 1;

            RB3 = 1;

            cont(150);

          

       }

    

      if(RB0 == 1)

      {

          __delay_ms(100);

          RB2 = 0;

          RB3 = 0;

          cont(150);

      }

      else

      {

       RB5 = 0;

       RB1 = 0;

       RB2 = 0;

       RB3 = 0;

       RB4 = 0;

       cont(150);

      }

  }

return 0;

}

void interrupt   tc_int  (void)       


{

        if(INTCONbits.T0IF && INTCONbits.T0IE)

    {                                   

                TMR0 = 255;             

                INTCONbits.T0IF = 0;               

    }


}
```


----------



## D@rkbytes (Dic 28, 2019)

LucheXx dijo:


> No, no entiendo por qué no hace nada ahora.


¿Y qué se supone que debe hacer el programa?
Es que con la explicación que diste en tu post #20 no sirve de mucho para comprender el funcionamiento del circuito.


LucheXx dijo:


> Pues se prende aunque no pulse el pulsador


¿Qué cosa se prende?

Te recomiendo que comentes tu programa para que se pueda entender lo que pretendes.
Si no lo haces, con el tiempo ni tú mismo vas a saber lo que estabas tratando de hacer.
Y si explicas mejor qué cosa quieres hacer, también serviría bastante para que se te pueda brindar un mejor asesoramiento.
Algo que también es conveniente aparte de comentar las líneas de tu programa, es que incluyas tu proyecto completo dentro de un archivo comprimido.


----------



## LucheXx (Dic 28, 2019)

Ok. Así es la cosa.
Mi intención es que A0 active el motor y los LED que se encuentran en RB2 y RB3 que indica "salida" y con RB0 apagar todo, con A1 prender RB4 y RB1 que indican "entrada"
El programa ya obedece a A0 pero con A1 no pasa nada.

```
#pragma config FOSC = XT        // Oscillator Selection bits (XT oscillator)

#pragma config WDTE = OFF       // Watchdog Timer (WDT disabled)

#pragma config PWRTE = OFF      // Power-up Timer Enable bit (Power-up Timer is disabled)

#pragma config CP = OFF         // Code Protection bit (Code protection disabled)

#include <xc.h>

#include <pic16f84a.h>

#define _XTAL_FREQ 4000000

#define INT_TRCC

#define cont(x) TMR0 = 0; while(TMR0 < x);

#define salida PORTAbits.RA0

#define entrada PORTAbits.RA1

#define led_salida PORTBbits.RB2

#define motor1 PORTBbits.RB3

#define paro PORTBbits.RB0

#define led_entrada PORTBbits.RB1

#define motor2 PORTBbits.RB4

#define led_paro PORTBbits.RB5

int main ()

{

TRISA = 1;

TRISB = 0b00000001;

ei();

OPTION_REG=0b11100111;

INTCONbits.GIE=1;

INTCONbits.TMR0IE=1;

INTCONbits.TMR0IF=0;

INTCONbits.INTE =1;

while (1)

  {

      if (salida == 1)

      {   

            led_salida = 1;

            __delay_ms(100);

            motor1 = 1;

            __delay_ms(100);

            led_salida = 0;

       }

      else

      {

          led_salida = 0;

          motor1 = 0;

        

      }

      if(entrada == 1)

      {

        led_entrada = 1;

        __delay_ms(50);

        motor2 = 1;

        __delay_ms(50);

      }

      else

      {

        led_entrada = 0;

        motor2 = 0;

      }

      if(paro == 1)

      {

          motor1 = 0;

          cont(150);

          led_salida = 0;

          cont(150);

          motor2 = 0;

          cont(150);

          led_entrada =0;

          cont(150);

          led_paro =1;

          __delay_ms(100);

        

       }

      else

      {

       led_paro = 0;

       led_entrada = 0;

       led_salida = 0;

       motor1 = 0;

       motor2 = 0;

       //cont(150);

      }

  }

return 0;

}

void interrupt   tc_int  (void)       


{

        if(INTCONbits.T0IF && INTCONbits.T0IE)

    {                                   

                TMR0 = 255;             

                INTCONbits.T0IF = 0;               

    }


}
```


----------



## D@rkbytes (Dic 28, 2019)

Las entradas RA0, RA1 y RB0 siguen estando sin una tensión lógica de referencia.
Necesitas colocar resistencias pull-down.


----------



## LucheXx (Dic 28, 2019)

D@rkbytes dijo:


> Las entradas RA0, RA1 y RB0 siguen estando sin una tensión lógica de referencia.
> Necesitas colocar resistencias pull-down.


Como hago eso en mplab xc8?


----------



## D@rkbytes (Dic 28, 2019)

LucheXx dijo:


> ¿Cómo hago eso en mplab xc8?


Lo que se puede programar en algunos microcontroladores son resistencias pull-up.
En tu caso, por como tienes las entradas en el esquema, necesitas resistencias pull-down y eso se hace en el hardware, o sea, necesitas colocarlas físicamente en el circuito.
Si no sabes lo que son resistencias pull-up o pull-down, vas por un mal camino.


----------



## LucheXx (Dic 29, 2019)

jajaja  a la desgracia ,pense que era el software gracias che


----------

