Почему используют две переменные long вместо одной?

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

В статье про энкодеры на нашем сайты прведен следующий код:

01	int brightness = 120; // яркость LED, начинаем с половины
02	 
03	int fadeAmount = 10; // шаг изменения яркости LED
04	 
05	unsigned long currentTime;
06	 
07	unsigned long loopTime;
08	 
09	const int pin_A = 12; // pin 12
10	 
11	const int pin_B = 11; // pin 11
12	 
13	unsigned char encoder_A;
14	 
15	unsigned char encoder_B;
16	 
17	unsigned char encoder_A_prev=0;
18	 
19	void setup() { // declare pin 9 to be an output:
20	 
21	pinMode(9, OUTPUT); // устанавливаем pin 9 как выход
22	 
23	pinMode(pin_A, INPUT);
24	 
25	pinMode(pin_B, INPUT);
26	 
27	currentTime = millis();
28	 
29	loopTime = currentTime; }
30	 
31	void loop() {
32	 
33	currentTime = millis();
34	 
35	if(currentTime >= (loopTime + 5)){ // проверяем каждые 5мс (200 Гц)
36	 
37	encoder_A = digitalRead(pin_A); // считываем состояние выхода А энкодера
38	 
39	encoder_B = digitalRead(pin_B); // считываем состояние выхода А энкодера
40	 
41	if((!encoder_A) && (encoder_A_prev)){ // если состояние изменилось с положительного к нулю
42	 
43	if(encoder_B) { // выход В в полож. сост., значит вращение по часовой стрелке
44	 
45	// увеличиваем яркость, не более чем до 255
46	 
47	if(brightness + fadeAmount <= 255) brightness += fadeAmount; }
48	 
49	else { // выход В в 0 сост., значит вращение против часовой стрелки
50	 
51	// уменьшаем яркость, но не ниже 0
52	 
53	if(brightness - fadeAmount >= 0) brightness -= fadeAmount; }
54	 
55	}
56	 
57	encoder_A_prev = encoder_A; // сохраняем значение А для следующего цикла
58	 
59	analogWrite(9, brightness); // устанавливаем яркость на 9 ножку
60	 
61	loopTime = currentTime; }
62	 
63	}

 

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Прошу прощения, сделал что-то не так. Продолжу.

В сектче объявлены две переменные типа long: currentTime и loopTime, которые участвуют в процедуре опроса.

Почему не прим енено нечто с одной переменной? Например:

If (millis()-loopTime=5)

   {

      //Что-то делаем

      loopTime=millis();

   }

Зачем две переменные, ведь память же жрем?

   

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Это вопрос веры. Кто-то верит, что частый вызов millis() протирает дыру в МК и вообще плохо на него влияет. Поэтому перегружают значение в переменную, ожидая, что это будет работать быстрее или меньше тревожить стек. Но наврядли думают о том, как компилятор распорядится с тем и другим способом получения миллисекунд. 

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

sadman41 пишет:

Кто-то верит, что частый вызов millis() протирает дыру в МК...

Прости, старик, вот этого не понял. Чтобы слить что-то из миллиса в переменную, надо же к нему все равно обратиться? Та же мошонка, только в профиль. Или я чушь порю? Но самое главное в моем недоумении: нет ли в "моем" способе каких-либо капканов? Скетч-то мой с "моей" конструкцией работает пока (?).

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

Для того, чтобы в строке №61 переменной looptime присвоить то самое значение, которое использовалось в сравнении в строке 35 (millis то мог и измениться между строками 35 и 61). 

Иногда это нужно и важно. Нужно ли это в данном коде - не знаю, внимательно не смотрел. Вы его так вставили, что смотреть невозможно. Номера какие-то левые.

Кстати, в строке №35 опасность глюка при переполнении millis

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

Sonologist пишет:
тобы слить что-то из миллиса в переменную, надо же к нему все равно обратиться? Та же мошонка, только в профиль.

У Вас сколько мошонок? Вы не видите разницы между "обратиться один раз" и "обращаться несколько раз"? Так вот как раз при "нескольких обращениях" значение может успеть измениться.

sadman41
Offline
Зарегистрирован: 19.10.2016

Согласен с ЕвгенийП по поводу "синхронизации" через доп. переменную. Но, как показывают мои наблюдения,  в 90% скетчей, с которыми приходят на форум - рассинхронизация является последним, что в них следует исправить ))

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016
?
if(currentTime-loopTime  >= 5){ 

 

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

ЕвгенийП пишет:

У Вас сколько мошонок? Вы не видите разницы между "обратиться один раз" и "обращаться несколько раз"?

Вот тут, если можно, разжуйте.

В каждом лупе первого скетча мы обращаемся к миллису и гоним его содержимое в переменную. Дальше сравниваем значение этой переменной со значением второй переменной.

В более коротком варианте - вроде, то же самое, только без перемещения значения миллис в первую переменную, а просто сравниваем  миллис со второй переменной. Короче же. Не врублюсь никак, в чем подвох?

И еще: это правда, что частое обращение к миллису портит процессор? Я знаю, что эпрон имеет конечное количество циклов "запись-перезапись". А тут та же ситуация?

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

Sonologist пишет:

В более коротком варианте - вроде, то же самое, только без перемещения значения миллис в первую переменную, а просто сравниваем  миллис со второй переменной. Короче же. Не врублюсь никак, в чем подвох?

Я это уже писал, но повторю ещё раз и медленно: в Вашем втором скетче 2 (ДВА) обращения к millis. Первое в операторе if строке № ХЗ (вставляйте скетчи как положено), а второе в операторе присваивания в строке № ХЗ.

Вы понимаете, что за время выполнения программы от строки №ХЗ до строки №ХЗ миллисекунда могла перещёлкнуться, и второе обращение к millis вернёт не тоже самое, что вернуло первое? В этом и есть разница между подходами. Большинству скетчей это пофиг, некоторым - нет. Вот и всё.

Sonologist пишет:

И еще: это правда, что частое обращение к миллису портит процессор? 

Это троллинг для новичков, типа как молодых матросов заставляют якоря точить. На деле - бред сивой кобылы.

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Спасибо, разъяснение вполне понятно. Действительно, в зависимости от задачи, перещелк миллиса или имеет значение или пофиг. Теперь разберусь со свими убогими (пока) скетчами.

[/quote]

Это троллинг для новичков, типа как молодых матросов заставляют якоря точить. На деле - бред сивой кобылы.

[/quote]

Я так и знал. И чуть не повелся, хоть уже вся грудь седая, а все как мальчик верю всякому вздору.

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

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

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

Sonologist пишет:

Прошу прощения, сделал что-то не так. Продолжу.

В сектче объявлены две переменные типа long: currentTime и loopTime, которые участвуют в процедуре опроса.

Почему не прим енено нечто с одной переменной? Например:

If (millis()-loopTime=5)

   {

      //Что-то делаем

      loopTime=millis();

   }

Зачем две переменные, ведь память же жрем?

   

 

Здесь есть два момента:

1. Если мы обращаемся к millis() один раз и запоминаем результат, мы тем самым обеспечиваем выбранный интервал. Хотя бы в среднем. А при многократных вызовах millis() мы можем лишь гарантировать, что интервал будет не менше выбранного. В каких-то случаях лучше одно, в каких-то - другое. Но следует помнить, что это разные варианты.

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

sadman41
Offline
Зарегистрирован: 19.10.2016

В коллекцию неоднозначностей, полагаю, стоит добавить такой пример:

uint32_t nowTime = millis();

if (nowTime - prevActionOneTime > 1000) {
   // Делаем что-то с неоднозначной задержкой. Открываем газ или разливаем бензин.
  // ...
  // ...
  uint8_t doRandomDelay = (random(100) > 98);
  delay (doRandomDelay ? 5000 : 0); 
  prevActionOneTime = nowTime;
}

if (nowTime - prevActionTwoTime > 500) {
   // Делаем что-то иное. Например - даём искру.
  // ...
  prevActionTwoTime = nowTime;
}

Как можно заметить - моментом начала выполнения первого  и второго условия с точки зрения МК скорее всего будет одна и та же миллисекунда. Для внешнего наблюдателя, в большинстве случаев, это может быть точно так же (если нет постоянной задержки в блоке первого условия) и при отладке он не увидит странного поведения. Но, когда по закону подлости, устройство собрано и установлено на газовый котёл, с вероятностью в... первое условие значительно задерживает исполнение алгоритма. И это видит (либо слышит шипение выходящего газа) внешний наблюдатель. Однако МК продолжает "думать", что условия проверяются так же быстро и последовательно, как и ранее, не фиксируя проблем. Искра, ещё искра...И снится нам не рокот космодрома...

 

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

asam пишет:

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

А вот этот момент важен. Спасибо!

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

andriano пишет:

Здесь есть два момента:

1. Если мы обращаемся к millis() один раз и запоминаем результат, мы тем самым обеспечиваем выбранный интервал. Хотя бы в среднем. А при многократных вызовах millis() мы можем лишь гарантировать, что интервал будет не менше выбранного. В каких-то случаях лучше одно, в каких-то - другое. Но следует помнить, что это разные варианты.

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

Спасибо, понял, ибо разъяснение предельно конкретное. Обязательно приму к сведению. Первый пункт - весьма важен, ибо мне будет нужно это учитывать. По второму пункту - все понял, но в конкретно моей задаче производительность не критична. Однако и это "на корочку" записал.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

шаблон работы с millis

uint_32  now = millis();

if (now-lastmillis < READINTERVAL) return;  // не пришло еще время чота делать

lastmillis = now;

//  чота делаем

now - переменная отоматическая, при выходе из функции - растворица в стеках

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

DetSimen пишет:

шаблон работы с millis

uint_32  now = millis();

if (now-lastmillis < READINTERVAL) return;  // не пришло еще время чота делать

lastmillis = now;

//  чота делаем

now - переменная отоматическая, при выходе из функции - растворица в стеках

Спасибо, логика шаблона понятна. Но для меня (поскольку еще в ардуинопрограммировании дуб дубом) удобнее все-таки заменить "<" на ">" и производить действа внутри If. Исключительно из соображений, чтоб потом не запутаться. И еще вопросик: "uint_32" в данном случае = "long", насколько я понимаю?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

using uint32_t = unsigned long;

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

DetSimen пишет:

using uint32_t = unsigned long;

Спасибо, усёк :)

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

Sonologist пишет:

asam пишет:

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

А вот этот момент важен. Спасибо!

Sonologist пишет:

andriano пишет:

2. Запоминая время в переменной мы расходуем немного больше оперативной памяти, но экономим на памяти программной, а заодно и экономим вычислительный ресурс. Если производительность для нас важна, либо программная память заканчивается, это может оказаться существенным.

По второму пункту - все понял, но в конкретно моей задаче производительность не критична. Однако и это "на корочку" записал.

Вы уж определитесь: "важный момент" или "не критично". )))))))))

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

andriano пишет:

Вы уж определитесь: "важный момент" или "не критично". )))))))))

Важный момент - с точки зрения методологии. Не критично - мой конкретный случай.