I am having trouble sending data to and from a 24LC512 EEPROM using the I2C bus on a PIC16F874A microcontroller. The PIC is the master and the EEPROM is the slave. I'm not sure if it is my code or the circuit. I have a 5K pull-up resistor on SDA and SCL. It seems to be stuck in a loop in the I2C_write() function, wating for an ACK from the slave device. I'm not sure what I am doing incorrectly. I tried to see the signal on SDA and SCL with a scope and I can't pick up anything. I'm using 400khz SCL and 3.3V to the EEPROM, with a voltage divider design and a zener diode. I would appreciate any help I can get for this problem. Thanks in advance.
Code:
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "Configuration.h"
#define _XTAL_FREQ 16000000
void init_I2C();
void init_uart1(const long int baudRate);
bit w_I2C(unsigned char d);
unsigned char r_I2C(unsigned char d);
void I2C_idle();
void strt_I2C();
void i2c_restart(void);
void i2c_ack(void);
void i2c_nack(void);
void stp_I2C();
void write_uart(unsigned char *data);
unsigned char read_uart();
void main(void) {
unsigned char data = 'A';
init_uart1(9600);
init_I2C();
write_uart("hello\r\n");
__delay_ms(100);
write_uart("start...\r\n");
__delay_ms(100);
I2C_idle(); //Check if I2C bus is busy
strt_I2C(); //Send start bit
__delay_ms(50);
w_I2C(0xA0); //Send address of slave and r/w bit
__delay_ms(50);
w_I2C(0x00); //Send MSB memory address of slave to write to
__delay_ms(50);
w_I2C(0x01); //Send LSB memory address of slave to write to
__delay_ms(50);
w_I2C(0x41); //Send data to memory of slave, wait for Ack
__delay_ms(50);
stp_I2C();
__delay_ms(100);
//Read Randomly from slave EEPROM
I2C_idle(); //Check if I2C bus is busy
strt_I2C(); //Send Start bit to initiate read from slave EEPROM
__delay_ms(20);
w_I2C(0xA0); //Send slave address(control byte) =
//'control code' + 'slave address:A2+A1+A0' + 'R/W' =
//1010' + '000' + '0'
__delay_ms(50);
w_I2C(0x00); //LSB of slave memory address
__delay_ms(50);
w_I2C(0x01); //MSB of slave memory address
__delay_ms(50);
i2c_restart(); //Resend start bit
__delay_ms(50);
w_I2C(0xA1); ////Send slave address =
//'control code' + 'slave address:A2+A1+A0' + 'R/W' =
//1010' + '000' + '1'
__delay_ms(50);
data = r_I2C(0); //read data from slave with a dummy value to SSPBUF
__delay_ms(50);
data = r_I2C(0); //read data from slave with a dummy value to SSPBUF
__delay_ms(50);
i2c_nack(); //Send NACK after a read cycle
__delay_ms(50);
stp_I2C(); //Send stop bit to slave for end of read cycle
__delay_ms(50);
write_uart(data);
return;
}
void init_I2C()
{
TRISCbits.TRISC3 = 1; //Set SDA as input
TRISCbits.TRISC4 = 1; //Set SCL as input
SSPSTATbits.SMP = 1; //Slew rate disabled
SSPSTATbits.CKE = 1; //Enable SMbus
SSPCONbits.SSPM3 = 1; //Set PIC for master mode
SSPCONbits.SSPM2 = 0;
SSPCONbits.SSPM1 = 0;
SSPCONbits.SSPM0 = 0;
SSPCON2bits.RCEN = 1;
SSPEN = 1; //Enable SDA and SCL pins
SSPADD = 0x09; //400khz for SCL
}
void init_uart1(const long int baudRate)
{
unsigned int x=0;
RCSTAbits.SPEN = 1; //Serial port enabled
RCSTAbits.RX9 = 0; //Eight bit reception
RCSTAbits.CREN = 1; //Continuous receive enable bit
RCSTAbits.ADDEN = 0; //Disable address detection
TXSTAbits.TXEN = 1; //Transmit enable
TXSTAbits.TX9 = 0; //Eight bit transmission
TXSTAbits.BRGH = 1; //High speed transfer rate baud rate >= 9600
TXSTAbits.SYNC = 0; //Asynchronous mode
TRISCbits.TRISC6 = 1; //TX set to output
TRISCbits.TRISC7 = 1; //RX set to input
RCIE = 1; //enable receive interrupt
GIE = 1;
PEIE = 1;
x = (_XTAL_FREQ/(16*baudRate))-1;
SPBRG = x;
}
void strt_I2C()
{
SSPCON2bits.SEN = 1; //Send start bit
while(!SSPIF) //Wait for start condition to finish
write_uart("SSPIF not setting after STRT\r\n");
SSPIF = 0; //Clear FLAG
}
void i2c_restart(void)
{
RSEN=1; //Initiate restart condition
while(!SSPIF) //Wait till completion of event
write_uart("Wait for SSPIF after RSTRT \r\n");
SSPIF = 0;
}
void I2C_idle()
{
while ( ( SSPCON2 & 0x1F ) || ( SSPSTAT & 0x04 ) )
write_uart("I2C NOT IDLE \r\n");
}
bit w_I2C(unsigned char d)
{
SSPBUF;
L1:if(WCOL)
WCOL = 0;
SSPBUF = d; //Write data to send to slave
while(!SSPIF);
while(SSPCON2bits.ACKSTAT) //Wait for data to send
{
write_uart("Wait for slave ACK \r\n");
i2c_restart();
__delay_ms(100);
goto L1;
}
SSPCON2bits.ACKSTAT = 0;
// write_uart("SSPIF LOW\r\n");
SSPIF = 0; //Clear Flag
return SSPCON2bits.ACKSTAT;
}
unsigned char r_I2C(unsigned char d)
{
SSPBUF = d;
RCEN = 1; //Enable master to receive
while(!BF) //Wait for byte to be received
write_uart("Wait for BF \r\n");
SSPIF = 0; //Clear Flag
RCEN = 0;
//while(!SSPSTATbits.BF); //Wait for SSPBUF to be full
//SSPIF = 0; //Clear Flag
//SSPCON2bits.ACKEN = 1; //Send slave receive acknowledge
return SSPBUF;
}
void i2c_ack(void)
{
ACKDT=0; //Set as acknowledgment
ACKEN=1; //Initiate acknowledgment signal
while(!SSPIF) //Wait till completion of event
write_uart("Wait for SSPIF after ACK \r\n");
SSPIF = 0;
}
void i2c_nack(void)
{
ACKDT=1; //Set as negative acknowledgment
ACKEN=1; //Initiate negative acknowledgment signal
while(!SSPIF) //Wait till completion of event
write_uart("Wait for SSPIF after NACK \r\n");
SSPIF = 0;
}
void stp_I2C()
{
PEN = 1;
while(!SSPIF)
write_uart("Wait for SSPIF after STP \r\n");
SSPIF = 0;
}
void write_uart(unsigned char *data)
{
int len=0,i = 0;
len = strlen(data);
//if(TXIF) //Check TX interrupt, if set TXREG is empty
//{
while(i < (len)){
while(!TXIF);
TXREG = data[i];
i++;
}
//}
}
unsigned char read_uart()
{
if(RCSTAbits.OERR || RCSTAbits.FERR) //Check for overrun or frame error
{
RCSTAbits.CREN = 0; //Reset CREN if either error occurs
RCSTAbits.CREN = 1;
}
while(!RCIF); //Stay here till a byte is received
return RCREG;
}