Запись в EEPROM

vvadim
Offline
Зарегистрирован: 23.05.2012

Не могу разобраться в старших-младших байтах. Нужно записать число 3000 в память и потом прочитать его - покажите пожалуйста примерчик как это сделать.

leshak
Offline
Зарегистрирован: 29.09.2011

Вначале решите задачу просто "3000 разобрать/собрать" в две байтовые локальные переменные. А потом будете заморчаватся как эти две переменные в EEPROM сохранить (я же так понял "сохранить один байт" уже проблем не вызывает).

Разобрать:

http://arduino.cc/en/Reference/LowByte

http://arduino.cc/en/Reference/HighByte

Собрать:

value= (hiByte<<8) | lowByte;

или

http://arduino.cc/en/Reference/WordCast

leshak
Offline
Зарегистрирован: 29.09.2011
void setup(){
  Serial.begin(57600);
  
  int value=3000;
  // разбираем
  byte hi=highByte(value);
  byte low=lowByte(value);
  
  // тут мы эти hi,low можем сохраить, прочитать из eePROM
  
  int value2=(hi<<8) | low; // собираем как "настоящие программеры"
  int value3=word(hi,low); // или собираем как "ардуинщики"
  
  Serial.println(value);
  Serial.println(value2);
  Serial.println(value3);
}

void loop(){
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

Ну или вот разбор "по крутому", без ардуино функций 

  byte hi=value>>8;
   byte low=value;

 

vvadim
Offline
Зарегистрирован: 23.05.2012

С сохранением байта проблем нет. По ссылкам я уже смотрел, но там чайнику ничего не понять. Методология как бы понятна - разбиваю число на 2 байта (старший -младший), пишу в память, читаю, собираю. Примеров на С++ найти не смог. 

leshak
Offline
Зарегистрирован: 29.09.2011

vvadim пишет:

С сохранением байта проблем нет. По ссылкам я уже смотрел, но там чайнику ничего не понять.

Ну тогда я не знаю что "чайнику понять". Вызов готовой функции это сложно? Ну проще уже просто ничего в природе не бывает. Разве что "сложить переменные". Я понимаю если еще вариант со сдвигами пугает. Но с highByte(),lowByte() и word()? Они же как раз "для чайников" и писались. В "боевом коде" вы их редко встретите (просто не нужны, зная битовые операции и без них не сложно)

vvadim пишет:

Примеров на С++ найти не смог. 

Не ну знаю как вы искали. В крайнем случае я вроде уже дал вам примеры. Причем "в двух вариатах" и через функции и через битовые операции.

vvadim
Offline
Зарегистрирован: 23.05.2012

Спасибо за пример. Буду пробовать.

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

vvadim пишет:

С сохранением байта проблем нет. По ссылкам я уже смотрел, но там чайнику ничего не понять. Методология как бы понятна - разбиваю число на 2 байта (старший -младший), пишу в память, читаю, собираю. Примеров на С++ найти не смог. 

Цитируемый тут "сдвиг на 8 позиций" (>>8) - как раз те самые примеры, ведь Ваши три тысячи представляются в двоичном коде как-то так:

~$ echo "obase=2;3000" | bc
101110111000

maksim
Offline
Зарегистрирован: 12.02.2012

Есть два способа. 
Первый - с помощью битовых операций, описанный выше.
И второй - работа с памятью:

  int val_1 = 3000;
  int val_2 = 0;

  // разбиваем
  byte *x = (byte *)&val_1;
  byte HighByte = x[1];
  byte LowByte = x[0];
  // собираем
  byte xx[] = {LowByte, HighByte};
  int *y = (int *)&xx;
  val_2 = y[0];

 

maksim
Offline
Зарегистрирован: 12.02.2012

Пример:

#include <EEPROM.h>

void setup()
{
  int val_1 = 3000;
  int val_2 = 0;

  // запись в ЕЕПРОМ
  byte *x = (byte *)&val_1;
  EEPROM.write(0, x[0]);
  EEPROM.write(1, x[1]);

  // чтение из ЕЕПРОМ
  byte xx[] = {EEPROM.read(0), EEPROM.read(1)};
  int *y = (int *)&xx;
  val_2 = y[0];

  // выводим в сериал
  Serial.begin(9600);
  Serial.println(val_2);
}

void loop(){}

 

leshak
Offline
Зарегистрирован: 29.09.2011

Ну точнее было-бы назвать это "через указатели". Хотя раз highByte() и lowByte() вызывали проблемы, то не думаю что через указатели будет "понятней".

Но раз пошло "писькомерство" :), то вот еще один вариант, через структуры и объединения.

struct splitted_val {
   union{
     int value;
     struct {
       byte hi;
       byte low;
     };
     byte bytes[2]; // 2 потому что int занимает в памяти два байта
   };
};

void setup(){
  Serial.begin(57600);
  
  splitted_val a; // это будем разбирать
  splitted_val b; // сюда будем собирать

  byte hi, low; // сюда будем "разбирать"
  
   a.value=3000; // установили значение
   
   // разбираем собираем первым способом, по именованным поляем
   hi=a.hi; 
   low=a.low;
   
   // сбираем по именованым
   b.hi=hi;
   b.low=low;
   
   Serial.println(b.value); // вывели что получилось
   
   

    a.value=5000; // возмем для разбора другое значение, что-бы виднее
    
    // разбираем через "массив байтов", просто но номерам байтов    
    hi=a.bytes[0];
    low=a.bytes[1];
    
    // собираем через массив
    b.bytes[0]=hi;
    b.bytes[1]=low;
    
       
   Serial.println(b.value); // вывели что получилось
   
    
    
    a.value=7000; // возмем для разбора другое значение, что-бы виднее
    
    // сделаем сразу "разбор, сбор" миную промеждуточные переменные. Разбор - через массив, сбор - через имена
    b.hi=a.bytes[0];
    b.low=a.bytes[1];
    
    Serial.println(b.value); // вывели что получилось
  

}

void loop(){
}

Топикстартеру: это "факультативно", если будет интерестно погуглить разобратся как оно работает. Это скорее не пример "нужно обязательно разобратся", а "наш ответ чемберлену/Максиму" ;) "Программерские этюды" :)

leshak
Offline
Зарегистрирован: 29.09.2011

А еще нагугливается куча всяких "библиотек-расширений" которые все эти задачи "разобрать/собрать" берут на себя. Сразу умеют int и прочие стандартные типы сохранять.

http://playground.arduino.cc/Code/EEPROMWriteAnything

http://playground.arduino.cc/Code/EepromUtil

http://playground.arduino.cc/Code/EEPROMex

и т.д. и т.п.

 

 

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

А вот есть ли смысл сразу проверять то, что записали? Или оно все равно может позже прочитаться с ошибкой?

leshak
Offline
Зарегистрирован: 29.09.2011

Andrey_Y_Ostanovsky пишет:

А вот есть ли смысл сразу проверять то, что записали? Или оно все равно может позже прочитаться с ошибкой?

Ну, теоретически может. Раз есть ресурс "сколько циклов записи выдерживает" - значить рано или поздно он закончится.

Но IMHO больший смысл имеет чтение ПЕРЕД записью. Если содержимое ячейки и "что собираемся записать" уже совпадает - нечего не делаем. Экономим ресурс EEPROM. А еще, можно, время от времени, менять адреса по котороым пишем. Если данных темного, то ресурс можно увеличить "размазывая" данные по адресному полю с течением времени.

vvadim
Offline
Зарегистрирован: 23.05.2012

 Спасибо за помощ.  Записываю и читаю с разделением на 2 байта.  Хотел использовать библиотеку EEPROMex , но там косяки. Замены по рекомендации автора библиотеки ничего не дают. Может после модернизации будет работать. 

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

leshak пишет:

А еще, можно, время от времени, менять адреса по котороым пишем. Если данных темного, то ресурс можно увеличить "размазывая" данные по адресному полю с течением времени.

А как их потом читать? Ведь мы в программе достаточно жестко зашиваем "координаты" тех или иных данных... Если мы их начинаем раскидывать в случайном порядке по всей памяти - надо где-то хранить координаты последих записанных данных. Или увеличить записываемые данные дополнительным полем "дата" и потом, читая все устройство целиком, выбирать максимальное значение даты. Не получится ли это дольше?

leshak
Offline
Зарегистрирован: 29.09.2011

Andrey_Y_Ostanovsky пишет:

leshak пишет:

А еще, можно, время от времени, менять адреса по котороым пишем. Если данных темного, то ресурс можно увеличить "размазывая" данные по адресному полю с течением времени.

А как их потом читать? Ведь мы в программе достаточно жестко зашиваем "координаты" тех или иных данных... Если мы их начинаем раскидывать в случайном порядке по всей памяти - надо где-то хранить координаты последих записанных данных. Или увеличить записываемые данные дополнительным полем "дата" и потом, читая все устройство целиком, выбирать максимальное значение даты. Не получится ли это дольше?

Ну тут уже на что фантазии хватит и от конкретного проекта зависит. Можно "по понедельникам" в одни адреса, по вторникам - в другие. Можно руками менять раз в месяц. Можно, к примеру, обнулить все EEPROM и записывать "в первую ячейку отличную от нуля". А читать, наоборот "идем с конца пока будет что-то не ноль, это и есть наши данные".

Можно - не делать запись вообще. Записывать только когда "выключаемся". Следить за питанием, поставить какой-нибудь конденсатор который может несколько микросекунд удержать дуину "на плаву". Начала "падать питание" - быстренько сохранили из памяти в EEPROM.

Вообщем это уже простор для вашей фантазии и потребностей. Если вы пишите в EEPROM два раза в день - можно и не морочится.

 

 

vvadim
Offline
Зарегистрирован: 23.05.2012

Читаю одновременно два параметра.  Как правильно записать.  Получается повторное декларирование.

    byte xx[] = {EEPROM.read(25), EEPROM.read(26)};
    int *y = (int *)&xx;
    val1 = y[0];  
    delay(10);
    byte xx[] = {EEPROM.read(35), EEPROM.read(36)};
    int *y = (int *)&xx;
    val2 = y[0]; 

 

maksim
Offline
Зарегистрирован: 12.02.2012

Так естественно, просто объедините в функцию...

#include <EEPROM.h>

int EEPROM_read(int addr)
{
  byte x[] = {EEPROM.read(addr), EEPROM.read(addr+1)};
  int *y = (int *)&x;
  return y[0]; 
}

void setup()
{
  val1 = EEPROM_read(25);
  val2 = EEPROM_read(35);
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

vvadim пишет:

Читаю одновременно два параметра.  Как правильно записать.  Получается повторное декларирование.

    byte xx[] = {EEPROM.read(25), EEPROM.read(26)};
    int *y = (int *)&xx;
    val1 = y[0];  
    delay(10);
    byte xx[] = {EEPROM.read(35), EEPROM.read(36)};
    int *y = (int *)&xx;
    val2 = y[0]; 

 

Сами спросили - сами ответили :) Все верно - повторное деклалирование.

Пользуйтесь уже готовым массиом.

xx[0]=EEPROM.read(35);
xx[1]=EEPROM.read(36);

 

leshak
Offline
Зарегистрирован: 29.09.2011

maksim пишет:

Так естественно, просто объедините в функцию...

А если в функцию, то не проще ли уже

#include <EEPROM.h>

int EEPROM_read(int addr)
{
  return word(EEPROM.read(addr),EEPROM.read(addr+1)); 
}

void setup()
{
  val1 = EEPROM_read(25);
  val2 = EEPROM_read(35);
}

 

maksim
Offline
Зарегистрирован: 12.02.2012

То же самое сделайте и с записью

#include <EEPROM.h>

void EEPROM_write(int addr, int val)
{
  byte *x = (byte *)&val;
  EEPROM.write(0, x[addr]);
  EEPROM.write(1, x[addr+1]);
}

void setup()
{
  EEPROM_write(25, 3000);
  EEPROM_write(35, 12000);
}

 

maksim
Offline
Зарегистрирован: 12.02.2012

leshak пишет:

maksim пишет:

Так естественно, просто объедините в функцию...

А если в функцию, то не проще ли уже

#include <EEPROM.h>

int EEPROM_read(int addr)
{
  return word(EEPROM.read(addr),EEPROM.read(addr+1)); 
}

void setup()
{
  val1 = EEPROM_read(25);
  val2 = EEPROM_read(35);
}

 

Тогда можно и без функции

val1 = word(EEPROM.read(25),EEPROM.read(26));
val2 = word(EEPROM.read(35),EEPROM.read(36));
maksim
Offline
Зарегистрирован: 12.02.2012

В общем дело вкуса...

hugoboss317
Offline
Зарегистрирован: 21.03.2013

Доброго здравия. Подскажите если не в тягость: При загрузке нового скетча через IDE очищается eeprom. Геморно потом снова перепрописывать, к то му же каждый час данные меняются. Есть возможность загружать скетчь, чтоб не трогать eeprom?

MaksMS
Offline
Зарегистрирован: 11.03.2013

Необходимо изменить фьюз EESAVE микроконтроллера

hugoboss317
Offline
Зарегистрирован: 21.03.2013

Всё гениальное - просто (А. Эйнштейн).  Принято, спасибо.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Вот ещё, может кому пригодиться, малёк закоментировал для себя:

#include <EEPROM.h>

void setup()
{
  Serial.begin(9600);
  delay(2000);
  EEPROMWriteInt(0, 65535); //занимаються ячейки 0 и 1
  // 65535 максмальное значение которое можно записать в 2 байта

  Serial.print("Read the following int at the eeprom address 0: ");
  Serial.println(EEPROMReadInt(0)); 
}

void loop()
{
// ничего не делаем
}

//кушаем аж 2 байта EEPROM
void EEPROMWriteInt(int p_address, unsigned int p_value)
{
  byte lowByte = ((p_value >> 0) & 0xFF);
  byte highByte = ((p_value >> 8) & 0xFF);
  EEPROM.write(p_address, lowByte);
  EEPROM.write(p_address + 1, highByte);
}

// считаем нашы два байта
unsigned int EEPROMReadInt(int p_address)
{
  byte lowByte = EEPROM.read(p_address);
  byte highByte = EEPROM.read(p_address + 1);
  return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}

 

Взято отсюда, проверено работает. 

salan
Offline
Зарегистрирован: 01.10.2014

е

salan
Offline
Зарегистрирован: 01.10.2014

можете помоч почти с подобным премером?

hugoboss317
Offline
Зарегистрирован: 21.03.2013

salan пишет:

можете помоч почти с подобным премером?

не, никак, мы токма позыркать тут...

salan
Offline
Зарегистрирован: 01.10.2014

leshak пишет:

Вначале решите задачу просто "3000 разобрать/собрать" в две байтовые локальные переменные. А потом будете заморчаватся как эти две переменные в EEPROM сохранить (я же так понял "сохранить один байт" уже проблем не вызывает).

Разобрать:

http://arduino.cc/en/Reference/LowByte

http://arduino.cc/en/Reference/HighByte

Собрать:

value= (hiByte<<8) | lowByte;

или

http://arduino.cc/en/Reference/WordCast

можете помоч в подобном премере?

std
Offline
Зарегистрирован: 05.01.2012

Люди, есть кнопка (передатчик) и реле (приёмник), реле должно помнить состояние себя, причём оно может его забыть, например перезагрузившись по watchdog. Если кнопка забудет - плевать, её просто нажмут второй раз.

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

Которая говорит, куда писала:

#define ESIZE (1024)
int AppendEEPROM(byte d){
int i;        // счётчик
byte b;       // буфер
boolean f;    // флаг
  f=false;                     // не писали
  for(i=0;i<ESIZE;i++){
    b=EEPROM.read(i);          // ищем инфу
    if(b!=0xFF){
      EEPROM.write(i,0xFF);    // возвращаем FF на место
      if(i<=ESIZE-2){
        EEPROM.write(i+1,d);   // пишем в следующий байт
        return i+1;
      }else{
        EEPROM.write(0x0,d);   // или если находимся в конце, пишем в ноль
        return 0;
      }
      f=true;                  // писали
//      break;                 // вроде не нужно т. к. return же
    }
  }
  if(!f){                      // если не нашли инфы значит память пустая
    EEPROM.write(0x0,d);       // пишем в ноль
    return 0;
  }
}

Которая пишет молча:

#define ESIZE (1024)
void AppendEEPROM(byte d){
int i;
byte b;
boolean f;
  f=false;
  for(i=0;i<ESIZE;i++){
    b=EEPROM.read(i);
    if(b!=0xFF){
      EEPROM.write(i,0xFF);
      if(i<=ESIZE-2) EEPROM.write(i+1,d);
       else EEPROM.write(0x0,d);
      f=true;
      break; // а здесь нужно. кстати куда оно выйдет из цикла?
    }
  }
  if(!f) EEPROM.write(0x0,d);  // надеюсь что сюда
}

 

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Поляризованное реле  не "забывает" своего состояния, а , наоборот, помнит.....

https://www.google.ru/search?q=%D0%BF%D0%BE%D0%BB%D1%8F%D1%80%D0%B8%D0%B...

std
Offline
Зарегистрирован: 05.01.2012

Лень доставку ждать :)

А про память есть мнение?

hugoboss317
Offline
Зарегистрирован: 21.03.2013

Чё то бардак какой то... Если записываем в ячеку состояние, то нужно сначала прочитать из какой то ячейки номер ячейки в которой записано последнее состояние перед отключением (или перезагрузкоой).

А ты читаешь из ячейки, только что объявленной переменной "i". 

 

std
Offline
Зарегистрирован: 05.01.2012

цЫкл же?

В том и смысл: мы не знаем куда писать. Ищем то место, которое отличается от 0xFF. "Стираем", записав поверх старой информации 0xFF и в следующий байт пишем. Не?

Читающая функция будет наподобие. Ну и ессно потом допишу "перед тем как писать, прочитать - если то же самое - ничё не делать".

Тут-то суть именно в том чтобы избавиться от "куда писали", потому что - ну пишем мы в первые байты, и в самые последние два - собсно адрес. Значит, эти последние два байта будут изнашиваться в (1024-2)/2=511 раз быстрее. Не? Мы же пишем переменно один/много байт информации в много байт памяти. И два байта адреса в два байта памяти. Надеюсь понятно...

maksim
Offline
Зарегистрирован: 12.02.2012

Все правильно. Я бы еще избавился от постоянного поиска нужной ячейки и делал бы его только при старте МК.

#define ESIZE (1024)

int curr_addr; // глобально

// в сетапе:
void InitState()
{
  curr_addr = -1;
  while(curr_addr < ESIZE)
  {
    byte temp = EEPROM.read(++curr_addr);
    if(temp != 0xFF)
    {
      state = temp; // востанавливаем состояние
      break;
    }
  }
}


void SaveState(byte state)
{
  EEPROM.write(curr_addr++, 0xFF);
  curr_addr %= ESIZE;
  EEPROM.write(curr_addr, state);
}

 

ssvs111
ssvs111 аватар
Offline
Зарегистрирован: 11.07.2014

По поводу записи/чтения EEPROM. Есть такая библиотека, которая умеет сохранять/читать числа с любой размерностью byte int long или даже массивы, причем делает это автоматически. Я пользовался - классная штука. Кому нужно могу на github-е выложить

kristow
kristow аватар
Offline
Зарегистрирован: 08.08.2013

leshak пишет:

Ну или вот разбор "по крутому", без ардуино функций 

  byte hi=value>>8;
   byte low=value;

 

Подскажите как число 999 999 999 (9-ти значное) записывать в память? т.е. метод разделить на старший и младший байт из этой темы не подойдет?

т.е. разделить число на 4 байта

 

nevkon
Offline
Зарегистрирован: 20.01.2015

Пойдет, только делите на большее чисто байт. Например так для 3-x байт:

byte b1=value>>16;

byte b2=value>>8;

byte b3=value;

По аналогии хоть 128 битное число раскладывайте, разница будет только в количестве байт.

kristow
kristow аватар
Offline
Зарегистрирован: 08.08.2013

Ясна спасибо.

Пробую запустить код на запись \ чтение памяти ds1302. Использую доработанную библиотеку отсюда: ссылка

По аналогии из этой темы написал:

long EEPROM_read(int addr){
    return word(rtc.readRAM(addr),rtc.readRAM(addr+1),rtc.readRAM(addr+2));
}

void EEPROM_Write(int p_address, unsigned long p_value)
{
  byte b1 = ((p_value >> 0) & 0xFF);
  byte b2 = ((p_value >> 8) & 0xFF);
  byte b3 = ((p_value >> 8) & 0xFF);
  rtc.writeRAM(p_address, b1);
  rtc.writeRAM(p_address + 1, b2);
  rtc.writeRAM(p_address + 2, b3);
}

long kontakt = 500000;
EEPROM_Write(0, kontakt);
kontakt_s = EEPROM_read(0);

При компиляции пишет ошибку на строку 2: error: no matching function for call to 'makeWord(uint8_t, uint8_t, uint8_t)'

Подскажите как переписать функцию сбора значения из памяти...

hugoboss317
Offline
Зарегистрирован: 21.03.2013

А строка 9 точно верно?

может надо 

byte b3 = ((p_value >> 16) & 0xFF);

 

kristow
kristow аватар
Offline
Зарегистрирован: 08.08.2013

да верно, опечатался...

Строка word(rtc.readRAM(addr),rtc.readRAM(addr+1)); = Компилируется

Строка word(rtc.readRAM(addr),rtc.readRAM(addr+1),rtc.readRAM(addr+2)); = не компилируется.

Если переделать для памяти конроллера, то так же ошибка.  word(EEPROM.read(addr),EEPROM.read(addr+1),EEPROM.read(addr+2));

Не силен в этом. Подскажите как можно собрать значение?

nevkon
Offline
Зарегистрирован: 20.01.2015

Способ был указан выше

return ((rtc.readRAM(addr) << 0) & 0xFF) + ((rtc.readRAM(addr+1) << 8) & 0xFF00) + ((rtc.readRAM(addr+2) << 16) & 0xFF0000);

kristow
kristow аватар
Offline
Зарегистрирован: 08.08.2013

Спасибо всем. Все работает ;)

korsianen
Offline
Зарегистрирован: 23.03.2013

А я сделал так. Просто взял придумал 256 иричную систему исчислений :) 

Считываю так:

cikly = EEPROM.read(0)*256+EEPROM.read(1);

Записываю так:

EEPROM.write(0,cikly/256);
EEPROM.write(1,cikly%256);

Короче много способов я думаю можно придумать чтобы разложить число по байтно.

toc
Offline
Зарегистрирован: 09.02.2013

изобретатели велосипедов, посмотрете библиотеку eepromex

ssvs111
ssvs111 аватар
Offline
Зарегистрирован: 11.07.2014

Не мучайтесь, вот готовая библиотека
https://github.com/ssvs111/ARDUINO_EEPROM2

toc
Offline
Зарегистрирован: 09.02.2013

Tails_MP, напишите в подфоруме "ищу исполнителя"

niki43
Offline
Зарегистрирован: 19.02.2014

Народ доброго дня суток, прошу помощи кто поможет разобраться с EEprom библиотекой. мне нужно после нажатия определенного пункта меню записоватькод Ibuton ключа в eeprom, а потом выводить эти ключи на lcd 1602, определенный ключ в определенном месте..

заранее всем спасибо за помощь)))