EEPROM 24C128 - 1024: универсальный инструмент

kostyamat
Offline
Зарегистрирован: 16.11.2017

Недавно сам встретился с тем фактом, что библиотек много и разных, но некоторые неудобны для использования. А некоторые  работаю очень медленно по причине того, что применяется побайтовая запись\считывание. При этом, по стандарту, после записи микросхеме нужно около 5мс для физической зарядки ячеек. Соответствено, при таком способе записи, структура размером\переменная, размером 43 байта, в количестве более 1000 экземпляров, в 24C512 писались более десяти минут, а на считываниет этой своеобразной БД и поиск между десятью полями в каждоой -  более 17 секунд.

Но есть решение, оказывается - современные 24C128 - 512 обладают возможность записи\считівания в страничном режиме. То-есть, -  за один запрос с ЕЕПРОМ можно считать, или записать в нее, 64 - 128 байт за один цикл. (конкретно размер страниці можно узнать из даташита на конкретный экземпляр)

Перелопатив кучу инфы, нашел несколько вариантов решений, в том числе так называемый "EEPROMAnything.h", описаный тут https://playground.arduino.cc/Code/EEPROMWriteAnything/

в результате родилось это:


#include <Arduino.h>  // for type definitions
#include <Wire.h>

#define deviceAddress 0x50                             // Адресс микросхемы по умолчанию, А0-А2 подключены к GND (или висят в воздухе, что не желательно). 
#define pageSize 128                                   // смотрите даташит на свою микросхему 24Схххх, размер может как 64, так и 128




/*
  Физический буффер i2c шины в Ардуино равен 32 байта, поэтому, воспользоваться прелестями чтения, 64 или 128 байт за раз, не получится.
  К тому же, увеличение буффера до 128 байт было бы слишком расточительно для ОЗУ ардуино.
*/

template <class T> void EEPROM_get(uint32_t eeaddress, T& value) {
  uint8_t num_bytes = sizeof(value);
  byte* p = (byte*)(void*)&value;
  byte countChank = num_bytes / 32;
  byte restChank = num_bytes % 32;
  uint32_t addressChank = 0;
  if (countChank > 0) {
    for (byte i = 0; i < countChank; i++) {
    addressChank = eeaddress + 32 * i;
    Wire.beginTransmission(deviceAddress);
    Wire.write((uint16_t)(addressChank >> 8));
    Wire.write((uint16_t)(addressChank & 0xFF));
    Wire.endTransmission();
    Wire.requestFrom(deviceAddress, 32);
    while (Wire.available()) *p++ = Wire.read();
  }
}
if (restChank > 0) {
  if (countChank > 0) addressChank += 32;
  
  Wire.beginTransmission(deviceAddress);
  Wire.write((uint32_t)((addressChank) >> 8));
  Wire.write((uint32_t)((addressChank) & 0xFF));
  Wire.endTransmission();
  Wire.requestFrom(deviceAddress, restChank);
  while (Wire.available()) *p++ = Wire.read();
}

}



template <class T> void  EEPROM_put(uint32_t eeaddress, const T& value) {
  const byte* p = (const byte*)(const void*)&value;

  byte i = 0, counter = 0;
  uint32_t address;
  byte page_space;
  byte page = 0;
  byte num_writes;
  uint16_t data_len = 0;
  byte first_write_size;
  byte last_write_size;
  byte write_size;

  // Calculate length of data
  data_len = sizeof(value);

  // Calculate space available in first page
  page_space = int(((eeaddress / pageSize) + 1) * pageSize) - eeaddress;

  // Calculate first write size
  if (page_space > 16) {
    first_write_size = page_space - ((page_space / 16) * 16);
    if (first_write_size == 0) {
      first_write_size = 16;
    }
  }
  else {
    first_write_size = page_space;
  }

  // calculate size of last write
  if (data_len > first_write_size) {
    last_write_size = (data_len - first_write_size) % 16;
  }

  // Calculate how many writes we need
  if (data_len > first_write_size) {
    num_writes = ((data_len - first_write_size) / 16) + 2;
  }
  else {
    num_writes = 1;
  }

  i = 0;
  address = eeaddress;
  for (page = 0; page < num_writes; page++)   {
    if (page == 0) {
      write_size = first_write_size;
    }
    else if (page == (num_writes - 1)) {
      write_size = last_write_size;
    }
    else {
      write_size = 16;
    }

    Wire.beginTransmission(deviceAddress);
    Wire.write((uint32_t)((address) >> 8));
    Wire.write((uint32_t)((address) & 0xFF));
    counter = 0;
    do {
      Wire.write((byte) *p++);
      i++;
      counter++;
    }
    while ((counter < write_size));
    Wire.endTransmission();
    address += write_size;                      // увеличиваем адрес для следующего буфера
    delay(5);                                   // задержка нужна для того, чтобы ЕЕПРОМ успела 
                                                // зарядить ячейки памяти (смотрите даташит своего устройства)
  }
}


 

sadman41
Offline
Зарегистрирован: 19.10.2016

Просто интересно - для чего такая конструкция: Wire.write((uint32_t)((address) >> 8));

kostyamat
Offline
Зарегистрирован: 16.11.2017

Способ использования прост до безобразия. Создаем новую вкладку в скетче, обзываем ее к примеру EEPROM24Cxxx.h, потом в основном скетче

#include "EEPROM24Cxxx.h"



void setup() {
  Wire.begin();
  Wire.setClock(800000L);                                   /* I2C на скорости 800кГц. 
  Лучше ставить значение  400000L (400кГц) если используется с дисплеями типа LCD1602 - 2004
  и аналогичными, висящими на той же шине, иначе дисплей зависает намертво, и вешает шину I2C. 
  Скорость записи\считывания соответственно существенно ниже. 
  Некоторые ЕЕПРОМ вполне работают на частоте до 1Мгц */

  Serial.begin(115200); 

  String myString = " Запись в ЕЕПРОМ - OK";

  unsigned int addRec  = 512;                                // Переменная содержит адрес записи в ЕЕПРОМ
  
  EEPROM_put(addRec,  myString);                             // Запись переменной

  delay(1000);

  myString = "новая строка"; 

  Serial.println(myString);
  delay(1000);

  EEPROM_get(addRec, myString);                                // Считывание переменной

  delay(1000);
  Serial.println(myString);

}


void loop () {
}

Функции читают и пишут все подряд - любые данные, любого типа (вплоть до сложных структур, размером от 1-го до 256 байтов. 

 

 

ПС. написано все это для новичков, в первую очередь (сам себя, по факту, еще из этого статуса не исключил), Гуру тутышние "сами с усами". Так что сильно не пинайте, кому-то пригодится. ;)

ППС. @Модераторы:   Закрепить бы где, а то утонет. Вещь вроде полезная.

kostyamat
Offline
Зарегистрирован: 16.11.2017

sadman41 пишет:

Просто интересно - для чего такая конструкция: Wire.write((uint32_t)((address) >> 8));

честно, сам не знаю, срисовал :))

sadman41
Offline
Зарегистрирован: 19.10.2016

Тут ЕвгенийП как-то писал, что подобные штуки темплейтами не очень полезно делать. Тем более, что первой операцией у Вас сразу идёт приведение указателя к байтовому. 

size_t EEPROM_put(uint32_t eeaddress, const uint8_t* ptrData, const size_t sizeData) {...; return writtenBytes; } должна меньше бить МК по ресурсам.

sadman41
Offline
Зарегистрирован: 19.10.2016

kostyamat пишет:

sadman41 пишет:

Просто интересно - для чего такая конструкция: Wire.write((uint32_t)((address) >> 8));

честно, сам не знаю, срисовал :))

Wire.h : inline size_t write(unsigned long n) { return write((uint8_t)n); }

Logik
Offline
Зарегистрирован: 05.08.2014

Ну вобще работать с ЕЕПРОМ странично - очень верно. Про неуместность шаблонов уже написано. Если уж сильно хочется имет функции прям для классов - ну напишите сразу для буфера а из шаблона уже её вызывайте, всеж компактней. Но куда интересней вот это:

// структура размером\переменная, размером 43 байта

однако

Физический буффер i2c шины в Ардуино равен 32 байта, поэтому, воспользоваться прелестями чтения, 64 или 128 байт за раз, не получится.

Получается не решена задача. Можна бы одной страницей, а прийдется за 2-3 ?

К тому же, увеличение буффера до 128 байт было бы слишком расточительно для ОЗУ ардуино.

И с этим я согласен. Потому логично не делать буферизацию чтения. Тогда и структуру принять можна, и ОЗУ не разбрасыватся. Попробуйте калбэк, вам понравится. Тем более что i2c синхронная шина, она просто просит такое.

 

PRC
Offline
Зарегистрирован: 03.02.2019

Сейчас лучше FRAM использовать. При питании работает как ОЗУ, сама при снятии питания сохраняет, а при включении восстанавливает данные. И скорость шины официально до 1МГц.

kostyamat
Offline
Зарегистрирован: 16.11.2017

Logik пишет:

Ну вобще работать с ЕЕПРОМ странично - очень верно. Про неуместность шаблонов уже написано. Если уж сильно хочется имет функции прям для классов - ну напишите сразу для буфера а из шаблона уже её вызывайте, всеж компактней. Но куда интересней вот это:

// структура размером\переменная, размером 43 байта

однако

Физический буффер i2c шины в Ардуино равен 32 байта, поэтому, воспользоваться прелестями чтения, 64 или 128 байт за раз, не получится.

Получается не решена задача. Можна бы одной страницей, а прийдется за 2-3 ?

К тому же, увеличение буффера до 128 байт было бы слишком расточительно для ОЗУ ардуино.

И с этим я согласен. Потому логично не делать буферизацию чтения. Тогда и структуру принять можна, и ОЗУ не разбрасыватся. Попробуйте калбэк, вам понравится. Тем более что i2c синхронная шина, она просто просит такое.

 


А что не так с шаблонами? Можно где то почитать?

Да, 43 байта, такова задача.

Проблема чтения страницами по 64/128 байт действительно решена не полностью, читаем фреймами по 32 байта, но за один вызов функции.

"Попробуйте калбэк, вам понравится. Тем более что i2c синхронная шина, она просто просит такое." - можно поподробнее, особенно интересно что вы имеете в виду под словом "калбек"?

Кстати, запись тех же данных сейчас - около 23 секунд, было больше 10 минут, а скорость поиска 1,2 секунды, а было 17. Если запускать шину на 800мГц, то ищет за 0.7 секунды.

sadman41
Offline
Зарегистрирован: 19.10.2016

PRC пишет:

Сейчас лучше FRAM использовать. При питании работает как ОЗУ, сама при снятии питания сохраняет, а при включении восстанавливает данные. И скорость шины официально до 1МГц.

В какой области фрамка сама сохраняет и восстанавливает данные - в ОЗУ микроконтроллера что ли?

Logik
Offline
Зарегистрирован: 05.08.2014

kostyamat пишет:
"Попробуйте калбэк, вам понравится. Тем более что i2c синхронная шина, она просто просит такое." - можно поподробнее

Все просто. При инициализации обмена (далее для определенности пусть чтение будет) вызываем соответствующую функцию с адресом и новый параметр - указатель на функцию принимающую данные, оно и есть калбек. Считали байт - вызвали эту функцию передав этот считаный байт, она уже знает что с ним делать, может и в структуру сложить, а может сразу обработать, зачемто же он нужен. Это простейшая реализация. Она позволяет отказатся от буфера но имеет и недостаток - побайтно может оказатся слишком медленно.

Совершенствуем. Пусть калбек принимает не один байт, а несколько, через небольшой буфер, который сам калбек и укажет и размер которого сама и сообщит. Тогда работа будет такая. Инитим обмен, передаем ей адрес и калбек. Начинается страничное чтение, но сколько считать и куда девать - неизвестно. Поэтому функция чтения вызывает калбек, а он возвращает сколько байт нужно считать и указатель на буфер куда их положить. Это может быть часть структуры, или вся, вобщем по ситуации. Функция страничного чтения считывает сколько нужно и снова вызывает калбек, передав считаные данные. При этом страничное чтение не завершается, оно просто откладывается пока калбек отработает. А синхронная шина это терпит, она нас никуда не торопит! Калбек их может обработать по ходу, а может и просто сохранить, но в любом случае он снова скажет сколько байт нужно и куда их укладывать. И так пока калбек не вернет 0 - чтение закончено. Так можна без буфера сразу в свою структуру читать, а если какие данные не нужны - то и пропустить можна. Насколько стандартная либа i2c к такому готова - незнаю, я её не пользую, вероятно допиливать надо. 

Таким образом можна строить и цепочки по обработке данных. Правда есть один подводный камень, не так давно напоролся. Предполагалось читать с 24с512 данные логирования процесса и выводить на экран график. Я построил вот так цепочку. И оно не заработало. Потому что экран и 24с512 на одном i2c висели, и вывод данных на экран нарушал страничное чтение. Решение очевидное - сделал второй аппаратный i2c и нормас.

Logik
Offline
Зарегистрирован: 05.08.2014

sadman41 пишет:

PRC пишет:

Сейчас лучше FRAM использовать. При питании работает как ОЗУ, сама при снятии питания сохраняет, а при включении восстанавливает данные. И скорость шины официально до 1МГц.

В какой области фрамка сама сохраняет и восстанавливает данные - в ОЗУ микроконтроллера что ли?

)))

В блокнот записывает;)

Загугли бы шоле FRAM, не позорился бы лишний раз.

Logik
Offline
Зарегистрирован: 05.08.2014

kostyamat пишет:
Кстати, запись тех же данных сейчас - около 23 секунд, было больше 10 минут, а скорость поиска 1,2 секунды, а было 17. Если запускать шину на 800мГц, то ищет за 0.7 секунды.

Так страничная работа рулит однозначно!

kostyamat
Offline
Зарегистрирован: 16.11.2017

Ну так... я уже хотел от задачи отказаться, скорость реакции  микроконтроллера с такой памятью сильно не устаривала, но интересно стало. Сейчас, в принципе, скорость практически устраивает.

Кстати, в этих "моих" функциях (хотя чтение я  сам писал, а  запись сильно правил) буфер как таковой даже не применяется. Я вот не помню, буфер I2C аппаратный, или на стеке висит? Но так или иначе он уже висит в памяти, а свой я не создаю, а даже переменную не создаю для возврата, а прямо в область памяти живой переменной данные вписываю. Остальные созданные переменные, при выходе из функции умрут.

Похоже это таки оптимально. Или ошибаюсь?

Кстати, если кто из грамотных людей это все еще причешет - я только рад буду. Может получится практически универсальная библиотека для 24Cxxx. Увы микросхемы поменьше объемом не совместимы остаются, у меня с 24C32 не заработало. Видать страничный режим ей не по зубам, даташит не искал, лень было, не очень то и хотелось, просто воткнул вместо 512, -  нет так нет.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

я делал с общим кэшем на запись/чтение на 16 байт, скорость мня устроила. 

Logik
Offline
Зарегистрирован: 05.08.2014

kostyamat пишет:

 Я вот не помню, буфер I2C аппаратный, или на стеке висит? 

Вобще и я таких вещей стараюсь не запоминать ;) Но открываю Wire.c,  а в нем

uint8_t TwoWire::rxBuffer[BUFFER_LENGTH];
....
uint8_t TwoWire::txBuffer[BUFFER_LENGTH];

Ну и в Wire.h загляну: #define BUFFER_LENGTH 32

Итого два буфера по 32байта глобально. 

У 24C32 страницы менше, может из-за этого. И кажется адресация немного по другому, т.к. адрес короче.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Адресация одинакова от 24с32 до 24с512, адрес 2 байта.  Размер страниц разный. 

24с32 стоит на китайских модулях часов 3231, я с ней баловался. Этот же код без переделок работал на внешних модулях 24с256, вроде ошибок не было, я там пиктограммы храню, при косяках с адресацией все наглядно на экранчике видно. 

kostyamat
Offline
Зарегистрирован: 16.11.2017

Вы правы, в даташите  на 24С32 размер страницы  равен 32 байта, мои функции должны вполне себе работать. Только в 24Cxxx.h нужно дефайн размера страницы в 32 поставить.

kostyamat
Offline
Зарегистрирован: 16.11.2017

Logik пишет:

kostyamat пишет:

 Я вот не помню, буфер I2C аппаратный, или на стеке висит? 

Вобще и я таких вещей стараюсь не запоминать ;) Но открываю Wire.c,  а в нем

uint8_t TwoWire::rxBuffer[BUFFER_LENGTH];
....
uint8_t TwoWire::txBuffer[BUFFER_LENGTH];

Ну и в Wire.h загляну: #define BUFFER_LENGTH 32

Итого два буфера по 32байта глобально. 

У 24C32 страницы менше, может из-за этого. И кажется адресация немного по другому, т.к. адрес короче.

интересный факт, я в библу Wire.h лазил, и размер буфера увеличивал до 64-х, дефайн менял на 64. По факту он все равно 32 байта оставался. Почему ?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Не лазил, не знаю.

Feofan
Offline
Зарегистрирован: 28.05.2017
kostyamat пишет:
По факту он все равно 32 байта оставался. Почему?
Попробуйте одновременно изменить в twi.h #define TWI_BUFFER_LENGTH 32. Но эти изменения могут негативно сказаться в других местах.
kostyamat
Offline
Зарегистрирован: 16.11.2017

В первичном коде были некоторые шероховатости. Все забываю выложить окончательный исправленный вариант

вот вспомнил




#ifndef EEPROM24Cxxx_h
#define EEPROM24Cxxx_h

#include <Arduino.h>                                    
#include <Wire.h>

#define deviceAddress 0x50         // Адресс микросхемы по умолчанию, А0-А2 подключены к GND (или висят в воздухе, что не желательно).
#define pageSize 64                // смотрите даташит на свою микросхему 24Схххх, размер страницы может быть 32 байта, 64 так и 128 байт.
#define waitForWrite 5             // время миллисекунд для физической зарядки ячеек памяти, зависит от типа микросхемы, смотрите даташит




/*
  Физический буффер i2c шины в Ардуино равен 32 байта. Поэтому пишем блоками по 16 байт, но целую страницу за один вызов функции записи,
  а читаем блоками по 32 байта, но целую страницу за один вызов функции чтения. Таким образом удается воспользоваться страничным режимом,
  что роезко увеличивает скорость чтения\записи в ЕЕПРОМ, поддерживающую страничный режим  записи\чтения данных.
*/

template <class T> void EEPROM_get(unsigned long eeaddress, T& value) {
  uint8_t num_bytes = sizeof(value);
  byte* p = (byte*)(void*)&value;
  byte countChank = num_bytes / 32;
  byte restChank = num_bytes % 32;
  unsigned long addressChank = 0;
  if (countChank > 0) {
    for (byte i = 0; i < countChank; i++) {
      addressChank = eeaddress + 32 * i;
      Wire.beginTransmission(deviceAddress);
      Wire.write((unsigned long)(addressChank >> 8));
      Wire.write((unsigned long)(addressChank & 0xFF));
      Wire.endTransmission();
      Wire.requestFrom(deviceAddress, 32);
      while (Wire.available()) *p++ = Wire.read();
    }
  }
  if (restChank > 0) {
    if (countChank > 0) addressChank += 32;

    Wire.beginTransmission(deviceAddress);
    Wire.write((unsigned long)((addressChank) >> 8));
    Wire.write((unsigned long)((addressChank) & 0xFF));
    Wire.endTransmission();
    Wire.requestFrom(deviceAddress, restChank);
    while (Wire.available()) *p++ = Wire.read();
  }

}



template <class T> void  EEPROM_put(unsigned long eeaddress, const T& value) {
  const byte* p = (const byte*)(const void*)&value;

  byte counter = 0;
  unsigned long address;
  byte page_space;
  byte page = 0;
  byte num_writes;
  uint16_t data_len = 0;
  byte first_write_size;
  byte last_write_size;
  byte write_size;

  // Calculate length of data
  data_len = sizeof(value);

  // Calculate space available in first page
  page_space = int(((eeaddress / pageSize) + 1) * pageSize) - eeaddress;

  // Calculate first write size
  if (page_space > 16) {
    first_write_size = page_space - ((page_space / 16) * 16);
    if (first_write_size == 0) {
      first_write_size = 16;
    }
  }
  else {
    first_write_size = page_space;
  }

  // calculate size of last write
  if (data_len > first_write_size) {
    last_write_size = (data_len - first_write_size) % 16;
  }

  // Calculate how many writes we need
  if (data_len > first_write_size) {
    num_writes = ((data_len - first_write_size) / 16) + 2;
  }
  else {
    num_writes = 1;
  }

 
  address = eeaddress;
  for (page = 0; page < num_writes; page++)   {
    if (page == 0) {
      write_size = first_write_size;
    }
    else if (page == (num_writes - 1)) {
      write_size = last_write_size;
    }
    else {
      write_size = 16;
    }

    Wire.beginTransmission(deviceAddress);
    Wire.write((unsigned long)((address) >> 8));
    Wire.write((unsigned long)((address) & 0xFF));
    counter = 0;
    do {
      Wire.write((byte) *p++);
      counter++;
    } while ((counter < write_size));
    Wire.endTransmission();
    address += write_size;                      // увеличиваем адрес для следующего буфера
    delay(waitForWrite);                        // задержка нужна для того, чтобы ЕЕПРОМ успела зарядить ячейки памяти
  }
}

 

Green
Offline
Зарегистрирован: 01.10.2015

kostyamat пишет:
  Физический буффер i2c шины в Ардуино равен 32 байта. Поэтому пишем блоками по 16 байт, но целую страницу за один вызов функции записи,  а читаем блоками по 32 байта, но целую страницу за один вызов функции чтения. Таким образом удается воспользоваться страничным режимом,  что роезко увеличивает скорость чтения\записи в ЕЕПРОМ, поддерживающую страничный режим  записи\чтения данных.

Запись 30-ти байтными блоками быстрее, нежели 16-ти. И написано... ужос(.

b707
Offline
Зарегистрирован: 26.05.2017

Green пишет:

Запись 30-ти байтными блоками быстрее, нежели 16-ти. И написано... ужос(.

а если отказаться от использования Wire.h, то можно будет писать блоками неограниченного размера, хоть всю микросхему одним движением. Да и размер кода поменьше.

писал подобное для FRAM : https://arduino.ru/forum/proekty/mini-biblioteka-zapisi-i-chteniya-pamyati-fram-fm24

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Green пишет:

Запись 30-ти байтными блоками быстрее, нежели 16-ти. И написано... ужос(.

с 16-байтными блоками арифметика проще.  Я писал давно для одного проекта универсальный (пстрактный) классик TCachedROM, от которого потом наследуются и класс встроенного EEPROM и любые внешние AT24Cxx, дак тоже остановился на 16 байтном буфере, в итоге получилось проще. 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

b707 пишет:
а если отказаться от использования Wire.h, то можно будет писать блоками неограниченного размера, хоть всю микросхему одним движением. Да и размер кода поменьше.

Это правда чистая.  :) 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

Green пишет:

Запись 30-ти байтными блоками быстрее, нежели 16-ти. И написано... ужос(.

с 16-байтными блоками арифметика проще.  Я писал давно для одного проекта универсальный (пстрактный) классик TCachedROM, от которого потом наследуются и класс встроенного EEPROM и любые внешние AT24Cxx, дак тоже остановился на 16 байтном буфере, в итоге получилось проще. 

дай ссылку

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ua6em пишет:
дай ссылку

Нет

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

ua6em пишет:
дай ссылку

Нет

нет потому что нет или потому что её нет?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Найду, тока когда дома буду.  В октябре.  А оно тебе точно ннада?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

Найду, тока когда дома буду.  В октябре.  А оно тебе точно ннада?

срочно точно не надо

Green
Offline
Зарегистрирован: 01.10.2015

b707 пишет:

а если отказаться от использования Wire.h, то можно будет писать блоками неограниченного размера, хоть всю микросхему одним движением. Да и размер кода поменьше.

писал подобное для FRAM : https://arduino.ru/forum/proekty/mini-biblioteka-zapisi-i-chteniya-pamyati-fram-fm24


Вместо Wire есть совсем компактные: https://habr.com/ru/company/ruvds/blog/670392/.
У FRAM адресация иная. И готовность не нужно ждать.)

Green
Offline
Зарегистрирован: 01.10.2015

У обычных 24с, при страничной записи, время записи получается всего в 2 раза ниже времени чтения. 

Write 5632 bytes... OK Time 314 ms
Compare... OK Time 151 ms

Не так уже и плохо.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Green пишет:

У обычных 24с, при страничной записи, время записи получается всего в 2 раза ниже времени чтения. 

Write 5632 bytes... OK Time 314 ms
Compare... OK Time 151 ms

Не так уже и плохо.

я бы сказал - отлично!

b707
Offline
Зарегистрирован: 26.05.2017

Green пишет:

У обычных 24с, при страничной записи, время записи получается всего в 2 раза ниже времени чтения.

это через что?

Green
Offline
Зарегистрирован: 01.10.2015

TWI без Wire, 128 байт блок, 24с512.

b707
Offline
Зарегистрирован: 26.05.2017

Green пишет:

TWI без Wire, 128 байт блок, 24с512.

а подряд через границы страниц оно не пишет? у ФРАМ можно хоть весь чип одним разом записать

Green
Offline
Зарегистрирован: 01.10.2015

Мне пока нет необходимости - как раз нужно писать 128-ти байтными блоками.
Но и с произвольными адресами/размерами не сложно сделать.

Green
Offline
Зарегистрирован: 01.10.2015

b707 пишет:

у ФРАМ можно хоть весь чип одним разом записать


Там по разному. Но в общем, страницы выбираются битами в slave адресе.

b707
Offline
Зарегистрирован: 26.05.2017

Green пишет:

страницы выбираются битами в slave адресе.

все верно. Но если выбрать стартовый адрес за 2 байта до конца страницы и потом записать по этому адресу пачку в 20 байт - 2 байта запишутся на текущую страницу, а 18 байт автоматически будут записаны в начало следующей.

Green
Offline
Зарегистрирован: 01.10.2015

Да? Я понял по другому:

After transmission of each data byte, just prior to the 
acknowledge, the FM24C512 increments the internal 
address latch. This allows the next sequential byte to 
be accessed with no additional addressing externally. 
When auto-incrementing, the user must be aware that 
the  address  DOES  NOT  increment  from  7FFFh  to 
8000h and DOES NOT wrap from FFFFh to 0000h. 
The  memory  should  be  treated  as  two  separate 
address  spaces,  an  upper  and  lower.  When  the  last 
address  in  the  lower  half  (7FFFh)  is  reached,  the 
address rolls  over  to 0000h. Likewise when  the last 
address  in  the  upper  half  (FFFFh)  is  reached,  the 
address rolls over to 8000h. 

Может не у всех? 

b707
Offline
Зарегистрирован: 26.05.2017

Green пишет:

Да? Я понял по другому:

The  memory  should  be  treated  as  two  separate 
address  spaces,  an  upper  and  lower.  When  the  last 
address  in  the  lower  half  (7FFFh)  is  reached,  the 
address rolls  over  to 0000h. Likewise when  the last 
address  in  the  upper  half  (FFFFh)  is  reached,  the 
address rolls over to 8000h. 

Может не у всех? 

ну то есть твой текст подтверждает мои слова :).

От нуля вплоть до адреса 8000h память непрерывна, а это сразу целых 256 кбит , то есть покрывает все нижние серии ФРАМ

 

Green
Offline
Зарегистрирован: 01.10.2015

ОК. Вопрос в терминологии.
 

 The  memory  is  separated  into  an 
upper and lower address space. The auto-increment 
feature of the device will cause the address to wrap 
from  7FFFh  to  0000h  in  the  lower  half  and  wrap 
from  FFFFh  to  8000h  for  the  upper  half  of  the 
memory. 

Фактически это и есть две (большие) страницы.)
По крайней мере, ведут они себя аналогично at24c512, разница только в размере этих страниц.
PS. Скорее, как два отдельный чипа.)

b707
Offline
Зарегистрирован: 26.05.2017

Green пишет:

ОК. Вопрос в терминологии.  Фактически это и есть две (большие) страницы.)

точно, в терминологии. Главное не путать эти две страницы и вот эти:)

Green пишет:

страницы выбираются битами в slave адресе.

за инфо про "большие" страницы в 256кбит спасибо, не знал этого