I have found over the years that except in speed-critical or multi-master applications, it's actually easier to bit-bang an I2C master than to try to use the I2C facilities built into many chips.
Note that if a device uses clock stretching, any time you release SCK, you must wait for it to actually go high. For simplicity, such delays are omitted from the following descriptions, but should be included if appropriate in your "release_SCK()" routine.
To start an I2C transaction, release SCK (if it isn't already) and, if the data line is low, assert SCK (drive it low), release SDA (if it isn't already), and release the SCK. Repeat this process up to nine times until SDA is high. If SDA is still low after nine repetitions, the bus is unusable.
To output each byte (including the address byte), assert SDA, and then for each bit repeat the sequence (assert SCK; set SDA high or low to match next bit of data; release SCK) eight times. After the last bit, assert SCK, release SDA, and release SCK. If SDA is low, a slave is acknowledging; if SDA is high, no slave is acknowledging and the transaction should be aborted.
When all output is complete, assert SCK, then SDA, and then release SCK, then SDA.
To input each byte, assert SDA, then release SCK if it isn't already (it will be for the first byte, but not others). Then reassert SCK, release SDA, and repeat the sequence (release SCK, read data bit, assert SCK) eight times. Note that at the end of this sequence, unlike when outputting a byte, SCK will be left asserted.
When all input is complete, release SDA (it should already already be released) and SCK.
Note that because the clock is left asserted after inputting each byte, it's not necessary to specify whether the byte should be ack'ed or nak'ed. If you read another byte, the last byte read will be nak'ed. If you terminate the read, it will be nak'ed.
Start; send address; write one byte, finish
SCK - -__-__-__-__-__-__-__-__-__-- -__-__-__-__-__-__-__-__-__--- -__--
SDA(M) - __777666555444333222111___--- --777666555444333222111000---- --__-
SDA(S) - -------------------------??AA A------------------------??AAA A----
Start; send address; read two bytes; finish
SCK - -__-__-__-__-__-__-__-__-__--- -__--_--_--_--_--_--_--_--__ -__--_--_--_--_--_--_--_--__ _-
SDA(M) - __777666555444333222111------- __-------------------------- __-------------------------- --
SDA(S) - -------------------------??AAA A??77?66?55?44?33?22?11?00?? -??77?66?55?44?33?22?11?00?? ?-
Your code looks fine. However writing driver on your own can be time consuming and is prone to errors. Microchip supplies basic libraries and I2C EEPROM as well.
Are you sure both lines have pull-ups ( resistors to 3V ) ? Without this no bits will be generated by PIC.
Here is an example for working I2C EEPROM driver which can use both plib or manual hardware I2C on PIC32 or PIC16/18(HW_CFG_SENSOR).
void i2cSetupMaster(unsigned int BaudRate)
{
#ifdef I2C_USE_PLIB
#ifndef HW_CFG_SENSOR
I2CEnable(I2C_MOD,0);
I2CConfigure(I2C_MOD,0);
I2CSetFrequency(I2C_MOD,GetPeripheralClock(), BaudRate);
I2CEnable(I2C_MOD,1);
#else
ANSELC = 0;
TRISC3 = 1;
TRISC4 = 1;
SSP1ADD = 39; // 39 @ 16Mhz = 100khz SCL pin clock period = ((ADD<7:0> + 1) *4)/FOSC
OpenI2C1(MASTER,SLEW_OFF);
#endif
#else
I2C_CONbits.ON = 0;
idleI2C();
//BRG
// I2C_BRG = BaudRate; // 0x0C2; //( (sourceClock/i2cClock)/2 ) - 2; // 100Khz at 40Mhz PBCLK
// I2C3ADD = 0x09;//( (_XTAL_FREQ/100000) /4 )-1;
I2CConfigure(I2C_MOD, I2C_ENABLE_SMB_SUPPORT); //I2C_ENABLE_SMB_SUPPORT
I2CSetFrequency(I2C_MOD, GetPeripheralClock(), BaudRate);
idleI2C();
//I2C_CONbits.ACKDT = 0; //send ACK on recieved byte
I2C_CONbits.STRICT = 0;
I2C_CONbits.SMEN = 1; //SMB bus compliant
//enable, master, no collision
I2C_CONbits.DISSLW = 1; //Slew rate control disabled for Standard Speed mode (100 kHz)
I2C_STATbits.I2COV = 0;
I2C_CONbits.ON = 1;
DelayMs(2);
#endif
}
bool i2cSendByte(unsigned char byte)
{
#ifdef I2C_USE_PLIB
#ifndef HW_CFG_SENSOR
if (I2CTransmitterIsReady(I2C_MOD))
{
return I2CSendByte( I2C_MOD, byte );
}
#else
putcI2C(byte);
while(SSPSTATbits.R_NOT_W){}
return 1;
#endif
#else
I2C_TRN = byte;
int timeout = 0;
msspWait(); //wait until byte is latched
while ( I2C_STATbits.TRSTAT == 1) {
timeout++;
if ( timeout > MAX_WAIT_CYCLES) {
return 0x00;
}
};
if ( msspOVF() || msspBWcol() ) { return 0; } //send failed
else { return 1; } //success
#endif
}
/********** Write fixed length in page portions *****************/
void seqWriteEE (byte deviceAddr, word Addr, word bytes, byte* buff)
{
#ifdef MEM_I2C_HARDWARE
i2cSetupMaster( I2C_BRG_100kHz ); //concurrency enable
/*If the master should transmit more than 64 bytes prior to
generating the Stop condition, the address counter will
roll over and the previously received data will be overwritten.*/
CurrentDeviceAddr = deviceAddr;
int pages = (int)(bytes / i2cMemPageSize);
int singleBytes = (int) (bytes % i2cMemPageSize);
word writeAddr = Addr;
int pageInc = 0;
if ( bytes >= i2cMemPageSize)
{
word BufferPos;
for (pageInc = 0; pageInc < pages; pageInc++)
{
writeAddr += pageInc > 0 ? i2cMemPageSize :0 ;
BufferPos = pageInc*i2cMemPageSize;
WriteSectorBytesAtAddr(writeAddr,&buff[BufferPos],i2cMemPageSize);
}
if (singleBytes > 0){
BufferPos = pages*i2cMemPageSize;
writeAddr += i2cMemPageSize;
WriteSectorBytesAtAddr(writeAddr,&buff[BufferPos],singleBytes);
}
}
else
{
WriteSectorBytesAtAddr(Addr,buff,bytes);
}
#else
i2cSetupMaster( I2C_BRG_100kHz ); //concurrency enable
/*If the master should transmit more than 64 bytes prior to
generating the Stop condition, the address counter will
roll over and the previously received data will be overwritten.*/
CurrentDeviceAddr = deviceAddr;
int pages = (int)(bytes / i2cMemPageSize);
int singleBytes = (int) (bytes % i2cMemPageSize);
word writeAddr = Addr;
int pageInc = 0;
if ( bytes >= i2cMemPageSize)
{
word BufferPos;
for (pageInc = 0; pageInc < pages; pageInc++)
{
writeAddr += pageInc > 0 ? i2cMemPageSize :0 ;
BufferPos = pageInc*i2cMemPageSize;
WriteSectorBytesAtAddr(writeAddr,&buff[BufferPos],i2cMemPageSize);
}
if (singleBytes > 0){
BufferPos = pages*i2cMemPageSize;
writeAddr += i2cMemPageSize;
WriteSectorBytesAtAddr(writeAddr,&buff[BufferPos],singleBytes);
}
}
else
{
WriteSectorBytesAtAddr(Addr,buff,bytes);
}
#endif
}
void WriteSectorBytesAtAddr(word Addr, byte* buff, word bytes){
if ( IsWriteOnSectorBoundries(Addr,bytes) ){
word buffPos;
word SecEndAddr = GetSectorEndForWriteAddr(Addr);
word SecBytesToEnd = SecEndAddr-Addr;
int binc = 0;
StartWriteAtAddr(Addr);
for(binc=0; binc< SecBytesToEnd; binc++)
{
i2cSendByte(buff[binc]);
}
FinishSectorWrite();
StartWriteAtAddr(Addr+SecBytesToEnd);
word BytesToEnd = bytes - SecBytesToEnd;
buffPos = SecBytesToEnd;
for(binc=0; binc< BytesToEnd; binc++)
{
i2cSendByte(buff[buffPos+binc]);
}
FinishSectorWrite();
}else{
StartWriteAtAddr(Addr);
int binc = 0;
for(binc=0; binc<bytes; binc++)
{
i2cSendByte(buff[binc]);
}
FinishSectorWrite();
}
}
However you will not get far without even most basic DSO. There's just too many things that can go wrong.
Best Answer
So when you set second, make sure bit 7 (CH) is 0. AND set bit 7 (CH) to zero every time the IC is powering up.
I worked this RTC with ATMega128 and CodeVisionAVR This code works: