📜 ⬆️ ⬇️

FRAM via I2C for Arduino as a replacement for EEPROM

I will continue to talk about the dashboard for a motorcycle. This wonderful device contains an odometer, that is, a trip meter in kilometers, and that one has a bad property - it must save data even when the power is turned off. Oh, there are still motochas, they also need to be stored somehow volatile.

Inside the Arduina there is an EEPROM, of course. A lot of space is not necessary to keep the heels of long wholes, but there is a nuance. EEPROM is too limited to write. I would like to write data every few seconds at least. The EEPROM resource allows you to do this quite within the foreseeable time, that is, the built-in memory is clearly not eternal.

At first, I wanted to deceive fate by writing the data structure to different places of 1K chip memory in a circle. He rested in the fact that the pointer must be stored somewhere too, and the data is random enough to use some sort of marker for sequential search.

The need to store the pointer can be fooled in various ways. For example:
')
struct MarkedSavedData { byte marker; // ,    . struct SavedData { //     } } data; 


MarkedSavedData structure is filled with eerpom or flash or something in a circle. In order not to write a pointer, in free records we make data.marker = 0x00, and in busy current data.marker = 0xff, for example. In the course of work, of course, the recording goes by the pointers, and when the controller starts, it is simply searching the entire memory for a structure with data.marker == 0xff - this is the latest correct data. It's bad that every time two records are obtained, it is necessary to reset the data.marker of the released record.

There is an option with a sequential counter.

 struct MarkedSavedData { unsugned int counter; //   . struct SavedData { //     } } data; 


For each record, increase the counter by one by scoring overflow. When starting the controller, look for the largest counter, taking into account possible overflow, which is not so difficult, and you can save the sram by making a function for this and putting intermediate structures in the stack in its local variables.

Everything is good, but it is a poultice.

Colleagues from the STC Metrotek prompted to look for FRAM. This is a ferrite memory with a high speed and 10 14 write cycles.

image

Helpful Aliexpress brought me this module . Memory in the form of a module is very expensive, by the way. The chip itself costs 16 p / pcs . In the chip 512 bytes, that is, like a bit, but taking into account the infinite number of records is enough.

Googling on the subject of something ready for this chip, I did not find anything. Great cat, I decided, I will train on it! I opened the Wire dock, FM24 datasheet, someone's EEPROM / I2C project with a similar interface and sketched a class for FRAM.

image

Github Project: github.com/nw-wind/FM24I2C

An example is attached here.

 #include "FM24I2C.h" //   .   i2c. FM24I2C fm(0x57); void setup() { Wire.begin(); Serial.begin(9600); char str1[]="12345678901234567890"; char str2[]="qwertyuiopasdfghjklzxcvbnm"; int a1=0x00; //   FRAM int a2=0x40; //    FRAM fm.pack(a1,str1,strlen(str1)+1); //    delay(5); fm.pack(a2,str2,strlen(str2)+1); //    delay(5); char buf[80]; fm.unpack(a2,buf,strlen(str2)+1); //   Serial.println(str2); fm.unpack(a1,buf,strlen(str1)+1); //   Serial.println(str1); } 

The i2c protocol for FRAM is much simpler than for the EEPROM. The memory is faster than transferring data over the bus and you can pour at least all 2K arduin brains at a time. The benefit of my code is that there is no unnecessary partitioning into blocks of 32 bytes, or even a single byte transmission.

 class FM24I2C { private: int id; public: FM24I2C(int id_addr); ~FM24I2C(); void pack(int addr, void* data, int len); //    FRAM int unpack(int addr, void* data, int len); //   FRAM.    . //     ,    . void inline writeUnsignedLong(int addr, unsigned long data) { pack(addr, (void*)&data, sizeof(unsigned long)); } //  . unsigned long inline readUnsignedLong(int addr) { unsigned long data; return unpack(addr, (void*)&data, sizeof(unsigned long)) == sizeof(unsigned long) ? data : 0UL; } //      /,     ,   . //        . }; 

A code a little bit absolutely.

 void FM24I2C::pack(int addr, void* data, int len) { Wire.beginTransmission(id); Wire.write((byte*)&addr,2); Wire.write((byte*)data,len); // ,    unsigned int  :) Wire.endTransmission(true); } int FM24I2C::unpack(int addr, void* data, int len) { int rc; byte *p; Wire.beginTransmission(id); Wire.write((byte*)&addr,2); Wire.endTransmission(false); Wire.requestFrom(id,len); //      rc  p-data :) for (rc=0, p=(byte*)data; Wire.available() && rc < len; rc++, p++) { *p=Wire.read(); } return(rc); } 

Since there is practically nothing on the module, except for the chip and connectors, I already want to buy several chips. Like it.

Source: https://habr.com/ru/post/319336/


All Articles