PIC18F4550 I2C Connection to Real Time Clock

Pull-up resistors on the I2C buss are present so the default value for data on the buss is a 1. A forced pull-down or low is what the PIC must provide.
Real Time Clock Schematic

// Define I2C Port.
// Bits will toggle between tristate and 0.
// The 1 is provided by the pull up.
#define SCL TRISBbits.TRISB1 // pin 23 i2c clock
#define SDA TRISBbits.TRISB4 // pin 16 i2c data
#define SCL_lo LATBbits.LATB1 // will be set low permanently for this application
#define SDA_lo LATBbits.LATB4 // will be set low permanently for this application
#define SCL_in PORTBbits.RB1 // for reading i2c port bits
#define SDA_in PORTBbits.RB4 // for reading i2c port bits



// Global Variables
// i2c clock 100 KHz (10us period)
int setup = 3; // 3 us
int valid = 5; // 5 us
int hold = 2; // 2 us


A data structure is needed to make it easy to assign a byte value yet also access each bit individually.

union s2p // serial or parallel access structure
{
unsigned char byte;
struct {
unsigned b0:1;
unsigned b1:1;
unsigned b2:1;
unsigned b3:1;
unsigned b4:1;
unsigned b5:1;
unsigned b6:1;
unsigned b7:1;
};
}s2p;



union sa // slave address of the Real Time Clock Chip
{
unsigned char byte;
struct {
unsigned b0:1;
unsigned b1:1;
unsigned b2:1;
unsigned b3:1;
unsigned b4:1;
unsigned b5:1;
unsigned b6:1;
unsigned b7:1;
};
} sa;



union sr // slave register in the RTC
{
unsigned char byte;
struct {
unsigned b0:1;
unsigned b1:1;
unsigned b2:1;
unsigned b3:1;
unsigned b4:1;
unsigned b5:1;
unsigned b6:1;
unsigned b7:1;
};
} sr;



union sd // slave data for bidirectional exchange with the RTC
{
unsigned char byte;
struct {
unsigned b0:1;
unsigned b1:1;
unsigned b2:1;
unsigned b3:1;
unsigned b4:1;
unsigned b5:1;
unsigned b6:1;
unsigned b7:1;
};
} sd;



void i2c_start(void) // @ 10 us
{
SDA = 1; SCL = 1; wait0_us(setup); SDA = 0; wait0_us(valid); SCL = 0; wait0_us(hold);
}



void i2c_stop(void) // @ 7.5 us
{
SDA = 0; SCL = 1; wait0_us(setup); SDA = 1; wait0_us(valid);
}



void i2c_read1(void)
{
LCD_byte(sa.byte);
}



unsigned char i2c_read(union s2p a, union s2p r)
{ // read cycle = 4 * 9 clocks + 2 * start + stop = @ 400 us
// clock = 100 kHz, clock period = 10 us (+/-)
unsigned char ack;
union s2p d;
i2c_start();
// Send the write mode slave address, D0.
SDA = a.b7; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = a.b6; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = a.b5; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = a.b4; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = a.b3; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = a.b2; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = a.b1; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = a.b0; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = 1; wait0_us(setup); SCL = 1; // acknowledge bit
wait0_us(valid/2); ack = SDA_in; wait0_us(valid/2); // read in the middle of the clock
SCL = 0; wait0_us(hold);
// Send the register number to read.
SDA = r.b7; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = r.b6; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = r.b5; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = r.b4; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = r.b3; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = r.b2; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = r.b1; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = r.b0; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
// Receive acknowledge bit from slave.
SDA = 1; wait0_us(setup); SCL = 1;
wait0_us(valid/2); ack = SDA_in; wait0_us(valid/2); // read in the middle of the clock
SCL = 0; wait0_us(hold);
i2c_start();
// Send the read mode slave address, (sa.byte | 0x01)
SDA = a.b7; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = a.b6; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = a.b5; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = a.b4; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = a.b3; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = a.b2; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = a.b1; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = 1 ; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold); // LSB = 1 for Read Mode
// acknowledge bit from slave
SDA = 1; wait0_us(setup); SCL = 1; // SDA is tristated
wait0_us(valid/2); ack = SDA_in; wait0_us(valid/2); // read in the middle of the clock
SCL = 0; wait0_us(hold);
// Accept the data into the dat variable
wait0_us(setup); SCL = 1; wait0_us(valid/2); // SDA remains tristated
d.b7 = SDA_in;
wait0_us(valid/2); SCL = 0; wait0_us(hold);
wait0_us(setup); SCL = 1; wait0_us(valid/2);
d.b6 = SDA_in;
wait0_us(valid/2); SCL = 0; wait0_us(hold);
wait0_us(setup); SCL = 1; wait0_us(valid/2);
d.b5 = SDA_in;
wait0_us(valid/2); SCL = 0; wait0_us(hold);
wait0_us(setup); SCL = 1; wait0_us(valid/2);
d.b4 = SDA_in;
wait0_us(valid/2); SCL = 0; wait0_us(hold);
wait0_us(setup); SCL = 1; wait0_us(valid/2);
d.b3 = SDA_in;
wait0_us(valid/2); SCL = 0; wait0_us(hold);
wait0_us(setup); SCL = 1; wait0_us(valid/2);
d.b2 = SDA_in;
wait0_us(valid/2); SCL = 0; wait0_us(hold);
wait0_us(setup); SCL = 1; wait0_us(valid/2);
d.b1 = SDA_in;
wait0_us(valid/2); SCL = 0; wait0_us(hold);
wait0_us(setup); SCL = 1; wait0_us(valid/2);
d.b0 = SDA_in;
wait0_us(valid/2); SCL = 0; wait0_us(hold);
// acknowledge bit from master - will be high if this is the last byte
SDA = 1; wait0_us(setup); SCL = 1; wait0_us(valid/2);
ack = SDA_in;
wait0_us(valid/2); SCL = 0; wait0_us(hold);
i2c_stop();
return d.byte;
}



void i2c_write(void)
{ // read cycle = 3*9 clocks + start + stop = @ 300 us
unsigned char ack;
i2c_start();
// Send the write mode slave address
SDA = sa.b7; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sa.b6; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sa.b5; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sa.b4; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sa.b3; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sa.b2; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sa.b1; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sa.b0; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = 1; wait0_us(setup); SCL = 1; // acknowledge bit
wait0_us(valid/2); ack = SDA_in; wait0_us(valid/2); // read in the middle of the clock
SCL = 0; wait0_us(hold);
// Send the register number to write to.
SDA = sr.b7; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sr.b6; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sr.b5; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sr.b4; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sr.b3; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sr.b2; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sr.b1; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sr.b0; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
// Receive acknowledge bit from slave.
SDA = 1; wait0_us(setup); SCL = 1;
wait0_us(valid/2); ack = SDA_in; wait0_us(valid/2); // read in the middle of the clock
SCL = 0; wait0_us(hold);
// write the data.
SDA = sd.b7; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sd.b6; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sd.b5; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sd.b4; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sd.b3; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sd.b2; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sd.b1; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
SDA = sd.b0; wait0_us(setup); SCL = 1; wait0_us(valid); SCL = 0; wait0_us(hold);
// Receive acknowledge bit from slave.
SDA = 1; wait0_us(setup); SCL = 1;
wait0_us(valid/2); ack = SDA_in; wait0_us(valid/2); // read in the middle of the clock
SCL = 0; wait0_us(hold);
i2c_stop();
}