- PVSM.RU - https://www.pvsm.ru -

FRAM через I2C для Arduino как замена EEPROM

Продолжу рассказывать про приборную панель для мотоцикла. Это замечательное устройство содержит одометр, то есть, счётчик пройденного пути в километрах, а у того есть плохое свойство — он должен сохранять данные и при выключенном питании. Ой, ещё есть моточасы, их тоже надо хранить как-то энергозависимо.

Внутри Ардуины есть EEPROM, конечно же. Много места не надо, чтобы хранить пяток длинных целых, но есть нюанс. EEPROM имеет слишком ограниченный ресурс на запись. Хотелось бы писать данные раз в несколько секунд хотя бы. Ресурс же EEPROM позволяет это делать вполне обозримое время, то есть, встроенная память явно не вечна.

Сначала я хотел обмануть судьбу записывая структурку данных в разные места 1К памяти чипа по кругу. Упёрся в то, что указатель надо где-то хранить тоже, а данные достаточно случайные, чтобы использовать какой-то маркер для последовательного поиска.

Необходимость хранения указателя можно обмануть разными способами. Например так:

struct MarkedSavedData {
  byte marker; // показывает, занято место или нет.
  struct SavedData {
    // Собственно данные для сохранения
  }
} data;

Структуркой MarkedSavedData заполняется eerpom или флеш или что-то по кругу. Чтобы не писать указатель, в свободных записях делаем data.marker=0x00, а в занятой текущей data.marker=0xff, например. В процессе работы, конечно же, запись идёт по указателям, а при старте контроллера просто поиском по всей памяти ищется структура с data.marker==0xff — это последние правильные данные. Плохо, что каждый раз две записи получаются тк надо обнулить data.marker освобождаемой записи.

Есть вариант с последовательным счётчиком.

struct MarkedSavedData {
  unsugned int counter; // последовательный номер записи.
  struct SavedData {
    // Собственно данные для сохранения
  }
} data;

На каждую запись увеличивать счётчик на единицу забив на переполнение. При старте контроллера искать самый большой счётчик с учётом возможного переполнения, что не так уж и трудно, а сэкономить sram можно сделав для этого функцию и поместив промежуточные структурки в стеке в локальных переменных её.

Всё это хорошо, но это припарки.

Коллеги из НТЦ Метротек [1] подсказали поискать FRAM. Это ферритовая память с бешеным быстродействием и 1014 циклами записи.

image

Услужливый Aliexpress привёз мне вот такой модуль [2]. Память в виде модуля дорогая весьма, кстати. Сам же чип стоит 16р/шт [3]. В микросхеме 512 байт, то есть, вроде и немного, но с учётом бесконечному числу записей вполне достаточно.

Погуглив на тему готового чего-то для этого чипа я не нашёл ничего. Отличная кошка, решил я, буду на ней тренироваться! Открыл доку по Wire, даташит по FM24, чей-то проект EEPROM/I2C с похожим интерфейсом и набросал класс для FRAM.

image

Проект на гитхабе: github.com/nw-wind/FM24I2C [4]

Пример прилагается вот такой.

#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);
}

Так как на модуле, кроме чипа и разъёмов, практически ничего нет, уже хочу купить несколько микросхем. Нравятся.

Автор: nwwind

Источник [5]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/arduino/232333

Ссылки в тексте:

[1] НТЦ Метротек: https://habrahabr.ru/company/metrotek/

[2] вот такой модуль: https://ru.aliexpress.com/item/FM24CXX-FRAM-Board-FM24C04B-F-RAM-Serial-5V-Memory-Evaluation-Development-Module/32699223304.html?spm=2114.13010608.0.0.qMLT0W

[3] чип стоит 16р/шт: https://ru.aliexpress.com/item/10PCS-lot-FM24C04B-in-stock-can-pay/32623325577.html?spm=2114.30010708.3.10.0JEwET&ws_ab_test=searchweb0_0,searchweb201602_1_10065_10068_10000009_10084_10083_10080_10082_10081_10110_10111_10060_10112_10113_10062_10114_10056_10055_10037_10054_10033_10059_10032_10099_10078_10079_10077_10000012_10093_10103_10073_10102_10000015_10096_10052_10108_10053_10107_10050_10106_10051,searchweb201603_9,afswitch_5_afChannel,single_sort_1_price_asc&btsid=d6f58b5a-baa6-4d9a-b248-5d68f07fdf25

[4] github.com/nw-wind/FM24I2C: https://github.com/nw-wind/FM24I2C

[5] Источник: https://habrahabr.ru/post/319336/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best