Requires Javascript to function properly
SPI Serial CMOS EEPROM
none SPI ATMega328 
Souce Code (3K)

Since memory on an AVR processor is at a premium an inexpensive solution is the Serial EEPROM memory. It can be purchase from 128 bytes to 125K bytes in many different configuations but we will be looking at the CAT25128 device in this article. Adding external memory to an AVR turned out to be a simple task requiring very little hookup and basic SPI code to communicate with the device.

From Mouser catalog

The ON Semiconductor CAT25128 SPI Serial CMOS EEPROM features software and hardware write protection, including partial and full array protection. The ON Semiconductor CAT25128 Serial CMOS EEPROM is 10 MHz SPI compatible, features 1.8V to 5.5V supply voltage range, and includes a 64-byte page write buffer. This ON Semiconductor device is enabled through a Chip Select input. Required bus signals of the CAT25128 EEPROM include clock input, data input, and data output lines. The HOLD input may be used to pause any serial communication with the CAT25128 device. Introduced by Mouser Electronics in January 2008.

For this project I used an ATMega328 with a 20MHz external crystal. because it was such a minimal hardware requirement I put everything on a Radio Shack proto board. It was a tad cramped but there was just enough room for everything.

Click thumbnail to enlarge

As an example of communicating with the EEPROM the listing below shows how to read a single byte from an address passed as a parameter and returned to the user. Any time we communicate with the chip we need to first drive the SS low, then pass an opcode, address then the actual data can be retrieved and finally drive the SS back high again to complete the cycle.


/* -- read_eeprom -----------------------------------------------------------
**
**	Description: Read a byte from eeprom
**
**	Params:	word - address to read from
**	Returns: byte - data
** -----------------------------------------------------------------------*/
byte read_eeprom(word address)
{
	int data;

	if (address >= EEPROM_SIZE)
		return null;

	PORTB &= ~_BV(SLAVESELECT);

	spi_transfer(READ); //transmit read opcode
	spi_transfer((char)(address>>8));   //send MSByte address first
	spi_transfer((char)(address));      //send LSByte address
	data = spi_transfer(0xFF); //get data byte

	PORTB = _BV(SLAVESELECT);

	return data;
	
	

To write data to the EEPROM requires that we send the Write enable opcode (WREN) to the EEPROM before an actual write can take place but othe then that the code looks similar to the read that we did above.

/* -- write_epprom -------------------------------------------------------
**
**	Description: Write data to EEPROM
**
**	Params:	byte* - pointer to data buffer
**			word - address to write data
**			word - length of data
**	Returns: bool - true on success false otherwise
** -----------------------------------------------------------------------*/
bool write_eeprom(word address, byte data)
{
	if (address >= EEPROM_SIZE)
		return false;

	write_enable_eeprom();
  
	PORTB &= ~_BV(SLAVESELECT);

	spi_transfer(WRITE); //write instruction
	spi_transfer((char)(address>>8));   //send MSByte address first
  	spi_transfer((char)(address));      //send LSByte address
  
    spi_transfer(data); //write data byte

	PORTB = _BV(SLAVESELECT);

	return true;
}
    

The helper methods used in the above examples are listed below.

/* -- write_enable_eeprom ------------------------------------------------
**
**	Description: Enable writting to the EEPROM
**
**	Params:	None
**	Returns: None
** -----------------------------------------------------------------------*/
void write_enable_eeprom()
{
	PORTB &= ~_BV(SLAVESELECT);

	spi_transfer(WREN); //write enable

	PORTB = _BV(SLAVESELECT);
}
    
/* -- spi_transfer -----------------------------------------------------------
**
**	Description: Transfers one byte of data via SPI
**
**	Params:	uint8_t	byte to send
**	Returns: None
** -----------------------------------------------------------------------*/
byte spi_transfer(byte data)
{
  SPDR = data;
  while (!(SPSR & _BV(SPIF)))     // Wait the end of the transmission
  {
  };
  return SPDR;                    // return the received byte
}