FRAM через I2C для Arduino
Внутри Ардуины есть EEPROM, конечно же. Много места не надо, чтобы хранить пяток длинных целых, но есть нюанс. EEPROM имеет слишком ограниченный ресурс на запись. Хотелось бы писать данные раз в несколько секунд хотя бы. Ресурс же EEPROM позволяет это делать вполне обозримое время, то есть, встроенная память явно не вечна.
Сначала я хотел обмануть судьбу записывая структурку данных в разные места 1К памяти чипа по кругу. Упёрся в то, что указатель надо где-то хранить тоже, а данные достаточно случайные, чтобы использовать какой-то маркер для последовательного поиска.
Коллеги из НТЦ Метротек подсказали поискать FRAM. Это ферритовая память с бешеным быстродействием и 1014 циклами записи.
Услужливый Aliexpress привёз мне вот такой модуль. Память дорогая весьма, кстати. В микросхеме 8К, то есть, больше, чем у Atmega32 в 4 раза. :)
Погуглив на тему готового чего-то для этого чипа я не нашёл ничего. Отличная кошка, решил я, буду на ней тренироваться! Открыл доку по Wire, даташит по FM24, чей-то проект EEPROM/I2C с похожим интерфейсом и набросал класс для FRAM.
Проект на гитхабе: github.com/nw-wind/FM24I2C
Пример прилагается вот такой.
#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);
}
Протокол i2c для FRAM сильно проще, чем для EEPROM. Память работает быстрее передачи данных по шине и можно лить хоть все 2К ардуининых мозгов за один раз. Польза от моего кода хоть в том, что нет лишнего разбиения на блоки по 32 байта или вообще побайтной передачи.
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;
}
// Можно для других типов сделать чтение/запись, но мне было не нужно, а флеш занимает.
// Каждый же может унаследовать класс и дописать сам.
};
Кода же немножко совсем.
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);
}
Так как на модуле, кроме чипа и разъёмов, практически ничего нет, уже хочу купить несколько микросхем. Нравятся.