Вопрос чайника по самописным функциям

freeman86
Offline
Зарегистрирован: 07.09.2016

Не могу понять как правильно написать функцию...

Вот код, который работает как задумано, печатает в порт раз в n секунд что-то: 

unsigned long Time = 0;

void setup() {
  Serial.begin(9600);
}

void Timer();
    
void loop() {
  Timer(1000);
 }

void Timer(int millisec) {
  if(millis() - Time > millisec){
      Serial.println(1);
      Time = millis();
      }
 }

Но когда я хочу вызвать эту функцию другой функцией, например Run(),  не понимаю как надо изменить Timer, какого типа его сделать и что он должен возвращать, и должен ли, чтобы внутри Run() она адекватно отрабатывала. Затык в функции bool Timer(int millisec). Если сделать return true до Time = millis();, то таймер никогда не обновится, и всегда будет true, а если наоборот, то тоже самое. В любом случае, первый попавшийся return завершает  функцию. А хочется сделать полноценную функцию, чтобы она как delay() работала, только не блокировала всю программу, а через нужный промежуток времени возвращала что-либо.

unsigned long Time = 0;

void setup() {
  Serial.begin(9600);
}

bool Timer();
    

void Run();

void loop() {
  Run();
 }

bool Timer(int millisec) {
  
    if(millis() - Time > millisec){
     Time = millis();
      return true;
     }
   }

void Run(){
  
  if(Timer(1000) == true) {
    
    Serial.println(1);
  }
  
}

 

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

Если функция должна что-то возвратить, то ей это необходимо делать всегда. А у Вас только в одном случае происходит возврат true.

Параметр функции точно должен быть int?

freeman86
Offline
Зарегистрирован: 07.09.2016

Параметр может быть любой, вплоть до возможных 32 бит, это же таймер. Так что может и int может быть мало.

Не очень понял, что значит "необходимо делать всегда". Функция отсчитала время и вернула true. А как можно сделать иначе? 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

В любом другом случае она должна возвращать false...

freeman86
Offline
Зарегистрирован: 07.09.2016

Вот например, аналогичный вариант. Куда воткнуть здесь Time = millis() внутри функции byte Timer(), чтобы чередовались return 1 и return 2? 

unsigned long Time = 0;
byte digit = 0;

void setup() {
  Serial.begin(9600);

}

byte Timer();

void loop() {
 
digit = Timer(1000);

Serial.println(digit);
}

byte Timer(int millisec) {
  
  if(millis() - Time < millisec){
      return 1;
     }
    
    if(millis() - Time > millisec){
      return 2;
      Time = millis();
      }
}

 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

byte Timer(int millisec) {
  
  if(millis() - Time < millisec){
   return 1;
  }
  else {
   return 2;
  }
}
b707
Offline
Зарегистрирован: 26.05.2017

только оно не будет чередоваться

 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Это смотря с каким параметором вызвать ...

freeman86
Offline
Зарегистрирован: 07.09.2016

b707 пишет:

только оно не будет чередоваться

 

 

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

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

freeman86 пишет:

Не очень понял, что значит "необходимо делать всегда". Функция отсчитала время и вернула true. А как можно сделать иначе? 

похоже вы не понимаете, как работает ваша функция. Слова "отсчитала время и вернула true" были бы уместны, если бы функция запускалась, ждала 1000мс и потом возвращала true. Но на самом деле все работает совсем не так - за время 1 сек loop() успевает запустить вашу функцию десяток тысяч раз и каждый раз она должна что-то возвращать - а в вашем коде этого нет.

И по этой же причине новый вариант, возвращающий "1" и "2" - не будет их "чередовать". Функция будет возвращать несколько тысяч "двоек" на одну "единицу"

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

freeman86 пишет:

Вот и я об этом. Я не понимаю как сделать в данном случае смену состояния.

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

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

 Крайняя выдаёт двойки и иногда (раз в заданное время единичку).

 Конкретно эту фунцию сложно вызывать из разных мест с разными параметрами - так как она привязана к одной и той же unsigned long Time

freeman86
Offline
Зарегистрирован: 07.09.2016

b707 пишет:

freeman86 пишет:

Не очень понял, что значит "необходимо делать всегда". Функция отсчитала время и вернула true. А как можно сделать иначе? 

похоже вы не понимаете, как работает ваша функция. Слова "отсчитала время и вернула true" были бы уместны, если бы функция запускалась, ждала 1000мс и потом возвращала true. Но на самом деле все работает совсем не так - за время 1 сек loop() успевает запустить вашу функцию десяток тысяч раз и каждый раз она должна что-то возвращать - а в вашем коде этого нет.

И по этой же причине новый вариант, возвращающий "1" и "2" - не будет их "чередовать". Функция будет возвращать несколько тысяч "двоек" на одну "единицу"

 

Это я уже все понял экспериментальным путем, и только после этого создал эту тему. Даже читал книгу по С++ ))  Кстати, здесь на сайте, сказано про return, что он завершает функцию и выходит из нее. Это в случае, если return "пустой"?

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

Komandir пишет:

Конкретно эту фунцию сложно вызывать из разных мест с разными параметрами - так как она привязана к одной и той же unsigned long Time

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

 

freeman86
Offline
Зарегистрирован: 07.09.2016

Komandir пишет:

 Крайняя выдаёт двойки и иногда (раз в заданное время единичку).

 Конкретно эту фунцию сложно вызывать из разных мест с разными параметрами - так как она привязана к одной и той же unsigned long Time

 

То что видно в мониторе порта, это сначала единички, а потом уже бесконечные двойки..

 

Нужно создавать в самой функции unsigned long Time

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

freeman86 пишет:

Нужно создавать в самой функции unsigned long Time

нет, это не поможет.

Можно описать эту функцию как класс - и на каждый период создавать свой экземпляр - только непонятно, нафига такие сложности...

freeman86
Offline
Зарегистрирован: 07.09.2016

b707 пишет:

Komandir пишет:

Конкретно эту фунцию сложно вызывать из разных мест с разными параметрами - так как она привязана к одной и той же unsigned long Time

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

 

А это как? Можно пример?

freeman86
Offline
Зарегистрирован: 07.09.2016

b707 пишет:

freeman86 пишет:

Нужно создавать в самой функции unsigned long Time

нет, это не поможет.

Можно описать эту функцию как класс - и на каждый период создавать свой экземпляр - только непонятно, нафига такие сложности...

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

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

freeman86 пишет:

Нужно создавать в самой функции unsigned long Time

Нельзя - она уничтожится вместе выходом из фунции.

Если нужны работающие одновременно такого плана функции, то они должны опираться каждая на свою опорную переменную. Ну или в вызов функции передавать ещё и указатель на опорную переменную. 

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

freeman86 пишет:

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

по мне так "вычисления из миллиса" проще, я никакими библиотеками таймеров не пользуюсь

freeman86
Offline
Зарегистрирован: 07.09.2016

b707 пишет:

freeman86 пишет:

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

по мне так "вычисления из миллиса" проще, я никакими библиотеками таймеров не пользуюсь

 

Я понимаю что проще, но так не интересно. ))) Значит пойду объекты и классы изучать ))

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

b707 пишет:

 только непонятно, нафига такие сложности...

упаси нас бог это узнать ...

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

freeman86 пишет:

Я понимаю что проще, но так не интересно. ))) Значит пойду объекты и классы изучать ))

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

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

ТС не ищет простых путей ...

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

Человек велосипед изобретает, а вы тут набросились - злые дядьки. ))

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

с треугольными колесами ...

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

freeman86,

sadman41 пишет:

Если функция должна что-то возвратить, то ей это необходимо делать всегда. 

freeman86 пишет:

что значит "необходимо делать всегда". Функция отсчитала время и вернула true. А как можно сделать иначе? 

Komandir пишет:

В любом другом случае она должна возвращать false...

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

Непрограммисты этоЙ шутки не понимают.

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

freeman86 пишет:

Параметр может быть любой, вплоть до возможных 32 бит, это же таймер. Так что может и int может быть мало.

Поэтому я и интересуюсь - сознательно ли применён int. signed int и unsigned int, например, занимают по 2 байта, но крайние значения у них несколько разные, если так можно выразиться.

freeman86 пишет:

Не очень понял, что значит "необходимо делать всегда". Функция отсчитала время и вернула true. А как можно сделать иначе? 

Следите за руками, упрощённо объясняю:

Вызов функции - это, для МК, переход с текущего адреса A по некоему адресу B в перечне выполняемых команд. Return - это, после выполнения всего, что находится в функции, возврат неизвестно откуда (B+9000, например) на адрес A+1.  Если вызываемая функция не является void, то логично предположить, что результатом её работы можно пользоваться. Для этого, после возврата на адрес A+1, в ячейке X должно находиться некоторое значение - результат работы (то, что происходит при return 1 или return 89). 

В Вашем случае в ячейку X заносится true только в одном случае - когда условие внутри функции истинно. А если оно не истинно? Правильно - ничего не заносится. Т.е. после возврата из функции в X может лежать старое true, может случайно занесённое false. Результат в любом случае непредсказуем. А Вы на этот результат начинаете опираться в следующих вычислениях и получаете барабан из "Поля чудес".

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

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

freeman86 пишет:

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

[/quote]

Похоже Вам надо чтото такое

#define led2 2
#define led3 3
#define led4 4
#define led5 13

unsigned long currentPause[4]; //Количество элементов - количеству таймеров
bool fl_pause[4];//Количество элементов - количеству таймеров

//Функция дребезга или паузы
boolean my_pausa(unsigned long time, byte nomer) //time-время задержки, nomer-номер таймера
{
  bool x = 0;
  if (fl_pause[nomer] == 0)currentPause[nomer] = millis(), fl_pause[nomer] = 1;
  if (millis() - currentPause[nomer] >= time) x = 1, fl_pause[nomer] = 0;
  return x;
}
///////////////////////////
//пример

void setup() {
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
  pinMode(led4, OUTPUT);
  pinMode(led5, OUTPUT);
}

void loop() {
  //Здесь просто мигаем диодами с разной частотой
  if (my_pausa(500, 0)) digitalWrite(led2, !digitalRead(led2));

  if (my_pausa(1500, 1)) digitalWrite(led3, !digitalRead(led3));

  if (my_pausa(2000, 2)) digitalWrite(led4, !digitalRead(led4));

  if (my_pausa(2500, 3)) digitalWrite(led5, !digitalRead(led5));

}

 

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

Красивая функция. Ее можно в дребезгах использовать?

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

Да. Я использую.

freeman86
Offline
Зарегистрирован: 07.09.2016

Большое спасибо за полезные советы и помощь! :)

freeman86
Offline
Зарегистрирован: 07.09.2016
boolean my_pausa(unsigned long time, byte nomer) //time-время задержки, nomer-номер таймера
11 {
12   bool x = 0;
13   if (fl_pause[nomer] == 0)currentPause[nomer] = millis(), fl_pause[nomer] = 1;
14   if (millis() - currentPause[nomer] >= time) x = 1, fl_pause[nomer] = 0;
15   return x;
16 }

 Объясните, зачем в этой функции(выше написана в скетче) обязательно нужно использовать флаг состояния в 13 строчке? Почему без него не будет работать? Заходим в функцию, устанавливаем Х = 0, запоминаем время в currentPause, а ниже уже проверка на время таймера. Почему обязательно нужен флаг состояния?

MaksVV
Offline
Зарегистрирован: 06.08.2015

Не нужен, только там где x=1 вместо флаг=0 пишем currentPause=millis()

freeman86
Offline
Зарегистрирован: 07.09.2016

Bозник еще один. Почему нельзя просто вернуть единицу: return 1; а нужно именно присваивать значение переменной и уже ее возвращать return x; ? Если делать так return 1; функция работает как если бы не было отсчета  времени.

MaksVV
Offline
Зарегистрирован: 06.08.2015

Потому что х изначально =0. И функция в основном этот ноль и возвращает. Единицу возвращает только когда таймер заканчивается (когда заходит в иф). Можно.обойтись без переменной х. Два раза написать return. В ифе return 1. После ифа return 0.