Запись в EEPROM

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

Для начала огласите какую именно библиотеку используете или только собираетесь использовать. Далее, для Еепром пофиг с чего вы записываете. Размер ячейки 8 бит, ячеек как гавна за сараем. Указываем номер и записываем байт, указываем номер и читаем байт. Если тип более 8 бит, в посте 48 достаточно неплохая биба, которая сама разлаживает и слаживеат данные и распределяет по ячейкам. Главное помните что в отличии от оперативки, память Еепром имеет ограниченное кол-во чтения записи (ок. 100 000 раз). Учитывайте это при написании кода (чтоб не заганять в цикл). 

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

Стардартную библиотеку eeprom

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

Я включаю библиотеку вот такой строкой как памятка.

#include <EEPROM.h> // EEPROM.write(addr, val);   value = EEPROM.read(address);

Вроде должно быть понятно.

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

Если честно, то не доконца, вот допустим я считую ключ ibuton
В массив:
Byte addr[8];
И куда мне эт подставить в вашем коде.

ssvs111
ssvs111 аватар
Offline
Зарегистрирован: 11.07.2014
Как-то вот так
#include <EEPROM2.h>
byte addr[8];

void setup() {
  EEPROM_write(15, addr);  //запись массива в EEPROM по адресам 15 - 22
  EEPROM_read(15, addr); //чтение массива из EEPROM
}
void loop() {
}

 

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

Теперь понятней спасибо большое

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

только это уже не та стандартная биллиотека, а как раз та о которую я и рекомендовал.

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

спасибо всем за помощь, с записью и чтением в EEPROM разобрался, если можно еще помогите как сравнить масив:

byte addr[8];

и то что записанно в EEPROM

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

если честно ничего не понял что там написанно, можно на пальцах объяснить, если не сложно

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

Сливаешь данные из EEPROM в другой массив и в цикле сравниваешь элементы массивов

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

если можно пример кода, как это делается

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

Зачем весь массив считывать? Считывайте элемент и тут же сравнивайте, после следующий. А так вам памяти никакой не хватит.

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

да я не собирался считывать весь масив, только по 8 байт,

У меня первый ключь хранится в EEprom  с 0-7 адресс,

второй  с 8-15 адресс,и т.д.

мне нужно как то считать эти ключь с EEPROMA и положить допустм в этот масив:

byte addr1[8];

и потом этот масив нужно сравнить с этим:

byte addr[8];

но как это сделать я чет не понимаю

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

if(!memcmp(addr, addr1, 8))
{
   //массивы равны
}

 

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

как сравнить массивы я понял, а как считать этих 8 байт из EEPROM и поместить их в этот массив:

byte addr1[8];

 

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

сообщение #55 строка 6

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

пробовал не выходит

niki43
Offline
Зарегистрирован: 19.02.2014
byte addr1[8];
  byte addr2[8];

   
   EEPROM_read(0, addr1);
    EEPROM_read(8, addr2); 
 
     
    if(!memcmp(addr, addr1, 8))
{
 count=1;  //массивы равны
}

ниче не получается

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

я разобрался, всем большое спасибо за помощь.

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

так в чём дело?

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

я просто все вывел в отдельную функцию, и не указал ссылку на функцию.

olegww
Offline
Зарегистрирован: 12.10.2015


Здравствуйте Имеется простой скетч для управления влажностью, для удобства хотелось бы реализовать изменение значения порога влажности #define humLowTrigger 55.
Чтоб с ик пульта двумя кнопками прибавлять и убавлять, но что бы оно сохранялось в eeproom.
и на дисплее во время нажатия просматривалось, то что устанавливается.
Подскажите как такую задачу решить? искал на форуме но данного примера не нашел, опыта в программировании ноль.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
//#include <DFR_Key.h>
#include "DHT.h"

#define temLowTrigger 27  //Setting the trigger value for the temperture, once the temperture lower than this trigger value, the heater band will start heating
#define humLowTrigger 55 //Setting the trigger value for the humidity, once the humidity lower than this value, start humidification


#define DHTPIN 13     // what pin we're connected to

// Uncomment whatever type you're using!
#define DHTTYPE DHT22   // DHT 22  (AM2302)



const int relay1 =  2;      // the number of the relay 1 pin
const int relay2 =  4;      // the number of the relay 2 pin



DHT dht(DHTPIN, DHTTYPE);


//Pin assignments for DFRobot LCD Keypad Shield
LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display
//---------------------------------------------

//DFR_Key keypad;

//int localKey = 0;
//String keyString = "";
                 
void setup() 
{ 
  
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  lcd.begin();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("initializing....");
  lcd.setCursor(0, 1);
  delay(1000);
  
  Serial.begin(9600);
  dht.begin();
  delay(1000);
  lcd.clear();
  
  /*
  OPTIONAL
  keypad.setRate(x);
  Sets the sample rate at once every x milliseconds.
  Default: 10ms
  */
//  keypad.setRate(10);
  
  digitalWrite(relay1, HIGH);
  digitalWrite(relay2, HIGH);

}

void loop() 
{ 
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  // check if returns are valid, if they are NaN (not a number) then something went wrong!
  if (isnan(t) || isnan(h)) {
    Serial.println("Failed to read from DHT");
  } else {

    //Display 
    lcd.home();
    lcd.print("Hum: ");
    lcd.print(h);
    lcd.print(" %");
    
    lcd.setCursor(0, 1);
    lcd.print("Tem: ");
    lcd.print(t);
    lcd.print(" *C");
    
    if(h < humLowTrigger) //if the humidity lower than the trigger value
      digitalWrite(relay1, LOW); //start humidification 
      else
      digitalWrite(relay1, HIGH);
      
     if(t < temLowTrigger)
      digitalWrite(relay2, LOW);//start heating
    else
      digitalWrite(relay2, HIGH);
  }
}

 

axill
Offline
Зарегистрирован: 05.09.2011

Смотрите библиотеку https://www.arduino.cc/en/Reference/EEPROM

vde69
Offline
Зарегистрирован: 10.01.2016

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

//*************************************************************************************************
// процедуры, для записи и получении всех данных EEPROM 
// собраны здесь для единообразного адресного хранения и визуализации карты использования EEPROM, 
// адреса в двух байтовом виде (1 - это два байта с физическим адресом 2 и 3)
//*************************************************************************************************

// ------------------- чтение -------------------
int Get_signature()    { return ReadInt(0); }                    // 0 - сигнатура
int Get_term_set()     { return ReadInt(1); }                    // 1 - установленая температура
                                                                 // 2...9 - резерв
int Get_temp(int Num) { return ReadInt(Num+10); }                // 10..19 - массив калибровочных температур
int Get_pow(int Num)  { return ReadInt(Num+20); }                // 20..29 - массив мощности поддержания температуры
int Get_tep(int Num)  { return ReadInt(Num+30); }                // 30..39 - массив температур инерционности
int Get_dt(int Num)   { return ReadInt(Num+40); }                // 40..49 - массив времени цикла on/off 
int Get_step(int Num) { return ReadInt(Num+50); }                // 50..59 - массив времени инерционности 
int Get_setup(int Num){ return ReadInt(Num+60); }                // 60..69 - массив флагов калибровки
                                                                 // 70..99 - резерв
                                                                 // 100..511 - свободно 

// ------------------- запись -------------------
int Set_signature(int value)        { WriteInt(0, value); }      // 0 - сигнатура
int Set_term_set(int value)         { WriteInt(1, value); }      // 1 - установленая температура
                                                                 // 2...9 - резерв
void Set_temp(int Num, int value)  { WriteInt(Num+10, value); }  // 10..19 - массив калибровочных температур
void Set_pow(int Num, int value)   { WriteInt(Num+20, value); }  // 20..29 - массив мощности поддержания температуры
void Set_tep(int Num, int value)   { WriteInt(Num+30, value); }  // 30..39 - массив температур инерционности
void Set_dt(int Num, int value)    { WriteInt(Num+40, value); }  // 40..49 - массив времени цикла on/off 
void Set_step(int Num, int value)  { WriteInt(Num+50, value); }  // 50..59 - массив времени инерционности 
void Set_setup(int Num, int value) { WriteInt(Num+60, value); }  // 60..69 - массив флагов калибровки
                                                                 // 70..99 - резерв
                                                                 // 100..511 - свободно



//*************************************************************************************************
// процедура читает из энерго независимой памяти двухбйтовое число
//   num - идентификатор памяти (номер двухбайтного слова)
//*************************************************************************************************
int ReadInt(int num)
{
  byte a;
  byte b;
  int result;
  
  a = int(EEPROM.read(num * 2));
  b = int(EEPROM.read(num * 2 + 1));   
  result = a * 255 + b;

  return result;
}

//*************************************************************************************************
// процедура записывает в энерго независимую память двухбйтовое число
//   num - идентификатор памяти (номер двухбайтного слова)
//   value - записываемое значение
//*************************************************************************************************
void WriteInt(int num, int value)
{
  byte a;
  byte b;
  
  b = value % 255;   
  a = (value - b) / 255;
  EEPROM.write(num * 2, a);
  EEPROM.write(num * 2 + 1, b);
  Beep(4); // сделано для выявления безконтрольной записи и снижения ресурса EEPROM
}



 

 

axill
Offline
Зарегистрирован: 05.09.2011

Вообще то в avg-gcc есть встроенный механизм распределения памяти eeprom

Ардеина его почему то не использует, хотя он очень удобный на мой взгляд

#include <avr/eeprom.h>

....

int a EEMEM:
int b[10] EEMEM;
int c;

...

int* ptr_a = &a:
int** ptr_b = &b;
Int* ptr_c = &c;

В этом примере ptr_a равно 0, ptr_b равно 2, ptr_c равно 22, т.е автоматически выделились адреса для данных в eeprom. Эти адреса можно использовать как с ардуиновской библиотекой EEPROM так и с Си-шной avr/eeprom.h

Кроме того если при обявлении переменным EEMEM присвоить значения линкер сформирует файл .eep который можно напрямую залить в eeprom. Правда на ардуине я это не проверял, только в студии

 

vde69
Offline
Зарегистрирован: 10.01.2016

axill пишет:

Вообще то в avg-gcc есть встроенный механизм распределения памяти eeprom

Ардеина его почему то не использует, хотя он очень удобный на мой взгляд

#include <avr/eeprom.h>

....

int a EEMEM:
int b[10] EEMEM;
int c;

...

int* ptr_a = &a:
int** ptr_b = &b;
Int* ptr_c = &c;

В этом примере ptr_a равно 0, ptr_b равно 2, ptr_c равно 22, т.е автоматически выделились адреса для данных в eeprom. Эти адреса можно использовать как с ардуиновской библиотекой EEPROM так и с Си-шной avr/eeprom.h

Кроме того если при обявлении переменным EEMEM присвоить значения линкер сформирует файл .eep который можно напрямую залить в eeprom. Правда на ардуине я это не проверял, только в студии

хороший вариант для хорошо продуманных (архитектурно) проектов!

но на мой взгляд поиск ошибки вылезания за границу выделеной памяти сложный, да и риск велик (много переменных, и при смене типизации не факт, что компилятор выдаст ошибку),

кроме того ЛЮБОЙ вариант с авто выделением памяти чреват тем, что при смене формата (или количества данных), новые данные разместятся по иному, и станут не валидными. Мой вариант с явным резервированием места в коде позволяет вдумчиво относится к смене местоположения старых и новых данных

axill
Offline
Зарегистрирован: 05.09.2011

абсолютно правильного варианта здесь нет, каждый сам для себя выбирает оптимальный

я делаю просто  - если структура данных в eeprom меняется, то я перезаливаю содержимое eeprom кроме того, что перезаливаю прошивку.

 

iliya555
Offline
Зарегистрирован: 11.06.2015

Недавно сталкнулся с проблемой того что во время записи/чтения из EEPROM одно значение потерялось (запись ведется примерно 1-2 раз в сутки). Пришлось задуматься о том что память несовешенна и требуется резервные копии этих значений.

Поискав на форумах узнал что некоторые не рекомендуют записывать данные в первые 8-16 ячеек, но про резервное копирование данных ни чего небыло. Ничего подобного ни кто не реализовывал.

По образованию я связист, а в связи (если это позволяет канал, и скорость передачи высокая а данных мало) то передаваемое значение типа "1234" передается как "111222333444", принимается и сравниваются последовательности из 3-х символов "111" - "1" и т.д. И если вносимые помехи или искажения приносят что то вреде "11хх223334х4" значение на выходе принимающего устройства будет "1234" так как "11х" - "1" "х22" - "2" "333" - "3" "4х4" - "4".

Так вот в реализуемом мной проекте значений вносимых в EEPROM не много, по этому написал код, где при чтении из EEPROM читаются 3 значения сравниваются, если все значения верны то прочитанное ИСТИНА, если одно отличается то исправляем по значениям из двух других, исли все значения разные то записываем значение по умолчанию.

#include <EEPROM.h>                       // подключаем библиотеку EEPROM
char PASS[5];                             // переменная для хранения пароля считанного из EEPROM
String PASS2 = PASS;                      // переменная для хранения пароля
int modeWork = 0;                         // Режим работы 0=Free 1=Start
int addr0 = 20;                           // переменная адреса режима работы
int addr1 = 22;                           // переменная адреса пароля
int addr0a = 220;                         // переменная адреса режима работы
int addr1a = 222;                         // переменная адреса пароля
int addr0b = 420;                         // переменная адреса режима работы
int addr1b = 422;                         // переменная адреса пароля
int p1;                                   // переменная для хнанения считанных значений из EEPROM
int p2;                                   // переменная для хнанения считанных значений из EEPROM
int p3;                                   // переменная для хнанения считанных значений из EEPROM
char P1a[5];                              // переменная для хранения пароля считанного из EEPROM
char P2a[5];                              // переменная для хранения пароля считанного из EEPROM
char P3a[5];                              // переменная для хранения пароля считанного из EEPROM
String P1 = P1a;                          // переменная для хранения пароля
String P2 = P2a;                          // переменная для хранения пароля
String P3 = P3a;                          // переменная для хранения пароля

void setup() {
  // пример записи в EEPROM переменной типа int
  EEPROM.update(addr0, modeWork);         // обновляем значение в EEPROM
  EEPROM.update(addr0a, modeWork);        // обновляем значение в EEPROM
  EEPROM.update(addr0b, modeWork);        // обновляем значение в EEPROM

  // пример записи в EEPROM переменной типа char
  EEPROM.put(addr1, PASS);                // записываем массив в EEPROM
  EEPROM.put(addr1a, PASS);               // записываем массив в EEPROM
  EEPROM.put(addr1b, PASS);               // записываем массив в EEPROM
}

void loop() {
  modeWorkREAD();                         // чтение режима работы устройства
  PASSREAD();                             // чтение пароля работы устройства
}
void modeWorkREAD()
{
  p1 = EEPROM.read(addr0);                //читаем переменные из EEPROM
  p2 = EEPROM.read(addr0a);               //читаем переменные из EEPROM
  p3 = EEPROM.read(addr0b);               //читаем переменные из EEPROM
  if (p1 == p2 == p3)                     // проверяем все значения на равенство
    modeWork = p1;
  // далее проверяем если одно значение не верно
  else if (p2 == p3)                      // проверяем все значения на равенство кроме p1
  {
    modeWork = p2;
    EEPROM.update(addr0, p2);             // записываем значение в EEPROM
  }
  else if (p1 == p3)                      // проверяем все значения на равенство кроме p2
  {
    modeWork = p1;
    EEPROM.update(addr0a, p1);            // записываем значение в EEPROM
  }
  else if (p1 == p2)                      // проверяем все значения на равенство кроме p3
  {
    modeWork = p1;
    EEPROM.update(addr0b, p1);            // записываем значение в EEPROM
  }
  else if (p1 != p2 != p3)                // если все значения разные то записываем значение по умолчанию
  {
    modeWork = 1;
    EEPROM.update(addr0, modeWork);
    EEPROM.update(addr0a, modeWork);
    EEPROM.update(addr0b, modeWork);
  }
}
void PASSREAD()
{
  EEPROM.get(addr1, P1a);                 // считываем массив символов по адресу addr1 в переменную
  EEPROM.get(addr1a, P2a);                // считываем массив символов по адресу addr1 в переменную
  EEPROM.get(addr1b, P3a);                // считываем массив символов по адресу addr1 в переменную
  P1 = P1a;
  P2 = P2a;
  P3 = P3a;
  if (P1 == P2 && P1 == P2 && P2 == P3)   // проверяем все значения на равенство
    PASS2 = P1;
  // далее проверяем если одно значение не верно
  else if (P2 == P3)                      // проверяем все значения на равенство кроме P1
  {
    PASS2 = P2;
    PASS2.toCharArray(P2a, 5);            //перевод из char в String
    EEPROM.put(addr1, P2a);               // записываем значение в EEPROM
  }
  else if (P1 == P3)                      // проверяем все значения на равенство кроме P2
  {
    PASS2 = P1;
    PASS2.toCharArray(P1a, 5);            //перевод из char в String
    EEPROM.put(addr1a, P1a);              // записываем значение в EEPROM
  }
  else if (P1 == P2)                      // проверяем все значения на равенство кроме P3
  {
    PASS2 = P1;
    PASS2.toCharArray(P1a, 5);            //перевод из char в String
    EEPROM.put(addr1b, P1a);              // записываем значение в EEPROM
  }
  else                                    // если все значения разные то записываем значение по умолчанию
  {
    PASS2 = "1234";
    EEPROM.put(addr1, PASS2);
    EEPROM.put(addr1a, PASS2);
    EEPROM.put(addr1b, PASS2);
  }
}

PS изначально хотел записывать и считывать по 5-ть значений, но код получился на 26 сравнениях, потом остановился на 3-х значениях где используется 4-е сравнения.

axill
Offline
Зарегистрирован: 05.09.2011

Перепроверять значения записанные в eeprom совершенно лишнее. Это борьба с последствиями, а не с причиной.

причин порчи данных в eeprom две - софт и железо. Софт - кривой скетч, который может сам портить данные например делая ошибки в адресах. Железо - у AVR известная есть болезнь по хаотичному исполнению кода со случайным затиранием ячеек памяти при неконтролируемом сбросе. Это может случаться если источник питания может создавать пограничные условия когда МК уже как бы запустился, но не так, чтобы стабильно работать. Это или кривые источники питания от сети или плохо продуманные источники автономного питания. Как правило включение BOD эту проблему решает

iliya555
Offline
Зарегистрирован: 11.06.2015

C проблемой потеренной информации встретился впервые, и чтобы исключить ее в дальнейшем решил ее именно так. 

А насчет BOD почитаю. Спасибо.

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

BOD из коробки (со штатными fuses) включено же. Хотя таки да, будет полезен электролит мкФ так на 3300.

iliya555, мне любопытно, хочу поглядеть код с 26 условиями (на длину 5 байт), мож как-нибудь ужму.

iliya555
Offline
Зарегистрирован: 11.06.2015

Код с 26 сравнениями писать сразу не стал, он аналогичен написанному вверху но с 5-ю значениями вместо 3-х.

Сравнения:

1==2==3==4==5    х=1
//сравниваем по 4 значения
1==2==3==4       x=1
1==2==3==5       x=1
1==2==4==5       x=1
1==3==4==5       x=1
2==3==4==5       x=2
//сравниваем по 3 значения
1==2==3          x=1
1==2==4          x=1
1==2==5          x=1
1==3==4          x=1
1==3==5          x=1
1==4==5          x=1
2==3==4          x=2
2==3==5          x=2
2==4==5          x=2
3==4==5          x=3
//сравниваем по 2 значения
1==2             x=1
1==3             x=1
1==4             x=1
1==5             x=1
2==3             x=2
2==4             x=2
2==5             x=2
3==4             x=3
3==5             x=3
4==5             x=4
//когда совсем все плохо
1!=2!=3!=4!=5    x=значение по умолчанию

 

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

А если запишется случайным образом скажем в 1 и 3 ячейку одинаковые значения (вероятность практически 0, но все таки) и в 4 например другое. То по вашему алгоритму будет все совсем плохо. Нет смысла сравнивать менее 50% одинаковых значений, это будет уже означать что данные запороты полностью.

iliya555
Offline
Зарегистрирован: 11.06.2015

Вероятность такого варианта есть.

Задача. Из EEPROM, где находятся 1024 ячеек с информацией, среди которых 2 ячейки с испорченной информацией, случайно считаны 5 ячеек. Какова вероятность того, что среди них будет 2 ячейки с испорченной информацией.

 

Всего ячеек: 1024-2 = 1022

Общее число возможных элементарных исходов для данных испытаний равно числу способов, которыми можно считать 5 ячеек из 1024:

2. Найдем вероятность того, что среди выбранных 5 ячеек 2 с испорченной информацией.

Количество вариантов выбора из 2 ячеек с испорченной информацией:

Количество вариантов выбора из 1022 ячеек остальные 3 ячейки исправны: 

 

Код писал под свои нужды, его можно написать компактнее.

Sindbad
Offline
Зарегистрирован: 08.12.2015

std пишет:

BOD из коробки (со штатными fuses) включено же. Хотя таки да, будет полезен электролит мкФ так на 3300.

Конденсаторы в цепях питания как раз растягивают время падения напряжения, оставляя микроконтроллеру больше возможностей запороть EEPROM. Именно для противодействия этому BOD и нужен.

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

Sindbad, к нему проверка на АЦП/компараторе полагается же.

Если опустилось - срочно записать и не трогать (поставить флаг, не прикасаться к памяти), пока конденсатор ещё не разрядился. Не?

axill
Offline
Зарегистрирован: 05.09.2011

std пишет:

BOD из коробки (со штатными fuses) включено же. Хотя таки да, будет полезен электролит мкФ так на 3300.

iliya555, мне любопытно, хочу поглядеть код с 26 условиями (на длину 5 байт), мож как-нибудь ужму.

у голого МК выключен. Вы про ардуину? Это не проверял, но у голого выключен

не путайте для атмега 8 два отдельных фьюза, один включает BOD, а второй определяет уровень срабатывания. Второй запрограмирован на одно из значений с завода, но не активен пока не включен первый

Sindbad
Offline
Зарегистрирован: 08.12.2015

std пишет:

Sindbad, к нему проверка на АЦП/компараторе полагается же.

Если опустилось - срочно записать и не трогать (поставить флаг, не прикасаться к памяти), пока конденсатор ещё не разрядился. Не?

Не совсем.

Если скетч не пишет в EEPROM вообще, то и проблемы при включении/выключении не возникает, т.к. в памяти нет команд записи в EEPROM.

Если же скетч пишет в EEPROM, то такие команды в памяти МК присутствуют. При уменьшении напряжения питания, МК начинает выполнять случайные команды из памяти, тогда он может выполнить и команду, которая перепишет EEPROM. И никакие флаги не помогут.

Задача BOD - остановка работы МК при падении напряжения ниже заданного уровня.

Или, если использовать внешний контроль питания, нужно прижать выход RESET к земле при падении напряжения ниже заданного.