PIC32MX: I2C EEPROM

From Mech
Revision as of 12:37, 16 February 2010 by Philip Dames (talk | contribs) (→‎Timing)
Jump to navigationJump to search

Original Assignment

Do not erase this section!

Your assignment is to interface to the I2C 24AA1025 EEPROM chip.

Overview

EEPROM stands for stands for Electrically Erasable Programmable Read-Only Memory. It is a type of non-volitile memory that can be used to store little bits of data. Data will remain on EEPROM when power is disconnected, (like an external hardrive). The EEPROM chip must be reprogrammed in its entirety, and has a life span in the tens or hundreds of thousands of read/writes.

You can use the PIC32 to communicate with an EEPROM chip using SPI. This page refers to the usage of a Serial EEPROM.

To test that the code data was writing and reading correctly, the attached code sends data to the EEPROM through I2C, then from the EEPROM back to the PIC, and out to a LED display screen.

EEPROM vs. Flash memory: The EEPROM erases the cell one by one, and only erases the 1's back to zero. Flash erases the whole block at once. Since erasing causes wear to the cell, the EEPROM will have a slightly longer lifetime.

Circuit

Connection schematic for 24AA1025 Chip.

I2C.jpg

For visual feedback the PIC32 was connected to and LCD screen(1602A1 USB-A/Mini-B5-06):

LCD.jpg


Timing

Communication speed is determined by the clock signal and the Baud rate register (BRG).

This is the rate at which a single bit will be sent/received. From the data sheet it says the EEPROM supports up to 400 kHz but we found that 100 kHz was more reliable. Overall timing is limited by the EEPROM's internal write cycle (on the order of 1 ms).

Table of times for sending data

# of Characters sendChar sendString
5 3.88 ms 3.79 ms
250 32.4 ms 30.8 ms

Table of times for reading data

# of Characters receiveString
5 0.93 ms
250 26.8 ms

Note, we are able to read a single character using the recieveChar function, but cannot read a string.

Code

Main.c

/**************************************************************
* I2C EEPROM using LCD screen as output display
*
**************************************************************/

/*Includes****************************************************/
#include <string.h> // used for string compare to verify data transmission
#include <plib.h>
#include <HardwareProfile.h>
#include <LCD.h> // used to display for debugging
#include <I2C_EEPROM.h>

/*Definitions*************************************************/
#define BAUD_RATE		100000
#define BRG_VAL 		((SYS_FREQ/2/BAUD_RATE)-2)

/*Global Variables *******************************************/
unsigned char SlaveAddress = 0x50;
short int memoryAddress = 0x0540;

/*Main Function***********************************************/
int main(void)
{
    char LCDbuffer[33]; // this will store the string for the LCD
    //unsigned char test[28]="Hello world\nI2C is working!";
    unsigned char test[13]="Hello world!";
    int size = sizeof(test);
    unsigned char readtest[size];

    //Initialize LCD
    lcd_init();

    //Enable channel
    OpenI2C1( I2C_EN, BRG_VAL );

    SlaveAddress = 0x50;	//0b1010(B0)(A0)(A1) 24AA1025 EEPROM address

    sendString(SlaveAddress, memoryAddress, test, size);

    receiveString(SlaveAddress, memoryAddress, readtest, size);

    //Print to LCD screen
    if(strcmp(readtest, test)==0) sprintf(LCDbuffer, "\fTrue"); // make the string 
    else sprintf(LCDbuffer, "\fFalse");
    putsLCD(LCDbuffer);  // write the contents of the variable

    StopI2C1();	//Send the Stop condition
    IdleI2C1();	//Wait to complete
    CloseI2C1();

    while(1)
    {}

}

I2C_EEPROM.h

#ifndef I2C_EEPROM_H
#define I2C_EEPROM_H

void sendString(unsigned char SlaveAddress, short address, unsigned char *data, int length);
void sendChar(unsigned char SlaveAddress, short address, unsigned char *data, int length);
void getWriteAck(unsigned char SlaveAddress);

void sendRecieveRequest(unsigned char SlaveAddress, short address);
void receiveString(unsigned char SlaveAddress, short address, unsigned char *data, int length);
void receiveChar(unsigned char SlaveAddress, short address, unsigned char *data, int length);

#endif 

I2C_EEPROM.c

#include <I2C_EEPROM.h>
#include <plib.h>

/*******************************************************************
*	Name:	sendString.c
*	
*	Inputs:
*	unsigned char SlaveAddress - I2C address of the chip
*	short address - 16-bit memory address to write the data to
*	unsigned char *data - pointer to data string to be stored
*	int length - length of data string to be stored
*	
*	Description:
*	This function uses I2C communication to interface with the 
*	24AA1025 EEPROM.  A string of data (with length bytes) 
*	is sent to the I2C device with SlaveAddress.  Data is written to
*	the specified memory address on the EEPROM using the function
*	MasterputsI2C1, which writes a string of data.  It then waits
*	for an acknowledgement from the EEPROM saying that it has finished
*	writing the data.
*	
*******************************************************************/
void sendString(unsigned char SlaveAddress, short address, unsigned char *data, int length)
{ 
	// Initialize data sending
	char i2cData[3];
	int  DataSz = 3;

	// Send Data to eeprom to program one location

	i2cData[0] = (SlaveAddress << 1) | 0;	//EEPROM Device Address and WR Command
	i2cData[1] = (address>>8);	//eeprom location to program (high address byte)
	i2cData[2] = (address);	//eeprom location to program (low address byte)

	StartI2C1();	//Send the Start Bit
	IdleI2C1();		//Wait to complete

	int Index = 0;
	while( DataSz )
	{
		MasterWriteI2C1( i2cData[Index++] );
		IdleI2C1();		//Wait to complete

		DataSz--;

		//ACKSTAT is 0 when slave acknowledge. if 1 then slave has not acknowledge the data.
		if( I2C1STATbits.ACKSTAT )
			break;
	}

	MasterputsI2C1(data);
	StopI2C1();	//Send the Stop condition
	IdleI2C1();	//Wait to complete

	// wait for eeprom to complete write process
	getWriteAck(SlaveAddress);
} 

/*******************************************************************
*	Name:	sendChar.c
*	
*	Inputs:
*	unsigned char SlaveAddress - I2C address of the chip
*	short address - 16-bit memory address to write the data to
*	unsigned char *data - pointer to data string to be stored
*	int length - length of data string to be stored
*	
*	Description:
*	This function uses I2C communication to interface with the 
*	24AA1025 EEPROM.  A string of data (with length bytes) 
*	is sent to the I2C device with SlaveAddress.  Data is written to
*	the specified memory address on the EEPROM using the function
*	MasterWriteI2C1, which writes a string of data one character at a time.
*	It then waits for an acknowledgement from the EEPROM saying that it 
*	has finished writing the data.
*	
*******************************************************************/
void sendChar(unsigned char SlaveAddress, short address, unsigned char *data, int length)
{ 
	// Initialize data sending
	char i2cData[3];
	int  DataSz = 3;

	// Send Data to eeprom to program one location

	i2cData[0] = (SlaveAddress << 1) | 0;	//EEPROM Device Address and WR Command
	i2cData[1] = (address>>8);	//eeprom location to program (high address byte)
	i2cData[2] = (address);	//eeprom location to program (low address byte)

	StartI2C1();	//Send the Start Bit
	IdleI2C1();		//Wait to complete

	int Index = 0;
	while( DataSz )
	{
		MasterWriteI2C1( i2cData[Index++] );
		IdleI2C1();		//Wait to complete

		DataSz--;

		//ACKSTAT is 0 when slave acknowledge. if 1 then slave has not acknowledge the data.
		if( I2C1STATbits.ACKSTAT )
			break;
	}

	while(length)
	{
		MasterWriteI2C1( *data );
		IdleI2C1();		//Wait to complete

		data++; // go to next memory address
		length--;

		//ACKSTAT is 0 when slave acknowledge. if 1 then slave has not acknowledge the data.
		if( I2C1STATbits.ACKSTAT )
			break;
	}
	StopI2C1();	//Send the Stop condition
	IdleI2C1();	//Wait to complete

	// wait for eeprom to complete write process
	getWriteAck(SlaveAddress);
}

/*******************************************************************
*	Name:	getWriteAck.c
*	
*	Inputs:
*	unsigned char SlaveAddress - I2C address of the chip
*	
*	Description:
*	This function uses I2C communication to interface with the 
*	24AA1025 EEPROM.  It polls the EEPROM until it recieves an
*	acknowlegement that it has completed the write process.
*	
*******************************************************************/
void getWriteAck(unsigned char SlaveAddress)
{ 
	// wait for eeprom to complete write process. poll the ack status
	while(1)
	{
		//i2c_wait(10);

		StartI2C1();	//Send the Start Bit
		IdleI2C1();		//Wait to complete

		MasterWriteI2C1( (SlaveAddress << 1) | 0 );
		IdleI2C1();		//Wait to complete

		if( I2C1STATbits.ACKSTAT == 0 )	//eeprom has acknowledged
		{
			StopI2C1();	//Send the Stop condition
			IdleI2C1();	//Wait to complete
			break;
		}

		StopI2C1();	//Send the Stop condition
		IdleI2C1();	//Wait to complete 
	}
}

/*******************************************************************
*	Name:	sendRecieveRequest.c
*	
*	Inputs:
*	unsigned char SlaveAddress - I2C address of the chip
*	short address - 16-bit memory address to write the data to
*	
*	Description:
*	This function uses I2C communication to interface with the 
*	24AA1025 EEPROM.  The read command is sent to the EEPROM
*	to initialize a reading sequence.
*	
*******************************************************************/
void sendRecieveRequest(unsigned char SlaveAddress, short address)
{ 
	char i2cData[3];
	int  DataSz = 3;

	// Now Readback the data from the serial eeprom

	i2cData[0] = (SlaveAddress << 1) | 0;	//EEPROM Device Address and WR Command (to write the address)
	i2cData[1] = address>>8;	//eeprom location to read (high address byte)
	i2cData[2] = address;	//eeprom location to read (low address byte)

	StartI2C1();	//Send the Start Bit
	IdleI2C1();		//Wait to complete

	//send the address to read from the serial eeprom
	int Index = 0;
	while( DataSz )
	{
		MasterWriteI2C1( i2cData[Index++] );
		IdleI2C1();		//Wait to complete

		DataSz--;

		//ACKSTAT is 0 when slave acknowledge. if 1 then slave has not acknowledge the data.
		if( I2C1STATbits.ACKSTAT )
			break;
	}

	//now send a start sequence again
	RestartI2C1();	//Send the Restart condition

	//wait for this bit to go back to zero
	IdleI2C1();	//Wait to complete

	MasterWriteI2C1( (SlaveAddress << 1) | 1 ); //transmit read command
	IdleI2C1();		//Wait to complete
}

/*******************************************************************
*	Name:	receiveString.c
*	
*	Inputs:
*	unsigned char SlaveAddress - I2C address of the chip
*	short address - 16-bit memory address to read the data from
*	unsigned char *data - pointer to data string to be read
*	int length - length of data string to be read
*	
*	Description:
*	This function uses I2C communication to interface with the 
*	24AA1025 EEPROM.  A string of data (with length bytes) 
*	is read from the I2C device with SlaveAddress.  Data is read from
*	the specified memory address on the EEPROM using the function
*	MastergetsI2C1, which reads a string of data all at once.
*	
*******************************************************************/
void receiveString(unsigned char SlaveAddress, short address, unsigned char *data, int length)
{ 
	sendRecieveRequest(SlaveAddress, address);	

	// get data as a string
	MastergetsI2C1(length-1, data, 100*length);
	
	// Set the last character to null as it will otherwise be random data from the EEPROM
	data+=length-1;
	*data = 0;
	
	StopI2C1();
	IdleI2C1();
} 

/*******************************************************************
*	Name:	receiveChar.c
*	
*	Inputs:
*	unsigned char SlaveAddress - I2C address of the chip
*	short address - 16-bit memory address to read the data from
*	unsigned char *data - pointer to data string to be read
*	int length - length of data string to be read
*	
*	Description:
*	This function uses I2C communication to interface with the 
*	24AA1025 EEPROM.  A string of data (with length bytes) 
*	is read from the I2C device with SlaveAddress.  Data is read from
*	the specified memory address on the EEPROM using the function
*	MasterReadI2C1, which reads a string of data one byte at a time.
*	
*******************************************************************/
void receiveChar(unsigned char SlaveAddress, short address, unsigned char *data, int length)
{
	sendRecieveRequest(SlaveAddress, address);	

	// get data one byte at a time
	while(length-1)
	{
		*data = MasterReadI2C1();
		IdleI2C1();		//Wait to complete

		data++; // go to next memory address
		length--;

		//ACKSTAT is 0 when slave acknowledge. if 1 then slave has not acknowledge the data.
		if( I2C1STATbits.ACKSTAT )
			break;
	}
	
	// Set the last character to null as it will otherwise be random data from the EEPROM
	data--;
	*data = 0;
	
	StopI2C1();
	IdleI2C1();
}

Further Reading

PIC32MX:_I2C_DAC

PIC32MX:_I2C_External_RAM

PIC32MX:_I2C_Communication_between_PIC32s