I am using a Master PIC18F4620, Slave DS1307.
All I am doing is setting the seconds to (0) at the beginning of the program, then I read it.
I can't seem to read correctly, the value that is being returned from the SSPBUF register is the Slave+Writebit Address.
Everything is perfect on HW & Simulation, how is so?
I used an I2C Debugger on Proteus and it is all working fine, I am indeed writing and reading CORRECT values, I then checked using my PicKit3 Debugger HW, and I found out something very interesting.
The SSPBUF DOES transmit and receive, therefore its value changes, but the assignment statement from the SSPBUF to a variable FAILS.
#include "18F4620_Config.h"
#include "I2C.h"
#include "Generic_LCD.h"
#include "stdio.h"
#define _XTAL_FREQ 16e6
#define _RTC_WRITE 0xD0
#define _RTC_READ 0xD1
#define SEC_ADDRESS 0x00 // Address to access Ds1307 SEC register
#define CONTROL 0x07 // Address to access Ds1307 CONTROL register
unsigned char seconds;
char myString[6] = {0};
void main(void)
{
LCD_Init();
LCD_Clear();
LCD_Print("Read:");
I2C_Master_Init();
DS1307_Set_Seconds(35);
while (1)
{
seconds = DS1307_Read_Seconds();
LCD_Goto(6, 1);
sprintf(myString,"%x ",seconds);
LCD_Print(myString);
__delay_ms(1000);
}
}
As for the two used functions.
void DS1307_Set_Seconds(unsigned char secs)
{
if (secs <= 60)
{
unsigned char higher = (secs / 10) << 4;
unsigned char lower = (secs % 10);
unsigned data = higher | lower;
I2C_Start();
I2C_Write(_RTC_WRITE);
I2C_Write(0x00);
I2C_Write(data);
I2C_Stop();
}
}
unsigned char DS1307_Read_Seconds(void)
{
unsigned char sec;
I2C_Start();
I2C_Write(_RTC_WRITE);
I2C_Write(SEC_ADDRESS);
I2C_Restart();
I2C_Write(_RTC_READ);
sec = I2C_Read_Byte();
I2C_NACK();
I2C_Stop();
return (sec);
}
The only thing that is being printed right now is (D1) on the LCD.
I noticed another strange behavior, if instead of assigning SSPBUF value to a variable, I pass the SSPBUF as a parameter to the sprintf function, IT WORKS!.
Like this
while (1)
{
seconds = DS1307_Read_Seconds();
LCD_Goto(6, 1);
sprintf(myString,"%x ",SSPBUF);
LCD_Print(myString);
__delay_ms(1000);
}
Edit 1: Solution
As mentioned in the comments, the solution is to read from the SSPBUF AFTER the I2C Communication has stopped entirely.
Therefore these are the code changes
void I2C_Read_Byte(void)
{
//---[ Receive & Return A Byte ]---
RCEN = 1; // Enable & Start Reception
while (!SSPIF); // Wait Until Completion
SSPIF = 0; // Clear The Interrupt Flag Bit
}
unsigned char DS1307_Read_Seconds(void)
{
I2C_Start();
I2C_Write(_RTC_WRITE);
I2C_Write(SEC_ADDRESS);
I2C_Restart();
I2C_Write(_RTC_READ);
I2C_Read_Byte();
I2C_NACK();
I2C_Stop();
return (SSPBUF);
}
while (1)
{
seconds = DS1307_Read_Seconds();
LCD_Goto(6, 1);
sprintf(myString, "%x ", seconds);
LCD_Print(myString);
__delay_ms(1000);
}
Best Answer
I can't be sure, but I suspect the PIC is latching the address into SSPBUF, and once you read that it will then put the data there. So if you read it a second time, you'll get the data. Note that in your second example, you call DS1307_Read_Seconds(), which tries to read it, and then you read SSPBUF which would be a second read from that register.