Этюды для начинающих: blink и без delay, и без millis

RxMaxx
Offline
Зарегистрирован: 10.04.2020

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

Обычно, когда речь идет о каких-либо периодических действиях, то чаще всего предлагается подобный алгоритм:

 if(millis() - previousMillis > interval) {
    previousMillis = millis();
    if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;
    digitalWrite(ledPin, ledState);
  }

И, этот способ ну, мягко говоря, не очень, особенно когда всего лишь нужно мигать светодиодом.

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

Т.к. программа обычно делает еще что-то кроме мигания светодиодом, то мы никогда точно не знаем когда она доберется до if(millis() - previousMillis > interval) – интервал мигания может быть очень нестабильный, если светодиодов несколько, и они могут мигать вообще вразнобой, что иногда плохо или банально некрасиво. Ну, и тут очень простоя программа мигания, если нужно сделать что-то красивее нужно городить еще кучу ifов.

Короче, предлагаю, простой, короткий и довольно универсальный вариант:

byte blnk;
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  blnk=0B01010011;
}
void loop() {
  digitalWrite(LED_BUILTIN, blnk&(1<<(millis()/250)%8));
…
}

Переменная blnk – задает тип мигания, если она равно 0 – диод не горит совсем, если 255 – то все время, в других случаях разнообразно мигает (надеюсь понятно почему как). Тут все легко масштабировать, как на кол-во светодиодов, так и по способу мигания. И да, разные светодиоды будут мигать в унисон. Управлять их миганием очень просто – нужно лишь внутри кода менять blnk по необходимости.

 

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

Не хочу расстраивать, но и в Вашем случае особой стабилизации интервала мигания не наблюдается.

RxMaxx
Offline
Зарегистрирован: 10.04.2020

sadman41 пишет:
Не хочу расстраивать, но и в Вашем случае особой стабилизации интервала мигания не наблюдается.

Почему же? Тут идет привязка точно к millis, ни что никуда не уплывет.

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

А в исходном варианте к чему привязка идёт?
Поставьте в 8-й строке "стабильного" варианта delay(random(255)) и проверьте - действительно ли есть различия.

RxMaxx
Offline
Зарегистрирован: 10.04.2020

sadman41 пишет:
А в исходном варианте к чему привязка идёт? Поставьте в 8-й строке "стабильного" варианта delay(random(255)) и проверьте - действительно ли есть различия.

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

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

Ну, если Вам нравятся дорогие % и / , которые почему-то работают лучше, чем классический вариант, то у меня вопросов более нет.

RxMaxx
Offline
Зарегистрирован: 10.04.2020

sadman41 пишет:
Ну, если Вам нравятся дорогие % и /

А чем они так дороги? Можете предложить способ лучше, со схожим функиционалом и простотой?

 

Bruzzer
Offline
Зарегистрирован: 17.03.2020

RxMaxx пишет:

В моем варианте, как минимум не будет происходит накопление ошибки.

Чтобы не происходило накопление ошибки

вместо        previousMillis = millis();

пишите       previousMillis += interval;

Komandir
Онлайн
Зарегистрирован: 18.08.2018

Размер заливаемого в камень кода гляньте с делениями и без ...

RxMaxx
Offline
Зарегистрирован: 10.04.2020

Bruzzer пишет:

Чтобы не происходило накопление ошибки

вместо        previousMillis = millis();

пишите       previousMillis += interval;

Это не мой пример, и не нравится он мне по многим причинам, я из более-менее все назвал выше.

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

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

RxMaxx
Offline
Зарегистрирован: 10.04.2020

Komandir пишет:

Размер заливаемого в камень кода гляньте с делениями и без ...

Ну, дык

int interval=1000;
unsigned long previousMillis;
bool ledState;
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
if(millis() - previousMillis > interval) {
  
   previousMillis = millis();
   if (ledState == LOW)
     ledState = HIGH;
   else
     ledState = LOW;
   digitalWrite(LED_BUILTIN, ledState);
 } 
}
Скетч использует 860 байт (2%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 14 байт (0%) динамической памяти, оставляя 2034 байт для локальных переменных. Максимум: 2048 байт.
 
Против, кода который делает точно тоже

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, (millis()/1000)%2);  
}
Скетч использует 848 байт (2%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.
 
Причем для простого мигания переменные вообще не нужны. А для более сложного - "классический" пример будет просто в разы больше и сложнее.

 

 

Bruzzer
Offline
Зарегистрирован: 17.03.2020

Вы пишите в тему "Этюды для начинающих: blink и без delay, и без millis"

В вашем случае millis() используется.

Komandir
Онлайн
Зарегистрирован: 18.08.2018

Так millis не кратно 1000 и будет один периодический конкретный рассинхрон.

Видимо для деления на 1000 есть короткий алгоритм, по этому код не сильно вырос 

RxMaxx
Offline
Зарегистрирован: 10.04.2020

Bruzzer пишет:

Вы пишите в тему "Этюды для начинающих: blink и без delay, и без millis"

В вашем случае millis() используется.

Предлагаете отдельную тему создать? 

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

 

Komandir
Онлайн
Зарегистрирован: 18.08.2018

RxMaxx что будет в редкие моменты обнуления millis ?

RxMaxx
Offline
Зарегистрирован: 10.04.2020

Komandir пишет:

Видимо для деления на 1000 есть короткий алгоритм, по этому код не сильно вырос 

Перебрал несколько вариантов - все в пределах 844 - 848 байт.

Komandir пишет:

Так millis не кратно 1000 и будет один периодический конкретный рассинхрон.

А чему оно кратно? Я в одном проекте этот способ использую, рассинхрона не наблюдаю. У меня там несколько диодов мигают. А вот со старым способом - наоборот все довольно быстро уплывало, нужно было  специально с этим бороться. 

Bruzzer
Offline
Зарегистрирован: 17.03.2020

RxMaxx пишет:

Предлагаете отдельную тему создать? 

Нет. Просто вдруг вы не заметили.

RxMaxx
Offline
Зарегистрирован: 10.04.2020

Komandir пишет:

RxMaxx что будет в редкие моменты обнуления millis ?

Ничего страшного. Один кривой мырг раз в 40 дней - это ерунда, сами millis - это далеко не RTC.

Komandir
Онлайн
Зарегистрирован: 18.08.2018

Реактор в Чернобыле не по вашему алгоритму работал ?

RxMaxx
Offline
Зарегистрирован: 10.04.2020

Komandir пишет:

Реактор в Чернобыле не по вашему алгоритму работал ?

Хотите краткий пересказ из-за чего авария случилась? Или думаете АЭС на МК кто-то делает?

По Вашему - управление АЭС и мигание светодиодами - это сопоставимые по критичности задачи?

Komandir
Онлайн
Зарегистрирован: 18.08.2018

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

RxMaxx
Offline
Зарегистрирован: 10.04.2020

Komandir пишет:

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

А Вы собственно, какую задачу решаете? Управление АЭС? Я вот решал задачу - красиво и просто мигать светодиодами, которые являются индикаторами некоторых процессов, от которых не зависят чья либо жизни и здоровье, а так же состояние экономики страны. Предложенный мною код, с этой задачей справляется на 99,9999%, при этом экономит, как ни странно, память и нервы. Опять таки, по сравнению с "классикой" я не вижу недостатков. А использование прерываний, а так же отслеживания обнуления millis - для данной задачи избыточная сложность, у нас тут, видите ли, МК с весьма ограниченными ресурсами, чтоб все их тратить на совсем уж идеальное мигание.

Если можете предложить способ ЛУЧШЕ, для данной задачи - милости просим. 

Komandir
Онлайн
Зарегистрирован: 18.08.2018

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

Размер кода на ассемблере всего в два раза больше кода, которые генерируют ваши три строки ...

RxMaxx
Offline
Зарегистрирован: 10.04.2020

Komandir пишет:

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

Ну, тогда я Вам официально запрещаю использовать предложенный мною код, не хочу брать грех на душу... Хотя нет, я  же не претендую на авторство, и ни как не могу регламентировать использование кода.

RxMaxx
Offline
Зарегистрирован: 10.04.2020

Komandir пишет:

Размер кода на ассемблере всего в два раза больше кода, которые генерируют ваши три строки ...

А еще можно отдельный МК на каждый светодиод повесить, и желательно все продублировать... и все застраховать, на всякий случай.

И строчка там одна, и она работает, вопреки всякой логике. Теперь, что, ради мигания диодом ассемблерные вставки делать?

Вы что сказать то в итоге хотите то? Я вот не могу понять.

Komandir
Онлайн
Зарегистрирован: 18.08.2018

Ваш алгоритм не верен - садись два !

RxMaxx
Offline
Зарегистрирован: 10.04.2020

Komandir пишет:

Ваш алгоритм не верен - садись два !

Обоснуйте.

Komandir
Онлайн
Зарегистрирован: 18.08.2018

RxMaxx пишет:

...Один кривой мырг ...

RxMaxx
Offline
Зарегистрирован: 10.04.2020

Komandir пишет:

RxMaxx пишет:

...Один кривой мырг ...

Раз в 40 дней, и то не факт, что он там будет, от которого ничего не зависит. И?

P.S. У меня форум уже второй раз глючит, дублирует сообщения. Давайте его тоже за это снесем.

Komandir
Онлайн
Зарегистрирован: 18.08.2018

RxMaxx видимо сегодня как раз тот 40 день у движка форума

RxMaxx
Offline
Зарегистрирован: 10.04.2020

Komandir пишет:

RxMaxx видимо сегодня как раз тот 40 день у движка форума

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

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

Чё накинулись? Больше мигалок, хороших и разных!

RxMaxx, миллис ещё одну подлость приготовил - он пропускает некоторые числовые значения. Как бы Вашей идее это не нагадило в плане стабильности :-(

Green
Offline
Зарегистрирован: 01.10.2015

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

Чё накинулись? Больше мигалок, хороших и разных!

У меня есть четырёхбайтовая.))) Но на асм.(

Komandir
Онлайн
Зарегистрирован: 18.08.2018

Green она будет так быстро мигать, что не каждым осциллографом увидишь ...

Green
Offline
Зарегистрирован: 01.10.2015

Видно невооруженным глазом.) И даже двухбайтная!)

Komandir
Онлайн
Зарегистрирован: 18.08.2018

Через всю память - это не два байта !

Green
Offline
Зарегистрирован: 01.10.2015

Да, но хекс то имеет именно такой размер, какие вопросы.)

Bruzzer
Offline
Зарегистрирован: 17.03.2020

RxMaxx пишет:

Если можете предложить способ ЛУЧШЕ, для данной задачи - милости просим. 

Это не ЛУЧШЕ, но некоторые замечания. (Времена примерные, т.к. смотрел на симуляторе.)

i=(unsignedLongL/1000)%2; занимает около 600 тактов

i=(unsignedLongL/1024)%2; занимает около 77 тактов.

Иногда это может иметь значение. И если надо просто моргать, а не отмерять интервалы, то может имеет смысл делить на 1024. (можно пойти дальше и читать внутренний счетчик и вообще не делить, но это наверное перебор ).

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

А если вместо 

i=(unsignedLongL/1024)%2

написать 

i = (L>>10) & 0x01; 

скока тактов занимает? 

Bruzzer
Offline
Зарегистрирован: 17.03.2020

DetSimen пишет:

скока тактов занимает? 

Два. Спасибо.

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

Дида, степени двойки gcc сам оптимизирует.

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

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

Дида, степени двойки gcc сам оптимизирует.

ну от как выяснилось, я лучше gcc оптимизирую. :) 

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

Bruzzer пишет:

DetSimen пишет:

скока тактов занимает? 

Два. Спасибо.

Два не может быть.

Хотя, отбросить вообще младший бит, старший сдвинуть на 2 вправо и сделать and или проверить бит.  Мошт, и два.  Тяжело сабражать в домашнем аресте. 

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

Не, два никак :-)

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

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

Не, два никак :-)

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

RxMaxx
Offline
Зарегистрирован: 10.04.2020

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

RxMaxx, миллис ещё одну подлость приготовил - он пропускает некоторые числовые значения.

Есть только Цезий-133 непогрешимый, все остальные "счётчики" так или иначе пропускают, даже Земля наша, не зря же високосные дни и даже секунды придумали.

Да, если делить не на 1000 а на степень двойки, то можно сэкономить еще целых 92 байта! Что делает код еще меньше и быстрее по сравнению с "классикой", но чаще все таки приходится использовать обычные секунды (и кратные им), и страдать за это, даже millis страдают. Наша система измерения времени вообще жутко неудобная с точки зрения программирования, что ж поделать то.

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

RxMaxx пишет:
не зря же високосные дни и даже секунды придумали.
Пример неудачный, ибо григорианский календарь есть явная провокация Врага рода человеческого. Посудите сами, ввёл его папа Григорий тринадцатый, а тринадцатые числа в нём попадают на пятницу чаще, чем на любой другой день недели!

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

Убери из классики лишний if... Будет короче.

Bruzzer
Offline
Зарегистрирован: 17.03.2020

DetSimen пишет:

А если вместо 

i=(unsignedLongL/1024)%2

написать 

i = (L>>10) & 0x01; 

скока тактов занимает? 

В своем ответе я крупно лопухнулся ответив 2.

Дело в том, что я не заменил строчку i=(uL/1024)%2, а добавил k=(uL>>10) & 0x01 и компилятор просто взял готовый ответ.

И в том и в другом случае занимает около 80 тактов.

00000094  CLI 		Global Interrupt Disable 
	k= (uL>>10) & 0x01;
00000095  LDS R24,0x0112		Load direct from data space 
00000097  LDS R25,0x0113		Load direct from data space 
00000099  LDS R26,0x0114		Load direct from data space 
0000009B  LDS R27,0x0115		Load direct from data space 
0000009D  LDI R18,0x0A		Load immediate 
0000009E  LSR R27		Logical shift right 
0000009F  ROR R26		Rotate right through carry 
000000A0  ROR R25		Rotate right through carry 
000000A1  ROR R24		Rotate right through carry 
000000A2  DEC R18		Decrement 
000000A3  BRNE PC-0x05		Branch if not equal 
000000A4  ANDI R24,0x01		Logical AND with immediate 
000000A5  STS 0x0117,R24		Store direct to data space 
	cli();
000000A7  CLI 		Global Interrupt Disable