FRAM через I2C для Arduino

Продовжу розповідати про приладову панель для мотоцикла. Це чудовий пристрій містить одометр, тобто, лічильник пройденого шляху в кілометрах, а в того є погане властивість — він повинен зберігати дані при вимкненому живленні. Ой, ще є мотогодини, їх теж треба зберігати як-то энергозависимо.

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

Спочатку я хотів обдурити долю записуючи структурку даних в різні місця 1К пам'яті чіпа по колу. Вперся в те, що покажчик треба десь зберігати теж, а дані досить випадкові, щоб використовувати якийсь маркер для послідовного пошуку.

Колеги з НТЦ Метротек підказали пошукати FRAM. Це ферритова пам'ять з шаленим швидкодією і 1014 циклами запису.

image

Послужливий Aliexpress привіз мені ось такий модуль. Пам'ять дорога вельми, до речі. У мікросхемі 8К, тобто, більше, ніж у Atmega32 в 4 рази. :)

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

image

Проект на гітхабі: 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. Повертає кількість переданих байтів.
// Це вже спеціально для мене, пише беззнакові цілі довгі.
inline void 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);
}


Так як на модулі, окрім чіпа і роз'ємів, практично нічого немає, вже хочу купити декілька мікросхем. Подобаються.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.