PIC16F873 talking to an I2C EEPROM

Have you got the greatest 48 bit multiplier ever conceived? Prove it - post your code here.

Moderator: phalanx

Post Reply
phekno
Posts: 8
Joined: Thu Jan 20, 2011 2:46 pm
Location: Minneapolis, MN
Contact:

PIC16F873 talking to an I2C EEPROM

Post by phekno » Thu Jan 20, 2011 2:55 pm

I have a PIC16F873 which I've connected to a Microchip 24LC256 (same as the one sold on SparkFun) and I'm trying to figure out how to get the PIC to write some sequential values in to the EEPROM. According to the EEPROM datasheet, I'm supposed to have a 10k pullup resistor on the SDA line, but it doesn't say anything about the SCL line, so I've left that out. Other than that, A0 through A2 (the chip select lines) are pulled low as is write protect. My code basically writes 0x00 - 0x64 to addresses 0x00 - 0x64 on the EEPROM and then it sits there and blinks an LED. When I power the circuit off, remove the EEPROM and read it using a programmer, using PICKIT2, it shows 0x00 for all addresses in the EEPROM. Below is my code. I think I'm missing something, but I can't seem to figure out what. Any ideas?

main.c

Code: Select all

#include <htc.h>

#include "i2c.h"
#include "eeprom.h"

#define _XTAL_FREQ 20000000

__CONFIG(HS & WDTDIS & PWRTDIS & BORDIS & LVPDIS & WRTDIS & UNPROTECT);

void main(void)
{
	ADCON1 = 0b00000110;		//No analog IO
	TRISA = 0x00;
	TRISB = 0x00;
	TRISC = 0x00;
	
	RB1 = 0x00;
	
	i2c_init();
	
	unsigned char data = 0;
	unsigned int i = 0;
	while(i<=100)
	{
		data = (unsigned char)i;
		i2c_eeprom_write(i, data);
		i++;
	}
	
	while(1)
	{
		RB1 = 0xFF;
		__delay_ms(250);
		RB1 = 0x00;
		__delay_ms(250);
	}
}		
eeprom.c

Code: Select all

#include "eeprom.h"
#include "i2c.h"

void i2c_eeprom_write(unsigned int address, unsigned char data)
{
	unsigned char address_high;
	unsigned char address_low;
	
	address_high = (unsigned char)(address >> 8);
	address_low = (unsigned char)(address);
	
	i2c_idle();
	i2c_start();
	i2c_write(0xA0);
	i2c_write(address_high);
	i2c_write(address_low);
	i2c_write(data);
	i2c_stop();
}

unsigned char i2c_eeprom_read(unsigned int address)
{
	unsigned char address_high;
	unsigned char address_low;
	unsigned char data;
	
	address_high = (unsigned char)(address >> 8);
	address_low = (unsigned char)(address);
	
	i2c_idle();
	i2c_start();
	i2c_write(0xA0);
	i2c_write(address_high);
	i2c_write(address_low);
	i2c_repstart();
	i2c_write(0xA1);
	data = i2c_read(0);
	i2c_stop();
	
	return data;
}	
i2c.c

Code: Select all

#include <htc.h>
#include "i2c.h"

/*	I2C Initialization */
void i2c_init(void)
{
	TRISC3 = 1;		//	SCL
	TRISC4 = 1;		//	SDA
	SSPADD = 0x31;	//	FOSC is 20MHz, I2C freq is 100kHz, so SSPADD is 49
	SSPSTAT = 0b10000000;	//Disable slew rate for 100kHz speed
	SSPCON2 = 0b00000000;	//Clear flags
	SSPCON = 0b00101000;	//Master mode, enable I2C
}

/*	Wait for the I2C bus to go idle	*/
void i2c_idle(void)
{
	while( (SSPCON2 & 0x1F) | RW );
}

/*	Send I2C start condition */
void i2c_start(void)
{
	SEN = 1;			//enable start
	while( SEN );		//wait until hardware clears the condition
}

/*	Send I2C repeated start condition */
void i2c_repstart(void)
{
	RSEN = 1;			//enable repeated start condition
	while( RSEN );		//wait until hardware clears the condition
}

/*	Send I2C stop condition	*/
void i2c_stop(void)
{
	PEN = 1;			//enable stop condition
	while( PEN );		//wait until hardware clears the condition
}

/*	Write a byte to the I2C bus */
unsigned char i2c_write(unsigned char data)
{
	i2c_idle();
	SSPBUF = data;			//put the passed in data in to the buffer
	while( BF );			//wait until the buffer is empty
	return( !ACKSTAT );		//return 1 if the slave acknowledged
}

unsigned char i2c_read(unsigned char ack)
{
	unsigned char data;
	
	i2c_idle();
	RCEN = 1;			//Enable I2C receive
	while( RCEN );		//Wait for the receive to be finished
	data = SSPBUF;		//put the now full buffer in to 'data'
	ACKDT = ~ack;		//Set ack bit
	ACKEN = 1;			//Send acknowledge
	while( ACKEN );		//Wait for the ack to be finished
	return data;		//Return the data read from the bus
}	

waltr
Support Volunteer
Posts: 2823
Joined: Tue Sep 08, 2009 12:07 pm
Location: Philadelphia, USA

Re: PIC16F873 talking to an I2C EEPROM

Post by waltr » Thu Jan 20, 2011 3:46 pm

You do need pull-ups on both I2C lines.

Have you looked at the app notes from Microchip? They do have several on I2C with code. I recommend:
AN735 for starting
AN989 and AN991 are written for the 18F PIC but has good info and scope pictures of I2C bus during operation.
Microchip also has an I2C tutorial that is worth reading if you have never used I2C before.

I also posted HiTech C code with I2C functions in this forum (Code Snippets). This used a PIC16F873 and has I2C routines for EEPROMS and the HMC6352 compass chip. These have been tested and work, so you can compare your code.

phekno
Posts: 8
Joined: Thu Jan 20, 2011 2:46 pm
Location: Minneapolis, MN
Contact:

Re: PIC16F873 talking to an I2C EEPROM

Post by phekno » Thu Jan 20, 2011 9:04 pm

Thanks. I checked out those application notes. They were helpful.

I also checked out your previously posted code and adapted it for my uses.

None of it, however, seems to have fixed the situation, and that is, It seems like it's writing to the EEPROM, but the only real way I have to verify that it's writing, is to power down the circuit, remove the EEPROM and try to read it using my programmer. Every time I read it, it's all 0x00. I guess I'm going to have to hook up my LCD and try to display the data on there. Is it possible that I got a bad chip?

waltr
Support Volunteer
Posts: 2823
Joined: Tue Sep 08, 2009 12:07 pm
Location: Philadelphia, USA

Re: PIC16F873 talking to an I2C EEPROM

Post by waltr » Fri Jan 21, 2011 8:01 am

I guess you don't have an O'scope. This is what is really needed to check on the I2C signals.
Can you borrow one?

The first signal to verify is the ACK from the EEPROM on receiving the I2C address.

Common problems and hints:
Re-check the EEPROM's I2C address and R/W bit. The 7 bit address is in bit 7-1.
Bit 0 is 0 to write and 1 to read.
Are you using the correct I2C address for the EEPROM?
What are the extenal address pins set for?

Add software delays of a few msec between each I2C function call can some times help.
Try a slow clock speed. The transfer is based on the clock so that really isn't a minimum clock speed. Try 50,000 Hz.

good luck

waltr
Support Volunteer
Posts: 2823
Joined: Tue Sep 08, 2009 12:07 pm
Location: Philadelphia, USA

Re: PIC16F873 talking to an I2C EEPROM

Post by waltr » Fri Jan 21, 2011 10:13 am

Did you add the pull-up resistor to the SCK line?
Pull-ups can be 4.7k to 10k.

Do you have a PICKit2 programming tool?
If yes then there is a logic analyzer feature in the stand along PICKit2 utility. The I2C clock will need to be slowed down but this is helpful if you don't have a scope.

phekno
Posts: 8
Joined: Thu Jan 20, 2011 2:46 pm
Location: Minneapolis, MN
Contact:

Re: PIC16F873 talking to an I2C EEPROM

Post by phekno » Fri Jan 21, 2011 11:47 am

Yeah, I had pull-ups in there originally before I asked the question, I just needed some clarification. I am using 4.7k pull-ups now. I was using 10k.

I don't have an exact PICKIT2, I have the CanaKit UK1300, which is supposedly 100% compatible with the PICKIT2. I may try to borrow a scope from work to see what's happening.

One other thing I've tried is running my code in a PIC simulator (I think by Oshonsoft, or something). The one thing I notice, is that it seems like its not sending any clock signals.

I may try with my PIC18F4550 tonight. At least HI-TECH has an I2C library for that family of processor.

Edit: I just took a look at the logic analyzer tool that's included with PICKIT2. It looks exactly like what I need. I'll give that a shot. Thanks!!!

phekno
Posts: 8
Joined: Thu Jan 20, 2011 2:46 pm
Location: Minneapolis, MN
Contact:

Re: PIC16F873 talking to an I2C EEPROM

Post by phekno » Sat Jan 22, 2011 8:06 am

Well, I checked the SDA and SCL lines and it seems like they're asserting a start condition, but then they just sit there and nothing else happens. I feel like I'm really missing something here, but I can't seem to figure out what it is. Maybe I have a bad EEPROM.

waltr
Support Volunteer
Posts: 2823
Joined: Tue Sep 08, 2009 12:07 pm
Location: Philadelphia, USA

Re: PIC16F873 talking to an I2C EEPROM

Post by waltr » Sat Jan 22, 2011 10:07 am

After the 'start' condition the PIC code should be sending the first byte, the EEPROM address.
You can step through the PIC code in the MPLAB simulator to ensure that this byte is being written to the I2C output buffer register SSPBUF.

If you suspect a bad EEPROM remove the EEPROM and run the PIC code while looking at the SCK & SDA lines.
You should see the 'start' and then the EEPROM address byte.

In the code you posted:

Code: Select all

   unsigned char data = 0;
   unsigned int i = 0;
   while(i<=100)
   {
      data = (unsigned char)i;
      i2c_eeprom_write(i, data);
      i++;
   }
you set 'data' to 0x00 which is what you are reading from the EEPROM reader. Try setting 'data' to 0x55 and compliment the data every time through the loop. This will get an alternating pattern of 0x55, 0xAA which will make it easier to determine if the EEPROM was written.
data ~= data;

Also the EEPROM does not respond while it is writing data into its data space, the 'write cycle' which is 5msec Max (timing parameter 17 in Table 1-2). So put at least a 5msec delay in the loop.

Another difference in your code and mine is I have an Idle() after each call the WriteI2C() functions. Tou said you looked at mine code and the app notes and made changes to your code.
Could you post your latest code. I'll give it a try to see if it works here.

I2C is rather tricking to get working the first time so keep at it.

phekno
Posts: 8
Joined: Thu Jan 20, 2011 2:46 pm
Location: Minneapolis, MN
Contact:

Re: PIC16F873 talking to an I2C EEPROM

Post by phekno » Sun Jan 23, 2011 7:25 am

Well, I appreciate your help. I ended up scrapping my 16F873 code and starting over, this time on an 18F4550 (yeah, I know, completely different). The I did take your suggestion and try watching the SDA and SCL lines using the PICKIT2 software. Apparently I can only watch one at a time, though. The PICKIT2 hardware has 4.7k pulldown resistors on Ch1 and 2 and they can't be disabled. When I watch SDA and SCL using Ch3 though, it seems to be working (i.e. I'm actually getting a clock signal, and a data signal), although it's hard to tell because I can only view one channel at a time.

After all that, I also took your suggestion and decided to run my code through the simulator. When I do that, it looks like it's getting hung waiting for the SEN bit to be cleared after a start condition is called for.

Code: Select all

while(SSPCON2bits.SEN);
If I read the datasheets correctly, shouldn't that bit be cleared automatically by hardware after the start has been sent or is that the wrong way to wait until the start condition is over? Do I need to have interrupts enabled or something?

waltr
Support Volunteer
Posts: 2823
Joined: Tue Sep 08, 2009 12:07 pm
Location: Philadelphia, USA

Re: PIC16F873 talking to an I2C EEPROM

Post by waltr » Sun Jan 23, 2011 9:12 am

In the real hardware the SEN bit will clear but not in the simulator, see the simulator limitations in MPLAB help.
For devices with the listed peripherals, these peripherals are NOT supported:
Serial I/O (i.e., SSP including I2C and SPI). As a result, the SSPSTAT register has been made readable and writable.
So to simulate open the View/Special Function Registers and click on the data field of the SSPSTAT register. Then type in a value to clear this bit and continue stepping through the code. This needs to be done on each of the bits in SSPSTAT that are set in code, SEN, RSEN, PEN. If you click 'run' instead of single stepping first set a break point at each place that is waiting for the SSPSTAT bits to clear, then clear the bit manually.

When using the logic analyzer on the PICKit2, ch3, look at the SCK first and set the time base to see the full time, the number of bytes sent. Do a save or screen capture.
Then repeat on the SDA line, reset or restart the PIC. You should then be able to compare the two captures by looking at the two captures one above the other.

If you have some logic chips (74HC04 or equiv) and a breadboard, you can use these to buffer the I2C lines and drive the PICKit2's ch1 & 2 inputs. This way you can capture both I2C lines at once.

Have you slowed down the I2C clock?
Have you added a delay between one call to i2c_eeprom_write and the next? In the loop.
I have found that delays can really help until you figure out what part of the code is not correct.

In the i2c_eeprom_write function are there Idle functions being called between calling each of the write or read functions?

phekno
Posts: 8
Joined: Thu Jan 20, 2011 2:46 pm
Location: Minneapolis, MN
Contact:

Re: PIC16F873 talking to an I2C EEPROM

Post by phekno » Sun Jan 23, 2011 1:06 pm

OK...well, turns out I'm a complete moron on several counts.

Firstly, while I was poking around the MPSIM debugger, I noticed that, yes, there are limitations to what the simulator can simulate. Specifically, serial I/O. If, while I was poking around, I had scrolled down, I would have noticed that I2C is limited, exactly like you had told me.

Secondly, if I had read the documentation for my programmer, I would have realized that you can't read EEPROMs with the ZIF sockets. Apparently you have to use the ICSP header and there's a very specific connection scheme.

When I read out my EEPROM the way I should have been doing it from the beginning, I would have noticed that I was able to write to the EEPROM and there was nothing wrong with my code in the first place.

Thank you very much for your time!

waltr
Support Volunteer
Posts: 2823
Joined: Tue Sep 08, 2009 12:07 pm
Location: Philadelphia, USA

Re: PIC16F873 talking to an I2C EEPROM

Post by waltr » Sun Jan 23, 2011 2:35 pm

Ok, that happens to the best of us and then we have a real head slapping moment.
Good your code works.

Garth
Posts: 54
Joined: Sun Jan 27, 2013 12:13 am
Location: SoCal
Contact:

Re: PIC16F873 talking to an I2C EEPROM

Post by Garth » Sat Mar 30, 2013 1:20 am

In case it helps, I have working PIC assembly code for bit-banging I2C on the PIC12CE673, and Forth code for working the 24256 serial EERPOM at http://wilsonminesco.com/6502primer/GENRLI2C.ASM, sections E and C, respectively. The latter has a few tricks in the comments that don't show through in the data sheet, and they caused me a lot of frustration until I figured them out.
http://WilsonMinesCo.com/ lots of 6502 resources

Post Reply