Запись и чтение из памяти большого числа

Паматрю вечером

1 лайк

код можно увидеть? (в смысле не код библиотеки, а Ваш , как вы пытались эту библиотеку использовать)

Да в общем то, так как указано в примере на гитхабе, пробовал и под at24c01, 02 и 04 с заменой соответствующих микросхем и подключением нужной библиотеки с указанием своего адреса микросхемы.

*Через час буду дома и покажу код.

Я писал, как раз для FM

Спойлер
#pragma once
#include <Wire.h>
// Превышение максимального размера буфера чтения/записи I2C_BUFFER_LENGTH не отслеживается!

class i2cEEPROM {
  public:
    i2cEEPROM(uint8_t addr) {_i2cAddr = addr; Wire.begin(); _selfTest();}
    uint16_t  write(uint16_t addr, void* buf, size_t len);
    uint8_t   write8(uint16_t addr, uint8_t buf);
    uint16_t  write16(uint16_t addr, uint16_t buf);
    uint16_t  write32(uint16_t addr, uint32_t buf);
    uint16_t  read(uint16_t addr, void* buf, size_t len);
    uint8_t   read8(uint16_t addr);
    uint16_t  read16(uint16_t addr);
    uint32_t  read32(uint16_t addr);
    void      printDump();
    uint16_t  maxAddress() {return _maxaddr;}
    void      erase(uint8_t tmpl = 0);
  private:
    void      _selfTest();
    uint8_t   _i2cAddr;
    uint16_t  _maxaddr = 65535;
};


#include "I2c_FM_EEPROM.h"
#include "Wire.h"
#include "Arduino.h"



void i2cEEPROM::_selfTest(){
  uint8_t b0 = read8(0);                            // Save first byte
  uint8_t bn;                 
  uint8_t testVal = 123;
  uint16_t sz[] = {2047, 4095, 8191, 16383, 32767, 65535};
  write8(0, 0);                                     // Set first bype to 0
  for (uint8_t i = 0; i < (sizeof(sz)/sizeof(sz[0])); i++){
    bn = read8(sz[i]+1);                            // Save test byte
    write8(sz[i]+1, testVal);                       // Trying to write bype to first "out of range" address
    if (read8(0) != 0 || read8(sz[i]+1) != testVal){  // Test write failed
      _maxaddr = sz[i];
      write8(0, b0);                                // Restore 0 byte     
      return;
    }
      write8(sz[i]+1, bn);                          // Restore test byte
  }
}


uint16_t i2cEEPROM::write(uint16_t addr, void* buf, size_t len) {
  if ((addr + len) > _maxaddr) return 0;
  uint16_t ctr = 0;
  Wire.beginTransmission(_i2cAddr);
  Wire.write(addr >> 8);
  Wire.write(addr & 0xFF);
  for (uint8_t i = 0; i < len; i++) {
    ctr += Wire.write(*(((uint8_t*)buf) + i));
  }
  Wire.endTransmission();
  return ctr;
}

uint8_t i2cEEPROM::write8(uint16_t addr, uint8_t buf) {
  if (addr > _maxaddr) return 0;
  uint16_t ctr {0};
  Wire.beginTransmission(_i2cAddr);
  Wire.write(addr >> 8);
  Wire.write(addr & 0xFF);
    ctr += Wire.write(buf);
  Wire.endTransmission();
  return ctr;
}


uint16_t i2cEEPROM::write16(uint16_t addr, uint16_t buf) {
  uint16_t ctr = write(addr, &buf, sizeof(buf));
  return ctr;
}

uint16_t i2cEEPROM::write32(uint16_t addr, uint32_t buf) {
  uint16_t ctr = write(addr, &buf, sizeof(buf));
  return ctr;
}

uint16_t i2cEEPROM::read(uint16_t addr, void* buf, size_t len) {
  uint8_t* dat = (uint8_t*)buf;
  uint16_t count{0};
  Wire.beginTransmission(_i2cAddr);
  Wire.write(addr >> 8);
  Wire.write(addr & 0xFF);
  Wire.endTransmission(false);
  Wire.requestFrom(_i2cAddr, len);
  for (uint16_t i = 0; i < len; i++) {
    *(dat + i) = Wire.read();
    count++;
  }
  Wire.endTransmission();
  return count;
}


uint8_t i2cEEPROM::read8(uint16_t addr) {
  Wire.beginTransmission(_i2cAddr);
  Wire.write(addr >> 8);
  Wire.write(addr & 0xFF);
  Wire.endTransmission(false);
  Wire.requestFrom(_i2cAddr, (uint8_t)1);
  uint8_t ret  = Wire.read();
  Wire.endTransmission();
  return ret;
}


uint16_t i2cEEPROM::read16(uint16_t addr) {
  Wire.beginTransmission(_i2cAddr);
  Wire.write(addr >> 8);
  Wire.write(addr & 0xFF);
  Wire.endTransmission(false);
  Wire.requestFrom(_i2cAddr, (uint8_t)2);
  uint16_t ret {0};
  for (uint8_t i = 0; i < 2; i++) {
    *((uint8_t*)&ret + i) = Wire.read();
  }
  Wire.endTransmission();
  return ret;
}


uint32_t i2cEEPROM::read32(uint16_t addr) {
  Wire.beginTransmission(_i2cAddr);
  Wire.write(addr >> 8);
  Wire.write(addr & 0xFF);
  Wire.endTransmission(false);
  Wire.requestFrom(_i2cAddr, (uint8_t)4);
  uint32_t ret {0};
  for (uint8_t i = 0; i < 4; i++) {
    *((uint8_t*)&ret + i) = Wire.read();
  }
  Wire.endTransmission();
  return ret;
}


void i2cEEPROM::erase(uint8_t tmpl){
  for (uint16_t i = 0; i<_maxaddr; i++){
    write8(i, tmpl);  
  }
}

// Печатаем на экран содержимое EEPROM для визуального контроля
void i2cEEPROM::printDump(){
  uint8_t buf[64]{0};
  Serial.println("Memory dump"); 
  for (uint16_t r{0}; r < _maxaddr/64; r++) {    
    Serial.print(r, HEX); Serial.print(": ");
      read(r*64, buf, sizeof(buf));
      for(uint8_t i{0}; i < 64; i++){
        Serial.print(buf[i], HEX); Serial.print(" ");
      }
    Serial.print('\n');
  } 
  Serial.print('\n'); 
}

важнее не FM24 или AT24, а емкость чипа, ибо код адресации ячеек зависит от размера.
Судя по вашему коду, вы этот факт не учитываете… или я не увидел

Учитывает. Метод selfTest. А страниц у фрам нет, адресация линейная. Ну и 5 миллис на запись не требуется ей
У меня есть версия этого пасквиля для не-фрам. Там размер страницы устанавливается исходя из определенного при создании экземпляра _maxaddr

Есть у объёмных. У 512 2 по 32К.

О! с поддержкой CAT протокола YAESU-897(817,857)

У меня таких нет, я и не заморачивался. Буржуи могут допилить класс под себя))

Типа как для АТ. Сложнее, но не сильно

Спойлер
#ifndef I2c_h

#define I2c_h

#include <Wire.h>

class i2cEEPROM {
  public:
    i2cEEPROM(uint8_t addr) {_i2cAddr = addr; selfTest();}
    //~i2cEEPROM();
    uint16_t  write(uint16_t addr, void* buf, size_t len);  // Запись в EEPROM. 
    uint8_t   write8(uint16_t addr, uint8_t dat);
    uint8_t   write16(uint16_t addr, uint16_t dat);
    uint8_t   write32(uint16_t addr, uint32_t dat);
    uint16_t  read(uint16_t addr, void* buf, size_t len);
    uint8_t   read8(uint16_t addr);
    uint16_t  read16(uint16_t addr);
    uint32_t  read32(uint16_t addr);
    void      printDump(uint16_t pages = 0);
    uint8_t   pageSize() {return _pageSize;}
    uint16_t  maxAddress() {return _maxaddr;}
    void      erase(uint8_t tmpl = 0);
  private:
    uint8_t   _i2cAddr;
    uint16_t  _maxaddr = 65535;
    uint8_t   _pageSize;
    void      selfTest();
    uint16_t  pageWrite(uint16_t addr, void* buf, size_t len);
    uint16_t  pageRead(uint16_t addr, void* buf, size_t len);
    uint16_t  firstPageAddr(uint16_t page);
};


#endif


#include "I2c_EEPROM.h"
#include "Wire.h"
#include "Arduino.h"



void i2cEEPROM::selfTest(){
  uint8_t b0 = read8(0);                            // Save first byte
  uint8_t bn;                 
  uint8_t testVal = 123;
  uint16_t sz[] = {2047, 4095, 8191, 16383, 32767, 65535};
  uint16_t mp[] = {16, 32, 32, 64, 64, 64};  
  write8(0, 0);                                     // Set first bype to 0
  for (uint8_t i = 0; i < (sizeof(sz)/sizeof(sz[0])); i++){
    bn = read8(sz[i]+1);                            // Save test byte
    write8(sz[i]+1, testVal);                       // Trying to write bype to first "out of range" address
    if (read8(0) != 0 || read8(sz[i]+1) != testVal){  // Test write failed
      _maxaddr = sz[i];
      _pageSize = mp[i];
      write8(0, b0);                                // Restore 0 byte     
      return;
    }
      write8(sz[i]+1, bn);                          // Restore test byte
  }
}



uint16_t i2cEEPROM::write(uint16_t addr, void* buf, size_t len) {
  if ((addr + len) > _maxaddr) return 0;
  uint16_t thisPage = addr / _pageSize;
  uint16_t count = 0;
  if (thisPage == (addr + len) / _pageSize) {
    count = pageWrite(addr, buf, len);
    return count;
  }
  else {
    count = pageWrite(addr, buf, firstPageAddr(thisPage + 1) - addr);
    thisPage++;
    while ((len - count) > _pageSize) {
      count += pageWrite(firstPageAddr(thisPage), (uint8_t*)buf + count, _pageSize);
      thisPage++;
    }
    count += pageWrite(firstPageAddr(thisPage), (uint8_t*)buf + count, len - count);
  }
  return count;
}


uint8_t i2cEEPROM::write16(uint16_t addr, uint16_t dat) {
  if ((addr + sizeof(dat)) > _maxaddr) return 0;
  uint16_t thisPage = addr / _pageSize;
  uint8_t count = 0;
  if (thisPage == (addr + sizeof(dat)) / _pageSize) {
    count = pageWrite(addr, &dat, sizeof(dat));
    return count;
  }
  else {
    count = pageWrite(addr, &dat, firstPageAddr(thisPage + 1) - addr);
    thisPage++;
    count += pageWrite(firstPageAddr(thisPage), &dat + count, sizeof(dat) - count);
  }
  return count;
}


uint8_t i2cEEPROM::write32(uint16_t addr, uint32_t dat) {
  if ((addr + sizeof(dat)) > _maxaddr) return 0;
  uint16_t thisPage = addr / _pageSize;
  uint8_t count = 0;
  if (thisPage == (addr + sizeof(dat)) / _pageSize) {
    count = pageWrite(addr, &dat, sizeof(dat));
    return count;
  }
  else {
    count = pageWrite(addr, &dat, firstPageAddr(thisPage + 1) - addr);
    thisPage++;
    count += pageWrite(firstPageAddr(thisPage), &dat + count, sizeof(dat) - count);
  }
  return count;
}


uint8_t i2cEEPROM::write8(uint16_t addr, uint8_t dat) {
  if (addr > _maxaddr) return 0;
    uint8_t count = pageWrite(addr, &dat, sizeof(dat));
    return count;
}


uint16_t i2cEEPROM::read(uint16_t addr, void* buf, size_t len) {
  if ((addr + len) > _maxaddr) return 0;
  uint16_t ctr = 0;
  if (len <= I2C_BUFFER_LENGTH - 2) {
    ctr = pageRead(addr, buf, len);
    return ctr;
  }
  else {
    uint16_t twiPages = 0;
    while ((len - ctr) > (I2C_BUFFER_LENGTH - 2)) {
      ctr += pageRead(addr + ((I2C_BUFFER_LENGTH - 2) * twiPages), (uint8_t*)buf + ctr, (I2C_BUFFER_LENGTH - 2));
      twiPages++;
    }
    ctr += pageRead(addr + ((I2C_BUFFER_LENGTH - 2) * twiPages), (uint8_t*)buf + ctr, len - ctr);
  }
  return ctr;
}


uint8_t i2cEEPROM::read8(uint16_t addr) {
  Wire.beginTransmission(_i2cAddr);
  Wire.write(addr >> 8);
  Wire.write(addr & 0xFF);
  Wire.endTransmission(false);
  Wire.requestFrom(_i2cAddr, sizeof(uint8_t));
  uint8_t ret  = Wire.read();
  Wire.endTransmission();
  return ret;
}

uint16_t i2cEEPROM::read16(uint16_t addr) {
  Wire.beginTransmission(_i2cAddr);
  Wire.write(addr >> 8);
  Wire.write(addr & 0xFF);
  Wire.endTransmission(false);
  Wire.requestFrom(_i2cAddr, (uint8_t)2);
  uint16_t ret = 0;
  for (uint8_t i = 0; i < 2; i++) {
    *((uint8_t*)&ret + i) = Wire.read();
  }
  Wire.endTransmission();
  return ret;
}


uint32_t i2cEEPROM::read32(uint16_t addr) {
  Wire.beginTransmission(_i2cAddr);
  Wire.write(addr >> 8);
  Wire.write(addr & 0xFF);
  Wire.endTransmission(false);
  Wire.requestFrom(_i2cAddr, (uint8_t)4);
  uint32_t ret = 0;
  for (uint8_t i = 0; i < 4; i++) {
    *((uint8_t*)&ret + i) = Wire.read();
  }
  Wire.endTransmission();
  return ret;
}



uint16_t i2cEEPROM::pageWrite(uint16_t addr, void* buf, size_t len) {
  uint16_t ctr = 0;
  Wire.beginTransmission(_i2cAddr);
  Wire.write(addr >> 8);
  Wire.write(addr & 0xFF);
  for (uint8_t i = 0; i < len; i++) {
    ctr += Wire.write(*(((uint8_t*)buf) + i));
  }
  Wire.endTransmission();
  delay(5);
  return ctr;
}


uint16_t i2cEEPROM::pageRead(uint16_t addr, void* buf, size_t len) {
  uint8_t* dat = (uint8_t*)buf;
  Wire.beginTransmission(_i2cAddr);
  Wire.write(addr >> 8);
  Wire.write(addr & 0xFF);
  Wire.endTransmission(false);
  Wire.requestFrom(_i2cAddr, len);
  for (uint8_t i = 0; i < len; i++) {
    *(dat + i) = Wire.read();
  }
  Wire.endTransmission();
  return len;
}

// Печатаем на экран содержимое EEPROM для визуального контроля
void i2cEEPROM::printDump(uint16_t pgs){
  Serial.print("Memory dump of "); Serial.print(pgs);Serial.println(" pages:");
  if (pgs == 0) pgs = _maxaddr / _pageSize;
  for (uint8_t r = 0; r < pgs; r++) {    
    Serial.print(r, HEX); Serial.print(": ");
    uint16_t addr = r * _pageSize;
    Wire.beginTransmission(_i2cAddr);
    Wire.write(addr >> 8);
    Wire.write(addr & 0xFF);
    Wire.endTransmission(false);
    Wire.requestFrom((int)_i2cAddr, (int)_pageSize);
      for(uint8_t i = 0; i < _pageSize; i++){
        Serial.print(Wire.read(), HEX); Serial.print(" ");
      }
    Serial.print('\n');
  } 
  Serial.print('\n'); 
}

void i2cEEPROM::erase(uint8_t tmpl){
  uint8_t arr[_maxaddr]; 
  for (uint16_t i = 0; i<_maxaddr; i++){
    arr[i] = tmpl;
  }
  write(0, arr, _maxaddr);
}

uint16_t i2cEEPROM::firstPageAddr(uint16_t page) {
  return (page * _pageSize);
}


да нет, не учитывает.
У маленьких Фрамок адрес ячейки из одного байта, у больших - из двух.
А в вашем коде адрес из двух байт всегда:

 Wire.beginTransmission(_i2cAddr);
  Wire.write(addr >> 8);
  Wire.write(addr & 0xFF);

Сл-но например на FM24CL16, ваша третья посылка с байтом addr & 0xFF уже будет интерпретирована как данные и запишется в память.
Посмотрите даташит:

Что то я туплю. Написано же - FM24CL16B is a serial F-RAM memory. The memory array is logically organized as 2,048 × 8 bits. Как можно 2048 адресовать байтом?

Там вообще много интересных решений.
Я кроме синтезатора, на базе этого проекта много чего понаваял.
А собственно синтезатор пристроил в Виолу на 145 МГц и в недоделанный самодельный УКВ трансивер.
Вообщем, весьма достойный проект. И не только этот, много чего интересного и грамотно исполненного у этого автора есть.
Жаль только, что он в настоящий момент шибко занационализировался… :frowning:

До 2048 ячеек адрес “страницы” из 256 ячеек прибавляется к I2c адресу микросхемы. То есть если у вас базовый адрес микрухи 0х50, то адрес нулевой страницы так и будет 0х50, адрес следующей 0х51 и так далеею То есть FM24CL16 видна в И2с сканере как восемь отдельных и2с устройств с адресами подряд.
А вот начиная с FM24CL32 адресация будет как у вас:

Посмотрел, как и все любительские проекты главный недостаток - отсутствие перечня используемых библиотек и адрес где их взять

собственно код из примера с гитхаба, микросхема у меня применена AT24C04

/**
 * This sketch shows examples on how to use all the features of this library
 * 
 * It can also be used as a test  to verify that you have your eprom configured
 * propery to your Arduino as it prints out the results so you can see if everything works
 */

#include <at24c04.h>

// Create a eprom object configured at address 0
// Sketch assumes that there is an eprom present at this address
AT24C04 eprom(AT24C_ADDRESS_0);// 0x50
// Create another eprom object configured att address 2
// Sketch assumes that there is NO eprom present at this address
AT24C04 badEprom(AT24C_ADDRESS_2);

void setup() {
  Serial.begin(115200);
  Serial.println("Starting up");

  // Initialize the i2c library
  Wire.begin();

  /** Write and read an integer */
  int foo = 42;
  // Write the integer foo to the eprom starting at address 0
  eprom.put(0, foo);
  int foo_in;
  // Read the integer foo_in from eprom starting at address 0
  eprom.get(0, foo_in);
  Serial.println(foo_in);

  /** Write and read a double */
  double pi = 3.141593;
  // Write the double pi to the eprom starting at address 0
  eprom.put(0, pi);
  double pi_in;
  // Read the double pi_in from eprom starting at address 0
  eprom.get(0, pi_in);
  // Create a buffer and convert pi_in to a string to be able to print it
  char buffer[10];
  dtostrf(pi_in, 9, 6, buffer); // Converts a double to a string
  Serial.println(buffer);

  /** Write and read a struct */
  // Declare the struct "Point"
  struct Point {
    int x;
    int y;
  };
  Point point = {17, 42};
  // Write the struct point to the eprom starting at address 0
  eprom.put(0, point);
  Point point_in;
  // Read the struct point_in from eprom starting at address 0
  eprom.get(0, point_in);
  Serial.println(point_in.x);
  Serial.println(point_in.y);

  /** Write and read a single byte */
  // Write the value 77 to the eprom byte at address 0
  eprom.write(0, 77);
  // Read the value of the byte at address 0
  int value = eprom.read(0);
  Serial.println(value);

  /** Write and read a byte buffer */
  uint8_t out[15] = "Test of buffer";
  // Write the 15 bytes long buffer "out" to eprom starting at address 0
  eprom.writeBuffer(0, out, 15);
  uint8_t in[15];
  // Read 15 bytes from eprom starting at address 0 into the buffer "in".
  eprom.readBuffer(0, in, 15);
  Serial.println((char*)in);

  /** Error handling on write, no error */
  // Read a byte from address 0, this should not result in an error
  eprom.write(0, 77);
  // Get the last error code, it should be 0 since there was no error
  int lastError = eprom.getLastError();
  Serial.print("last status on write: ");
  Serial.println(lastError);

  /** Error handling on write, using a eprom address without eprom */
  // Write the value 77 to an eprom that is not connected - this will fail
  badEprom.write(0, 77);
  // Get the last error code, it should not be zero, but 2 which means that there were no response from the eprom
  int lastError2 = badEprom.getLastError();
  Serial.print("last error on write: ");
  Serial.println(lastError2);  

  /** Error handling on read, using a eprom address without eprom */
  // Read the value from address 0 from an eprom that is not connected (and print it)
  badEprom.read(0);
  // Get the last error code, it should not be zero, but 2 which means that there were no response from the eprom
  int lastError3 = badEprom.getLastError();
  Serial.print("last error on read: ");
  Serial.println(lastError3);  

  // Read the size of the eprom and print it
  Serial.println(eprom.length());

  /** Write and read a long byte buffer, >32 which is TwoWire's internal buffer size and 
    * >64 which is the at24c256 page size) */
  uint8_t out2[80] = "Writing a really long message, testing some of several buffer limits on the way";
  // Write the long buffer to the eprom starting at address 0 and check how many bytes were actually written
  int written = eprom.writeBuffer(0, out2, 80);
  Serial.print(written);
  Serial.print(" bytes written, last error on write: ");
  // Get the last error and print it
  Serial.println(eprom.getLastError());
  uint8_t in2[80];
  // Read 80 bytes from eprom starting at address 0 and store it in the in2 buffer. Check how many bytes were actually read
  int readBytes = eprom.readBuffer(0, in2, 80);
  Serial.print(readBytes);
  Serial.print(" bytes read, last error on read: ");
  // Get the last error and print it
  Serial.println(eprom.getLastError()); 
  in2[79] = '\0';
  Serial.println((char*)in2);
}

// This test program has no loop, it just runs once
void loop() {
}

на выходе вот такое

Starting up
16249
-0.000000
12218
-6499
77
ۺ⸮⸮⸮⸮Lq⸮⸮⸮{Test of buffer
last status on write: 2
last error on write: 0
last error on read: 0
512
0 bytes written, last error on write: 2
0 bytes read, last error on read: 2
M⸮⸮:⸮3⸮澮?⸮⸮⸮c'⸮⸮M⸮p⸮ڬ⸮k⸮⸮⸮⸮⸮⸮⸮ye⸮⸮⸮x⸮⸮eʿ⸮⸮]v⸮o⸮7⸮⸮⸮⸮⸮⸮⸮⸮}i⸮⸮⸮3⸮⸮⸮⸮p⸮⸮U

github com/stefangs/arduino-library-at24cxxx/blob/main/examples/example/example.ino

Всё есть. Либо непосредственно в архиве проекта, либо на гитхабе автора. Более подробно на его форуме.

заглянул в код библиотеки - похоже такая же ошибка как у @Estorsky выше - не учитывается разница в адресации маленьких и больших чипов.
Если я прав, эта библиотека правильно работает только на ЕЕПРОМках от АТ24С32 и выше.

в архиве нет, ссылка на форум блокируется, если есть список - скинь в личку

Ну так на странице самой библиотеки автор в описании явно указывает

И для подключения микрух нужно в коде указывать какая менно будет использоваться микросхема и по какому адресу.

я не хочу с вами спорить, я с годами стал невнимателен, может что и пропустил.
Я вижу что автор указал в описании, и что при подключении нужно указывать размер. Но дальше в коде эти данные используются ТОЛЬКО для прверки выхода за пределы памяти и не для чего другого.
Изменения адресации в зависимости от размера - я в коде не нашел. По моему мнению, так оно работать не будет.

Но я старенький, что с меня взять, мог и ошибиться.

PS Посмотрел даташит на АТ24С04 - адресация ячейки из одного байта. А в библиотеке из двух. И почему-то код из примеров у вас не заработал… думаете это связано или нет?