I2C-Modul reagiert nicht

Ich schreibe einen einfachen I2C-Slave als Teil meines Codes auf einer 20-poligen 8-Bit-PIC16f-MCU . Soweit ich das beurteilen kann, hat dieser Mikrocontroller ein Modul, das den I2C-Bus überwacht und meine Register nur aktualisiert, wenn an den PIC adressierte Nachrichten eingehen - ich kann mit keinen anderen Nachrichten interagieren. Der Master ist eine CPU, auf der ein Linux-Kernel und einige andere Software ausgeführt werden. Von der CPU aus kann ich ein I2C-Probe-Skript ausführen, das nur versucht, von jeder möglichen Geräteadresse auf der I2C-Kette (Bus) zu lesen.

Ich habe Probleme, den PIC dazu zu bringen, auf Sondierungen zu reagieren. Ich habe den einfachsten I2C-Code geschrieben, den ich verwalten kann – er sollte nur die LEDs blinken lassen, wenn das Gerät irgendeine Art von I2C-Nachricht erhält, die an ihn gerichtet ist.

Ich habe die Leitungen an das Oszilloskop angeschlossen und konnte bestätigen, dass Verkehr durch die Takt- und Datenleitungen kommt, wenn ich die Kette untersuche.

Mein Code ist unten. Mein Compiler ist die kostenlose Version (ohne Optimierungen) von Microchips XC8 . Ich verwende die neueste Version (v1.34) für Windows.

Fehlen mir irgendwelche Initialisierungs- oder Konfigurationsschritte, die notwendig sind, damit mein PIC eingehende I2C-Nachrichten sieht?

#include <xc.h>                 /* XC8 General Include File */
#include <pic16lf1709.h>        /* Definitions of I/O pins */

#pragma config WDTE = OFF       // disable watchdog timer, for simplicity

// I2C address is 7 bits: 1111110
#define I2C_ADDRESS 0x7E

typedef unsigned char byte;

void main(void) {

    /* configure MSSP module */
    TRISBbits.TRISB4    = 1;    // set SDA to input
    TRISBbits.TRISB6    = 1;    // set SCL to input
    SSPCON1bits.SSPEN   = 1;    // enable SSP module
    SSPCON1bits.SSPM    = 0x6;  // SSP is in I2C slave mode, 7-bit addressing
    SSP1ADD             = I2C_ADDRESS<<1;  // set the device address (left-aligned)
    SSPCON1bits.CKP     = 1;    // release clock
    TRISC               = 0x00; // set LEDs to output
    PORTC               = 0xFF; // initialize LEDs to OFF

    while(1) {

        byte ssp_buf;           // for the data we read from the bus

        if(SSPSTATbits.BF) {    // if the I2C buffer is not empty
            PORTC = 0x00;       // turn on LEDs for a moment
            for(int i=0; i<100; i++) _delay(250);
            PORTC = 0xFF;       // turn them back off
            ssp_buf = SSPBUF;   // read the buffer
                                // BF flag is cleared by hardware
        }

        SSPCON1bits.CKP = 1;    // finally, release clock

    }

}
Haben Sie bemerkt, dass Sie die I2C-Adresse zweimal verschieben? (Einmal in der Define und einmal in der Modulkonfiguration). Ich habe keine Ahnung, ob das richtig oder falsch ist – es sieht nur verdächtig aus.
@Justin Ups! Ich habe das behoben und kann immer noch nicht blinzeln.
100 Taktzyklen? Wenn der PIC mit 4 MHz FOSC läuft, beträgt die Befehlsrate 1 M/s. Eine Schleife von 100 * 2 (vollständig optimiert) beträgt mindestens 200 uS. Dem menschlichen Auge fällt es schwer, Dinge schneller als etwa 50 ms zu sehen. Es könnte blinken, aber zu schnell, um es zu sehen. Ich würde den Code ändern, um eine INT-Variable zu implementieren, counterdie bei jeder whileIteration +1 erhöht. Schalten Sie die LED ein (und setzen Sie sie zurück counter), wenn Daten empfangen werden, aber schalten Sie sie erst aus, nachdem 10.000 While-Schleifen begonnen haben.

Antworten (2)

Es gibt mehrere E/A-Verbindungsmerkmale, die von verschiedenen PICs verwendet werden. Der von Ihnen verwendete PIC hat die folgenden Funktionen (wie aus der Überschrift der entsprechenden Kapitel des Datenblatts kopiert):

11.0 E/A-PORTS

Jeder Port hat sechs Standardregister für seinen Betrieb. Diese Register sind:

  • TRISx-Register (Datenrichtung)

  • PORTx-Register (liest die Pegel an den Pins des Geräts)

  • LATx-Register (Ausgangslatch)

  • INLVLx (Eingangspegelregler)

  • ODCONx-Register (Open-Drain)

  • SLRCONx-Register (Anstiegsrate

Einige Ports können eines oder mehrere der folgenden zusätzlichen Register haben. Diese Register sind:

  • ANSELx (analoge Auswahl)

  • WPUx (schwacher Pull-up)

Ports, die analoge Eingänge unterstützen, haben ein zugehöriges ANSELx-Register. Wenn ein ANSEL-Bit gesetzt ist, wird der diesem Bit zugeordnete digitale Eingangspuffer deaktiviert. Das Deaktivieren des Eingangspuffers verhindert, dass analoge Signalpegel auf dem Pin zwischen einem logischen High und Low einen übermäßigen Strom in der Logikeingangsschaltung verursachen.

11.3.5 ANALOGSTEUERUNG

Das ANSELB-Register (Register 11-12) wird verwendet, um den Eingangsmodus eines I/O-Pins auf analog zu konfigurieren. Das Setzen des entsprechenden ANSELB-Bits auf hoch bewirkt, dass alle digitalen Lesevorgänge auf dem Pin als '0' gelesen werden und analoge Funktionen auf dem Pin korrekt funktionieren. Der Zustand der ANSELB-Bits hat keine Auswirkung auf digitale Ausgangsfunktionen. Ein Pin mit TRIS frei und ANSELB gesetzt funktioniert weiterhin als digitaler Ausgang, aber der Eingangsmodus ist analog. Dies kann zu unerwartetem Verhalten führen, wenn Lese-, Änderungs- und Schreibanweisungen auf dem betroffenen Port ausgeführt werden.

Hinweis: Die ANSELB-Bits gehen nach dem Zurücksetzen standardmäßig in den analogen Modus. Um beliebige Pins als digitale Allzweck- oder Peripherieeingänge zu verwenden, müssen die entsprechenden ANSEL-Bits von der Benutzersoftware auf '0' initialisiert werden.

12.0 PERIPHERIE-PIN-AUSWAHLMODUL (PPS).

Das Peripheral Pin Select (PPS)-Modul verbindet periphere Ein- und Ausgänge mit den I/O-Pins des Geräts. Nur digitale Signale sind in der Auswahl enthalten. Alle analogen Ein- und Ausgänge bleiben fest auf ihren zugewiesenen Pins. Eingangs- und Ausgangsauswahl sind unabhängig, wie im vereinfachten Blockdiagramm in Abbildung 12-1 gezeigt.

12.2 PPS-Ausgänge

Jeder I/O-Pin hat ein PPS-Register, mit dem die Pin-Ausgangsquelle ausgewählt wird. Mit wenigen Ausnahmen behält die diesem Pin zugeordnete Port-TRIS-Steuerung die Kontrolle über den Pin-Ausgangstreiber. Peripheriegeräte, die den Pin-Ausgangstreiber als Teil des Peripheriebetriebs steuern, setzen die TRIS-Steuerung nach Bedarf außer Kraft. Zu diesen Peripheriegeräten gehören:

  • EUSART (Synchronbetrieb)

  • MSSP (I2C)

  • COG (automatische Abschaltung)

12.3 Bidirektionale Pins

PPS-Auswahlen für Peripheriegeräte mit bidirektionalen Signalen an einem einzelnen Pin müssen so vorgenommen werden, dass der PPS-Eingang und der PPS-Ausgang denselben Pin auswählen. Zu den Peripheriegeräten mit bidirektionalen Signalen gehören:

  • EUSART (Synchronbetrieb)

  • MSSP (I2C)

Sie sollten die relevanten Teile des Datenblatts lesen und alle E/A-Konfigurationsregister einrichten, die für Ihre Konfiguration benötigt werden. In diesem Fall fehlt das Einrichten von ANSELB zum Aktivieren der digitalen Eingänge und das Einrichten der PPS-Register zum Weiterleiten der Eingangs- und Ausgangsfunktionen des I2C-Moduls an die gewünschten Pins. Außerdem sind die PPS-Register vor unerwünschten Änderungen geschützt, sodass Sie einen Sperrmechanismus verwenden müssen, bevor Sie auf die Register zugreifen können.

Nachdem Sie die obigen Änderungen vorgenommen haben, sollte der Code in etwa so aussehen. Beachten Sie, dass das Gerät für den Master möglicherweise nicht "sichtbar" ist, da die I2C-Meldungen noch nicht richtig bestätigt werden, aber Sie sollten zumindest das Licht blinken sehen.

#include <xc.h>                 /* XC8 General Include File */
#include <pic16lf1709.h>        /* Definitions of I/O pins */

#pragma config WDTE = OFF

// I2C address is 7 bits: 1111110
#define I2C_ADDRESS 0x6E

typedef unsigned char byte;

void main(void) {

    /* configure ports */
    TRISBbits.TRISB4    = 1;    // set SDA to input
    TRISBbits.TRISB6    = 1;    // set SCL to input

    ANSELBbits.ANSB4    = 0;    // make sure SDA is set to digital

    TRISC               = 0x00; // set LEDs to output
    PORTC               = 0xFF; // initialize LEDs to OFF

    /* configure peripheral pin select */
    PPSLOCK             = 0x55;
    PPSLOCK             = 0xAA;
    PPSLOCK             = 0x00; // PPS is now unlocked
    SSPDATPPS           = 0x0C; // RB4 input is SDA (pg 140)
    SSPCLKPPS           = 0x0E; // RB6 input is SCL (pg 140)
    RB4PPS              = 0x11; // RB4 output is SDA (pg 141)
    RB6PPS              = 0x10; // RB6 output is SCL (pg 141)
    PPSLOCK             = 0x55;
    PPSLOCK             = 0xAA;
    PPSLOCK             = 0x01; // PPS is now locked

    /* configure MSSP module */
    SSPCON1bits.SSPEN   = 1;    // enable SSP module
    SSPCON1bits.SSPM    = 0x6;  // SSP is in I2C slave mode, 7-bit addressing
    SSP1ADD             = I2C_ADDRESS<<1;  // set the device address (left-aligned)
    SSPCON1bits.CKP     = 1;    // release clock

    while(1) {

        byte ssp_buf;

        if(SSPSTATbits.BF) {    // if the I2C buffer is not empty
            PORTC = 0x00;       // turn on LEDs for a moment
            for(int i=0; i<100; i++) _delay(250);
            PORTC = 0xFF;       // turn them back off
            ssp_buf = SSPBUF;   // read the buffer
                                // BF flag is cleared by hardware
        }

        SSPCON1bits.CKP = 1;    // finally, release clock

    }

}
Danke! Ich werde morgen früh die PPS-Register einrichten und Ihnen das grüne Häkchen geben, wenn es funktioniert. Was die Ansel-Register betrifft, denke ich, dass digital die Standardeinstellung ist, aber ich werde weitermachen und explizit sein.
hmmm. Ich habe auch gerade bemerkt, dass die Adresse, die ich verwende, reserviert ist. das korrigiere ich auch.
Aha! Ich habe das Licht bekommen, um mit Ihren Vorschlägen zu blinken.

Die Buskapazität beeinflusst das Timing, daher müssen Sie die Start-/Stopp-Haltezeiten des Master-SDA richtig einstellen, damit der Slave die Start- und Stoppbedingungen erkennen kann.

Kann ich stattdessen den Slave so einstellen, dass er mit dem Master übereinstimmt?
Ich möchte hinzufügen, dass ein Oszilloskop normalerweise nicht das beste Instrument ist, um "I2C-Daten zu sehen". Versuchen Sie es aus wirtschaftlichen Gründen stattdessen mit einem Saleae oder Rigol DS1054Z (mit der Option zur Datendecodierung).