PIC32MX: I2C EEPROM

From Mech
Jump to: navigation, search

Contents

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.

Pins 1 and 2 are used to define the I2C address of the chip. The address is 0b1010(Location)(A0)(A1), where Location, A0, and A1 are all bits. Location allows you to select the half of memory to write to, so it will be in the bounds 0x(Location)0000 to 0x(Location)FFFF. A0 and A1 are the determined by the connections made to pins 1 and 2 on the EEPROM chip (1 is high, 0 is low). Since there are two pins, up to 4 EEPROMs may be used by a single I2C master. The first 4 bits of the address are pre-specified by the manufacturer and cannot be changed. A2 (pin 3) must always be connected to logical high or the chip will not function. Pin 7 is write protect; if it is low then write and read operations are allowed, but if this is high then only read operations are allowed.

IMPORTANT: Pull-up resistors must always be used on the I2C communication lines as shown in the diagram.

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).

F_{clock} = \frac{F_{PB}}{2(BRG+1)}

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 only able to read a single character using the recieveChar function, but cannot read a string.

Code

We have created our own library of files to called I2C_EEPROM.h and I2C_EEPROM.h, found below. This makes use of existing functions in the Peripheral Library (any function with "I2C1" in the title, see chapter 15 of the Peripheral Library Guide for more information. All of our functions use the first I2C bus on the PIC, so to use the other bus you will have to change every instance of I2C1 to I2C2. We also based this code on the example found in C:\Program Files\Microchip\MPLAB C32\examples\plib_examples\i2c\i2c_master\source\i2c_master.c (note that this in in MPLAB C32, NOT MPLAB C32 SUITE which contains an updated version we found to be more confusing).

Main.c

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

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

/*Definitions*************************************************/
#define BAUD_RATE		100000   // 100 kHz
#define BRG_VAL 		((SYS_FREQ/2/BAUD_RATE)-2)  // can use SYS_FREQ because same as peripheral bus freg

/*Global Variables *******************************************/
unsigned char SlaveAddress;
short int memoryAddress;

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

    //Initialize LCD
    lcd_init();

    //Enable channel
    OpenI2C1( I2C_EN, BRG_VAL );

    memoryAddress = 0x0540;    //arbitrarily chosen for this test
    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

Personal tools