La interrupción al cambiar ocurre dos veces en PIC16F1825

Tengo un mal comportamiento alucinante en mi PIC16F1825. Básicamente, estoy usando el pin 3 (RA4) para alternar un LED usando una interrupción en el cambio. El problema es que, aunque cambia el LED cada vez que presiono el botón (que se levanta externamente), después de un segundo, el LED cambia de nuevo, lo que significa que la rutina de interrupción se llamó nuevamente, y no debería, aunque borre la bandera de interrput.

Aquí está el código (solo la función IOconfig_portA() y toggleLed() son importantes para el problema en cuestión:

#include <stdio.h>
#include <stdlib.h>
#include <PIC16F1825.h>

char temp=0;

void interrupt toggleLed() {

    INTCONbits.GIE = 0;

    if(INTCONbits.IOCIF == 1) {
        temp = PORTA;
        INTCONbits.IOCIF = 0;
        LATC3 = ~LATC3;
        for (int i = 0; i < 100000; i++);
    }

    INTCONbits.GIE = 1;
}

void CLOCKconfig() {
    OSCCON  = 0x6A; //Sets the internal oscillator fosc = 4 MHz
    OSCSTAT = 0x00;
    OSCTUNE = 0x00;
}

void IOconfig_portA() {
    ANSELA = 0x00;           // All ports set as DIGITAL
    TRISAbits.TRISA4 = 1;    // Set as input
    //OPTION_REG &= 0x7F;    // Clear bit 7, to enable the weak pull-up
    //WPUA |= (1<<2);        // Enable the WPU for RA2
    CM1CON0 = 0x00;
    CM1CON1 = 0x00;
    IOCANbits.IOCAN4  = 1;  // Generate Interrupt on Negedge
    IOCAP  = 0x00;  // Disable Interrupt on Posedge
    INTCON = 0x88;    // Enable GIE and IoC interrupts
}

int main(int argc, char** argv) {

    TRISC = 0;

    CLOCKconfig();

    ANSELC &= 0x00; // All bits on port C are set to Digital I/O's
    TRISC  &= 0x00; // All bits on Port C are set to Outputs
    APFCON0 |= (1<<5); // Don't use special features on Pin RC3
    APFCON1 |= (1<<2); // Don't use special features on Pin RC3

    IOconfig_portA();

    LATC |= (1<<3);

    while(1) {

    }

    return (EXIT_SUCCESS);
}

¡Gracias de antemano!

¿Tienes algún antirrebote en el interruptor? ¿Es posible que, mientras mantiene presionado el botón, lo suelte ligeramente, lo que provoca un cambio? Puede probar esto reemplazando el botón con una fuente de voltaje.
Se supone que el bucle for es para eliminar rebotes. Además, sucede lo mismo cuando simulo este firmware en Proteus, donde tienes interruptores ideales
Me parece bien. Otra cosa, ¡siento que debo señalar que for (int i = 0; i < 100000; i++);eso no tiene cabida en un ISR! La primera regla de las interrupciones es que los controladores deben terminar lo más rápido posible y no bloquearse durante largos períodos de tiempo. También las INTCONbits.GIE = ...líneas son innecesarias (lo hace internamente el PIC al entrar y salir del ISR).
Lo sé. Esa línea para fines de verificación de cordura. De lo contrario, el LED cambiaría dos veces tan rápido que ni siquiera podría ver el parpadeo del LED.
Lo más probable es que su problema sea que no está borrando el indicador de interrupción. De la hoja de datos: "El bit de indicador IOCIF es de solo lectura y se borra cuando el software borra todos los indicadores de interrupción por cambio en el registro IOCxF". Entonces, escribir en el bit IOCIF no tiene sentido. En su lugar, debe escribir en el bit IOCAF.

Respuestas (3)

De acuerdo con la página 92 ​​de la hoja de datos PIC16F1825 :

Nota 1: El bit de indicador IOCIF es de solo lectura y se borra cuando el software borra todos los indicadores de interrupción por cambio en el registro IOCxF.

Básicamente, cuando haces esto en tu código:

INTCONbits.IOCIF = 0;

En realidad, no hace nada en absoluto: el indicador de interrupción no se borra porque ese bit es de solo lectura.

Para borrar el indicador de interrupción para las fuentes de Interrupción por cambio, debe escribir en el IOCAFregistro para borrar los indicadores que desea. Si desea borrarlos todos, puede hacer lo siguiente:

IOCAF = 0x0;

Si solo desea borrar ciertos bits, como IOCAF4 en su caso, puede hacer:

IOCAFbits.IOCAF4 = 0;
Buen punto, tienes razón! Pero no resolvió el problema. Observe que, aunque el indicador no se borró correctamente, el problema es que la rutina de interrupción se llama dos veces por pulsación y la llamada a la rutina de interrupción ocurre independientemente del estado de los indicadores.
encontró el problema. Revisa mi respuesta.

¡Encontré la respuesta! Es bastante oscuro, debo decir. Básicamente, el PIC tiene un temporizador Watchdog que reinicia el dispositivo cada 4 segundos. Por extraño que parezca, viene habilitado de forma predeterminada (¿eso tiene sentido?). Para deshabilitar el temporizador de vigilancia, debe agregar la siguiente directiva de compilación previa:

#pragma config WDTE = OFF
Parece factible. Aunque la afirmación era que no estabas detectando reinicios adicionales. Si hay un temporizador de software estándar que siempre está cargado (para manejar el borrado de WDT), entonces hacer largas demoras en otra interrupción podría hacer que el WDT también caduque inesperadamente.
No lo detecté porque mi patrón único de parpadeo de LED fue más largo que el período de vigilancia.
OK entonces,,, Buena Suerte...

¿Es posible que el circuito se restablezca debido a múltiples interrupciones (por ejemplo, desbordamiento de pila).

En un reinicio, repetiría todo el código y volvería a habilitar la interrupción. (Como en la otra respuesta, podría haber un rebote del interruptor que provoque esto).

El retraso del contador de 100k puede ser demasiado corto, intente aumentar esto a algo humanamente medible como un retraso total de 500ms si es posible.

Además, para probar un reinicio no deseado, realice una rutina de encendido única para que destelle un código único en el LED, que le indicará si todo se recicló.

Hizo eso, y no, no parece desbordarse.
Encontré el problema. Revisa mi respuesta :)