Урок 14. EEPROM в Ардуино. Контроль целостности данных.

Arduino UNO R3

В уроке рассказывается, как работать с внутренним EEPROM платы Ардуино, о контроле целостности данных при хранении и передаче.

Предыдущий урок     Список уроков     Следующий урок

Внутреннее EEPROM в системе Ардуино.

EEPROM это энергонезависимая память. Расшифровывается как электрически стираемое программируемое постоянное запоминающее устройство. Применяется для хранения данных, которые должны быть сохранены при выключении питания.

 

Платы Ардуино имеют EEPROM разных объемов в зависимости от типа используемого микроконтроллера.

Микроконтроллер Объем внутреннего EEPROM
ATmega328 1024 байт
ATmega8, ATmega168 512 байт
ATmega1280, ATmega2560 4096 байт

Такого объема памяти вполне достаточно для хранения режимов, технологических параметров, коэффициентов и т.п.

  • Принцип действия EEPROM основан на создании электрического заряда в диэлектрике полупроводниковой структуры. Заряды вечно не хранятся, но разработчики гарантируют 20 лет.
  • EEPROM имеет ограниченное число циклов записи, обычно не менее 100 000.
  • Для записи информации в EEPROM требуется достаточно большое время, порядка 3 мс.
  • Чтение EEPROM происходит без задержки и ресурс работы памяти не уменьшает.

Библиотека EEPROM.

Для работы с энергонезависимой памятью в Ардуино есть библиотека EEPROM. Она имеет две функции для записи и чтении байта в EEPROM.

byte read(int address)

Функция возвращает считанный по адресу address байт.

#include <EEPROM.h>
byte dt; dt= EEPROM.read(15);  // чтение байта по адресу 15

void write(int address, byte value)

Записывает байт value по адресу address в EEPROM. Запись выполняется за 3,3 мс. Гарантируется 100 000 циклов записи.

#include <EEPROM.h>
EEPROM.write(15, 0);  // запись 0 по адресу 15

 

Программа проверки записи в EEPROM.

Давайте напишем простую программу проверки работы EEPROM. Программа должна записывать до 16ти символов, принятых с последовательного порта и в цикле выводить 16 символов, считанных из EEPROM. С помощью монитора порта Arduino IDE мы сможем записывать данные в EEPROM и контролировать содержимое энергонезависимой памяти.

// проверка работы EEPROM
#include <EEPROM.h>
int i, d;

void setup() {
    Serial.begin(9600); // инициализируем порт, скорость 9600
}

void loop() {
  // чтение EEPROM и вывод 16 данных в последовательный порт
  Serial.println();
  Serial.print("EEPROM= ");
  i= 0; while(i < 16) { 
  Serial.print((char)EEPROM.read(i));
  i++;   
  }
 
  // проверка есть ли данные для записи
  if ( Serial.available() != 0 ) {
    delay(50);  // ожидание окончания приема данных

    // запись в EEPROM
    i= 0; while(i < 20) { 
    d= Serial.read();
    if (d == -1) d= ' ';  // если символы закончились, заполнение пробелами
    EEPROM.write(i, (byte)d);   // запись EEPROM
    i++;
    }     
  } 
  delay(500);
}

Загружаем программу в плату Ардуино, проверяем. Открываем монитор порта, посылаем данные на плату:”Проверка EEPROM”.

Окно монитора портаДанные записываются в EEPROM и теперь в цикле выводятся в окне монитора. Если мы отключим плату от компьютера и затем снова подключим, то данные будут продолжать посылаться на компьютер. Они сохранены в энергонезависимой памяти и при выключении питания платы не меняются.

 

Контроль целостности данных.

В EEPROM обычно хранят данные абсолютно необходимые для работы программы. Например, если вы делаете регулятор температуры, то это может быть : заданная температура, коэффициенты регуляторов, параметры защитных функций и т.п. Представьте, что эти параметры будут испорчены, а регулятор продолжит работу. Он может включить нагревательный элемент на полную мощность и выставить недопустимо высокую температуру. При неправильных коэффициентах регулятор может “пойти в разнос”. Перестанут работать защитные функции. Неприятности будут большие.

А ведь ситуация, когда в EEPROM могут оказаться недостоверные данные вполне реальная.

  • При первом включении устройства данные в EEPROM недостоверны. Их просто никто еще не задавал.
  • В момент записи данных в EEPROM может отключиться питание платы и тогда часть данных запишется, а часть не успеет.
  • EEPROM имеет ограниченное число циклов записи, и это свойство также может привести к разрушению данных в этой памяти.

Поэтому крайне важно быть уверенными, что в EEPROM хранятся достоверные данные. В случае, если они ошибочны, программа должна принять определенные действия. Это может быть:

  • использовать копию данных из EEPROM;
  • использовать данные специально созданные для аварийного режима;
  • отключить устройство;
  • сигнализировать об ошибке;
  • и много других вариантов.

Целостность данных это показатель, того что данные не были изменены при хранении, передаче, отображении и т.п. Целостность данных может проверяться в любой ситуации, потенциально опасной для разрушения данных. Обычно контроль достоверности данных производят:

  • В энергонезависимой памяти (EEPROM, FLASH, HDD…).
  • При передаче данных ( последовательные интерфейсы, WiFi, GSM, TCP/IP…).
  • В оперативной памяти для особо важных данных.
  • Некоторые компоненты (например, датчик DS18B20) имеют протокол обмена данными с контролем целостности.

Для контроля целостности к блоку данных добавляется контрольный код. Этот код рассчитывается по определенному алгоритму при записи данных. При проверке данных код заново вычисляется и сравнивается с кодом, сформированным при записи. Если коды не совпадают – данные ошибочны.

Алгоритм расчета контрольного кода определяет вероятность определения ошибки данных.  Существует большое число алгоритмов формирования контрольных кодов: циклические коды, различные функции хэширования. Но самым простым способом вычисления контрольного кода является контрольная сумма.

Просто делается математическая сумма всех байтов блока данных. Такой алгоритм требует минимума ресурсов для реализации, и в то же время позволяет с достаточно высокой вероятностью определить ошибочные данные в случае небольших объемов информации. Контрольная сумма может быть разной разрядности. В минимальном варианте это один байт. В этом случае происходит переполнение результата суммы, что уменьшает вероятность обнаружения ошибок.

Я всегда к вычисленной контрольной сумме применяю операцию “исключающее ИЛИ” с кодом подобным E5h. Это позволяет исключить весьма вероятную ситуацию, когда все данные равны 0. Сумма 0 равна 0. Поэтому если блок данных будет ошибочно обнулен (а это бывает), то простая сумма не вычислит эту ошибку.  А когда контрольная сумма для всех 0 равна E5h, то ошибка будет выявлена.

Давайте добавим в предыдущую программу контроль целостности данных.

// проверка работы EEPROM
#include <EEPROM.h>
int i, d;
byte sum; // контрольная сумма

void setup() {
    Serial.begin(9600); // инициализируем порт, скорость 9600
}

void loop() {
  // вычисление контрольной суммы
  sum= 0;
  i= 0; while(i < 16) { 
  sum += EEPROM.read(i); 
  i++;
  }
  // проверка контрольной суммы
  if ( (sum^0xe5) == EEPROM.read(i)) {
    // контрольна сумма правильная
   
    // чтение EEPROM и вывод 16 данных в последовательный порт 
    Serial.println();
    Serial.print("EEPROM= ");
    i= 0; while(i < 16) { 
    Serial.print((char)EEPROM.read(i));
    i++;   
    }   
  }
  else {
    // контрольная сумма неправильная
    Serial.println();
    Serial.print("EEPROM= data error");       
  }
   
  // проверка есть ли данные для записи
  if ( Serial.available() != 0 ) {
    delay(50);  // ожидание окончания приема данных

    // запись в EEPROM
    sum= 0;
    i= 0; while(i < 16) { 
    d= Serial.read();
    if (d == -1) d= ' ';  // если символы закончились, заполнение пробелами
    EEPROM.write(i, (byte)d);   // запись EEPROM
    sum += (byte)d;   // вычисление контрольной суммы
    i++;
    }   
    EEPROM.write(i, sum ^ 0xe5);   // запись контрольной суммы 
  } 
  delay(500);
}

Отмечу только, что программа принимает не 16, а 14 символов, т.к. монитор порта добавляет к каждой строке символы переноса строки \r и \n.

Загрузим программу и запустим монитор.

Окно монитора порта

В окне бегут сообщения об ошибке данных. Мы еще не загрузили в EEPROM никаких данных, и алгоритм распознает эту ситуацию. Пошлем строку, например, ”проверка”. Теперь в окне отображаются данные из EEPROM.

Окно монитора порта

Таким же образом можно защищать целостность данных при передаче по последовательному порту. Любая импульсная помеха может исказить сигнал в кабеле связи и вызвать ошибку данных. Конечно, программа приема данных на компьютере должна поддерживать протокол с контрольной суммой. Монитор порта Arduino IDE этой функции не имеет.

 

В следующем уроке узнаем, что такое указатели в C для Ардуино, научимся записывать в EEPROM данные различных типов (int, float…).

Предыдущий урок     Список уроков     Следующий урок

4

Автор публикации

не в сети 9 часов

Эдуард

285
Комментарии: 1946Публикации: 198Регистрация: 13-12-2015

46 комментариев на «Урок 14. EEPROM в Ардуино. Контроль целостности данных.»

  1. Как загрузить программу узи дальномера в ардуино и сохранить скотч при выключении питания

    0
    • В уроке 3 написано как загрузить. Загрузить в программу Arduino IDE и нажать кнопку «->».
      Программа загрузится во Flash память и останется там до следующей загрузки.

      0
  2. Вы написали ссылку на команду Е5h, эта команда ассемблера? Могли бы пояснить про нее, или литературу подсказать.

    0
    • E5h это не команда Ассемблера. Это просто число, которое прибавляется к контрольной сумме для исключения ситуации, когда все данные равны 0 и контрольная сумма равна 0.
      Я цитирую из урока:
      «Я всегда к вычисленной контрольной сумме применяю операцию “исключающее ИЛИ” с кодом подобным E5h. Это позволяет исключить весьма вероятную ситуацию, когда все данные равны 0. Сумма 0 равна 0. Поэтому если блок данных будет ошибочно обнулен (а это бывает), то простая сумма не вычислит эту ошибку. А когда контрольная сумма для всех 0 равна E5h, то ошибка будет выявлена.»

      0
  3. Хороший способ, но есть один недостаток.
    Если в программе допустим изменяются в настройках не все данные, а только часть и их нужно будет сохранять при выходе из меню. А данных к примеру очень много. То нужно будет каждый раз пересчитывать контрольную сумму для всех данных.

    0
  4. А если данных много, но памяти предостаточно, то может записывать дубликат значения и сравнивать при чтении? При этом можно создать процедуру записи значения сразу в несколько адресов… единственный недостаток — долгая запись. Но при изменении части данных, можно только часть и перезаписать.

    0
  5. А как сделать в eeprom изначально таблицу так же как мы в flash памяти размещаем константы? Чтоб при загрузке скетча в eeprom прописывались исходные данные а программа потом уже их меняла.

    0
    • Я задавался этим вопросом, но не нашел решения. В Ардуино даже не возможно просто на этапе компиляции загрузить данные в EEPROM.

      0
      • Решил что просто надо написать отдельный скетч загрузки данных в EEPROM и запускать когда нужно.

        0
  6. А если сделать счетчик импульсов с записью результатов на энергонезависимую память, чтоб при выключении результат не сбивался и при включении продолжал отсчеты, запись данных нужно сохранять в епром, или на флэш память?

    0
  7. Подскажите, пожалуйста, правильно ли я понимаю:
    — складывая все байты содержимого EEPROM в один байт sum, мы этот байт регулярно переполняем, и
    — в некоторый, но определённый момент sum переходит через 0
    — информация о предыдущих слагаемых практически полностью теряется
    — и если поменять местами два байта на участке последовательности между переходами через 0, то
    — наша проверка ничего подозрительного не обнаружит

    это ведь уязвимость? пусть вероятность такого события и стремится к нулю…

    0
    • Здравствуйте!
      При переполнении байта информация полностью не теряется. Байт не обнуляется, а в нем остается остаток от суммы. Хотя, действительно, сумма не самый лучший способ формирования контрольного кода. Это самый простой способ. Гораздо эффективнее использовать циклические коды. В уроке 56 я затрагиваю эту тему.

      0
  8. Вопрос не по теме. PRO MIN подключаю 5 модулей. Запитываю два ( часы и блютуз от платы) а три от автономного ИП на 5вольт. Они не работают. Если подключаю по очереди к плате то работают. В чем дело. Обратные токи от 3 модулей мешают работать друг другу?.

    0
    • решил вопрос диодами. Снова вопрос . Питание насоса 12в 1а и клапана на воду 12в 2а от одного источника на 3а. Не перегорит ли насос=?

      0
          • Сила тока определяется по закону Ома, как отношение напряжения к сопротивлению. А источник питания должен быть рассчитан на максимальный ток нагрузки.

            0
          • Если ваш насос потребляет 1А, то он и будет потреблять 1А, если ИП способен отдать этот 1А. Если ИП будет хоть на 100А, то ничего страшного в этом нет, ваш насос так и будет потреблять необходимые для него 1А. Представьте, в вашей машине аккумулятор, что способен отдавать сотни ампер при запуске стартера, но ведь ничего страшного не происходит, даже если вы подключите к этому аккумулятору только одну лампочку 😉

            0
  9. Добрый день. Может быть сможете мне помочь. Я сделал на основе промини полив участка. Все работает и включается как нужно , но проблема с питанием. Процессор и датчики питаю от адаптера питания 1а 12в с понижением до 5 вольт. А вот с питанием клапана и насоса проблемы. Запитываю их от отдельного блока питания 12в 3 а. Насос 12в 1а. Клапан на воду 12в , по сопротивлению обмотки посчитал по закону Ома, что сила тока 1,8ампера. Получается в сумме 2,8А. Блок питания сгорел. Тогда запитало насос от 12в 1а, он работает отлично, а клапан запитал от БП 12в 3 А. БП снова сгорел. Что делаю не так. Клапан китайский соленоид. При открытии нагревается корпус до 40-50градусов. Пару раз открывается, потом БП накрывается.

    0
    • померить реальный ток потребления клапаном. для этого подключить амперметр последовательно с клапаном и в момент работы клапана посмотреть реальный ток. и желательно посмотреть как сильно просаживается напряжение на бп — тк на китайских бп пишут одно а в реале совершенно другое

      0
    • Здравствуйте!
      Возможно у вас неисправен клапан. Обмотка нагревается, происходит межвитковое замыкание и ток потребления резко увеличивается. Согласен, что надо проверить реальный ток потребления в нагретом состоянии.

      0
    • При пуске соленоидного клапана, потребляемый ток возрастает в несколько раз. В доке на клапан надо смотреть номинальный ток и еще есть параметр — пусковой ток. Так же, может быть обратный индукционный ток, т.е. если для управления клапаном вы не применяете промежуточное реле, а управляете подачей питания на блок питания, то при снятии напряжения с источника питания из катушки индуктивности соленоида пойдет обратный ток. При такой схеме надо ставить шунтирующий диод параллельно обмотке.

      0
  10. Спасибо за статью. Следует еще заметить, что существуют команды EEPROM.put и EEPROM.get, которые позволяют сохранять данные любого типа без сложностей с циклами.

    0
  11. Здравствуйте Эдуард.
    Не могу разобраться с этой строкой:
    // проверка контрольной суммы
    if ( (sum^0xe5) == EEPROM.read(i)) {
    ^ -это как я понял логический оператор, но как мы можем в данном случае сравнить sum (который равен 16) и 0xe5(который вообще не пойму чему равен)?. Где здесь 0 и 1?
    Что за переменная 0xe5, как она работает если не была объявлена и какой у неё тип данных?
    Куда в итоге логический оператор ^ вернет результат?
    Можете человеческим языком прочитать эту строку?
    Принцип я понял, переменные (sum^0xe5) и i должны иметь одинаковое значение, но как оно получается одинаковым это ступор)) Спасибо

    0
    • Здравствуйте!
      Все очень просто. У вас в EEPROM хранится число, допустим типа int. В EEPROM данные содержатся в байтах, т.е. 2 байта.
      Число может быть загружено в EEPROM вашей программой, а может оно уже было там. Любое состояние ячеек EEPROM — какое-нибудь число. Как вариант — число может быть «испорчено» при загрузке. Например, загрузился один байт, и в этот момент питание выключили. Второй байт записаться не успел.
      Возникает проблема выделить подобные ситуации, т.е. определить было ли число записано программой или нет.
      Самый простой вариант — суммировать байты числа и записать в следующую ячейку. А при чтении проверять равенство суммы байтов контрольному коду.Так в программе и происходит. Но есть нехорошая ситуация, когда при инициализации программы все данные обнуляются. Сумма нулевых данных будет равна нулю. Т.е. если в EEPROM записать все нули, то программа воспримет это как правильные данные. Поэтому к сумме байтов применяется операция «исключающее ИЛИ» с константой 0xe5. Это просто константа, которую я выбрал с «потолка» для того, чтобы при нулевых данных контрольный код не был равен нулю.

      0
      • Спасибо за ответ. Вопрос скорее про то как в данном случае работает оператор «исключающее ИЛИ»? На сколько я понимаю if ( (sum^0xe5) == EEPROM.read(i)) {} читается как :
        Если sum или 0xe5 равно i, выполняется функция в скобках. Это верно?

        0
        • Для тех кто не смог разобраться, как работает оператор «исключающее ИЛИ» в данном случае, вот ссылка на видео с понятным объяснением. https://youtu.be/fNvet7tKLmg

          Если коротко то, числа преобразовываются в двоичную систему, далее битовое исключающее или по порядку сравнивает биты первого числа и второго таким образом :результат 1 тогда и только тогда, когда один из аргументов 1, а другой- 0

          0
          • теперь правильно . в 0xe5 0x означает что это шестнадцетиричное число , ну а e5 и есть само число . если бы было обычное число скажем 43 то оно расписывается как (4х10) + 3
            но у нас шестнадцетиричное число ( от слова 16шестнадцать) и поэтому вместо 10 везде задействуем 16 . е у нас равно в этой шестнадцетиричной системе 14 в нашем мире .и следовательно считаем е5=(ех16)+5=(14х16)+5=229.
            операция по модулю два проводится между двоичными числами .значит нам нужно получить 229 в двоичном виде то есть путем последовательного деления до конца и сборки в обратном порядке и получим 11100101 но можно так не трудиться то же само получим сразу раписывая е5 в двоичном виде . е=14=1110 также 5=0101 ставим их рядом и получаем тот же 11100101 . это нам нужно для операции со значком ^ называется сумма по модулю 2 . в ней например возьмем число любое скажем 11010111 и сделаем умножение по модулю 2 с нашим числом229=11100101
            в каждом числе по 8 элементов . и ответ тоже будет состоять также из 8 элементов . первый элемент ответа равен сумме по модулю2 двух первых элементов от каждого число , второй элемент равен сумме по модулю 2 дух уже тоже вторых элементов и так до восьмого элемента . а сумма по модулю вычисляется просто , если равны элементы то есть 0 и 0 , или 1 и 1 то сумма по модулю 2 наоборот считается равной 0 ( тут слово сумма неправильное надо было по другому назвать) ну а если не равны то есть один элемент равен 1 а другой 0 вот тогда сумма по модулю 2 ( ну и дурное название) равна 1 . и получаем 11100101^11010111=00110010 этот ответ и будет сравниваться с тем что прочитает процессор на 16 шаге когда вылетит из цикла (while i<16) то есть когда i станет 15 будет последний вздох цикла так как ещевыполняется условие i<16 а при следующем шаге когда i увеличится на единицу и станет равной 16то она уже не меньше 16 а равна ей и программа покидает цикл while идет дальше и натыкается на команду прочитать if ( (sum^0xe5) == EEPROM.read(i)) здесь команда EEPROM.read(i) прочитать в ЕЕПРОМе по адресу i ну а раз i у нас вылетел из цикла while равным 16 то и значит процессор получает команду того что находится на 16-ом месте ну и далее сравнивает это с ответом умножения по модулю два которое еще называют операцией исключающего или .

            0
  12. особо надо отметить два пункта .
    1. зачем вообще нужны шестнадцетиричные числа ? ведь процессор работает только с переключениями электронных схем внутри него типа — транзистор открыт ( подразумеваем HIGH или 1) и транзистор закрыт ( подразумеваем LOW или 0) . два переключения из одного состояния в другой это и есть двоичные числа . большой и сложной мешаниной всех этих состояний—переключений выражают буквы числа слова команды и так далее . шестнадцетеричные числа нам нужны наверно только в одном случае — это для сокращения написания длинной строчки нулей и единиц при настройке регистров , ведь при описании команд не языком программирования а дачей установок регистрам процессор освобождается от лишней работы по переводу языка программирования на понятный ему язык установок регистров так он из них и состоит . так вот например если нам охота блеснуть своими познаниями в области регистров то мы записываем в программу настройку регистра чтобы народ удивить . а так как регистр состоит из 8 или шестнадцати транзисторных схем называемых битами и каждая схема регулирует то или иное действие ( то есть туда пускает ток и напряжение 5 вольт а сюда не пускает (просто соединяется с 0 вольт )) то в настройках регистра мы должны писать все эти 8 или 16 состояний битов . но строчку из 8 или 16 битов писать можно с ошибкой и долго поэтому просто делят эти 8 или 18 битов на группы по 4 элемента . а 4 элемента не превышают 16 например максимальное это F=1111=15 , поэтому удобнее и красивее вместо длинной строки из 8 или 16 нулей и единиц писать всего 2 или 4 шестнадцетиричных числа . чтобы их не спутать с понятными нам обычными числами их обозначают приставкой 0x ( это ноль и маленькая английская буква X) . пожалуй это единственное применение 16-ричных чисел .
    2. зачем нужны непонятные операции умножение по модулю два и вообще непонятные выкрутасы о команды в программирования ? нельзя ли язык программирования писать простым человеческим языком .? Да , можно создать процессоры понимающие понятный человеческий язык , но достигнуто это будет ценой резкого усложнения электронной схемы процессора и в результате цена его будет в сто раз дороже и его уже не смогут покупать люди . процессоры делаются с простой схемой доступной для продажи и соответственно чтобы их программировать приходится идти на непопулярные меры в виде малопонятных операций и команд типа умножения по модулю два . всё объясняется затратами на производство и прибылью .

    0
  13. Добрый день Эдуард!

    Подскажите пожалуйста как записать в ЕЕПРОМ требуемые температуры в каждый блок?
    Для своего проекта использую только энкодер и Вашу библиотеку (урок 55). Попытался
    энкодером установить температуры и записать их в ЕЕПРОМ, когда вращаю энкодер то значения
    меняются во всех трех адресах ЕЕПРОМа. Почему так происходит? Ведь я захожу в отдельный
    блок к примеру START, вращаю ручку энкодера, то в этом блоке все и должно происходить?!
    На монитор последовательного порта смотрю, что во всех трех блоках значения энкодера
    (encoder.setTemp) меняются синхронно.
    Спасибо.


    void loop(){

    Serial.print(EEPROM.read(START_ADR)); Serial.print(EEPROM.read(STOP_ADR);
    Serial.println(EEPROM.read(ALARM_ADR));


    ———————————————————————


    //блок установки температуры для START и записать в ЕЕПРОМ

    if(mode==1){
    EEPROM.write(START_ADR, encoder.setTemp); //запись уставки температуры в память
    if(button.flagClick==true){
    button.flagClick==false;
    mode=0; //выход в главное окно
    }
    }

    //блок установки температуры для STOP и записать в ЕЕПРОМ
    if(mode==2){
    EEPROM.write(STOP_ADR, encoder.setTemp); //запись уставки температуры в память
    if(button.flagClick==true){
    button.flagClick==false;
    mode=0; //выход в главное окно
    }
    }

    //блок установки температуры для ALARM и записать в ЕЕПРОМ
    if(mode==3){
    EEPROM.write(STOP_ADR, encoder.setTemp); //запись уставки температуры в память
    if(button.flagClick==true){
    button.flagClick==false;
    mode=0; //выход в главное окно
    }
    }

    }

    0
      • Спасибо про button.
        У меня вопрос выше, про то что энкодер в память пишет одновременно в три разных адреса одинаковые значения. Как сделать, что бы разные значения записать в каждый адрес?

        0
  14. Нет, не работает.
    Я сделал и через указали как в Вашем скечте урок 42 про Пельтье, также пишет во все разные адреса одни и теже значения.

    0
    • Вот мой пробный скетч

      #include //билиотека энкодер
      #include // библиотека 1-таймер
      #include // библиотека дисплей
      #include // библиотека кнопка
      #include // библиотека ЕЕПРОМ

      #define BUTTON_PIN 8
      LiquidCrystal disp(6,7,5,4,3,2);
      Encoder2 encoder (A2, A3, 4);

      Button button (BUTTON_PIN, 15);

      byte START_1_ADR; // адрес ПУСК
      byte STOP_1_ADR; // адрес СТОП

      int setTemp1;
      int setTemp2;

      byte menu;

      void setup() {
      Serial.begin (9600);
      disp.begin(16,2);
      Timer1.initialize (250);
      Timer1.attachInterrupt (timerInterrupt, 250);

      }

      void loop() {

      Serial.print(«menu «);Serial.print(encoder.menu); Serial.print(«, TEMP «);
      Serial.print(encoder.setTemp); Serial.print(«, START «);Serial.print(EEPROM.read(START_1_ADR));
      Serial.print(«, STOP «);Serial.println(EEPROM.read(STOP_1_ADR));

      //положение ручки энкодера
      if(encoder.menu==0||encoder.menu==2||encoder.menu==-2) encoder.menu=0;
      else if(encoder.menu==1||encoder.menu==-1) encoder.menu=1;

      if (menu==0){
      menu=0;
      disp.setCursor(0,0);
      disp.print(«START «);
      disp.setCursor(6,0);
      disp.print(EEPROM.read(START_1_ADR));
      setTemp1=encoder.setTemp;
      writeTempToEEPROM(START_1_ADR, & setTemp1); //запись в ЕЕПРОМ уставка температуры
      if(button.flagClick==true){
      button.flagClick=false;
      menu=1;
      disp.clear();
      }
      }

      else if(menu==1){//настройка уставки температуры
      menu=1;
      disp.setCursor(0,1);
      disp.print(«STOP «);
      disp.setCursor(5,1);
      disp.print(EEPROM.read(STOP_1_ADR));
      setTemp2=encoder.setTemp;
      writeTempToEEPROM(STOP_1_ADR, & setTemp2); //запись в ЕЕПРОМ уставка температуры
      if(button.flagClick==true){
      button.flagClick=false;
      menu=0;
      disp.clear();
      }
      }
      }

      void writeTempToEEPROM(int adress, int *value){
      EEPROM.write(adress, *value);
      }

      void timerInterrupt(){
      encoder.scanState();
      button.scanState();
      }

      0
      • Здравствуйте!
        Вы в функцию writeTempToEEPROM через указатель передаете int, а EEPROM.write записывает byte.
        Тогда надо:
        EEPROM.write(adress, * ((byte *)value));
        EEPROM.write(adress + 1, * ((byte *)value + 1));
        А не проще передавать вторым аргументом функции writeTempToEEPROM число int, а на байты разбивать и записывать в EEPROM в функции writeTempToEEPROM.

        0
  15. Здравствуйте Эдуард!
    Из Вашего последнего сообщения «…а не проще передавать вторым аргументом функции writeTempToEEPROM…»
    я ничего не понял, Вы уж извените. Загвоздка у меня не в том какой тип переменной записывать в память. В память
    значения пишутся и отображаются на экране и последовательном мониторе.
    А в том, что одни и те же значения пишутся одновременно и в память для ПУСК и в память для СТОП!
    Подключил кнопки (энкодер убрал) взял Ваши блоки (урок 42, скетч _42_PelteContr_1_1) управления кнопками и запись
    значений в память. Таже самая проблемма. Одни и те же значения пишутся одновременно в память и ПУСК и СТОП.
    Как такое может быть? Где у меня ошибка?

    #include //библиотека таймер 1
    #include //библиотека дисплей
    #include //библиотека кнопка
    #include //библиотека память

    LiquidCrystal disp(6,7,5,4,3,2);

    #define INCR_SET_TEMP 1

    Button buttonPlus (8, 15);
    Button buttonMinus (9, 15);
    Button buttonChoice(10, 15);

    boolean wasChoice=false;
    boolean wasPress=false;
    byte menu;

    byte START_1_ADR; //адрес памяти для ПУСК
    byte STOP_1_ADR; //адрес памяти для СТОП

    byte setTemp1;
    byte setTemp2;

    void setup() {
    Serial.begin (9600);
    disp.begin(16,2);
    Timer1.initialize (250);
    Timer1.attachInterrupt (timerInterrupt, 250);
    }

    void loop() {
    Serial.print(«menu «);Serial.print(menu);
    Serial.print(«, START «);Serial.print(EEPROM.read(START_1_ADR));
    Serial.print(«, STOP «);Serial.println(EEPROM.read(STOP_1_ADR));

    //—————-управление индикацией—————
    if(buttonChoice.flagPress==false){//кнопка ВЫБОР не нажата
    if(wasChoice==false){//—кнопка ВЫБОР не нажата и была не нажата
    wasPress=false;//сброс признака нажатия кнопок + или —

    //режим индикации
    if(buttonPlus.flagClick==true){
    buttonPlus.flagClick=false;
    menu++;
    if(menu>1) menu=0;
    }
    if(buttonMinus.flagClick==true){
    buttonMinus.flagClick=false;
    menu—;
    if(menu>128)menu=1;
    }
    }
    else{//———кнопка ВЫБОР не нажата, а была нажата (отпустили)
    wasChoice=false;

    //запись в ЕЕПРОМ
    if(wasPress==true){
    if(menu==0) writeTempToEeprom(START_1_ADR, & setTemp1);//запись значений в память для ПУСК
    if(menu==1) writeTempToEeprom(STOP_1_ADR, & setTemp2);//запись значений в память для СТОП
    }
    }
    }

    else{//———-кнопка ВЫБОР нажата
    wasChoice=true;
    if(buttonPlus.flagClick==true){
    buttonPlus.flagClick=false;
    wasPress=true;
    incrParameter();
    }
    if(buttonMinus.flagClick==true){
    buttonMinus.flagClick=false;
    wasPress=true;
    decrParameter();
    }

    if(menu==0){
    disp.setCursor(0,0);
    disp.print(«START «);
    disp.print(» «);
    disp.setCursor(6,0);
    disp.print(EEPROM.read(START_1_ADR));//чтение значения из памяти для ПУСК
    }
    if(menu==1){
    disp.setCursor(0,1);
    disp.print(«STOP «);
    disp.print(» «);
    disp.setCursor(5,1);
    disp.print(EEPROM.read(STOP_1_ADR));//чтение значения из памяти для СТОП
    }
    }
    }

    void incrParameter(){//увелечение параметра
    if(menu==0){//заданная температура для ПУСК
    setTemp1+= INCR_SET_TEMP;
    }
    if(menu==1){//заданная температура для СТОП
    setTemp2+= INCR_SET_TEMP;
    }
    }

    void decrParameter(){//уменьшение параметра
    if(menu==0){//заданная температура для ПУСК
    setTemp1-= INCR_SET_TEMP;
    }
    if(menu==1){//заданная температура для СТОП
    setTemp2-= INCR_SET_TEMP;
    }
    }

    void writeTempToEeprom(int adress, byte *value){
    EEPROM.write(adress, *value);
    }

    void timerInterrupt(){
    buttonPlus.scanState();
    buttonMinus.scanState();
    buttonChoice.scanState();
    }

    0
    • Здравствуйте!
      Вы завели для адресов две ячейки:
      byte START_1_ADR; //адрес памяти для ПУСК
      byte STOP_1_ADR; //адрес памяти для СТОП

      В них при запуске программы установились нули. Их значения вы не изменяли. По нулевому адресу EEPROM и записываются оба параметра.
      Надо:
      byte START_1_ADR= 0; //адрес памяти для ПУСК
      byte STOP_1_ADR= 2; //адрес памяти для СТОП
      Еще лучше
      #define START_1_ADR 0
      #define STOP_1_ADR 2
      EEPROM.write в качестве аргумента использует адрес EEPROM, а не адресное пространство микроконтроллера.

      0
  16. Подскажите пожалуйста, при записи нового или измененного старого скетча EEPROM сбрасывается или сохраненные ранее значения остаются в памяти?

    0

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Нажимая кнопку "Отправить" Вы даёте свое согласие на обработку введенной персональной информации в соответствии с Федеральным Законом №152-ФЗ от 27.07.2006 "О персональных данных".