Schreiben von SSPBUF aus Variable im I2C-Slave-Protokoll in PIC18

Ich migriere diese Frage von StackOverflow in dieses Forum, weil ich glaube, dass sie angemessener ist.

Ich schreibe eine I2C-Slave-Routine für PIC18F25K80 und stecke bei einem seltsamen Problem fest.

Dies ist meine Routine: (BEARBEITET MIT DER NEUESTEN VERSION BASIERT AUF DEN KOMMENTAREN - NOCH DAS GLEICHE PROBLEM)

void interrupt interruption_handler() {
INTCON1bits.GIE = 0; // Disable Master Synchronous Serial Port Interrupt

if (PIR1bits.ADIF == 1) {
    //This is a A/D interruption
    PIR1bits.ADIF = 0;     
    INTCON1bits.GIE = 1; // Enable Master Synchronous Serial Port Interrupt
    return;
} else
if (PIR1bits.SSPIF == 1) {
    //This is a I2C interruption
    PIR1bits.SSPIF = 0;
    //Treat overflow
    if ((SSPCON1bits.SSPOV) || (SSPCON1bits.WCOL)) {
        dummy = SSPBUF; // Read the previous value to clear the buffer
        SSPCON1bits.SSPOV = 0; // Clear the overflow flag
        SSPCON1bits.WCOL = 0; // Clear the collision bit
        SSPCON1bits.CKP = 1;
        board_state = BOARD_STATE_ERROR;
    } else {
        if (!SSPSTATbits.D_NOT_A) {
            //Slave address
            debug(0, ON);
            //Read address (A/D number)
            address = SSPBUF; //Clear BF
            while (BF); //Wait until completion
            if (SSPSTATbits.R_NOT_W) {
                SSPCON1bits.WCOL = 0;
                unsigned char a = 0x01;
                SSPBUF =  a; //0x01; //a+1; //Deliver first byte
            }
        } else {
            if (SSPSTATbits.BF) {
                dummy = SSPBUF; // Clear BF (just in case)
                while (BF);
            }
            if (SSPSTATbits.R_NOT_W) {
                //Multi-byte read
                debug(1, ON);
                SSPCON1bits.WCOL = 0;
                SSPBUF = 0x02; //Deliver second byte
            } else {
                //WRITE
                debug(2, ON);
            }
        }
        transmitted = TRUE;
        SSPCON1bits.CKP = 1;
        PIR1bits.SSPIF = 0; //Clear again just in case

        INTCON1bits.GIE = 1; // Enable Master Synchronous Serial Port Interrupt
    }
} else
    PIR1 = 0x00; //Just in case
}

Es funktioniert wie ein Zauber, wenn ich konstante Werte für SSPBUF setze. Wenn Sie zum Beispiel Folgendes tun:

SSPBUF = 0x01;
(...)
SSPBUF = 0x02;

Ich bekomme die zwei Bytes auf dem Master. Ich kann sogar die Wellenformen der übertragenen Bytes auf dem Oszilloskop sehen. Ziemlich lustig!

Aber wenn ich versuche, SSPBUF mit einer Variablen wie der folgenden festzulegen:

unsigned char a = 0x01;
SSPBUF = a;

Beim Master bekomme ich null.

Es macht mich verrückt.

Einige Hypothesen, die ich verworfen habe:

  1. Der Watchdog-Timer vermasselt das Unterbrechen mitten im Protokoll: Das ist es nicht. Es ist deaktiviert und das Problem tritt in beiden SSPBUF-Zuweisungen auf
  2. Ich muss warten, bis BF niedrig ist, um fortzufahren: Tue ich nicht. AFAIK, Sie richten den SSPBUF ein, löschen SSPIF, setzen CKP und kehren von der Unterbrechung zurück, um sich um das Leben in 4 MHz zu kümmern, während die Hardware Daten in wenigen kHz sendet. Es wird Sie wieder unterbrechen, wenn es fertig ist.

Es ergibt keinen Sinn für mich. Wie gut ist es, wenn Sie keinen beliebigen Wert über eine Variable definieren können?

Bitte Gurus da draußen, klärt diesen armen Programmierer auf.

Vielen Dank im Voraus.

Keine Lösung, aber ein unabhängiger Hinweis: Wenn Sie überprüfen, ob der Interrupt von I2C ( if (PIR1bits.SSPIF != 1)) verursacht wurde, löschen Sie keine Interrupt-Flags. Wenn der Interrupt tatsächlich von einer anderen Interrupt-Quelle verursacht wurde, wird die ISR sofort wieder aufgerufen, für immer ...
Guter Punkt. Dies ist ein versteckter Fehler. Es stört jetzt nicht, da bei meinem Setup keine andere Unterbrechung möglich ist. Ich werde es reparieren. Tks! Lassen Sie mich wissen, wenn Sie einen anderen Kommentar haben, der mir hilft, nachts zu schlafen. Daran arbeite ich seit Tagen.
Ich sehe nichts Offensichtliches, aber ich hoffe, dass jemand anderes es tun wird! Sie sollten sicherlich in der Lage sein, eine Variable zu verwenden, um SSPBUF zu füttern. Wie Sie sagen, wäre es sonst nicht sehr nützlich.
Versuchenvolatile unsigned char a = 0x01;
rdtsc, habe ich. Funktioniert nicht. Ich habe die Antwort bearbeitet und Option 4 eingefügt. Ich verstehe immer noch nicht und betrachte meine Problemumgehung nicht als endgültige Lösung.
Hilft Ihnen nicht weiter, aber das sind die üblichen Probleme heutzutage mit XC8-Compilern. Ich habe mehrere Codes, die perfekt funktionierten, die zehn Jahre vor der XC8-Ära optimiert wurden, und jetzt verbringe ich immer Stunden damit, mir mit einem nicht verständlichen Problem an den Haaren zu ziehen. Meine 15 Jahre Erfahrung in PIC sind so nichts wert. Aus diesem Grund habe ich komplett aufgehört, Microchip-Dinge zu verwenden.
bitsmack: Es ist ein Designfehler, wenn du einen Intertupt aktivierst und ihn nicht im ISR handhabst. Es hängt sich zwar die MCU auf, aber im PIC kann man "any other still pending"-Flags nicht löschen. Im Allgemeinen können die Flags normalerweise auch nicht gelöscht werden. Die meisten Flags werden automatisch gelöscht, wenn Sie die Ursache beseitigen, die das Flag aktiviert hat. Wenn Sie beispielsweise etwas in einen Puffer schreiben, wird in einem "Puffer leer"-Zustand das Puffer-Leer-Flag automatisch gelöscht.
Gábor, zwei Fragen: 1) Wenn Sie sagen, dass es sich um einen Konstruktionsfehler handelt, beziehen Sie sich auf mein Programm oder auf den PIC selbst? Ich deaktiviere Unterbrechungen und lösche SSPIF in der ISR. In meiner letzten Version deaktiviere ich den SSPIF am Anfang statt am Ende der ISR. Kein Unterschied... Gleicher Fehler. 2) Sie haben aufgehört, nur XC8 oder den PIC selbst zu verwenden? Ich bin sehr unzufrieden mit solchen Dingen, die passiert sind, und Ihre 15-jährige Erfahrung ist mir wichtig.

Antworten (2)

Schalten Sie den erweiterten Befehlssatz in Ihren Codes-Konfigurationsbiteinstellungen aus, dh:

#pragma config XINST = OFF   // Extended Instruction Set (Disabled)

Es macht alle möglichen seltsamen Probleme, denken Sie nur daran, es von Anfang an auszuschalten.

I2C-Sachen geraten manchmal auch in Aufregung, wenn Sie keine kleine Verzögerung zwischen der Initialisierung von I2C, dem Aufrufen von Funktionen usw. einplanen.

Der erweiterte Befehlssatz wird nicht einmal von dem Compiler unterstützt, den das OP verwendet, daher ist er standardmäßig deaktiviert.
Hallo M, hey, es wird auch nicht von mir unterstützt. Wenn Sie die obige Zeile aus Ihrer Konfiguration herauslassen, wird sie manchmal standardmäßig auf EIN gesetzt und führt Sie zu einer fröhlichen Verfolgungsjagd um die Wände herum, nachdem Sie Verhaltensvariablen verpasst haben. Das letzte Mal war für mich 18f45k80, nachdem ich einen kleinen Testcode für i2c geschrieben hatte, der sich als verschwendeter halber Tag für das obige Problem herausstellte. Vielleicht hilft jemand...
Es hat das Problem gelöst!

Ich habe eine Problemumgehung gefunden. Es ist absolut umwerfend. Wenn ich mich wechsle:

SSPBUF = a;

Zu

SSPBUF = a+1;

Es klappt. Die Daten + 1 gehen an den I2C-Server. Ich denke, es hat etwas mit dem generierten Assembler-Code zu tun.

Lassen Sie uns die Optionen analysieren:

Option 1 - Konstante zuweisen (funktioniert)

Der Code

SSPBUF = 0x23;

ergibt:

1548                           ;naplaca.c: 320: SSPBUF = 0x2;
1549  00008A  0E02                  movlw   2
1550  00008C  D00F                  goto    L3
(...)
1661  0000BA                     L3:
1662  0000BA  6EC9                  movwf   4041,c  ;volatile

Option 2 - Variable zuweisen (funktioniert nicht)

Der Code:

unsigned char a = 0x2;
SSPBUF = a;//0x01; //Deliver first byte

ergibt:

1544                           ;naplaca.c: 318: unsigned char a = 0x2;
1545  000086  0E02                  movlw   2
1546  000088  6E11                  movwf   interruption_handler@a,c
1547                           
1548                           ;naplaca.c: 319: SSPBUF = a;
1549  00008A  C011  FFC9            movff   interruption_handler@a,4041 ;volatile

Option 3 - Zuweisen des Ergebnisses eines Ausdrucks (funktioniert, aber sende Daten +1)

Der Code:

unsigned char a = 0x2;
SSPBUF = a+1;//0x01; //Deliver first byte

ergibt:

1544                           ;naplaca.c: 318: unsigned char a = 0x2;
1545  000086  0E02                  movlw   2
1546  000088  6E11                  movwf   interruption_handler@a,c
1547                           
1548                           ;naplaca.c: 319: SSPBUF = a+1;
1549  00008A  2811                  incf    interruption_handler@a,w,c
1550  00008C  D00F                  goto    L3
1551  00
(...)
1661  0000BA                     L3:
1662  0000BA  6EC9                  movwf   4041,c  ;volatile   

Ich habe versucht, 0 zu summieren, aber der Compiler optimiert es und macht es gleich Option 2. Das gleiche passiert für a+1-1 und a+2-1-1.

Daher besteht die bisherige Problemumgehung darin, Daten + 1 zu senden.

Option 4 (flüchtige Variable – funktioniert nicht) – bearbeiten

Der Code:

volatile unsigned char a = 0x09;
SSPBUF =  a;// byte1_0 + 1; //0x01; //Deliver first byte

ergibt:

1634                           ;naplaca.c: 350: volatile unsigned char a = 0x09;
1635  000098  0E09                  movlw   9
1636  00009A  6E11                  movwf   interruption_handler@a,c    ;volatile
1637                           
1638                           ;naplaca.c: 351: SSPBUF = a;
1639  00009C  C011  FFC9            movff   interruption_handler@a,4041 ;volatile
1640                  

Abschluss

Es scheint zu funktionieren, wenn der Compiler SSPBUF mit movwf einen Wert zuweist. Wenn es Move verwendet, funktioniert es nicht.

Ich poste dies als Antwort, aber ich werde es nicht als die richtige Antwort markieren. Ich hoffe jemand bringt etwas Licht in dieses Problem.