Using FRAM as Nonvolatile Memory With Arduino

One of the biggest advantages of FRAM (or FeRAM, Ferroelectric RAM) over EEPROM is that FRAM has a much higher write speed and typically can operate at bus speed. This means that no delay instructions are needed when performing write operations, which greatly reduces coding complexity and increases the overall throughput. FRAM is also capable of supporting beyond 1012 read/write cycles whereas most EEPROM can only handle around 106 read/write cycles. These properties make FRAM very attractive for using with microcontrollers where frequent write to nonvolatile memory is needed.

In this blog post I will give an example on interfacing two Ramtron FM25C160 FRAM chips with Arduino. Since the basic operations on an FRAM are very similar to those on a standard EEPROM, I would recommend reading this SPI EEPROM tutorial first if you are not familiar with the protocol.

The main difference between the operation modes of an FM25C160 and a standard EEPROM is that FM25C160 allows multi-byte sequential reads and writes whereas EEPROM typically has a limited write buffer and long writes beyond the buffer length must be broken into multiple writes. We will take advantage of this in our code later.

The following schematic shows the simplest way to interface multiple FRAM (or EEPROM) chips with Arduino.

Interfacing 2 FM25C160 with Arduino

Interfacing 2 FM25C160 with Arduino

For our simple implementation, the HOLD and WP pins are disabled (tie to Vcc) and the corresponding data pins (SO, SI and SCK) are connected together. The CS pin of each FRAM is controlled individually by an Arduino IO pin so that each FRAM can be selected and operated on individually.

The following code shows how to access multiple FRAM chips using Arduino. For FM25C160, the maximum address is 0x7FF (since it has 16 Kbits and 2K bytes) so we assume that addresses between 0x800 and 0x1000 belong to the second chip, and addresses beyond 0x1000 are invalid.

#include <SPI.h>

const byte CMD_WREN = 0x06; //0000 0110 Set Write Enable Latch
const byte CMD_WRDI = 0x04; //0000 0100 Write Disable
const byte CMD_RDSR = 0x05; //0000 0101 Read Status Register
const byte CMD_WRSR = 0x01; //0000 0001 Write Status Register
const byte CMD_READ = 0x03; //0000 0011 Read Memory Data
const byte CMD_WRITE = 0x02; //0000 0010 Write Memory Data

const int FRAM_CS1 = 10; //chip select 1
const int FRAM_CS2 = 9; //chip select 2

/**
 * Write to FRAM (assuming 2 FM25C160 are used)
 * addr: starting address
 * buf: pointer to data
 * count: data length. 
 *        If this parameter is omitted, it is defaulted to one byte.
 * returns: 0 operation is successful
 *          1 address out of range
 */
int FRAMWrite(int addr, byte *buf, int count=1)
{
  int cs = 0;
  
  if (addr > 0x7ff)  {
    addr -=0x800;
    cs = FRAM_CS2;
  } else {
    cs = FRAM_CS1;
  }
  
  if (addr > 0x7ff) return -1;
  
  byte addrMSB = (addr >> 8) & 0xff;
  byte addrLSB = addr & 0xff;
  
  digitalWrite(cs, LOW);   
  SPI.transfer(CMD_WREN);  //write enable 
  digitalWrite(cs, HIGH);
  
  digitalWrite(cs, LOW);
  SPI.transfer(CMD_WRITE); //write command
  SPI.transfer(addrMSB);
  SPI.transfer(addrLSB);
  
  for (int i = 0;i < count;i++) SPI.transfer(buf[i]);

  digitalWrite(cs, HIGH);
  
  return 0;
}

/**
 * Read from FRAM (assuming 2 FM25C160 are used)
 * addr: starting address
 * buf: pointer to data
 * count: data length. 
 *        If this parameter is omitted, it is defaulted to one byte.
 * returns: 0 operation is successful
 *          1 address out of range
 */
int FRAMRead(int addr, byte *buf, int count=1)
{
  int cs = 0;
  
  if (addr > 0x7ff)  {
    addr -=0x800;
    cs = FRAM_CS2;
  } else {
    cs = FRAM_CS1;
  }
  
  if (addr > 0x7ff) return -1;

  byte addrMSB = (addr >> 8) & 0xff;
  byte addrLSB = addr & 0xff;
  
  digitalWrite(cs, LOW);
  
  SPI.transfer(CMD_READ);
  SPI.transfer(addrMSB);
  SPI.transfer(addrLSB);
  
  for (int i=0; i < count; i++) buf[i] = SPI.transfer(0x00);

  digitalWrite(cs, HIGH);
  
  return 0;
}

void setup()
{
  Serial.begin(9600);  
  pinMode(FRAM_CS1, OUTPUT);
  digitalWrite(FRAM_CS1, HIGH);
  pinMode(FRAM_CS2, OUTPUT);
  digitalWrite(FRAM_CS2, HIGH);

  //Setting up the SPI bus
  SPI.begin();
  SPI.setDataMode(SPI_MODE0);  
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV2);
  
  //Test
  char buf[]="This is a test";
  
  FRAMWrite(1, (byte*) buf, 14);  
  FRAMRead(1, (byte*) buf, 14);
  
  for (int i = 0; i < 14; i++) Serial.print(buf[i]);
}

void loop()
{
}

While the above code supports memory spanning multiple chips, each read/write operation is limited within a single chip. This should be sufficient under most scenarios. If reading/writing beyond a single chip’s boundary is required, logic needs to be added so that when the address wraps around to 0 the CS of the current chip is deselected and the CS of the next chip is selected.

Be Sociable, Share!

7 Comments

  1. ciprian says:

    Hi,

    You still need to check if multiple bytes are written towards the end of one FRAM, but more than the available space on that FRAM chip.
    IE you have only 3 bytes available so you cannot write “Hello World!”.
    But this goes beyond the example i think.

    Thank you.

  2. Nikolai Dk says:

    Hi
    This is very interesting. Iam considering using multiple fram instead of single sd in my project. What would be the correct way to set this up. Any ideas?

  3. Nikolai Dk says:

    Multiple meaning more then 2 – maybe 4 to 6 fram blocks.

    • kwong says:

      All you need to do is to be able to control the CS pins of those FRAMs (with all the other corresponding signal pins connected together). If you take a look at FRAMWrite function you will see that we are only addressing one chip at a time by writing a low to the CS pin of the chip we want to address. So it doesn’t matter how many chips you have there.

  4. Nikolai Dk says:

    Hi
    Thanks alot for your answer. I will order det fram blocks and try it out. Looks quite simple as you pointed out :)

  5. Nikolai Dk says:

    I went for this one: Will give a follow up if your interested :)

    http://www.everspin.com/product.php?pn=MR25H40CDC&hjk=SERIAL&a1f3=4Mb

  6. Gisap says:

    Hi,
    compliments for the post, very interesting.
    I should read all data (dump) and possibly write (or clear) a only one FRAM EEPROM model FM25L512 with Arduino.
    could you kindly explain to me how to do?

    Thanks and regards,
    Gisap

Leave a Reply