delay()-millis() как оптимизировать код

lilik
Offline
Зарегистрирован: 19.10.2017

В последние дни много тем о данном переходе. Ковыряюсь в направлении "объединить-переписать два скетча" примерно так:

// 1 поток
void setup()
{
pinMode(13, OUTPUT);
}
void loop()
{
delay(500);// кадр 1 
digitalWrite(13,HIGH);
delay(100);// кадр 2
digitalWrite(13,LOW);
  
}
// 2 поток
void setup()
{
pinMode(12, OUTPUT);
}
void loop()
{
delay(666);// кадр 1   
digitalWrite(12,HIGH);
delay(222);// кадр 2
digitalWrite(12,LOW);
}
// слитые 1 и 2 поток
long Y1=0;
long Y2=0;
int K1=1;//счётчик кадров в псевдопотоке 1
int K2=1;//счётчик кадров в псевдопотоке 2

void setup()
{
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
}
void loop()
{
loop_1();//
loop_2();//
}
//////////////////////////////
void loop_1(){
if(K1==1&&millis()-Y1>=100){Y1=millis();K1=2;digitalWrite(13,LOW);}
if(K1==2&&millis()-Y1>=500){Y1=millis();K1=1;digitalWrite(13,HIGH);}    
}
void loop_2(){
if(K2==1&&millis()-Y2>=222){Y2=millis();K2=2;digitalWrite(12,LOW);}
if(K2==2&&millis()-Y2>=666){Y2=millis();K2=1;digitalWrite(12,HIGH);}    
}

 

lilik
Offline
Зарегистрирован: 19.10.2017

Сводится всё примерно к наборам типа:

/*
 void loop()
 {
 if(K==1&&millis()-Y>...){Y=millis();K=2;...}
 if(K==2&&millis()-Y>...){Y=millis();K=3;...}
 if(K==3&&millis()-Y>...){Y=millis();K=4;...}
 if(K==4&&millis()-Y>...){Y=millis();K=5;...}
 ...
 if(K==N&&millis()-Y>...){Y=millis();K=1;...}
 }
 */

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

Как это можно сделать?

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020
struct taskData
{
  bool state;        // текущий статус задачи - активна/неактивна
  uint32_t interval; // интервал выполнения задачи в милисекундах
  uint32_t timer;    // таймер задачи
};

taskData frame12 = (taskData){true, 500, 0};
taskData frame34 = (taskData){true, 666, 0};

void setTaskInterval(taskData &task, uint32_t _interval)
{
  task.interval = _interval;
}

bool checkTask(taskData &task)
{
  bool result = false;
  if (task.state && (millis() - task.timer >= task.interval))
  {
    result = true;         // если задача активна и момент ее срабатывания наступил
    task.timer = millis(); // сохраняем таймер для следующей сработки
  }
  return (result); // возвращаем полученный результат
}

// ==== кадр 1 и 2 ========================
void setLed1()
{
  static bool led_state = false;
  led_state = !led_state;
  digitalWrite(13, led_state);
  (led_state) ? setTaskInterval(frame12, 500) : setTaskInterval(frame12, 100);
}

// ==== кадр 3 и 4 ========================
void setLed2()
{
  static bool led_state = false;
  led_state = !led_state;
  digitalWrite(12, led_state);
  (led_state) ? setTaskInterval(frame34, 666) : setTaskInterval(frame34, 222);
}

void tick()
{
  if (checkTask(frame12))
  {
    setLed1();
  }
  if (checkTask(frame34))
  {
    setLed2();
  }
}

void setup()
{
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
}

void loop()
{
  tick();
}

 

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

lilik пишет:

Сводится всё примерно к наборам типа:

/*
 void loop()
 {
 if(K==1&&millis()-Y>...){Y=millis();K=2;...}
 if(K==2&&millis()-Y>...){Y=millis();K=3;...}
 if(K==3&&millis()-Y>...){Y=millis();K=4;...}
 if(K==4&&millis()-Y>...){Y=millis();K=5;...}
 ...
 if(K==N&&millis()-Y>...){Y=millis();K=1;...}
 }
 */

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

Как это можно сделать?

switch (K)
{
case 1:
  if(millis()-Y>...){Y=millis();K=2;...}
  break;
case 2:
  if(millis()-Y>...){Y=millis();K=3;...}
  break;
}

 

lilik
Offline
Зарегистрирован: 19.10.2017

Это не будут проверяться условия с номером кадра больше текущего?, а до текущего будут?

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

lilik пишет:

Это не будут проверяться условия с номером кадра больше текущего?, а до текущего будут?

Странный вопрос. Оператор switch

lilik
Offline
Зарегистрирован: 19.10.2017

Ну именно как в вопросе я и понял после 4 прочтения :-). Тогда это частичное сокращение проверок.

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Будет выполняться только кейс с совпадающим номером кадра. Главное, не забывать операторы break; в конце каждого кейса.

lilik
Offline
Зарегистрирован: 19.10.2017

Ясно, при последнем кадре будут проведены все проверки до него.

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Русский язык не родной? 

v258 пишет:

Будет выполняться только кейс с совпадающим номером кадра. Главное, не забывать операторы break; в конце каждого кейса.

lilik
Offline
Зарегистрирован: 19.10.2017

Родной, - Родной ! :-)

Не выполняться, а проверяться. Вот фраза из ссылки:

Когда найден оператор case, значение которого равно значению переменной,   выполняется программный код в этом операторе.

НАЙДЕН...

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Именно так. Оператор switch берет значение К и пробегает по списку кейсов, сравнивая К с их значениями. Если находит совпадающий, то выполняет его код. По другому никак не получится. Человек - и тот сначала ищет совпадающий кейс, а потом уже смотрит, что в нем. Так у человека глаза есть. И обработка информации где-то в глубинах мозга. А МК как сможет узнать, куда прыгать? ;)

lilik
Offline
Зарегистрирован: 19.10.2017

v258 пишет:

По другому никак не получится.  А МК как сможет узнать, куда прыгать? ;)

Печаль... видно бейсик из детства навеял со своими goto между строками-частями кода.

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

В детстве и деревья были повыше, и трава зеленее. А если сейчас сядешь за бейсик и попробуешь сделать на нем, окажется, что все равно нужно сравнивать и выбирать нужную метку по условию сравнения ;)

 

b707
Offline
Зарегистрирован: 26.05.2017

lilik  и v258

не нужен тут switch() и куча if-ов

Сделайте массив интервалов с индексом по переменной K - и все проверки выродятся в одну строчку

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

lilik пишет:

В последние дни много тем о данном переходе. Ковыряюсь в направлении "объединить-переписать два скетча" примерно так:

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

Совершенно непонятно, как из первого кода получить второй.

Нужно указать, что объединение кодов должно происходить в два этапа:

1. В каждом из фрагментов перейти от использования delay() к использованию millis(). (обязательно проверив их работоспособность после переделки)

2. Физически объединить полученные на 1 этапе скетчи в один.

Вы опустили результат первого этапа, чем сделали свой пример совершенно непонятным.

lilik
Offline
Зарегистрирован: 19.10.2017

andriano пишет:

Вы опустили результат первого этапа, чем сделали свой пример совершенно непонятным.

Да, упустил.

//переписываем скетч (1 поток) в эквивалентный
long Y1=0;
int K1=1;//счётчик кадров в псевдопотоке 1
void setup()
{
pinMode(13, OUTPUT);
}
void loop()
{
loop_1();//
}
//////////////////////////////
void loop_1(){
if(K1==1&&millis()-Y1>=500){Y1=millis();K1=2;digitalWrite(13,HIGH);}
if(K1==2&&millis()-Y1>=100){Y1=millis();K1=1;digitalWrite(13,LOW);}    
}
//переписываем скетч (2 поток) в эквивалентный 
long Y2=0;
int K2=1;//счётчик кадров в псевдопотоке 2
void setup()
{
pinMode(12, OUTPUT);
}
void loop()
{
loop_2();//
}
//////////////////////////////
void loop_2(){
if(K2==1&&millis()-Y2>=666){Y2=millis();K2=2;digitalWrite(12,HIGH);}
if(K2==2&&millis()-Y2>=222){Y2=millis();K2=1;digitalWrite(12,LOW);}    
}

 

lilik
Offline
Зарегистрирован: 19.10.2017

После объединяем в общий скетч:

// слитые 1 и 2 поток
long Y1=0;
long Y2=0;
int K1=1;//счётчик кадров в псевдопотоке 1
int K2=1;//счётчик кадров в псевдопотоке 2

void setup()
{
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
}
void loop()
{
loop_1();//
loop_2();//
}
//////////////////////////////
void loop_1(){
if(K1==1&&millis()-Y1>=500){Y1=millis();K1=2;digitalWrite(13,HIGH);}
if(K1==2&&millis()-Y1>=100){Y1=millis();K1=1;digitalWrite(13,LOW);}    
}
void loop_2(){
if(K2==1&&millis()-Y2>=666){Y2=millis();K2=2;digitalWrite(12,HIGH);}
if(K2==2&&millis()-Y2>=222){Y2=millis();K2=1;digitalWrite(12,LOW);}    
}

 

lilik
Offline
Зарегистрирован: 19.10.2017

Пока эксперименты выглядят по такому плану:

1. Делим скетчи на фрагменты-кадры по входящим в них delay();

2. Меняем фрагменты на эквиваленты с использованием millis();

3. Собираем каждый скетч в отдельности и проверяем эквивалентность в работе.

4. Собираем скетчи в общий и перепроверяем в работе. 

lilik
Offline
Зарегистрирован: 19.10.2017

b707 пишет:

lilik  и v258

не нужен тут switch() и куча if-ов

Сделайте массив интервалов с индексом по переменной K - и все проверки выродятся в одну строчку

Это да, но их не станет меньше. И проверки скорее всего будут несводимые к массивам (разные наборы условий и реализаций по ним).

Дим-мычъ
Offline
Зарегистрирован: 20.03.2021

lilik пишет:

void loop_1(){
if(K1==1&&millis()-Y1>=500){Y1=millis();K1=2;digitalWrite(13,HIGH);}
if(K1==2&&millis()-Y1>=100){Y1=millis();K1=1;digitalWrite(13,LOW);}    
}

Я извиняюсь, вопрос новичка: а разве не надо в разных условиях применять разные переменные?

Имею ввиду Y1

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Интервалы заданы константами (500 и 100, соответственно), а переменная K1/K2 как раз разделяет условия. Вот что первоначальной инициализации нормальной нет, это вопрос. Я бы сделал так:

long Y1;
long Y2;
int K1=1;  // счётчик кадров в псевдопотоке 1
int K2=1;  // счётчик кадров в псевдопотоке 2

void setup() {
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  Y1 = millis();
  Y2 = millis();
}

 

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

lilik, Нужно писать как положено. Членораздельно, прежде всего! На кой хрен мне (и всем остальным) нужно раскодировать все эти записи! Вы кто? Радистка Кэт, чтоле? Ужос! 
Это просто неуважение к читающим! И я вам об этом уже говорил. Но, видать, "не в коня корм", к сожалению.(

Дим-мычъ
Offline
Зарегистрирован: 20.03.2021

BOOM пишет:

Интервалы заданы константами (500 и 100, соответственно), а переменная K1/K2 как раз разделяет условия.

Я к тому, что в #17 возможно надо не long Y1 = 0; long Y2 = 0; ,   a long Y1 = 0;...longY4 = 0;?

Иначе будет сбой по интервалам. Или нет?

lilik
Offline
Зарегистрирован: 19.10.2017

Green пишет:

Вы кто? Радистка Кэт, чтоле? Ужос! 

Трудно перестроиться на программирование. Привычка (сильна) ужимать до отдельных букв и действий между ними. А писать Kadr и Yschedschee тоже не хочется. Просто идею саму хотел донести.

lilik
Offline
Зарегистрирован: 19.10.2017

BOOM пишет:

Интервалы заданы константами (500 и 100, соответственно), а переменная K1/K2 как раз разделяет условия. Вот что первоначальной инициализации нормальной нет, это вопрос. Я бы сделал так:

long Y1;
long Y2;
int K1=1;  // счётчик кадров в псевдопотоке 1
int K2=1;  // счётчик кадров в псевдопотоке 2

void setup() {
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  Y1 = millis();
  Y2 = millis();
}

 

Согласен, обычно пишу long Y1=0;// тут чего то ступил.

lilik
Offline
Зарегистрирован: 19.10.2017

Дим-мычъ пишет:

BOOM пишет:

Интервалы заданы константами (500 и 100, соответственно), а переменная K1/K2 как раз разделяет условия.

Я к тому, что в #17 возможно надо не long Y1 = 0; long Y2 = 0; ,   a long Y1 = 0;...longY4 = 0;?

Иначе будет сбой по интервалам. Или нет?

Нет. Y1 это образно говоря течение 1 потока, Y2 - 2 потока. А значения K1, K2 показывают как далеко от начала соответствующего loop() дошло исполнение соответствующего скетча. 

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

lilik пишет:

BOOM пишет:

Интервалы заданы константами (500 и 100, соответственно), а переменная K1/K2 как раз разделяет условия. Вот что первоначальной инициализации нормальной нет, это вопрос. Я бы сделал так:

long Y1;
long Y2;
int K1=1;  // счётчик кадров в псевдопотоке 1
int K2=1;  // счётчик кадров в псевдопотоке 2

void setup() {
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  Y1 = millis();
  Y2 = millis();
}

 

Согласен, обычно пишу long Y1=0;// тут чего то ступил.

Да так и было у тебя изначально (равно нулю), но это не правильно. Миллис уже могло отсчитать не мало (или запоминает значение при ребуте или ещё что (миллион вариантов), поэтому первые интервалы будут не верными. Правильно сразу миллис инициализировать и как можно ближе к loop() или даже в самом loop(). Это пофиг со светодиодами, а там где 1 миллисекунда важна - не прокатит.

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Дим-мычъ пишет:

BOOM пишет:

Интервалы заданы константами (500 и 100, соответственно), а переменная K1/K2 как раз разделяет условия.

Я к тому, что в #17 возможно надо не long Y1 = 0; long Y2 = 0; ,   a long Y1 = 0;...longY4 = 0;?

Иначе будет сбой по интервалам. Или нет?

В #26 все доходчевее чем я пояснили.)

Logik
Offline
Зарегистрирован: 05.08.2014

lilik пишет:

b707 пишет:

lilik  и v258

не нужен тут switch() и куча if-ов

Сделайте массив интервалов с индексом по переменной K - и все проверки выродятся в одну строчку

Это да, но их не станет меньше. И проверки скорее всего будут несводимые к массивам (разные наборы условий и реализаций по ним).

Еще как сводимые.

Если сильно надо чтоб проверок действительно стало меньше - есть способ. 

1. Для всех "задач" определяем через сколько времени им выполняться и выбираем минимальное из них.

2. Формируем через обычную работу с миллис это интервал из п.1 и по завершении - выполняем "задачу" для которой он был.

3. Переходим к п.1

На словах не сложно но на практике  - не для новичка. Но проверок меньше.

Дим-мычъ
Offline
Зарегистрирован: 20.03.2021

BOOM пишет:

В #26 все доходчевее чем я пояснили.)

 lilik, BOOM, спасибо за разъяснения

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

В большинстве случаев она не критична. Возможно я и здесь не прав, отвечать не надо, я лучше "тихо сам

с собой" ещё подумаю))

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

lilik пишет:

Green пишет:

Вы кто? Радистка Кэт, чтоле? Ужос! 

Трудно перестроиться на программирование. Привычка (сильна) ужимать до отдельных букв и действий между ними. А писать Kadr и Yschedschee тоже не хочется. Просто идею саму хотел донести.

Вот именно форма записи и затрудняет восприятие Вашей идеи.

IMHO если что-то делать, то делать как следует. А иначе - вообще не имеет смысла.

SAB
Offline
Зарегистрирован: 27.12.2016

andriano пишет:

IMHO если что-то делать, то делать как следует. А иначе - вообще не имеет смысла.

Истина в том, что всё что можно измерить, всегда можно улучшить. И смысл всегда есть, потому что нет предела совершенства.

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

Logik пишет:

Если сильно надо чтоб проверок действительно стало меньше - есть способ. 

1. Для всех "задач" определяем через сколько времени им выполняться и выбираем минимальное из них.

2. Формируем через обычную работу с миллис это интервал из п.1 и по завершении - выполняем "задачу" для которой он был.

3. Переходим к п.1

На словах не сложно но на практике  - не для новичка. Но проверок меньше.


Это называется с использованием системного тика. Так обычно делают где нет миллис. Вместо тика иногда даже delay(T_SYS).
В некоторых случаях, когда просят что нибудь добавить в монстроподобный скетч, заряжаю свободный таймер на прерывание и в нём, в фоне, уже делаю свою работу.

Дим-мычъ
Offline
Зарегистрирован: 20.03.2021

Дим-мычъ пишет:

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

В большинстве случаев она не критична. Возможно я и здесь не прав

Таки да, я и здесь не прав. Разобрался, всё работает корректно, без ошибок. Код за фиг. скобками, если он меньше периода конечно, не влияет.

Спасибо, полезный пример.

lilik
Offline
Зарегистрирован: 19.10.2017

andriano пишет:

Вот именно форма записи и затрудняет восприятие Вашей идеи.

:-(

Согласен, ещё заметил, что запись типа if(){....;....;if(){}....;....;if(){if(){}}...;} с "однобуквенными переменными" легче понимается мной чем "лесенки со словами - shot ,gone

 

Logik
Offline
Зарегистрирован: 05.08.2014

Green пишет:

Logik пишет:

Если сильно надо чтоб проверок действительно стало меньше - есть способ. 

1. Для всех "задач" определяем через сколько времени им выполняться и выбираем минимальное из них.

2. Формируем через обычную работу с миллис это интервал из п.1 и по завершении - выполняем "задачу" для которой он был.

3. Переходим к п.1

На словах не сложно но на практике  - не для новичка. Но проверок меньше.


Это называется с использованием системного тика. Так обычно делают где нет миллис. Вместо тика иногда даже delay(T_SYS).
В некоторых случаях, когда просят что нибудь добавить в монстроподобный скетч, заряжаю свободный таймер на прерывание и в нём, в фоне, уже делаю свою работу.

Так тут интервал как формировать - не существенно. Существенно и сложно п.1. По сути нужно держать список порядка вызова задач сортированный по нарастанию времени, уметь добавлять в него новую задачу и т.д.

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

Но, повторюсь, это точно не для новичков.

lilik
Offline
Зарегистрирован: 19.10.2017

Дим-мычъ пишет:

Спасибо, полезный пример.

Он безграничен для тренировок. Как воткнуть в 1 кадр "делей из цикла" и т.д. :-)

// 1 поток
int k=0;//
void setup()
{
pinMode(13, OUTPUT);
digitalWrite(13,LOW);

}
void loop()
{
while( k<20){
delay(300);   
digitalWrite(13,!digitalRead(13));
k++;
}  
}

 

Дим-мычъ
Offline
Зарегистрирован: 20.03.2021

lilik пишет:

Он безграничен для тренировок. Как воткнуть в 1 кадр "делей из цикла" и т.д. :-)

Когда-то Alexey_Rem показал мне простой и доходчивый пример

http://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/popytka-odolet-delay#comment-606475

чем помог разобраться(ещё раз ему спасибо).

С тех пор использую в разных вариациях. По сути это то же самое, просто не разобрался сразу ,

опыта видимо маловато ещё...

Но чем больше разберу примеров - тем лучше))

lilik
Offline
Зарегистрирован: 19.10.2017

Дим-мычъ пишет:

По сути это то же самое,... 

Но чем больше разберу примеров - тем лучше))

Ну, да, строка if(millis()>T){T=millis();...} эквивалент в "мгновенном loop()" для delay(T); из "долгого loop()". Остальные "добавки лишь для указания местоположения" в долгом.