Problem bei der Durchführung eines I2C-Lesevorgangs von ds1307 zu pic18F

Wie von meinen Stacker-Kollegen in meinem vorherigen Beitrag empfohlen, habe ich die „Write_to_slave“-Funktion in Proteus ebenfalls in Echtzeit getestet, indem ich Bytes an die RTC gesendet habe und der Slave entsprechend mit einem Bestätigungsbit für jedes empfangene Byte antwortet, daher die Der Schreibvorgang funktioniert einwandfrei. Jetzt habe ich jedoch Probleme mit meiner Funktion "Read_from_slave". Das Problem ist, dass es während der Lesesequenz bis zum Senden der "Slave-Adresse + Lesen" dh 0xD1 funktioniert und auch eine Bestätigung vom Slave erhält, aber das Problem tritt nach der Ausführung der I2C_restart-Funktion auf. Die vom SSPBUF gelesenen oder vom Slave empfangenen Daten sind FF, während es die Sekunden sein sollten, die von 0 bis 60 zählen (und 63 auf dem LCD anzeigen), gefolgt von einem NACK. Und nach dem Senden des NACK, I2C_stop() sollte die Transaktion beenden, während dies nicht geschieht, wie Sie in Proteus Snapshot sehen können. Unten ist der Code, ich habe nur einen Lesevorgang durchgeführt, um das Sekundenregister zu lesen, um zu überprüfen, ob meine Methode funktioniert, aber leider nicht:

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

}
  }

Nur für ein klares Bild der Transaktionen, die auf dem I2C-Bus stattfinden, habe ich eine Momentaufnahme des I2C-Debugger-Fensters beigefügt, das die I2C-Operationen während der Simulation in Proteus anzeigt. Die erste Sequenz ist wie folgt: S = Start, D0 = Slave-Adresse + Schreiben, A = Bestätigung vom Slave, 05 = Registeradresse in der RTC, A = Bestätigung vom Slave, 01 = Daten auf 05H geschrieben, A = Bestätigung vom Slave, P = Stopp. Ähnlich für andere Schreibzyklen und auch Lesen, während beim Lesen „Sr“, dh I2C_restart, gefolgt von „N“ NACK ist, und wie wir sehen können, gibt es kein „P“, dh Stopp nach NACK. Ich habe auch das Bild der Hardware beigefügt. Ich habe den Slave als Sendermodus im DS1307-Datenblatt erneut gelesen, aber ich finde nichts Falsches an der Sequenz "Read_from _slave".

Geben Sie hier die Bildbeschreibung ein

Entschuldigen Sie die langwierige Nachricht, da weitere Ratschläge oder Anweisungen zu meinem Ansatz zur Durchführung des Lesevorgangs oder zur Identifizierung von Problemen mit den Methoden eine große Hilfe wären. Danke noch einmal!

Gruß ~VD

Antworten (3)

Ein sehr häufiger Grund, warum Sie ein 0xFF erhalten, ist, dass die SCL-Leitung des I2C nicht lange genug gehalten wird, damit der Slave Daten übertragen kann. Ich schlage also vor, eine Verzögerung von beispielsweise 10 Mikrosekunden hinzuzufügen, um die Uhr nach der i2c-Lesefunktion zu dehnen und zu prüfen, ob Sie Daten empfangen. Es ist im Grunde eine Trial-and-Error-Methode. Erhöhen Sie einfach die Verzögerung, bis Sie relevante Daten erhalten. Ich hatte das gleiche Problem schon einmal mit einem Atmega und es wurde tatsächlich auf SE gelöscht: 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);

Hoffe, es funktioniert! Und wenn es funktioniert, versuchen Sie, die Verzögerung zu verringern, damit Sie Ihre Werte in möglichst kurzer Zeit erhalten.

Ich bin mir nicht sicher, aber:

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

Anscheinend haben Sie zuerst nach dem ACK gesucht, bevor Sie auf die Bestätigung der erfolgreichen Übertragung gewartet haben. Dies ist auch in Ihrer vorhanden write_to_slave(). Vielleicht könnten Sie versuchen, die Reihenfolge zu ändern, nur für den Fall.

Ich denke das sollte keinen Unterschied machen. Denn während "SSPBUF = addr" (8 Taktzyklen werden zum Senden der Adresse verwendet) und im 9. Taktzyklus wird ACK empfangen, wonach ich überprüfe, ob ich diese ACK vom Slave erhalten habe. Sobald ich die ACK erhalten habe, warte ich, bis der SSPIF hoch geht, setze ihn dann auf Null zurück und verlasse die Schleife. Der SSPIF geht bei der Übertragung des ACK auf High. Aufgrund dieser Sequenz glaube ich, dass ich in jedem Fall zuerst warten kann, bis SSPIF hoch geht, und dann nach dem ACK suchen oder umgekehrt.
Brianho hat Recht. Diese Anweisung wartet nicht auf diese 8 I2C-Taktzyklen - sie lädt einfach Ihr Byte in das SSPBUF-Register im I2C-Peripheriemodul. Das Modul nimmt sich dann Zeit, um die 8 Bits tatsächlich auszutakten und die ACK zu lesen, während die CPU weiterhin Anweisungen ausführt. Der Versuch, die ACK unmittelbar nach dem Laden des Registers zu lesen, funktioniert nicht. Sie müssen zunächst warten, bis die Übertragung abgeschlossen ist.
Danke für eure Antwort Jungs. Ich werde die von Ihnen allen vorgeschlagenen Änderungen vornehmen und mich mit den Ergebnissen zurückmelden. Ich bin mir sicher, dass ich diesmal sicher damit fertig bin.

Unten ist der Fluss, den ich regelmäßig verwende, um Daten von DS1307 zu lesen.
1. Senden Sie START.
2. Schreiben Sie SLAVE_ADDR + W. 3. Holen Sie sich eine Bestätigung
vom Slave von Slave 6. Senden Sie REPEATED_START 7. Schreiben Sie SLAVE_ADDR + R 8. Erhalten Sie ACK von Slave 9. Warten Sie auf DATA 10. Senden Sie ACK, um die nächsten DATA von ds1307 zu erhalten, und gehen Sie dann zu Schritt 9 11. Senden Sie NACK, um die Übertragung zu stoppen 12. Senden Sie STOP









In Ihrer Funktion Read_from_slave() sind step-6 und step-7 vertauscht, dh Sie senden RTC_ADDRR, bevor Sie I2C_restart() ausgeben.

Außerdem müssen Sie warten, bis die Übertragung abgeschlossen ist, und dann prüfen, ob Sie ACK oder NACK erhalten haben, wie von anderen Mitgliedern vorgeschlagen.