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
}