Problema al realizar la operación de lectura I2C de ds1307 a pic18F

Como me aconsejaron mis compañeros apiladores en mi publicación anterior, también probé la función "Write_to_slave" en Proteus en tiempo real, enviando bytes al RTC y el esclavo responde adecuadamente con un bit de reconocimiento por cada byte que recibe, por lo tanto, el la operación de escritura está funcionando bien. Sin embargo, ahora tengo problemas con mi función "Read_from_slave". El problema es que, durante la secuencia de lectura, hasta el envío de la "Dirección del esclavo + Lectura", es decir, 0xD1, funciona bien y también recibe un reconocimiento del esclavo, pero el problema surge después de la ejecución de la función I2C_restart. Los datos leídos del SSPBUF o los datos recibidos del esclavo son FF mientras que deberían ser los segundos contando de 0 a 60 (y muestra 63 en la pantalla LCD), seguido de un NACK. Y después de enviar el NACK, I2C_stop() debería finalizar la transacción mientras eso no sucede, como puede ver en la instantánea de Proteus. A continuación se muestra el código, he realizado solo una operación de lectura para leer el registro de segundos para verificar si mi método funciona, pero desafortunadamente no es así:

void Wait_MSSP() 
{
while(SSPIF==0);
SSPIF = 0;
}

void check_ACK_Master_Transmit()
{
if(SSPCON2bits.ACKDT == 0) //If ACKDT is 0, ACK has been recieved 
   arraydisp("ACK success");
   else if (SSPCON2bits.ACKDT == 1) //If ACKDT is 1, ACK has not been recieved
   arraydisp("ACK fail");
}
    unsigned char Read_from_slave(unsigned char addr)
{
    unsigned char x;
    I2C_start();
    SSPBUF = RTC_ADDRW; 
    Wait_MSSP();                            //Slave address+Write
    SSPBUF = addr; 
    Wait_MSSP();                              ////RTC register to be read
    I2C_restart();
    SSPBUF = RTC_ADDRR;
    Wait_MSSP();  
    SSPCON2bits.RCEN = 1;                       //Enable Master to receive data from slave
    x = SSPBUF;                             //Read the SSPBUF
    SSPCON2bits.ACKDT = 1;                     //send NACK after receiving the data
    SSPCON2bits.ACKEN = 1;                    //Enable Acknowledge sequence on SDA and SCL
    I2C_stop();                                //Wait until stop operation is completed
    return(x);
 }
void Write_to_slave(unsigned char addr, unsigned char data)
{
I2C_start();

SSPBUF = RTC_ADDRW; //Slave address + Write
Wait_MSSP();
check_ACK_Master_Transmit(); //Checks the
SSPBUF = addr; // RTC Registor location address to be written
Wait_MSSP();
check_ACK_Master_Transmit();
SSPBUF = data; //data to be writen to the address location
Wait_MSSP();
check_ACK_Master_Transmit();
I2C_stop();
Wait_MSSP();
}

void I2C_write(unsigned char addr)
{
    PIR1bits.SSPIF = 0;
    SSPBUF = addr;
    while(PIR1bits.SSPIF == 0);
    return;
}

void Reset_time()
{
    I2C_start();
    I2C_write(RTC_ADDRW);
    I2C_write(0x00);
    I2C_write(0x00);
    I2C_write(0x00);
    I2C_write(0x01);
    I2C_write(0x01);
    I2C_write(0x01);
    I2C_write(0x01);
    I2C_write(0x00);
    I2C_stop();
    return;
}

void Set_time()
{
    Write_to_slave(0x00,0x00);     //Write data 0x00 to address 00H (Seconds) of RTC (CH = 0)
    Write_to_slave(0x01,0x30);      //Write data  0x00 to address 01H(Minutes) of the RTC
    Write_to_slave(0x02,0x10);      //Write data 0x00 to address 02H(HOUR) of the RTC
    Write_to_slave(0x03,0x06);      //Write data 0x01 to address 03H (DAY) of the RTC
    Write_to_slave(0x04,0x01);      //Write data 0x01 to address 04H (Date) of the RTC
    Write_to_slave(0x05,0x01);      //Write data 0x01 to address 05H (Month) of the RTC
    Write_to_slave(0x06,0x16);      //Write data 0x00 to address 06H (Year) of the RTC
}




   void main()
{
    TRISCbits.TRISC0 = 0;
    TRISCbits.TRISC1 = 0;
    TRISCbits.TRISC2 = 0;
    TRISD = 0;
    char16x2LCDInit();
    I2C_Init();
    Reset_time();
    Set_time();



while(1)
{
     sec = Read_from_slave(0x00);
     __delay_ms(10);
Write_Command(0xC0);
    LCDWriteInt(BCD2Lowerch(sec),1);
    LCDWriteInt(BCD2Upperch(sec),1);

}
  }

Solo para obtener una imagen clara de las transacciones que ocurren en el bus I2C, he adjuntado una instantánea de la ventana del depurador I2C que muestra las operaciones I2C durante la simulación en Proteus. La primera secuencia es la siguiente: S = inicio, D0 = dirección del esclavo + escritura, A = reconocimiento del esclavo, 05 = dirección de registro en el RTC, A = reconocimiento del esclavo, 01 = datos escritos en 05H, A = reconocimiento del esclavo, P = Detener. De manera similar para otros ciclos de escritura y también de lectura, mientras que en lectura hay "Sr", es decir, I2C_restart seguido de "N" NACK y, como podemos ver, no hay "P", es decir, parada después de NACK. También he adjuntado la imagen del hardware. Vuelvo a leer el modo esclavo como transmisor en la hoja de datos DS1307, pero no encuentro nada malo con la secuencia "Read_from_slave".

ingrese la descripción de la imagen aquí

Perdón por el mensaje tedioso, ya que sería de gran ayuda cualquier otro consejo o dirección con respecto a mi enfoque para realizar la operación de lectura o la identificación de cualquier problema con los métodos. ¡Gracias de nuevo!

Saludos ~VD

Respuestas (3)

Una razón muy común por la que recibe un 0xFF es porque la línea SCL del I2C no se retiene el tiempo suficiente para que el esclavo transmita datos. Entonces, lo que sugiero es agregar un retraso de, digamos, 10 microsegundos para estirar el reloj después de la función de lectura i2c y verificar si está recibiendo datos. Es un método de prueba y error básicamente. Simplemente siga aumentando la demora hasta que reciba datos relevantes. He tenido el mismo problema antes con un Atmega y en realidad se solucionó en SE: P.

    sec = Read_from_slave(0x00); //Read address 00H(Seconds) from the RTC and display on LCD
    _delay_ms(10); //add a small delay to stretch the clock.
    Write_Command(0xC0);

¡Espero que funcione! Y si funciona, intente disminuir la demora para que obtenga sus valores en el mínimo tiempo posible.

no estoy seguro pero:

    SSPBUF = addr; //RTC register to be read
    check_ACK_Master_Transmit();
    Wait_MSSP();

Parece que primero comprobó el ACK antes de esperar a obtener la confirmación de la transmisión exitosa. Esto también está presente en su write_to_slave(). Tal vez podría intentar cambiar el orden, por si acaso.

Creo que no debería hacer ninguna diferencia. Porque, durante "SSPBUF = addr" (se usan 8 ciclos de reloj para enviar la dirección) y en el noveno ciclo de reloj se recibe ACK, después de lo cual verifico si recibí ese ACK del esclavo. Una vez que recibí el ACK, espero hasta que el SSPIF suba y luego lo restablezco a cero y salgo del ciclo. El SSPIF se eleva en la transferencia del ACK. Debido a esta secuencia, de cualquier manera, creo que primero puedo esperar hasta que SSPIF suba y luego verifique el ACK o viceversa.
Brianho tiene razón. Esa instrucción no espera esos 8 ciclos de reloj I2C, simplemente carga su byte en el registro SSPBUF en el módulo periférico I2C. Luego, el módulo se toma su tiempo para registrar los 8 bits y leer el ACK mientras la CPU continúa ejecutando instrucciones. Intentar leer el ACK inmediatamente después de cargar el registro no funcionará. Primero debe esperar a que se complete la transferencia.
Gracias por su respuesta chicos. Haré los cambios sugeridos por todos ustedes y volveré con los resultados. Estoy seguro de que terminaré con esto esta vez seguro.

A continuación se muestra el flujo que uso regularmente para leer datos de DS1307
1. Enviar INICIO
2. Escribir SLAVE_ADDR + W
3. Obtener ACK del esclavo
4. Escribir REG_ADDR (este es el registro ds1307 desde donde desea leer los datos)
5. Obtener ACK desde el esclavo
6. Envíe REPEATED_START
7. Escriba SLAVE_ADDR + R
8. Obtenga ACK del esclavo
9. Espere DATA
10. Envíe ACK para obtener los siguientes DATA de ds1307 y luego vaya al paso 9
11. Envíe NACK para detener la transferencia
12. Envíe STOP

En su función Read_from_slave(), el paso 6 y el paso 7 se intercambian, es decir, está enviando RTC_ADDRR antes de emitir I2C_restart().

También debe esperar hasta que se complete la transmisión, luego verifique si recibió ACK o NACK según lo sugerido por otros miembros.