Что не так с моим EEPROM?
Я уже давно работаю над проектом и обнаружил серьезную ошибку.
По сути, у пользователя есть сетка 8x8, и каждая ячейка в этой сетке имеет ряд свойств. Я хочу иметь возможность сохранить все эти свойства как «профиль» в EEPROM, и раньше это работало нормально, но теперь, похоже, есть несколько ошибок.
Самым ярким из которых является то, что когда я сохраняю сетку в профиле (скажем, профиль 3), а затем загружаю следующий профиль (в данном случае профиль 4), крайний правый столбец профиля 3 появляется в крайнем левом углу. столбец профиля 4. Аналогично, сохранение заполненной сетки в профиле 1, а затем сохранение пустой сетки в профиле 2 приводит к очистке ПРАВОГО столбца профиля 1.
Раньше он отлично работал в предыдущей версии, но там было совершенно другое оборудование, поддерживающее графический интерфейс, что делало невозможным загрузку более старых версий кода и тестирование того, как он работал тогда.
Вот код, в котором я сохраняю/загружаю профили
#include <EEPROM.h>
#include"EEPROMAnything.h"
const int profileSize = 224; // На самом деле все в два раза меньше, чем вы думали (WTF, но ок)
void saveProfile(int profileNum) // сохраняет полную текущую конфигурацию системы в памяти под профилем для последующего восстановления
{
tft.fillScreen(BGCOL);// Очистка экрана.
tft.setTextSize(2);
setLine0();
tft.print("Saving...");
setLine1();
tft.print(sizeof(saveConfiguration)/8); // Оригинал
tft.print(" Bytes");
setLine2();
tft.print("please wait");
// tft.sendBuffer();
for (int ROMRow = 0; ROMRow< 8; ROMRow++) // Заполнение структуры ПЗУ
{
for (int ROMCol = 0; ROMCol < 8; ROMCol++)
{
saveConfiguration.ROMcellType[ROMRow][ROMCol] = cellType[ROMRow][ROMCol];
saveConfiguration.ROMindexVal[ROMRow][ROMCol] = indexVal[ROMRow][ROMCol];
saveConfiguration.ROMMIDIVariable[ROMRow][ROMCol] = MIDIVariable[ROMRow][ROMCol];
saveConfiguration.ROMcellChannel[ROMRow][ROMCol] = cellChannel[ROMRow][ROMCol];
saveConfiguration.ROMorientation[ROMRow][ROMCol] = orientation[ROMRow][ROMCol];
saveConfiguration.ROMfaderGroundX[ROMRow][ROMCol] = faderGroundX[ROMRow][ROMCol];
saveConfiguration.ROMfaderGroundY[ROMRow][ROMCol] = faderGroundY[ROMRow][ROMCol];
}
}
EEPROM_writeAnything((profileNum*profileSize), saveConfiguration);
delay(500);
menuState = 1; // Возвращаемся в главное меню
menuLocatorY = 0; // В противном случае текст не будет отображаться на следующем экране;
updateScreen();
}
//{EEPROM_writeAnything(profileNum*profileSize, Configuration);}
void loadProfile(int profileNum) // сохраняет полную текущую конфигурацию системы в памяти под профилем для последующего восстановления
{
tft.fillScreen(BGCOL);// Очистка экрана.
tft.setTextSize(2);
setLine0();
tft.print("Loading...");
setLine1();
tft.print(sizeof(saveConfiguration)/8);
tft.print(" Bytes.");
setLine2();
tft.print("please wait");
initGrid(); // Очистка любой информации о текущей сетке. На самом деле это не обязательно, но делает код более безопасным.
EEPROM_readAnything(profileNum*profileSize,saveConfiguration);
for (int ROMRow = 0; ROMRow< 8; ROMRow++) // Заполнение структуры ПЗУ
{
for (int ROMCol = 0; ROMCol < 8; ROMCol++)
{
cellType[ROMRow][ROMCol] = saveConfiguration.ROMcellType[ROMRow][ROMCol];
indexVal[ROMRow][ROMCol] = saveConfiguration.ROMindexVal[ROMRow][ROMCol];
MIDIVariable[ROMRow][ROMCol] = saveConfiguration.ROMMIDIVariable[ROMRow][ROMCol];
cellChannel[ROMRow][ROMCol] = saveConfiguration.ROMcellChannel[ROMRow][ROMCol];
orientation[ROMRow][ROMCol] = saveConfiguration.ROMorientation[ROMRow][ROMCol];
faderGroundX[ROMRow][ROMCol] = saveConfiguration.ROMfaderGroundX[ROMRow][ROMCol];
faderGroundY[ROMRow][ROMCol] = saveConfiguration.ROMfaderGroundY[ROMRow][ROMCol];
/*
if (cellType[ROMRow][ROMCol] == 3)
{
customValue = orientation[ROMRow][ROMCol];
customMessage = indexVal[ROMRow][ROMCol];
MIDIVariable[ROMRow][ROMCol] = 0; // Первоначально устанавливает значение считываемого фейдера равным 0, переменная customvalue используется для управления физической ориентацией в этой функции
customChannel = cellChannel[ROMRow][ROMCol]; // MIDI-канал, которому назначена данная ячейка
initType3(ROMRow,ROMCol);
}
*/
}
}
// Здесь мы возвращаемся к виду сетки
delay(500); // Просто чтобы пользователь мог увидеть сообщение
menuState = 2; // Возвращаемся в режим сетки
menuLocatorX = 0;
menuLocatorY = 0; // В противном случае текст не будет отображаться на следующем экране;
updateScreen();
}
//{EEPROM_readAnything((profileNum*profileSize), конфигурация);}
Вот объявление структуры, которую я использую
//--------------------------------СТРУКТУРА EEPROM --------- ----------------------------------
struct config_t // Я думаю, это позволит упростить сохранение EEPROM
{
int ROMcellType[8][8]; // [Строка][Столбец] Указывает тип входных данных, назначенных ячейке. Вверху слева = 0,0 Строка, Столбец // Тип ячейки 9 будет ячейкой резервирования для фейдеров.
int ROMindexVal[8][8]; // Указывает значение примечания / Команда, присвоенная ячейке
int ROMMIDIVariable[8][8]; // Используется для сохранения желаемой скорости / Текущего значения непрерывного ввода.
int ROMcellChannel[8][8]; // MIDI-канал, которому назначена данная ячейка
int ROMorientation[8][8]; // Отслеживает направление ориентации больших модулей
int ROMfaderGroundX[8][8]; // Отслеживает, где находится «корень» фейдера
int ROMfaderGroundY[8][8]; // Отслеживает, где находится «корень» фейдера
} saveConfiguration;
//------------------------------------------------ --------------------------------------------
IndexVal, cellType и т. д. – это глобальные переменные (я знаю, я знаю, глобальная переменная плоха)
И, наконец, заголовочный файл с моими функциями EEPROM базового уровня
#include <EEPROM.h>
#include <Arduino.h> // для определений типов
template <class T> int EEPROM_writeAnything(int ee, const T& value)
{
const byte* p = (const byte*)(const void*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
EEPROM.write(ee++, *p++);
return i;
}
template <class T> int EEPROM_readAnything(int ee, T& value)
{
byte* p = (byte*)(void*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
*p++ = EEPROM.read(ee++);
return i;
}
Прошло много времени с тех пор, как я писал этот код, поэтому я сам немного потерялся в нем. Вероятно, мне следует добавить, что если я сохраняю профиль 1, тестирую другие профили, а затем снова загружаю профиль 1, это всегда правильно.
Кроме того, часть, где печатается sizeof(saveConfiguration), возвращает 224, и я также удалил «/8», чтобы убедиться в отсутствии ошибки округления. Если я удвою значение ProfileSize, это просто уменьшит вдвое количество доступных профилей, распределив их по размеру структуры в памяти.
Пожалуйста, игнорируйте раздел (if cellType == 3), он используется для типа объекта, который использует 3 последовательные ячейки и сам по себе представляет собой целую банку червей. Для этого теста я просто использую типы ячеек, которые представляют собой модули, использующие одну ячейку.
Я чувствую, что мне действительно нужно в ближайшее время добиться некоторого прогресса в этом вопросе ради моего собственного психического благополучия.
Кто-нибудь может помочь?
Дополнительная информация для комментаторов: Я использую плату PJRC Teensy 3.5.
/8 должен был отображать количество сохраняемых целых чисел. Думаю, когда я изначально писал этот код, я ошибочно подумал, что устройство 8-битное, и оценил размер профиля в 448. Я полагаю, что EEPROM имеет ширину 16 бит?
@GigaJoules, 👍1
Обсуждение1 ответ
В зависимости от того, используете ли вы 8-битную или 32-битную версию Arduino, ваш профиль имеет размер 896 или 1792 байта. Нет 224. (Как вы думаете, почему /8 — это вариант?)
Я предполагаю, что вы используете 32-битную версию Arduino с 1792/8 = 224.
В любом случае 1792 — это больше EEPROM, чем любой обычный Arduino. Даже если бы у вас был 8-битный Arduino и размер профиля был 896, в EEPROM хватило бы места только для одного.
Я думаю, вам нужно несколько переосмыслить свою стратегию, чтобы либо значительно сократить объем сохраняемых данных, либо изменить место хранения данных.
Итак, малышка 3.5 — это 32-микроконтроллерный контроллер с 4096 байтами EEPROM. В нынешнем виде я, вероятно, мог бы закодировать позиции X и Y в одну переменную, поскольку ни одно из значений не может быть > 8. Я подумываю о включении в окончательную версию внешнего EEPROM большего размера, но на данный момент первые несколько профилей должны работать нормально, даже если размер профиля является худшим сценарием, верно? Я могу сохранять данные в другие профили, но у меня возникает проблема, когда крайние правый и левый столбцы перезаписывают столбец a в следующем/последнем профиле соответственно., @GigaJoules
Я думаю, что /8 происходит от кода, изначально написанного на Arduino Mega?, @GigaJoules
Я не вижу места для /8, независимо от того, для чего был написан код. Размер есть размер. Если вы попытаетесь работать с восьмой частью размера, то да, все пойдет наперекосяк., @Majenko
Если вы сохраняете значения < 256, то нет необходимости использовать int — оно в 4 раза больше, чем вам нужно. Используйте uint8_t
., @Majenko
Да, хороший крик, это определенно артефакт от предыдущей работы с меньшим U-контроллером., @GigaJoules
Изменив структуру и глобальные переменные на uint_8, sizeof вернет 448 (именно это было в моей электронной таблице, где я планировал все распределение памяти). Мы кое-чего добились! :), @GigaJoules
Хорошо, теперь ошибка изменилась... Я полностью очистил профили 5,6 и 7 и создал «крест» с входами двух разных типов, идущими по диагонали от угла к углу. Оставшиеся 6 свободных ячеек в крайнем правом и левом столбцах установлены на другой тип ячеек. когда я сохраняю это в профиле 6; Профиль 5 не изменился, но профиль 7 содержит копию профиля 6, но в 4-м столбце есть 6 входов совершенно другого типа, чем те, которые использовались для тестирования, причем верхняя и нижняя ячейки пусты. Какая головная боль., @GigaJoules
Такое поведение не происходит при сохранении в профилях ниже 6, и это здорово!, @GigaJoules
Большое спасибо Маженко, это было очень полезно. Я также заметил, что функция сохранения конфигурации моей системы (отличающаяся от конфигурации сетки и всего несколько байт) была настроена на сохранение по адресу, который приведет к переносу структуры в EEPROM. Изменено это так, что вместо того, чтобы начинаться с номера профиля 9*, он начинается с номера профиля 8* (всего несколько байт). Это исправило новую ошибку :), @GigaJoules
@GigaJoules Отлично. Я рад, что вы узнали инструменты для решения проблемы. Мне не нравится, когда люди требуют, чтобы я кормил их ответом с ложечки ;), @Majenko
- Использование EEPROM для записи значения счетчика
- Как запрограммировать ардуино на чистом C/C++?
- Очистка EEPROM
- RtcDateTime' не называет тип
- как отправить аргумент объектам ESP8266WebServer в функции
- Какие Arduino поддерживают ATOMIC_BLOCK?
- GSM-модуль IOT-GA6 Arduino + ошибка CME 58
- Разница между массивом char и массивом unsigned char
Какую доску вы используете? И почему вы думаете, что захотите разделить размер на 8...?, @Majenko
EEPROMAnything можно заменить на EEPROM.put и EEPROM.get., @Jot
Я добавлю эту информацию. 2 секунды., @GigaJoules
Хорошо, замена на .put и .get работает, но у меня та же проблема., @GigaJoules