Что не так с моим 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 бит?

, 👍1

Обсуждение

Какую доску вы используете? И почему вы думаете, что захотите разделить размер на 8...?, @Majenko

EEPROMAnything можно заменить на EEPROM.put и EEPROM.get., @Jot

Я добавлю эту информацию. 2 секунды., @GigaJoules

Хорошо, замена на .put и .get работает, но у меня та же проблема., @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