Помехоустойчивость EEPROM в Arduino Pro Mini
- Войдите на сайт для отправки комментариев
С мозгами из китайской Arduino Pro Mini 328p, на базе стандартных библиотек сделал ИК-выключатель с ограничением времени работы для включения/выключения ТВ-бокса и телевизора.
Смысл устройства такой, что при нажатии кнопки (посажена на pin 3) оно запоминает в EEPROM ИК код пульта аэромыши (вкл/выкл - это единственные ИК команды аэромыши, все прочие команды она передает через WiFi-адаптер).
Через оптопару MOC3063M ардуинка управляет симистором, и, через него нагрузкой. На всякий случай добавил таймер выключения на 3,5 часа (нечего полдня телевизор смотреть).
Блок питания - зарядник от телефона Samsung. По питанию стоит электролит и керамический конденсатор на 2,2 мкФ.
Собственно проблема: у нас по сети несколько раз в неделю проходят такие наводки, что комп, подключенный через UPS мгновенно выключается (всякие роутеры в соседней комнате - не реагируют), при этом запомненный в EEPROM ардуинки код пульта стирается и приходится снова его записывать. Может быть кто-то поделится умными мыслями: как с этим можно бороться, куда копать, и, вообще, есть ли способ обуздать стихию?
А может не стирается, а просто не записывается? О чем можно рассуждать, не видя кода?
О чем можно рассуждать, не видя кода?
Как о чём? О девочках! Или Вам и для этого код нужен? :-)
Код, если любопытно, привожу:
#include <IRLibRecv.h> #include <IRLibDecodeBase.h> #include <IRLib_P01_NEC.h> #include <IRLibCombo.h> #include <EEPROM.h> #include <avr/sleep.h> const int ledPin = 10; // номер выхода, подключенного к оптопаре const int irPin = 2; // номер выхода, подключенного к ИК-приемнику const int butPin = 3; // номер выхода подключенного к кнопке // Variables will change: IRrecv MyReceiver(irPin); // указываем вывод, к которому подключён приёмник long previousMillis = 0; // храним время последнего переключения светодиода IRdecode MyDecoder; // код пульта uint32_t codeValue; // сохраненный код пульта int pushButtonState = false; // нажатие кнопки long interval = 16800000; // интервал между включение-выключением void setup() { pinMode(ledPin, OUTPUT); // задаем режим выхода для порта, подключенного к светодиоду digitalWrite(ledPin, LOW); // выключаем светодиод for (byte i=0; i<ledPin; i++) { //make all pins inputs with pullups enabled pinMode(i, INPUT_PULLUP); } for (byte i=ledPin+1; i<20; i++) { //make all pins inputs with pullups enabled pinMode(i, INPUT_PULLUP); } pinMode(13, OUTPUT); // задаем режим выхода для порта, подключенного к светодиоду digitalWrite(13, LOW); // выключаем светодиод //Serial.begin(9600); //pinMode(ledPin, OUTPUT); // задаем режим выхода для порта, подключенного к оптопаре //digitalWrite(ledPin, LOW); // выключаем оптопару //pinMode(butPin, INPUT_PULLUP); // Пин на прерывание pushButtonState = false; EEPROM.get(0, codeValue); MyReceiver.enableIRIn(); // запускаем приём //Serial.println("EEPROM: "); //Serial.println(codeValue, HEX); EIFR = bit (INTF1); attachInterrupt(1, PushInt, CHANGE); // Разрешаем внешние прерывание при изменении уровня pinMode(irPin, INPUT); attachInterrupt(0, IRInt, LOW); // Разрешаем внешние прерывание при изменении уровня digitalWrite(ledPin, LOW); // выключаем оптопару //delay(400); set_sleep_mode (SLEEP_MODE_STANDBY); sleep_mode(); } void loop() { if (MyReceiver.getResults()) {//wait till it returns true MyDecoder.decode(); if(pushButtonState){// если нужно сохранить в EEPROM //delay(200); detachInterrupt(1); codeValue = (uint32_t)MyDecoder.value; //Serial.println("To EEPROM: "); //Serial.println(codeValue, HEX); EEPROM.put(0, codeValue); pushButtonState = false; // Инициализация начального состояния EIFR = bit (INTF1); attachInterrupt(1, PushInt, CHANGE); // Разрешаем внешние прерывание при изменении уровня } else { // если не нужно сохранять сравним с сохраненным decodeIR(); } MyReceiver.enableIRIn(); //restart the receiver } //проверяем вкл или выкл if(digitalRead(ledPin)==HIGH){ unsigned long currentMillis = millis(); // извлекаем текущее время //проверяем не прошел ли нужный интервал, если прошел то if(currentMillis - previousMillis > interval) { digitalWrite(ledPin, LOW); // выключаем светодиод attachInterrupt(0, IRInt, LOW); // Разрешаем внешние прерывание при изменении уровня set_sleep_mode (SLEEP_MODE_STANDBY); sleep_mode(); } } } // определяем значение нажатой клавиши void decodeIR() { if((uint32_t)MyDecoder.value == codeValue){ //Serial.println("Read "); //Serial.println((uint32_t)MyDecoder.value, HEX); //проверяем вкл или выкл if(digitalRead(ledPin)==LOW){ digitalWrite(ledPin, HIGH); // зажигаем оптопару previousMillis = millis(); // сохраняем время включения } else { digitalWrite(ledPin, LOW); // гасим оптопару attachInterrupt(0, IRInt, LOW); // Разрешаем внешние прерывание при изменении уровня set_sleep_mode (SLEEP_MODE_STANDBY); sleep_mode(); } } else { if(digitalRead(ledPin)==LOW){ attachInterrupt(0, IRInt, LOW); // Разрешаем внешние прерывание при изменении уровня set_sleep_mode (SLEEP_MODE_STANDBY); sleep_mode(); } } } void PushInt(){ // Обработчик прерывания pushButtonState = true; } void IRInt(){ // Обработчик прерывания detachInterrupt(0); }Но, еще раз, код запоминается в EEPROM, при нажатии кнопки. Это очень легко проверить: далее отключение/включение питания никак не сказывается на работоспособности устройства. Сбои происходят только при неведомых помехах.
Может быть неясно написал: при помехе одновременно выключается комп, находящийся в той же комнате, кратковременно включает и тут же выключает телевизор, описываемое устройство, и - все, в памяти ардуинки нет запомненных кодов ИК! Мой компьютер, в данном случае - индикатор прихода помехи.
A l e x, как минимум запитать арду от заведомо надёжного БП (лучше олд-скульного трансформаторного) и проверить, что установлен fuse (brown-out detectin level).Но лучше найти источник этих помех, я б бросил все силы именно на поиск помех.
В жилом доме, даже если я дядю Петю, который втихаря сваркой пользуется найду, устранить не смогу.
Опять же китайский роутер, микроволновка, всякие другие телевизоры и духовки не реагируют почему то. Только компьютер и еще несколько устройств на таких же ардуинках. Т.е. помеха только для некоторых устройств критична.
Помеха критична не для некоторых устройств, а для устройств на некоторых блоках питания. Если не можете исключить саму помеху, исключайте ее влияние. Подбирайте БП и/или фильтры.
A l e x, вызовите хорошего электрика. Вероятность что проблема именно в вашей квартире/проводке/щитке -очень высока. У меня однажды были похожие симптомы - выяснилось , что старая алюминиевая проводка медленно прогорала, причём прямо в штукатурке, в стене.
A l e x, переменные, которые изменяются не только в прерываниях, нужно объявлять как volatile. В вашем случае это
To dimax:
Вы заставили меня задуматься. У нас, именно, старая алюминиевая проводка и уже дважды находил "сюрпризы" - скрутки от предыдущих жильцов в квартире. Второй раз пришлось идти по проводу - искать скрутку под слоем штукатурки.
Сейчас попробовал включить одновременно чайник и утюг, т.е. примерно 4 кВт. Напряжение просело с 230 В до 220 В, что вроде, в пределах допустимого. Ни компьютер, ни др. потребители не сбоили.
A l e x, переменные, которые изменяются не только в прерываниях, нужно объявлять как volatile. В вашем случае это
Покажите мне идиота который поставил минус за это сообщение !
Что блин за люди, мало того, что не понимают, что не соблюдают атомарность, так еще и вертят как хотят переменными в прерывании. Нихрена не понимают что это и зачем, а минусы ставят....
Тут нужно не только volatile, но еще и объявить ее как bool или byte.....
Как вариант, сохраняйте код в 3-х экземплярах, а при включении выбирайте тот который равен другому.
Но, конечно, лучше устранить причину.
To brokly. Я минус не ставил. С точки зрения правильности - поддерживаю, посыпаю голову пеплом. Спасибо!
Ресурсов микроконтроллера хватает с избытком, компилятор эту штуку понимает (еще раз, по сути, Вы на 100% правы), в данном случае- сойдет и так.
Как вариант, сохраняйте код в 3-х экземплярах, а при включении выбирайте тот который равен другому.
Но, конечно, лучше устранить причину.
Вряд ли это поможет. Сдается мне, что там ничего не стирается, а в момент прохождения помехи срабатывает прерывание, и МК с чистой совестью записывает нуль.
Как вариант, я бы вообще в данном случае от прерываний отказался. Имхо, в этом коде они лишние
To v258. Надо бы проверить. Но: не знаю как внутри библиотеки все организовано, но нужно, чтобы код пришел. Пока не вернется считанное значение программа будет ждать.
Прерывания используются для минимизации потребления в ждущем режиме (ток уменьшается примерно на 10 мА).
У меня еще на китайских ардуинках, в этой же комнате, открыватели мебельных ящиков с гладкими фасадами реализованы. При появлении помехи ящики рандомно начинают открываться. Кстати там в блоке питания тороидальный трансформатор стоит, а за ним - импульсный стабилизатор. Как помехоустойчивость без перепрокладки проводки поднять.
To brokly. Я минус не ставил. С точки зрения правильности - поддерживаю, посыпаю голову пеплом. Спасибо!
Ресурсов микроконтроллера хватает с избытком, компилятор эту штуку понимает (еще раз, по сути, Вы на 100% правы), в данном случае- сойдет и так.
Беда в том, что не сойдет. Ни в каком случае не сойдет. Обязательно глюком вылезет. Может быть это и не причина вашей текущей проблемы, но тогда причина будущей проблемы.
Беда в том, что не сойдет. Ни в каком случае не сойдет. Обязательно глюком вылезет. Может быть это и не причина вашей текущей проблемы, но тогда причина будущей проблемы.
Где вы думаете в ДАННОМ КОНКРЕТНОМ случае может произойти глюк?
Для переменной int pushButtonState которая используется для TRUE или FALSE атомарность не важна,
Отсутствие volatile в ДАННОМ КОНКРЕТНОМ случае тоже не влияет на работу.
Или я где то заблуждаюсь в ДАННОМ КОНКРЕТНОМ случае?
To v258. Надо бы проверить. Но: не знаю как внутри библиотеки все организовано, но нужно, чтобы код пришел. Пока не вернется считанное значение программа будет ждать.
Тут могут быть два возражения: а) а вы уверены, что не может вернуться нулевое значение, например, по истечении таймаута? и б) а вы уверены, что значение в EEPROM именно обнуляется, а не записывается левым значением?
ЗЫ: кстати, можно попробовать после настройки, когда в EEPROM записано нужное значение, закомментировать в коде строку 66 и перепрошить плату. И посмотреть, останется ли проблема. Вы же не каждый день этой кнопкой пользуетесь? ))
Прерывания используются для минимизации потребления в ждущем режиме (ток уменьшается примерно на 10 мА).
А проблема возникает, когда схема работает или находится в ждущем режиме?
ЗЫ: если схема питается от сетевого источника, то какой смысл в экономии 10мА?
А может все дело во фьюзах. У Вас какое питание и какие установлены фьюзы (bodlevel0,1,2)
А может все дело во фьюзах. У Вас какое питание и какие установлены фьюзы (bodlevel0,1,2)
To v258. Начну, по обыкновению, с конца. Смысл в 10 мА - малюсенькая экономия электроэнергии: если есть возможность, то почему бы и не сэкономить. Тем более, когда подобных устройств, как у меня, десяток - это уже 0,1 А.
Что записывается в EEPROM я вообще не знаю. Можно при небольшой модификации программы в консольке посмотреть. Но что мне это даст? Ну ноль, ну случайная величина. Тем более, что сбои могут пару раз в день быть, с могут и через пару дней.
Внутреннее сопротивление PULL-UP, ЕМПНИП, 20-50 КОм. В принципе на нем 1,5-2 В могут навестись, и, что же теперь вообще от прерываний в устройствах отказаться?
Надо как-то помехоустойчивость повышать.
To vladsf. Прошивал через USB-TTL преобразователь, соответственно, состояние фьюзов не знаю, что китайцы по умолчанию выставили, то и осталось. Можно посмотреть ISP-ом. Но, опять же, контроллер не просто сбрасывается по питанию, а или, как предположил v258 (спасибо!) вызывается прерывание для записи в память, или что-то ещё память сбрасывает.
To v258. Начну, по обыкновению, с конца. Смысл в 10 мА - малюсенькая экономия электроэнергии: если есть возможность, то почему бы и не сэкономить. Тем более, когда подобных устройств, как у меня, десяток - это уже 0,1 А.
0,1А * 5В = 0,5Вт * 24 = 12Вт/ч * 30 = 0,36кВт/ч в месяц. Потребление на уровне погрешности. Я бы не заморачивался ))
Что записывается в EEPROM я вообще не знаю. Можно при небольшой модификации программы в консольке посмотреть. Но что мне это даст? Ну ноль, ну случайная величина. Тем более, что сбои могут пару раз в день быть, с могут и через пару дней.
Это даст понимание, что происходит. И главное, попробуйте таки закомментировать строку 66 после настройки и перепрошить плату. Чтобы у МК вообще не было возможности поменять значение в EEPROM. Собственно, если вы не планируете менять аэромышь, то смысла в постоянной возможности настройки кодов нет в принципе - раз настроил и забыл