не адекватное поведение delay() в конструкторе

Looka
Offline
Зарегистрирован: 24.04.2012

Простой пример.   Виснет.  В сериал ничего не поподает.
Если закомментировать delay()  -  работает нормально.
Попробовал на разных платках  (Nano, Mega, Demilanove...)   В раных версиях IDE.

Собственно почему? 

 

class dim_DS18x20
{  int  _qty;
  
  public:
  dim_DS18x20( void )
  {// Конструктор
      int i;
            i = 25;
            delay( 250 );
        } 

};  // end class dim_DS18x20


dim_DS18x20 dimDS;    
void setup()
{   
  Serial.begin( 57600 );
}

void loop()
{ Serial.print( "_qty_DS18x20 = " );
  delay( 1000 );
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Возможно, дело в том, что переменная dimDS описана глобально, значт, Ваш конструктор вызывается ДО полной инициализации.  Проверка - перенесите описание в setup. Если заработает, то так и есть.

Looka
Offline
Зарегистрирован: 24.04.2012

Возможно, вечером проверю.   Но вопрос все равно останется. 

Нет такого ограничегния (или я не знаю) на объявления переменных.
Сам этим активно пользуюсь, примеров  есть куча, когда переменные объявляются  глобально, и не важно кокого типа. 

Скажем можно использовать Serial  в конструкторе,   что  для меня  менее очевидно, но работает.  А в чем уникальность delay() - не понимаю

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Looka пишет:

Скажем можно использовать Serial  в конструкторе,   что  для меня  менее очевидно, но работает.  А в чем уникальность delay() - не понимаю

Проверьте, и если подтвердится, то можно подумать и код посмотреть. Так на вскидку, там может быть ещё таймер не "заведён" как следует.

Looka
Offline
Зарегистрирован: 24.04.2012

Да, если объявлять не глобально, то  работает корректно.
Но вопрос остается.

А где у IDE  исходники встроенных библитек лежат?

 

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Looka пишет:

Нет такого ограничегния (или я не знаю) на объявления переменных.

Отнюдь.

В С++ нет простых переменных, все переменные - объекты. И для всех объектов при их создании вызывается конструктор.

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

Сделано это по ошибке или специально - не знаю.

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

Это факт.

Специально для таких случаев принято писать дополнительную функцию инициализации, которую обычно называют begin или init.

Цитата:

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

Скажем можно использовать Serial  в конструкторе,   что  для меня  менее очевидно, но работает.  А в чем уникальность delay() - не понимаю

Тем не менее, сам Serial почему-то инициализируется не в конструкторе, а в функции begin.

Так что Ваш конструктор не уникален.

Сам пытался инициализировать дисплей в конструкторе, и так же, как и Вы обломался.

Looka
Offline
Зарегистрирован: 24.04.2012
Спасибо за коментарии. 
 
Так в том то и дело что вызов конструктара для каждого объекта это парадигма  ООП и с++ в частности.
Отсюда и вопрос почему не работает. 
 
Про Serial.
Это как раз пример когда конструктор вызывается до Setup().    
И почему инициализируется не в конструкторе понятно,  
что бы не требовать от программиста объявлять этот объект явно.
И повторюсь - я не встречал явного ограничения на объявление глобальных переменных (хотя бы для пользовательских типов/классов)
 
 
Было бы интересно посмотреть во что все таки разворачивает IDE Arduino  конструккцию   из Setup()  и Loop().
Как то встречал как это сделать, не упомню сейчас. 
 
И вопрос остается  где исходники встренных библиотек можно глянуть. 
 
А теперь пора спать.....
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Looka пишет:

И вопрос остается  где исходники встренных библиотек можно глянуть. 

Тексты разбросаны по кускам. Поройтесь там. Основные вещи у меня лежат в 

C:\Arduino\arduino-1.6.5-r2\hardware\arduino\avr\cores\arduino

У Вас, видимо начало будет другое.

delay() живёт в файле wiring.c Судя по тексту, он должен работать, если работает функция micros(). 

micros() находится в том же файле. Ей для работы нужна инициализированная переменная timer0_overflow_count и нормально работающий таймер/каунтер 0.

Чтобы таймерр нормально работал, ему нужно задать делитель частоты, разрешить прерывания и т.п. Эти волшебные пассы делаются в функции init() (в том же файле). Кстати, в начале функции есть комментарий прямо относящийся к теме нашего разговора:

// this needs to be called before setup() or some functions won't
// work there

Функция init() вызывается из main() в файле main.cpp ... в общем, дальше сами разбирайтесь.

Общий вывод понятен. При инициализации глобальных переменных, ещё не установлен таймер 0 и потому не работают micros() millis() delay() и т.п.

Вам следует либо глобально объявить указатель, а экземпляр класса создавать в setup(), либо оставить глобальное объявление, но оставить конструктор пустым, а инициализацию перенести в метод типа Init() и вызывать его уже из setup().

Looka пишет:
И повторюсь - я не встречал явного ограничения на объявление глобальных переменных (хотя бы для пользовательских типов/классов)

Теперь встретили. Знайте.
Looka
Offline
Зарегистрирован: 24.04.2012

to:    ЕвгенийП
Спасибо! 

Буду смотреть.  Еще бы время на это где нибудь найти!

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Looka пишет:
Так в том то и дело что вызов конструктара для каждого объекта это парадигма  ООП и с++ в частности.

Отсюда и вопрос почему не работает.
Все это так.
Именно поэтому я тоже, когда делал библиотеку для дисплея, решил, что незачем вызывать init, как это было в тех образцах, с которыми я сравнивал, а нужно сделать "правильный" конструктор.
Не тут то было!
Я уже писал, что не знаю, сделано это умышленно или по ошибке.
Вполне вероятно, что умышленно, т.к. это - единственный способ прикладному программисту хоть что-то сделать до инициализации оборудования. Вдруг кому понадобится. Поэтому эту дырку и оставили.
Если бы сделали в строгом соответствии с ортодоксальной парадигмой ООП, у пользователя не было бы возможности вмешаться в выполнение программы до инициализации железа.
Looka
Offline
Зарегистрирован: 24.04.2012
И еще небольшая ремарка.  
millis() в конструкторе  работает! 
так что пока заменил 
delay( 250 )   
на 
for( unsigned long _i = millis();  millis()-_i < 250;  ); 
nevkon
Offline
Зарегистрирован: 20.01.2015

Почитайте эту тему, там есть решение вашей проблемы с delay():

http://arduino.ru/forum/programmirovanie/novichkam-avtomatnoe-programmir...

SunX
SunX аватар
Offline
Зарегистрирован: 04.10.2014

andriano пишет:

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

Скорее сделали так потому, что по другому, не изобретая свой C++ невозможно. Все глобальные переменные создаются ДО вызова функции main(), в которой вызывается init(), которая все инициализирует и разработчики ардуино не могут на это никак  повлиять (кроме разве что написать свой компилятор, но зачем?).

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