No se pueden leer datos escritos de 24AA1025

Tengo un PIC18F con MSSP que estoy interactuando con un 24AA1025. Estoy usando MPLAB 8 y las funciones de C18 para hacerme la vida más fácil. El único problema es que (supuestamente) he escrito un byte en el 24AA1025, pero cuando lo leo, obtengo 0xFF en lugar del byte que escribí.

Así es como tengo la EEPROM conectada:

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

Aquí está mi función de escritura (ahora editada con código de trabajo):

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

Aquí está mi función de lectura (ahora editada con código de trabajo):

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

EDITAR: la importante función SendControlByte(), que realiza el sondeo de reconocimiento necesario:

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 nunca devuelve un error, así que asumo que realmente funcionó...

Utilicé la herramienta de análisis de protocolo I2C de mi rastreador lógico y parece que todos los datos se envían/reciben correctamente:

ingrese la descripción de la imagen aquí

¿Alguien puede sugerir algo que hacer a continuación para la depuración? El byte de control parece correcto, ya que es 0b1010 después de INICIO, seguido del identificador de bloque, A0, A1 y R/!W. He probado direcciones de >64 KB y he confirmado que B1 está configurado correctamente. Mi EEPROM tiene A0 y A1 conectados a tierra, por lo que también parece correcto. R/!W es bajo para escritura y alto justo antes de la lectura. Lo único que aún no he hecho es agregar un retraso después de la escritura, pero lo intentaré mañana.

EDITAR: la opción de análisis I2C muestra lo que han estado diciendo:

ingrese la descripción de la imagen aquí

Ciertamente necesita un retraso después de la escritura de datos (pruebe con 10 ms para comenzar, aunque recomendaría usar el sondeo de reconocimiento descrito cuando lo esté puliendo más adelante). Sin él, es probable que nunca se realice la lectura, ya que el dispositivo no responderá a los comandos durante un ciclo de escritura. Mi suposición es que devuelve 0xff porque SDA permanece alto (ya que el dispositivo nunca lo baja, ¡está ocupado!).
@Dave Hola, Dave, has hecho un trabajo encomiable con las herramientas de análisis exhaustivas que tienes. ¿Puedes decir cuáles son las herramientas que has usado para depurar I2c y capturar las señales de envío y mostrarlas como se muestra arriba? Y también me gustaría explicar cómo funciona SendControlByte, sé que funciona con el sondeo de reconocimiento, pero cuando intenté compilar con el código que publicaste... no funcionó bien. Lo intenté con c18. y que valores le has dado a verdadero y falso?.gracias
Hola @Rookie91, ha pasado un tiempo, así que déjame tratar de recordar. El software es del proyecto Open Logic Sniffer. Hace un año me resultó difícil conseguir una compilación que funcionara, pero las últimas compilaciones parecen bastante buenas. Pruebe este hilo para obtener el enlace de descarga: dangerprototypes.com/forum/… . En cuanto a verdadero y falso, técnicamente puede usar lo que quiera, pero 0 para falso y 1 para verdadero son típicos. O incluya <stdbool.h> y use su definición.

Respuestas (1)

Supongo que el problema es que debe retrasarse después de la escritura.

El dispositivo estará ocupado durante aproximadamente 3 a 5 milisegundos después de una escritura, durante los cuales no responderá a ningún comando. Si emite una lectura durante este período de tiempo, el dispositivo la ignorará y la línea SDA permanecerá alta, lo que de hecho conduciría a que todos se lean en los pulsos del reloj.

En primer lugar, intente agregar un retraso después de la escritura, quizás 10 milisegundos más o menos.
Si eso funciona, consulte el capítulo de la hoja de datos sobre sondeo de confirmación para mejorar el rendimiento. En resumen, confirmar el sondeo significa enviar un comando de escritura una y otra vez hasta que el dispositivo lo reconozca, momento en el que sabrá que el ciclo de escritura está completo.

En realidad, se debe esperar a que la EEPROM reconozca la escritura. Las hojas de datos describen esto (se llama sondeo ACK ). De esa manera, evita un retraso fijo y espera solo el tiempo que realmente toma la escritura (pero OTOH es una espera ocupada y necesita el bus I2C).
Aceptar. También debe verificar que el dispositivo reconozca la solicitud de lectura antes de continuar con la lectura de los bytes de datos.
Sí, está claro a partir del seguimiento del analizador lógico que el chip no está ACKing los bytes de comando/dirección para la lectura. Este es exactamente el tipo de detalle que se supone que el analizador debe ayudarlo a encontrar; asegúrese de que está haciendo pleno uso de él. Además, las rutinas de firmware I2C (WriteI2C(), en particular) deberían haber detectado la falta de ACK y devolver un error. Claramente, algo está mal allí también.
Gracias, chicos, corregiré el código (y los archivos de la biblioteca) ahora.
@hli Mencioné eso.
@exscape gracias de nuevo, aunque fue bueno agregar la demora para verificar que el código EEPROM generalmente funcionó, el sondeo de confirmación fue la clave. Me perdí por completo ese detalle cuando estaba revisando la hoja de datos... realmente descuidado de mi parte.
@exscape lo siento, después de toda la charla sobre los retrasos me lo perdí por completo :( Acabo de analizar el texto para 'ACK' y me lo perdí...