Arduino глючит! Помогите отловить косяк в коде чтения\записи EEPROM!
- Войдите на сайт для отправки комментариев
Здравствуйте, тестирую одну функицю программы в отдельном проекте, - компилится но ведет себя неадекватно..
тестил 2 типа функций, сначала запись в EEPROM, потом чтение.
Проблема возникает при чтении - в какой-то момент программа глохнет не завершив цикл for, либо же начинает гнать циклов больше чем нужно.. Причем раз на раз не приходится. Расставил по коду Serial.println чтобы отловить момент глюка - но данные вроде все верные а програ глохнет.. Причем при вставке очередного Сериал-принта, программа может продвинуться на полтора цикла дальше чем до этого.. Вобщем довольно странно.
Привожу код программы. Код один, меняются только вызовы на чтение\запись из setup.
тестовые значения которыми заполнял память вначале сетапа (закоментировано для чтения).
#include <arduino.h> #include <EEPROM.h> #include "eepromanything.h" typedef void(*CHEKER)(void); //создаем тип указателей на функции struct Data { // double value; long interval; double lastmod; double lastst; byte type; byte inf; CHEKER control; //указатель на функцию обработчик }; Data arrayData[5]; //создаем массив структур Data &tempRoom = arrayData[0]; Data &humRoom = arrayData[1]; Data &tempGround = arrayData[2]; Data &tempAirDs = arrayData[3]; Data &tempAir = arrayData[4]; Data &humAir = arrayData[5]; Data &humGround = arrayData[6]; Data &lampSt = arrayData[7]; Data &lampH= arrayData[8]; Data &fanPwr = arrayData[9]; Data &compPwr = arrayData[10]; Data &filterSt = arrayData[11]; Data &doorSt = arrayData[12]; const byte DS18B20 = 0; const byte DHT_11 = 1; const byte DHT_22 = 2; const byte PIN = 3; const byte CHNG = 4; const byte AIR = 0; const byte GRND = 1; const byte TEMP = 0; const byte HUM = 1; int setCont; //число сетов настроек long *stampTime; //глобальный массив - время начала настроек struct Settings { //текущие настройки периода float maxTemp; boolean lamp; boolean filter; int fanMinPwr; int fanMaxPwr; int compMinPwr; int compMaxPwr; int waterLvl; int waterT; int lampD; }thisSet; void setup() { /*tempAirDs.inf = AIR; //устанавливаем значения Data.inf tempGround.inf = GRND; tempAir.inf = TEMP; tempRoom.inf = TEMP; humAir.inf = HUM; humRoom.inf = HUM; tempRoom.type = DHT_11; //устанавливаем значения Data.type humRoom.type = DHT_11; tempGround.type = DS18B20; tempAirDs.type = DS18B20; tempAir.type = DHT_22; humAir.type = DHT_22; humGround.type = PIN; lampSt.type = CHNG; lampH.type = CHNG; fanPwr.type = CHNG; compPwr.type = CHNG; filterSt.type = CHNG; doorSt.type = PIN; thisSet.maxTemp=0.67; thisSet.lamp=0; thisSet.filter=1; thisSet.fanMinPwr=123; thisSet.fanMaxPwr=231; thisSet.compMinPwr=255; thisSet.compMaxPwr=200; thisSet.waterLvl=11; thisSet.waterT=21; thisSet.lampD=30; tempRoom.control = NULL; //устанавливаем значения Data.control humRoom.control = NULL; tempGround.control = NULL; tempAirDs.control = NULL; tempAir.control = tempAir1; humAir.control = NULL; humGround.control = NULL; lampSt.control = lampSt1; lampH.control = NULL; fanPwr.control = NULL; compPwr.control = NULL; filterSt.control = NULL; doorSt.control = doorSt1; */ delay (5000); Serial.begin(9600); /*DATA_write(); //записываем setWrite(); */ DATA_get(); //читаем настройки Serial.println("DONE!"); getSet();//получаем расписание настроек for (int i = 0; i < 1; ++i) { //распечатать все настройки в расписании Serial.println(stampTime[i]); setRead(i); Serial.println(thisSet.maxTemp); Serial.println(thisSet.lamp); Serial.println(thisSet.filter); Serial.println(thisSet.fanMinPwr); Serial.println(thisSet.fanMaxPwr); Serial.println(thisSet.compMinPwr); Serial.println(thisSet.compMaxPwr); Serial.println(thisSet.waterLvl); Serial.println(thisSet.waterT); Serial.println(thisSet.lampD); }; Serial.println("DONE, NOW DATA:"); //dataCheck(); } void loop() { } void DATA_get() //чтение массива структур настроек из EEPROM { EEPROM_readAnything(0, arrayData); //читаем данные с начала памяти } void setRead(int i) //получаем настройки конкретного сета из памяти { int j = i + 1; //чтобы не перемножать на ноль int adr = ((sizeof(arrayData))+(j*sizeof(stampTime[i]) + 1) + (j - 1)*sizeof(Settings)); //получаем адрес начала настроек текущего сета Serial.println(adr); //печатаем адрес EEPROM_readAnything(adr, thisSet); //записываем данные в стракт настроек } void setWrite() //записываем настройки в память. { byte cont=2; //число заголовков настроек long newTime[2]={1182933,29292012}; //формируем массив заголовков сетов int Size = sizeof(arrayData)+sizeof(cont);//размер данных for (int i = 0; i<cont; ++i){ //проверяем сколько памяти займут настройки Size += sizeof(newTime[i]) + sizeof(thisSet); }; if (Size>=1023){ //если слишком много, то ошибка Serial.println("OUT OF MEMORY! TOO MANY TIMESETS!"); } else{//если нет, то записываем Serial.println("Writing new sets.."); int adr = sizeof(arrayData); //адрес записи EEPROM.write(adr, cont); //записываем общую сумму в первый байт adr++; for (int i = 0; i<cont; ++i){// записываем настройки Size = EEPROM_writeAnything(adr, newTime[i]); //записываем заголовок старта adr += Size; //увеличиваем адрес на размер Size = EEPROM_writeAnything(adr, thisSet); //записываем настройки сета adr += Size; }; }; Serial.println("Secsess, Updating sets.."); getSet(); Serial.println("Done!"); } void getSet() //чтение списка настроек из епром { //получаем список настроек int adr = sizeof(arrayData); Serial.println(adr); //начинаем со следующего байта после количества настроек. setCont = EEPROM.read(adr); //получаем количество настроек из байта следующего за настройками датчиков Serial.println(setCont); //печатать число заголовков if (setCont == 0){ Serial.println("No settings found"); } else{ //если количество настроек не пусто и больше нуля. stampTime = new long[setCont]; //массив времени начала периода настроек adr++; int Size; for (int i = 0; i<setCont; ++i){ //перебираем все заголовки настроек, в цикле. if (adr >= 1022){ Serial.println("OUT OF MEMORY! CHECK EEPROM!"); }//если вышли за пределы eeprom else{ Size = EEPROM_readAnything(adr, stampTime[i]); adr += Size + sizeof(thisSet); //перепрыгиваем на адрес +4байта long (заголовка) и длины блока настроек. }; }; }; } void DATA_write() { EEPROM_writeAnything(0, arrayData); //записываем данные датчиков с начала памяти. } /*void tempAir1() { Serial.println("tempAir"); } void doorSt1() { Serial.println("doorst"); } void lampSt1() { Serial.println("lampSt"); }*/
В итоге должно вывестить два раза одно и то же содержимое thisSet , с разными заголовками., До чтения Data даже не доходит..
PS
используется библиотека EEPROM_Anything тестировал её отдельно со структурами и массивами - все работает отлично.
тут опечатка arrayData[5], там конечно же 13, в коде, и в цикле в функции setup, там не i<1, а i<setСont.
Вероятно, где-то памяти под массивы не хватает...
нет, не ве этом дело. Там стоит метка которая печатает адрес записи. Первый блок занимает 206 байт, потом еще 1 бат, 4 байта заголовокХ2, около 20байт структураХ2, итого около 256 байт всего, а у ардуины уно 1024 байт Еепром.. К тому же, при работе видно, что последний стракт прочитался, просто выводится он до половины..
Методом научного тыка ошибка была локализована! Прошу помочь с решением!
Ошибка возникает при попытке чтения массива структур из EEPROM. возможно она вознакает на этапе записи, но это не видно. А вот при чтении возникает бесконечная зацикленность, причем это явно проблема с тем что происходит какой-то сбой в адресном пространстве, поскольку после первых двух циклов которые должны быть по программе, "лишние" выводятся разрезая пополам сообщения Сериал принта. т.е. например, код
// тут следует вывод из памяти
Serial.println("DONE");
После двух положенных циклов выводится следующим образом:
DO23
112
0
1
2
...
т.е. DONE обрезается... это явно какая-то хрень.
Вот код отвечающий за объявление, чтение и запись массива структур. Именно из-за него возникает глюк, остальное пишется и читается без проблем.
Прошу помочь разобраться... по всей видимости библиотека EEPROM_Anything неверно обрабатывает массивы структур, если так, то помогите придумать иной способ их записи в EEPROM.
Методом научного тыка ошибка была локализована! Прошу помочь с решением!
Ошибка возникает при попытке чтения массива структур из EEPROM. возможно она вознакает на этапе записи, но это не видно. А вот при чтении возникает бесконечная зацикленность, причем это явно проблема с тем что происходит какой-то сбой в адресном пространстве, поскольку после первых двух циклов которые должны быть по программе, "лишние" выводятся разрезая пополам сообщения Сериал принта. т.е. например, код
// тут следует вывод из памяти
Serial.println("DONE");
После двух положенных циклов выводится следующим образом:
DO23
112
0
1
2
...
т.е. DONE обрезается... это явно какая-то хрень.
Вот код отвечающий за объявление, чтение и запись массива структур. Именно из-за него возникает глюк, остальное пишется и читается без проблем.
Прошу помочь разобраться... по всей видимости библиотека EEPROM_Anything неверно обрабатывает массивы структур, если так, то помогите придумать иной способ их записи в EEPROM.
Для начала стоит заменить double на float, это должно сэкономить память. Насколько я помню, тип double должен занимать 64 бита - 8 байт, вряд ли тебе столько надо. Странно, что происходит затирание памяти, но, возможно, классу Serial тоже требуется оперативная память, а ему не хватает, поскольку она занята твоим массивом с данными. Ну и в кучу раз константу печатаешь, пиши уж Serial.print(F("DONE!"));
Для начала стоит заменить double на float, это должно сэкономить память.
.
Для начала стоит заменить double на float, это должно сэкономить память.
.
Ну в ардуине я новичок. Значит это не настоящий дабл, настоящий описан в стандарте http://en.wikipedia.org/wiki/IEEE_floating_point . Я думал, что раз слово есть, то как в стандарте. Это, впрочем, не отменяет моего мнения, что у него с распределением оперативной памяти проблемы.
Думаю что если бы дело было в нехватке памяти, об этом бы звопил компилятор - все-таки он работает с конкретной платой, которая задается в установках, и учитывает место под программу\ оперативную память.
Думаю что если бы дело было в нехватке памяти, об этом бы звопил компилятор - все-таки он работает с конкретной платой, которая задается в установках, и учитывает место под программу\ оперативную память.
Не обязательно - проблемы с памятью могут быть и из-за большого количества локальных переменных, размещаемых в стеке при вызове соответствующей функции. И компилятору просто не под силу предсказать, когда и какая функция будет вызываться, каков будет уровень вложенности вызываемых функций, ну и - соответственно - в какой момент растущий вниз стек вломится в область статических переменных.
Ну а если еще и куча используется...
Ну если я правильно понимаю, то все местные переменные, которые объявляются внутри функции удаляются из памяти при переходе к другой фунции. Да и в принципе оперативная память помоему на УНО 1024байт, а это 256 long\float\double переменных и 512 интов... Думаю едвали наберется треть этого количества во всем коде.. так что это маловероятно..
К тому же, если объявить все содержимое EEPROM в программе и потом прочитать - то никаких ошибок не возникает, значит данные в память помещаются.
Скорее всего при чтении создается указатель или ссылка на адрес который уже занят в программе, и происходит это вероятнее всего из-за ошибки в библиотеке.. ну и остается еще вариант аппаратной ошибки в eeprom например.
Ну если я правильно понимаю, то все местные переменные, которые объявляются внутри функции удаляются из памяти при переходе к другой фунции. Да и в принципе оперативная память помоему на УНО 1024байт, а это 256 long\float\double переменных и 512 интов... Думаю едвали наберется треть этого количества во всем коде.. так что это маловероятно..
К тому же, если объявить все содержимое EEPROM в программе и потом прочитать - то никаких ошибок не возникает, значит данные в память помещаются.
Скорее всего при чтении создается указатель или ссылка на адрес который уже занят в программе, и происходит это вероятнее всего из-за ошибки в библиотеке.. ну и остается еще вариант аппаратной ошибки в eeprom например.
Первое, что должен запомнить любой программист: чудес не бывает. Твоё название темы "глючит ардуино" подразумевает, что ты нашёл ошибку в её конструкции, но я пока что наблюдаю только ошибки в другом месте. Компилятор, как правильно тебе указали, не может статически проконтролировать будущее поведение программы, только корректность в каждом локальном участке. Разбирайся с расходом и использованием памяти или поменяй сферу деятельности.
Вот это разве не доказывает, что проблема не с распределением памяти:
К тому же, если объявить все содержимое EEPROM в программе и потом прочитать - то никаких ошибок не возникает, значит данные в память помещаются.
Я пробовал объявлять все переменные глобально (за исключением i в циклах for), включая все эти массивы структур и прочее - программа работает корректно. Значит дело не в нехватке памяти. Дело в чтении и записи данных в Епром.
А сфера деятельности у меня и так другая, это скорее увлечение.
Как я и думал дело оказалось в глюках указателей на функцию. Пришлось установить дебагер чтобы выяснить. Ну и кроме всего прочего, сменил библиотеку чтения\записи.
Проблема заключается в том, что неверно обрабатываются указатели на функцию, после прочтения из памяти. Нужно как-то это побороть. конкретно вот этот кусочек:
Если эту проверку закоментить, то с новой библиотекой и некоторыми другими исправлениями все работает, е сли оставить, то начинается опять чехорда - то по циклу запускает Setup(), то выводит какую-то чушь в сериал.. т.е. Видимо неверно записывается либо неверно читается указатель на функцию. Т.к. опять же, если задавать его в коде, то все работает.
РЕшено - ошибка оказалась в том, что указатели вообще нельзя сохранять в епром.
Вопрос решен в другой теме, спасибо всем кто поучаствовал!
ПРОШУ УДАЛИТЬ ЭТУ ТЕМУ!! и преведенные мной отрезки кода!
РЕшено - ошибка оказалась в том, что указатели вообще нельзя сохранять в епром.
А код рабочий привести не судьба? )
////указатели вообще нельзя сохранять в епром.
С чего бы вдруг? Можна если сильно нужно.
Только хорошей идеей это не назовеш, т.к. энергонезависимая память сохранит указатель и при заливке нового софта, а указатели останутся от старого, попытка их использовать даст глюк. Вот и имеем такой эффект плюс обычные ошибки - однин этот код чего стоит )))