1
\$\begingroup\$

As advised by my fellow stackers in my previous post, I tested the "Write_to_slave" function in Proteus as well in real time, by sending bytes to the RTC and the slave is responding appropriately with an acknowledge bit for each byte it receives, therefore the write operation is working fine. However, now I am having issues with my "Read_from_slave" function. The problem is that, during the read sequence, until the sending of the "Slave address + Read" i.e 0xD1, it works fine and also receives an acknowledgement from the slave, but the problem arises after the execution of the I2C_restart function. The data read from the SSPBUF or the data received from the slave is FF while it should be the seconds counting from 0 to 60 (and displays 63 on the LCD), followed by a NACK. And after sending the NACK, the I2C_stop() should end the transaction while that is not happening as you can see in Proteus snapshot . Below is the code, I have performed just one read operation to read the seconds register to check if my method is working, but unfortunately it isn't :

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

}
  }

Just for a clear picture of the transactions happening on the I2C bus, I have attached a snap shot of the I2C Debugger window displaying the I2C operations during the simulation in Proteus. First sequence is as follows: S = start, D0 = Slave address + Write, A = Acknowledgement from slave, 05 = Register address in the RTC, A = Acknowledgement from slave, 01 = data written to 05H, A = Acknowledgement from slave, P = Stop. Similarly for other write cycles and also read, while in read there is "Sr" i.e. I2C_restart followed by "N" NACK and as we can see there is no "P" i.e. stop after NACK. I have also attached the picture of the hardware. I re-read the slave as transmitter mode in DS1307 datasheet, but I do not find anything wrong with the "Read_from _slave" sequence.

enter image description here

Sorry for the tedious message, as any further advise or direction regarding my approach to performing the read operation or identification of any issues with the methods would be of great help. Thanks again!

Regards ~VD

\$\endgroup\$

3 Answers 3

1
\$\begingroup\$

A very common reason to as to why you receive an 0xFF is because the SCL line of the I2C isn't being held long enough for the slave to transmit data. So what I suggest is add a delay of say 10 microseconds to stretch the clock after the i2c read function and check if you're receiving data. It's a trial and error method basically. Just keep increasing the delay till you receive relevant data. I've had the same problem before with an Atmega and it actually got cleared on 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);

Hope it works! And if it works try decreasing the delay so that you get your values in minimum possible time.

\$\endgroup\$
0
2
\$\begingroup\$

I'm not sure but:

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

It seems you checked for the ACK first before waiting to get confirmation of successful transmission. This is also present in your write_to_slave(). Perhaps you could try changing the order, just in case.

\$\endgroup\$
3
  • \$\begingroup\$ I think it shouldn't make any difference. Because, during "SSPBUF = addr" (8 clock cycles are used for sending the address ) and on the 9th clock cycle ACK is received, after which I am checking If I received that ACK from slave. Once I received the ACK, I wait until the SSPIF goes high and then reset it back to zero and go out of the loop. The SSPIF goes high on the transfer of the ACK. Because of this sequence, either way, I believe I can first wait until SSPIF goes high and then check for the ACK or vice versa. \$\endgroup\$
    – PsychedGuy
    Jan 1, 2016 at 2:06
  • \$\begingroup\$ brianho is correct. That instruction does not wait for those 8 I2C clock cycles - it simply loads your byte into the SSPBUF register in the I2C peripheral module. The module then takes its time to actually clocks the 8 bits out & read the ACK while the CPU continues to execute instructions. Trying to read the ACK immediately after loading the register will not work. You must wait for the transfer to complete first. \$\endgroup\$
    – brhans
    Jan 1, 2016 at 4:53
  • \$\begingroup\$ Thanks for your response guys. I'll do the changes as suggested by you all and get back with the results. I am sure l be done with it this time for sure. \$\endgroup\$
    – PsychedGuy
    Jan 2, 2016 at 1:39
0
\$\begingroup\$

Below is the flow which i use regularly to read data from DS1307
1. Send START
2. Write SLAVE_ADDR + W
3. Get ACK from slave
4. Write REG_ADDR (this is ds1307 register from where you want to read data)
5. Get ACK from slave
6. Send REPEATED_START
7. Write SLAVE_ADDR + R
8. Get ACK from slave
9. Wait for DATA
10. Send ACK to get next DATA from ds1307 then goto step-9
11. Send NACK to stop transfer
12. Send STOP

In your function Read_from_slave(), step-6 and step-7 are interchanged i.e you are sending RTC_ADDRR before issuing I2C_restart().

Also you need to wait till Transmit is complete, then check whether you received ACK or NACK as suggested by other members.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.