FRAM and the Spark

Hi All

I thought I would share my experiences with interfacing an FRAM device with the Spark using I2C, since it seems that people are interested in some form of persistent storage. The FRAM device I am using is an FM24C256-SE from Ramtron. The parts have actually been sticking in my collection for about 10 years waiting for a project.

In order to connect this device, I needed to modify the Relay Shield since I2C uses D0 and D1. Therefore, I cut the tracks on the board, and connected the relay drivers to D6 and D7.

This allowed me to connect the FM24C256-SE up. I connected D0 to pin 5, and D1 to pin 6. Pin 4 was connected to Ground, and Pin 8 was connected to Vin. The part is only supposed to receive 5.5V maximum, and so I assumed that the relay shield was providing no more than this to the Spark module. I also connected D0 and D1 to the Vin via a couple of 4k7 resistors.

I left pins 1-3 open circuit. These pins are for the Address of the module, and leaving them open means that the address is set to 0 since it is pulled low internally. I also left pin 7 open, which is write protect. With the internal pull down resistor, it is in read/write mode.

So, hardware worked well. Software required a bit more work, mostly since I had never used I2C before.

As noted in another thread, some sample code I used to talk to the device and verify the operation appears below. This code saves startTime to the FRAM. The address of 0x50, translates to 01010000. Making this 7 bits, it becomes 1010000. The lower three bits are the hardware address which we set to 000, and the upper four bits are for the type of chip The code then takes this, shifts it left one bit, and sets the low bit depending on mode.

The two 0x00 values are for the start address within the FRAM module.

Wire.beginTransmission(0x50);
Wire.write(0x00);  // send the address to write
Wire.write(0x00);  // send the address to write
Wire.write(startTime & 0xFF);  // send the address to write
Wire.write((startTime >> 8) & 0xFF);  // send the address to write
Wire.write((startTime >> 16) & 0xFF);  // send the address to write
Wire.write((startTime >> 24) & 0xFF);  // send the address to write
Wire.endTransmission();  // capture any error codes

Reading is almost as easy. The only complication is that we need to write the address before requesting that data is read.

Wire.beginTransmission(0x50);
Wire.write(0x00);  // send the address to write
Wire.write(0x00);  // send the address to write
Wire.endTransmission();  // capture any error codes
Wire.requestFrom (0x50, 4);
gotTime = Wire.read();
gotTime |= (Wire.read() << 8);
gotTime |= (Wire.read() << 16);
gotTime |= (Wire.read() << 24);

It would be nice to be able to say that is everything you need to know, but there is one complication. I have not managed to work out if it is a bug in my code, or an issue with the I2C Wire library, but attempts at dealing with writing or reading more than 31 bytes were unsuccessful. I would normally blame my own code, but in this case I cannot find a failure mode.

Breaking reads or writes to under 32 bytes, and adding a new beginTransmission as required solved my issue. My working code appears below. FYI, schedules contains three 32 bit integers and SCHEDS is defined as 16, meaning that the code attempts to only store 192 bytes, which does not fill over into a second ‘page’.

void framLoad()
{
  // Assumes a simgle PAGE at the moment only
  Serial.println ("Fram Load");

  for (int j = 0; j < SCHEDS; j++){
    Wire.beginTransmission(0x50);
    Wire.write(0x01);  // send the address to write
    Wire.write(j * sizeof (schedules[0]));  // send the address to write
    Wire.endTransmission();
    Wire.requestFrom (0x50, sizeof (schedules[0]));
    for (int i = 0; i < sizeof (schedules[0]); i++){
      ((char*) &schedules[j])[i] = Wire.read ( );
    }
  }
}

During debugging, I was happy to be using my Saleae16 Logic Analyser. It made it much easier to debug the I2C, since it not only displays the waveforms on the screen, but also decodes the transmissions. I suspect that the BusPirate would also help, but without the GUI.

Another things - the I2C is running at 100 kHz at the moment. The board and the chip can do 400 kHz, but this is not available yet in the firmware. I believe that there are moves to add the ability to select the frequency to the code.

Darryl

2 Likes

Oh, I have just confirmed that I2C read and write sequences are buffered before transmission in a fixed length buffer. This buffer is 32 bytes in length.

What this means is that if you want to write or read more than 32 bytes you need to break it up into a second beginTransmission transaction. Some experimentation should probably be made because I have had cases where I am sure that I have only been able to read 31 bytes.

So, if your I2C is not working when you are, try breaking the communications up!

Darryl

1 Like