HOWTO: Using an external serial eeprom with an AVR chip
Submitted by chad on Thu, 2006-07-20 04:07.
This article demonstrates how to use a Microchip 25LC080 serial eeprom with an attiny2313. The code was written using gcc. This code will write one byte to the eeprom, then read it back in.
Code:
#include <util/delay.h>
#include <avr/interrupt.h>
#define NOP asm("nop");
#define SPIEE_CS 4
#define SPIEE_CS_PORT PORTB
#define SPI_EEPROM_WREN 0x6
#define SPI_EEPROM_RDSR 0x5
#define SPI_EEPROM_WRITE 0x2
#define SPI_EEPROM_READ 0x3
uint8_t spieeprom_read (uint16_t);
uint8_t spi_transfer (uint8_t);
uint8_t spieeprom_write (uint16_t, uint8_t);
int
main (void)
{
DDRD = 0xFF; // all output on PORT D
DDRB = 0xDF; // input on MOSI/DI (for SPI), all others output
// deselect EEPROM
PORTB |= (1 << SPIEE_CS);
// Load data to the eeprom. We will load one byte to memory position 100.
// we will load the number 51 to the eeprom
spieeprom_write (100, 51);
_delay_loop_2 (5000);
// Read the data back from the eeprom. Read one byte from memory position 100
uint8_t buff = spieeprom_read (100);
// Light up some leds
PORTD = buff;
while (1)
{
}
}
uint8_t
spieeprom_read (uint16_t addr)
{
// pull CS low to activate eeprom
SPIEE_CS_PORT &= ~(1 << SPIEE_CS);
NOP;
NOP;
NOP;
NOP; // wait 500 ns, CS setup time
// send READ command 0000 0011
spi_transfer (SPI_EEPROM_READ);
// Send in the memory address we will start to read from
// This is a 16 bit address. We must send in 8 bits at a time
spi_transfer (addr >> 8); // send high bits of address
spi_transfer (addr & 0xFF); // send low bits of address
// We will read back one byte
uint8_t buff = spi_transfer (0);
// Once done with the read, set the CS high to terminate the operation
SPIEE_CS_PORT |= (1 << SPIEE_CS);
return buff;
}
uint8_t
spieeprom_write (uint16_t addr, uint8_t data)
{
uint8_t i;
// Before performing the write, first check to see if eeprom is in the middle of
// a read. Send in the Read Status (RDSR 0000 0101). Check the returned status
// to see if it is ok to do a write.
do
{
asm ("wdr");
// Activate eeprom by pulling CS low
SPIEE_CS_PORT &= ~(1 << SPIEE_CS);
NOP;
NOP;
NOP;
NOP; // wait 500 ns, CS setup time
spi_transfer (SPI_EEPROM_RDSR); // write "READ STATUS REG" cmd
i = spi_transfer (0); // read status
SPIEE_CS_PORT |= (1 << SPIEE_CS); // pull CS high
}
while ((i & 0x1) != 0);
// pull CS low to activate eeprom
SPIEE_CS_PORT &= ~(1 << SPIEE_CS);
NOP;
NOP;
NOP;
NOP; // wait 500 ns, CS setup time
// Set the write enable latch by issuing the WREN command 0000 0110
// then bring CS back high
spi_transfer (SPI_EEPROM_WREN); // send command
SPIEE_CS_PORT |= (1 << SPIEE_CS); // pull CS high
NOP;
NOP;
NOP;
NOP; // wait 500 ns, CS setup time
// Once the latch is set, proceed by setting CS low and
// sending the WRITE command 0000 0010
SPIEE_CS_PORT &= ~(1 << SPIEE_CS); // pull CS low
spi_transfer (SPI_EEPROM_WRITE); // send WRITE command
// Send in the memory address you will write to.
// The address is 16bits, so send in 8 bites at a time
spi_transfer (addr >> 8); // send high bits of address
spi_transfer (addr & 0xFF); // send low bits of address
spi_transfer (data); // send data
NOP;
NOP;
NOP;
NOP; // wait 500 ns, CS setup time
// pull CS high to terminate operation
SPIEE_CS_PORT |= (1 << SPIEE_CS);
return 0;
}
// Transfer the data to the eeprom using SPI
uint8_t
spi_transfer (uint8_t c)
{
USIDR = c;
USISR = (1 << USIOIF);
while (!(USISR & (1 << USIOIF)))
{
USICR = (1 << USIWM0) | (1 << USICS1) | (1 << USICLK) | (1 << USITC);
}
return USIDR;
}
Code:
#include <util/delay.h>
#include <avr/interrupt.h>
#define NOP asm("nop");
#define SPIEE_CS 4
#define SPIEE_CS_PORT PORTB
#define SPI_EEPROM_WREN 0x6
#define SPI_EEPROM_RDSR 0x5
#define SPI_EEPROM_WRITE 0x2
#define SPI_EEPROM_READ 0x3
uint8_t spieeprom_read (uint16_t);
uint8_t spi_transfer (uint8_t);
uint8_t spieeprom_write (uint16_t, uint8_t);
int
main (void)
{
DDRD = 0xFF; // all output on PORT D
DDRB = 0xDF; // input on MOSI/DI (for SPI), all others output
// deselect EEPROM
PORTB |= (1 << SPIEE_CS);
// Load data to the eeprom. We will load one byte to memory position 100.
// we will load the number 51 to the eeprom
spieeprom_write (100, 51);
_delay_loop_2 (5000);
// Read the data back from the eeprom. Read one byte from memory position 100
uint8_t buff = spieeprom_read (100);
// Light up some leds
PORTD = buff;
while (1)
{
}
}
uint8_t
spieeprom_read (uint16_t addr)
{
// pull CS low to activate eeprom
SPIEE_CS_PORT &= ~(1 << SPIEE_CS);
NOP;
NOP;
NOP;
NOP; // wait 500 ns, CS setup time
// send READ command 0000 0011
spi_transfer (SPI_EEPROM_READ);
// Send in the memory address we will start to read from
// This is a 16 bit address. We must send in 8 bits at a time
spi_transfer (addr >> 8); // send high bits of address
spi_transfer (addr & 0xFF); // send low bits of address
// We will read back one byte
uint8_t buff = spi_transfer (0);
// Once done with the read, set the CS high to terminate the operation
SPIEE_CS_PORT |= (1 << SPIEE_CS);
return buff;
}
uint8_t
spieeprom_write (uint16_t addr, uint8_t data)
{
uint8_t i;
// Before performing the write, first check to see if eeprom is in the middle of
// a read. Send in the Read Status (RDSR 0000 0101). Check the returned status
// to see if it is ok to do a write.
do
{
asm ("wdr");
// Activate eeprom by pulling CS low
SPIEE_CS_PORT &= ~(1 << SPIEE_CS);
NOP;
NOP;
NOP;
NOP; // wait 500 ns, CS setup time
spi_transfer (SPI_EEPROM_RDSR); // write "READ STATUS REG" cmd
i = spi_transfer (0); // read status
SPIEE_CS_PORT |= (1 << SPIEE_CS); // pull CS high
}
while ((i & 0x1) != 0);
// pull CS low to activate eeprom
SPIEE_CS_PORT &= ~(1 << SPIEE_CS);
NOP;
NOP;
NOP;
NOP; // wait 500 ns, CS setup time
// Set the write enable latch by issuing the WREN command 0000 0110
// then bring CS back high
spi_transfer (SPI_EEPROM_WREN); // send command
SPIEE_CS_PORT |= (1 << SPIEE_CS); // pull CS high
NOP;
NOP;
NOP;
NOP; // wait 500 ns, CS setup time
// Once the latch is set, proceed by setting CS low and
// sending the WRITE command 0000 0010
SPIEE_CS_PORT &= ~(1 << SPIEE_CS); // pull CS low
spi_transfer (SPI_EEPROM_WRITE); // send WRITE command
// Send in the memory address you will write to.
// The address is 16bits, so send in 8 bites at a time
spi_transfer (addr >> 8); // send high bits of address
spi_transfer (addr & 0xFF); // send low bits of address
spi_transfer (data); // send data
NOP;
NOP;
NOP;
NOP; // wait 500 ns, CS setup time
// pull CS high to terminate operation
SPIEE_CS_PORT |= (1 << SPIEE_CS);
return 0;
}
// Transfer the data to the eeprom using SPI
uint8_t
spi_transfer (uint8_t c)
{
USIDR = c;
USISR = (1 << USIOIF);
while (!(USISR & (1 << USIOIF)))
{
USICR = (1 << USIWM0) | (1 << USICS1) | (1 << USICLK) | (1 << USITC);
}
return USIDR;
}

Has anyone tried this code?
Has anyone tried this code? I don't understand, because if the AVR is master, that means MISO should be the input (Master-in-slave-out), instead of MOSI. This doesn't seem to square with:
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
atmega 64 + external non volatile memory interface
i am using Atmega64L. i want to extend my non volatile memory. can u give some ideas about interface or codings
try dataflash
Look at Atmels' dataflash. http://www.arduino.cc/playground/Code/Dataflash
I haven't tried it yet, but it looks like a fairly easy way to get extra non volatile memory.
thanks
chad
Enjoy AVR related articles
I really enjoy these AVR related articles where you do stuff like this. I recently started to play with AVR's and I've learned alot from your articles. Keep up the good work. Any chance of an article on interfacing to 1-wire devices to retrieve information such as temperature or to use a 1-wire counter?
links for 1-wire
The 1-wire stuff looks cool, but I doubt if I will work with one anytime soon. Here are some links you might check out though:
avr wiki 1 wire
and
avrfreaks 1 wire
thanks
chad
Post new comment