Geschriebene Daten aus 24AA1025 können nicht gelesen werden

Ich habe einen PIC18F mit MSSP, den ich mit einem 24AA1025 verbinde. Ich verwende MPLAB 8 und die Funktionen von C18, um mir das Leben zu erleichtern. Das einzige Problem ist, dass ich (angeblich) ein Byte in den 24AA1025 geschrieben habe, aber wenn ich es zurücklese, bekomme ich 0xFF anstelle des Bytes, das ich geschrieben habe.

So habe ich das EEPROM verdrahtet:

A0 - GND
A1 - GND
A2 - Vcc
Vss - GND
SDA - pulled up to +5 via 2.2k resistor, and connected to SDA on PIC
SCL - pulled up to +5 via 2.2k resistor, and connected to SCL on PIC
WP - Vss
Vcc - 5V

Hier ist meine Schreibfunktion (jetzt mit funktionierendem Code bearbeitet):

bool I2CWriteByte( long address, unsigned char data)
{
    unsigned char ret;
    unsigned char control_byte;
    unsigned char high_address_byte;
    unsigned char low_address_byte;

    control_byte = (address >= 65536) ? 0b10101000 : 0b10100000;
    high_address_byte = (char)((address & 0x0000FF00) >> 8);
    low_address_byte = (char)(address & 0x000000FF);

    IdleI2C();

    // perform ack polling around control byte sending every time
    ret = SendControlByte( control_byte);
    if( ret == -1)
        return false;

    ret = WriteI2C( high_address_byte);
    if( ret == -1)
        return false;
    ret = WriteI2C( low_address_byte);
    if( ret == -1)
        return false;
    ret = WriteI2C( data);
    if( ret == -1)
        return false;

    StopI2C();
    return true;
}

Hier ist meine Lesefunktion (jetzt mit Arbeitscode bearbeitet):

bool I2CReadByte( long address, unsigned char* data)
{
    unsigned char ret;
    // to do a read, first do part of a write but don't send the data byte, then send a new control byte with bit 0 set to 1 for read.
    // see 24AA1025 datasheet page 12
    unsigned char control_byte;
    unsigned char high_address_byte;
    unsigned char low_address_byte;

    control_byte = (address >= 65536) ? 0b10101000 : 0b10100000;
    high_address_byte = (char)((address & 0x0000FF00) >> 8);
    low_address_byte = (char)(address & 0x000000FF);

    IdleI2C();
    ret = SendControlByte( control_byte);
    if( ret == -1)
        return false;

    ret = WriteI2C( high_address_byte);
    if( ret == -1)
        return false;
    ret = WriteI2C( low_address_byte);
    if( ret == -1)
        return false;

    control_byte = (address >= 65536) ? 0b10101001 : 0b10100001;
    ret = SendControlByte( control_byte);
    if( ret == -1)
        return false;

    // now return value
    *data = ReadI2C();
    StopI2C();
    return true;
}

BEARBEITEN – Die überaus wichtige SendControlByte()-Funktion, die die erforderliche Bestätigungsabfrage durchführt:

bool SendControlByte( unsigned char control_byte)
{
    bool nack;
    bool ret;

    nack = true;

    while( nack) {
        StartI2C();
        ret = WriteI2C( control_byte);
        if( ret == -1)
            return false;
        if( SSPCON2bits.ACKSTAT == 0)
            nack = false;
    }
}

WriteI2C gibt nie einen Fehler zurück, also gehe ich davon aus, dass es tatsächlich funktioniert hat ...

Ich habe das I2C-Protokollanalysetool meines Logik-Sniffers verwendet, und es sieht so aus, als würden alle Daten ordnungsgemäß gesendet / empfangen:

Geben Sie hier die Bildbeschreibung ein

Kann jemand etwas vorschlagen, was als nächstes zum Debuggen zu tun ist? Das Steuerbyte sieht korrekt aus, da es nach START 0b1010 ist, gefolgt von der Blockkennung, A0, A1 und R/!W. Ich habe >64-KB-Adressen getestet und bestätigt, dass B1 richtig eingestellt ist. Mein EEPROM hat A0 und A1 geerdet, also sieht das auch richtig aus. R/!W ist niedrig für Schreibvorgänge und hoch kurz vor dem Lesen. Das einzige, was ich noch nicht gemacht habe, ist eine Verzögerung nach dem Schreiben, aber ich werde es morgen versuchen.

BEARBEITEN - Die I2C-Analyseoption zeigt, was Sie gesagt haben:

Geben Sie hier die Bildbeschreibung ein

Sie brauchen auf jeden Fall eine Verzögerung nach dem Datenschreiben (versuchen Sie es zunächst mit 10 ms, obwohl ich empfehlen würde, die beschriebene Bestätigungsabfrage zu verwenden, wenn Sie sie später aufpolieren). Ohne sie wird der Lesevorgang wahrscheinlich nie durchgeführt, da das Gerät während eines Schreibzyklus nicht auf Befehle reagiert. Meine Vermutung ist, dass es 0xff zurückgibt, weil SDA hoch bleibt (da das Gerät es nie niedrig zieht - es ist beschäftigt!).
@Dave Hallo Dave, Sie haben mit den gründlichen Analysetools, die Sie haben, einen lobenswerten Job gemacht. Können Sie sagen, welche Tools Sie zum Debuggen von I2c und zum Erfassen der Sendesignale verwendet haben und wie oben gezeigt angezeigt werden. Und ich möchte auch erklären, wie das SendControlByte funktioniert. Ich weiß, dass es mit Acknowledge-Polling funktioniert, aber als ich versuchte, mit dem von Ihnen geposteten Code zu kompilieren, funktionierte es nicht gut. Ich versuchte es mit c18. Und welche Werte haben Sie für wahr und falsch angegeben? Danke
Hallo @Rookie91, es ist schon eine Weile her, also lass mich versuchen, mich zu erinnern. Die Software stammt aus dem Open Logic Sniffer-Projekt. Vor einem Jahr fand ich es schwierig, einen funktionierenden Build zu finden, aber die neuesten Builds scheinen ziemlich gut zu sein. Versuchen Sie diesen Thread, um den Download-Link zu erhalten: Dangerousprototypes.com/forum/… . In Bezug auf wahr und falsch können Sie technisch alles verwenden, was Sie wollen, aber 0 für falsch und 1 für wahr sind typisch. Oder schließen Sie <stdbool.h> ein und verwenden Sie seine Definition.

Antworten (1)

Ich vermute, dass das Problem tatsächlich darin besteht, dass Sie nach dem Schreiben verzögern müssen.

Das Gerät ist nach einem Schreibvorgang etwa 3-5 Millisekunden beschäftigt, währenddessen es auf keine Befehle antwortet. Wenn Sie während dieser Zeit einen Lesevorgang ausgeben, wird das Gerät ihn ignorieren und die SDA-Leitung bleibt hoch – was tatsächlich dazu führen würde, dass alle Einsen auf den Taktimpulsen gelesen werden.

Versuchen Sie zunächst, nach dem Schreiben eine Verzögerung hinzuzufügen, vielleicht 10 Millisekunden oder so.
Wenn dies funktioniert, überprüfen Sie das Datenblattkapitel zum Bestätigungsabruf, um die Leistung zu verbessern. Kurz gesagt, Bestätigungsabfrage bedeutet, dass ein Schreibbefehl immer wieder gesendet wird, bis das Gerät ihn bestätigt. An diesem Punkt wissen Sie, dass der Schreibzyklus abgeschlossen ist.

Eigentlich sollte man warten, bis das EEPROM den Schreibvorgang bestätigt. Die Datenblätter beschreiben dies (es wird ACK-Polling genannt ). Auf diese Weise vermeiden Sie eine feste Verzögerung und warten nur so lange, wie das Schreiben wirklich dauert (aber OTOH ist ein geschäftiges Warten und benötigt den I2C-Bus).
Zustimmen. Sie sollten auch prüfen, ob das Gerät die Leseanforderung bestätigt, bevor Sie mit dem Lesen des/der Datenbyte(s) fortfahren.
Ja, aus der Ablaufverfolgung des Logikanalysators geht hervor, dass der Chip die Befehls-/Adressbytes für den Lesevorgang nicht bestätigt. Das ist genau die Art von Details, die Ihnen der Analysator beim Auffinden helfen soll; Stellen Sie sicher, dass Sie es voll ausnutzen. Außerdem sollten die I2C-Firmware-Routinen (insbesondere WriteI2C()) das Fehlen von ACK erkennen und einen Fehler zurückgeben. Da stimmt offensichtlich auch etwas nicht.
Danke, Leute, ich werde den Code (und die Bibliotheksdateien) jetzt reparieren.
@hli das habe ich erwähnt.
@exscape Nochmals vielen Dank, während die Verzögerung gut hinzugefügt werden konnte, um zu überprüfen, ob der EEPROM-Code im Allgemeinen funktionierte, war die Bestätigungsabfrage der Schlüssel. Ich habe dieses Detail total übersehen, als ich das Datenblatt durchgegangen bin ... wirklich schlampig von meiner Seite.
@exscape Entschuldigung, nach all dem Gerede über die Verzögerungen habe ich das total verpasst :( Ich habe gerade den Text nach 'ACK' geparst und das verpasst ...