Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Problem with PIC16F874A I2C communication with a 24LC512 EEPROM

Status
Not open for further replies.

vero

New Member
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;    
}
 

Attachments

  • I2C_schematic.pdf
    86.7 KB · Views: 173
I do not understand
1) EEPROM power pin 8 is connected to SDA pin-5?
2) What voltage for U1-32 VDD? It looks like 3.3V.
3) What voltage for U2-8? It looks like 3.3V.
I have questions about your voltage dividers but I need to know the supply voltage(s) first.

119681
 
Both SDA and SCK need pullups. You appear to have a pulldown on SDA.

Mike.
 
Everything seems to be running at 3.3V via D1, so I dont see the need for anything but pullups.

Mike.
Edit, sorry, misread your post.
 
It's all rather bizarre? - not mentioned so far is the lack of proper connections to pins 11 and 12, it's important that ALL Vss and Vdd pins are connected. But there's so much on that circuit stopping it ever working it's hard to know where to start?.
 
The points marked 3.3V are connected together. VDD on the PIC is 5V and I have a voltage divider to get 3.3V to the EEPROM. The voltage divider on the SDA and SCL are making lines pull down, then I will remove the voltage divider. The EEPROM can run between 2.5V and 5V.
 
I didn't answer these questions.

I do not understand
1) EEPROM power pin 8 is connected to SDA pin-5?
2) What voltage for U1-32 VDD? It looks like 3.3V.
3) What voltage for U2-8? It looks like 3.3V.
I have questions about your voltage dividers but I need to know the supply voltage(s) first.

Answers:
1) that is a mistake on the schematic, sorry. Pin 8 isn't connected to SDA pin-5.
2) VDD is 5V
3) Yes, 3.3V
 
That means that SDA and SCK are both directly connected to 3.3V. Plus the pic appears to be running at 3.3V due to the resistor/zener regulator.

That just can't work.

Mike.
 
I made mistakes on the schematic. I think I fixed the errors and I re-posted the schematic.
 

Attachments

  • I2C_schematic.pdf
    86.7 KB · Views: 172
I have answers.
The schematic is drawn wrong. So I will go with the words.
Pictured below. The point "uComputer" is a pin on the computer. The point "EEPROM" is a pin on the memory.
Computer is powered with 5V and its outputs are low=0V, high=5V, or tristate when they are an input.
When the computer is driving a pin the divider works. H=3.2V L=0.5V which is ok. Assuming the computer drives the pin high. Actually a high should be made by tristate-ing the pin and allowing the pull up to bring the pin high. When this happens the pin goes to 1V, because the divider is not right.
When the memory is sending data out we have a big problem. L=0V but H=1V.
119704

Many people write the software so the computer drives the pin high. The I2C bus was built for the only pull up is a resistor and no silicon pulls up.
The EEPROM does not pull up. A pull up resistor pulls up. 5K to 3.3V and 2.2k to ground makes about 1 volt.

I would do 1) or 2):
1) Make all ICs run on the same power supply and remove the voltage divider. Keep only the pull up resistors.
2) Fix the software so the computer does not pull up to 5V but turns off the port and let the pull up resistors pull up to 3.3V. (remove the divider)

and read the I2C bus specifications.
I drew a red circle around the pull down transistor in each IC. There is no pull up transistor.
119707
 
Last edited:
Thank you everyone for all the help and being patient with the mistakes. I will try your 1) recommendation. I don't understand how to do 2). I will research how to do that one if needed. I'll also read the 12C bus specifications. I will update when I have the results of what you have suggested. Thanks again.
 
Assuming the OP is using hardware I²C then it should never drive the lines high just make them high impedance.

From what has been said so far, 3.3V or 5V will work with all components.

Mike.
 
I made mistakes on the schematic. I think I fixed the errors and I re-posted the schematic.


You've still not addressed pins 11 and 12, and what's with the crazy I2C connections - either run the EEPROM at 5V, or run the PIC at 3.3V - don't try to run them at different voltages - and why would you?.
 
I had a voltage divider because I previously was communicating with an SRAM chip using SPI and the SRAM chip only ran on 3.3V. I removed the voltage dividers on SDA and SCL. I posted the schematic with the changes. Pin 12 now is shown connected to ground. Pin 11 is connected to the pickit3 and a 4.7K resistor. I am now getting a signal on SCL, after removing the voltage divider. When I run the program it gets stuck waiting for the slave to acknowledge in the w_I2C() function. I'm not sure what is wrong with the code.
 

Attachments

  • I2C_schematic.pdf
    85.8 KB · Views: 157
I don't have time to go through the software;
Is the address correct? 000
You say SCL is working. Does SDA have information on it? I know it hangs up at ACK but is there information just before.

119846
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top