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
}
}
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
}
}
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.
Justin
Woodrow Barlow
rdtsc
counter
die bei jederwhile
Iteration +1 erhöht. Schalten Sie die LED ein (und setzen Sie sie zurückcounter
), wenn Daten empfangen werden, aber schalten Sie sie erst aus, nachdem 10.000 While-Schleifen begonnen haben.