Паматрю вечером
код можно увидеть? (в смысле не код библиотеки, а Ваш , как вы пытались эту библиотеку использовать)
Да в общем то, так как указано в примере на гитхабе, пробовал и под 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 МГц и в недоделанный самодельный УКВ трансивер.
Вообщем, весьма достойный проект. И не только этот, много чего интересного и грамотно исполненного у этого автора есть.
Жаль только, что он в настоящий момент шибко занационализировался…
До 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 - адресация ячейки из одного байта. А в библиотеке из двух. И почему-то код из примеров у вас не заработал… думаете это связано или нет?