Во время создания прошивки для одного прибора, столкнулся с тем фактом, что мне нужно создать импровизированную Базу Данных, количество записей в которую может достигать пару тысяч. Понятно, что для такого количества данных, встроенного ЕЕПРОМ в Atmega328, явно недостаточно. Остановил свой выбор на микросхеме внешнего EEPROM, с поддержкой интерфейса I2C, 24-й серии, а точнее на 24C256. Перебрав кучу разных странных библиотек, понял что задержки чтения\записи меня совершенно не устраивают. К примеру нужный мне массив записей создавался более 10 минут (минут, Карл!), а поиск в этом массиве мог занимать более 15 секунд, что было совершенно неприемлемо. Мне нужна была реакция прибора не более секунды.
Почитав даташит на эти микросхемы, я узнал, что большинство из них умею писать\читать в страничном режиме. В связи с этим у меня встал вопрос о том, что нужно создать свой инструмент, который сможет работать в таком режиме, потому как практически все библиотеки использовали побайтовый способ записи\чтения.
Взяв за основу известный инструмент EEPROMAnything.h написал свой. Прошу любить и жаловать.
В связи с ограничение буфера чтения и записи I2C в ардуино в 32 байта, запись и чтение страницами работает не в полной мере, но скорость все равно выросла в десятки и сотни раз. Так запись нужного мне массива данных, вместо 10 минут, стала занимать несколько десятков секунд, а поиск в такой БД ~ 0,7c., что меня вполне устраивает.
Инструмент достаточно универсален, и пригоден к использованию практически с любой микросхемой серии 24, использующей два байта адреса, как 24c32 так и вплоть до 24c512. Увы, но микросхемы 24с1024 и 24с2048, без специальных ухищрений не могут использоваться с этим инструментом, в связи с ограничениями библиотеки Wire из ARDUINO.
Настройка этого инструмента в вашем проекте очень проста. Вам нужно узнать размер страницы из даташита на вашу микросхему и вписать в дефайн, вписать в дефайн правильный адрес на шине (или оставить дефолтный), посмотреть сколько мс занимает физическая зарядка ячеек (тоже из даташита) и вписать свое значение, или оставить дефолтные 5мс, которых хватает как правило, и все.
Еще не плохо бы глянуть с какой скоростью ваша микросхема может работать с шиной, возможные значения для атмеги 100кГц, 400кГц, 800кГц и 1МГц. Само собой, стоит запускать шину с максимальной скоростью (если скорость действительно важна), но нужно учитывать ограничения других устройств на шине. К примеру, LCD1602 c I2C приблудой не работает с частотой шины выше 400кГц.
Для использования:
#include "EEPROM24xxx.h"
запись EEPROM_put(adress, value);
чтение EEPROM_get(adress, value); // НЕ value = EEPROM_get(adress, value), темплейт пишет на прямую в переменную в памяти.
Размер и тип данных может быть любой, как однобайтный, так и сложные структуры размером до 65,535 байт.
Лично я писал\читал структуры размером в 43 байта.
Буду рад, если этот инструмент будет кому-то полезен.
UPDATE 25\11\2023 Исправлен баг с нулевым адресом .
Почитав даташит на эти микросхемы, я узнал, что большинство из них умею писать\читать в страничном режиме. В связи с этим у меня встал вопрос о том, что нужно создать свой инструмент, который сможет работать в таком режиме, потому как практически все библиотеки использовали побайтовый способ записи\чтения.
Взяв за основу известный инструмент EEPROMAnything.h написал свой. Прошу любить и жаловать.
В связи с ограничение буфера чтения и записи I2C в ардуино в 32 байта, запись и чтение страницами работает не в полной мере, но скорость все равно выросла в десятки и сотни раз. Так запись нужного мне массива данных, вместо 10 минут, стала занимать несколько десятков секунд, а поиск в такой БД ~ 0,7c., что меня вполне устраивает.
Инструмент достаточно универсален, и пригоден к использованию практически с любой микросхемой серии 24, использующей два байта адреса, как 24c32 так и вплоть до 24c512. Увы, но микросхемы 24с1024 и 24с2048, без специальных ухищрений не могут использоваться с этим инструментом, в связи с ограничениями библиотеки Wire из ARDUINO.
Настройка этого инструмента в вашем проекте очень проста. Вам нужно узнать размер страницы из даташита на вашу микросхему и вписать в дефайн, вписать в дефайн правильный адрес на шине (или оставить дефолтный), посмотреть сколько мс занимает физическая зарядка ячеек (тоже из даташита) и вписать свое значение, или оставить дефолтные 5мс, которых хватает как правило, и все.
Еще не плохо бы глянуть с какой скоростью ваша микросхема может работать с шиной, возможные значения для атмеги 100кГц, 400кГц, 800кГц и 1МГц. Само собой, стоит запускать шину с максимальной скоростью (если скорость действительно важна), но нужно учитывать ограничения других устройств на шине. К примеру, LCD1602 c I2C приблудой не работает с частотой шины выше 400кГц.
Для использования:
#include "EEPROM24xxx.h"
запись EEPROM_put(adress, value);
чтение EEPROM_get(adress, value); // НЕ value = EEPROM_get(adress, value), темплейт пишет на прямую в переменную в памяти.
Размер и тип данных может быть любой, как однобайтный, так и сложные структуры размером до 65,535 байт.
Лично я писал\читал структуры размером в 43 байта.
Буду рад, если этот инструмент будет кому-то полезен.
EEPROM24xxx.h:
#ifndef EEPROM24Cxxx_h
#define EEPROM24Cxxx_h
#include <Arduino.h>
#include <Wire.h>
#define DEVICE_ADRESS 0x50 // Адресс микросхемы по умолчанию, А0-А2 подключены к GND (или висят в воздухе, что не желательно).
#define PAGE_SIZE 64 // смотрите даташит на свою микросхему 24Схххх, размер страницы может быть 32 байта, 64 так и 128 байт.
#define WAIT_FOR_FISICAL_WRITE 5 // время миллисекунд для физической зарядки ячеек памяти, зависит от типа микросхемы, смотрите даташит
/*
Физический буффер i2c шины в Ардуино равен 32 байта. Поэтому пишем блоками по 16 байт (потому как в буфер записи помещаются не только данные,
но и адрес, все 32 байта буфера не доступны), наполняя страницу за страницей. И только если страница передана полностью, или кончились данные,
- происходит физическая запись. Объект будет записан полностью за один вызов функции записи.
Читаем блоками по 32 байта, набивая страницу за страницей, пока весь объект (переменная, структура и т.п.) не будет вычетана полностью за один
вызов функции чтения.
Таким образом удается воспользоваться страничным режимом, что резко увеличивает скорость чтения\записи в ЕЕПРОМ, поддерживающую страничный
режим записи\чтения данных. В случае чтения\записи больших объектов, скорость увеличивается в несколько десятков, а при записи и сотен, раз.
*/
template <class T> void EEPROM_get(uint16_t eeaddress, T& value) {
uint16_t num_bytes = sizeof(value);
byte* p = (byte*)(void*)&value;
byte countChank = num_bytes / 32;
byte restChank = num_bytes % 32;
uint16_t addressChank = 0;
if (countChank > 0) {
for (byte i = 0; i < countChank; i++) {
addressChank = eeaddress + 32 * i;
Wire.beginTransmission(DEVICE_ADRESS);
Wire.write((uint8_t)(addressChank >> 8));
Wire.write((uint8_t)(addressChank & 0xFF));
Wire.endTransmission();
Wire.requestFrom(DEVICE_ADRESS, 32);
while (Wire.available()) *p++ = Wire.read();
}
}
if (restChank > 0) {
if (countChank > 0)
addressChank += 32;
else
addressChank = eeaddress;
Wire.beginTransmission(DEVICE_ADRESS);
Wire.write((unsigned long)((addressChank) >> 8));
Wire.write((unsigned long)((addressChank) & 0xFF));
Wire.endTransmission();
Wire.requestFrom(DEVICE_ADRESS, restChank);
while (Wire.available()) *p++ = Wire.read();
}
}
template <class T> void EEPROM_put(uint16_t eeaddress, const T& value) {
const byte* p = (const byte*)(const void*)&value;
byte counter = 0;
uint16_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 / PAGE_SIZE) + 1) * PAGE_SIZE) - 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(DEVICE_ADRESS);
Wire.write((uint8_t)((address) >> 8));
Wire.write((uint8_t)((address) & 0xFF));
counter = 0;
do {
Wire.write((byte) *p++);
counter++;
} while ((counter < write_size));
Wire.endTransmission();
address += write_size; // увеличиваем адрес для записи следующего буфера
delay(WAIT_FOR_FISICAL_WRITE); // задержка нужна для того, чтобы ЕЕПРОМ успела физически зарядить ячейки памяти
}
}
#endif
Вложения
-
5.3 KB Просмотры: 213
Изменено: