Interrupt-on-Change tritt zweimal auf PIC16F1825 auf

Ich habe ein überwältigendes Fehlverhalten in meinem PIC16F1825. Grundsätzlich verwende ich Pin 3 (RA4), um eine LED mit einem Interrupt-on-Change umzuschalten. Das Problem ist, obwohl es die LED umschaltet, wenn ich die Taste drücke (die extern hochgezogen wird), schaltet die LED nach einer Sekunde erneut um, was bedeutet, dass die Interrupt-Routine erneut aufgerufen wurde und dies nicht sollte, obwohl ich lösche das Interrput-Flag.

Hier ist der Code (nur die Funktionen IOconfig_portA() und toggleLed() sind für das vorliegende Problem von Bedeutung:

#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);
}

Vielen Dank im Voraus!

Hast du eine Entprellung am Schalter? Ist es möglich, dass Sie die Taste gedrückt halten und sie leicht loslassen, was eine Änderung bewirkt? Sie können dies testen, indem Sie den Taster durch eine Spannungsquelle ersetzen.
Die for-Schleife soll zum Entprellen dienen. Außerdem passiert dasselbe, wenn ich diese Firmware auf Proteus simuliere, wo Sie ideale Schalter haben
Fair genug. Eine andere Sache, ich glaube, ich sollte darauf hinweisen, dass for (int i = 0; i < 100000; i++);das in einer ISR nichts zu suchen hat! Die erste Regel für Interrupts ist, dass die Handler so schnell wie möglich fertig werden und nicht für längere Zeit blockieren sollten. Auch die INTCONbits.GIE = ...Linien sind unnötig (wird intern vom PIC beim Betreten und Verlassen des ISR durchgeführt).
Ich weiss. Diese Zeile für Plausibilitätsprüfungszwecke. Andernfalls würde die LED so schnell doppelt umschalten, dass ich nicht einmal die LED blinken sehen konnte
Höchstwahrscheinlich besteht Ihr Problem darin, dass Sie das Interrupt-Flag nicht löschen. Aus dem Datenblatt: "Das IOCIF-Flag-Bit ist schreibgeschützt und wird gelöscht, wenn alle Interrupt-on-Change-Flags im IOCxF-Register per Software gelöscht wurden." Das Schreiben in das IOCIF-Bit ist also sinnlos. Sie müssen stattdessen in das IOCAF-Bit schreiben.

Antworten (3)

Gemäß Seite 92 des PIC16F1825- Datenblatts :

Hinweis 1: Das IOCIF-Flag-Bit ist schreibgeschützt und wird gelöscht, wenn alle Interrupt-on-Change-Flags im IOCxF-Register per Software gelöscht wurden

Also im Grunde, wenn Sie dies in Ihrem Code tun:

INTCONbits.IOCIF = 0;

Es tut eigentlich überhaupt nichts - das Interrupt-Flag wird nicht gelöscht, da dieses Bit schreibgeschützt ist.

Um das Interrupt-Flag für Interrupt-on-Change-Quellen zu löschen, müssen Sie in das IOCAFRegister schreiben, um die gewünschten Flags zu löschen. Wenn Sie sie alle löschen möchten, können Sie Folgendes tun:

IOCAF = 0x0;

Wenn Sie nur bestimmte Bits löschen möchten, wie in Ihrem Fall IOCAF4, können Sie Folgendes tun:

IOCAFbits.IOCAF4 = 0;
Guter Punkt, du hast recht! Aber es löste das Problem nicht. Beachten Sie, dass, obwohl das Flag nicht ordnungsgemäß gelöscht wurde, das Problem darin besteht, dass die Interrupt-Routine zweimal pro Push aufgerufen wird und der Aufruf der Interrupt-Routine unabhängig vom Status der Flags erfolgt.
habe das Problem gefunden. Überprüfen Sie meine Antwort.

Antwort gefunden! Es ist ziemlich undurchsichtig, muss ich sagen. Grundsätzlich verfügt der PIC über einen Watchdog-Timer, der das Gerät alle 4 Sekunden zurücksetzt. Seltsamerweise ist es standardmäßig aktiviert (macht das überhaupt Sinn?). Um den Watchdog-Timer zu deaktivieren, müssen Sie die folgende Pre-Compiler-Direktive hinzufügen:

#pragma config WDTE = OFF
Scheint machbar. Obwohl die Behauptung war, dass Sie keine zusätzlichen Resets erkannt haben. Wenn es einen Standard-Software-Timer gibt, der immer geladen ist (um das WDT-Löschen zu handhaben), dann kann das Durchführen langer Verzögerungen in einem anderen Interrupt auch den WDT unerwartet ablaufen lassen.
Ich habe es nicht erkannt, weil mein einzigartiges LED-Blinkmuster länger als die Watchdog-Periode war.
Okay dann, viel Glück...

Ist es möglich, dass die Schaltung aufgrund mehrerer Unterbrechungen (z. B. Stapelüberlauf) zurückgesetzt wird?

Bei einem Reset würden Sie den gesamten Code wiederholen und den Interrupt erneut aktivieren. (Wie in der anderen Antwort könnte es einen Schaltersprung geben, der dies verursacht.)

Die Zählerverzögerung von 100k kann zu kurz sein, versuchen Sie, diese auf etwas menschlich Messbares zu erhöhen, wie eine Gesamtverzögerung von 500 ms, wenn möglich.

Um auch auf einen unerwünschten Reset zu testen, geben Sie sich eine einmalige Einschaltroutine, um einen eindeutigen Code auf der LED zu blinken, der Ihnen sagt, ob das Ganze recycelt wurde.

Hab das gemacht, und nein, es scheint nicht überzulaufen.
Habe das Problem gefunden. Überprüfen Sie meine Antwort :)