Предусмотреть однократную задержку в выполнении функции

vasya00
Offline
Зарегистрирован: 30.05.2016
Здравствуйте, подскажите, пожалуйста, реально ли организовать следующее:
нужна функция, которая будет подавать импульс длительностью 300 мс, при этом очень хотелось бы не тормозить выполнение остальной программы, тоесть delay (300) не подходит. Примеры с millis() тут тоже не актуальны, так как они выполняются обычно в loop, где цикл за циклом после выполнения остальных задач проверяется не подошло ли нужное время, а у меня именно отдельная функция.. Как можно реализовать задержку в ней, не прерывая остальных задач?
Спасибо!
void Swich::swich () {
  digitalWrite (lamp_pin, HIGH);
  delay (300);
  digitalWrite (lamp_pin, LOW);
}

 

 

bwn
Offline
Зарегистрирован: 25.08.2014

vasya00 пишет:

Примеры с millis() тут тоже не актуальны, так как они выполняются обычно в loop, где цикл за циклом после выполнения остальных задач проверяется не подошло ли нужное время, а у меня именно отдельная функция.. Как можно реализовать задержку в ней, не прерывая остальных задач

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

vasya00
Offline
Зарегистрирован: 30.05.2016

Да, спасибо, организую так, буду заглядывать туда в каждом проходе, просто до этого, попадал в свою функцию c millis() один раз по условию, при следующих следующих проходах, после выполнения моей функции условие уже не соблюдалось и в эту функцию уже не попадал. Думал вдруг можно как то паролельно работать с этим..

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

vasya00 пишет:

Думал вдруг можно как то паролельно работать с этим..

Можно и параллельно, только всё равно придётся куда-то заглядывать. Как вариант централизованного "заглядывания":

1. Допустим, все функции имеют сигнатуру void Function();

2. Заводим список указателей на функции:

typedef void (*PFunction) ();

Vector<PFunction> toCall;

3. Делегируем принятие решений обработчику:

void DecisionMaker()
{
   if(someFlag)
     toCall.push_back(function1);

  if(someFlag2)
    toCall.push_back(function2);
}

void function1()
{
  Serial.println("f1");
}

void function2()
{
  Serial.println("f2");
}

4. Делегируем вызов этих функций:

void Caller()
{
  for(size_t i=0;i<toCall.size();i++)
  {
     PFunction f = toCall[i];
    f();
  }
  toCall.clear();
}

5. Когда нужно - вызываем делегат для принятия решений и делегат для вызова функций:

void loop()
{
    DecisionMaker();
    Caller();
}

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

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

Ни на что не претендую - просто как вариант давно известной архитектуры.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Ну метры они такие даже скетчи у них мудренные оказываются . Так переписал- компилируется. но там еще обкатывать и обкатывать.

/**/
unsigned long mill;
//--------------------------------
template <typename T> class Vector { // примерный шаблон класса вектор 
  protected:
  public:
    void clear() { // очистить память
    }
    void push_back(T AAA) { // запихнуть функцию в память
    }
    unsigned int size() { // сколько функций в памяти 
      return 0;
    }
    T read(int num) { // прочитать текущую функцию 
      return NULL;
    }
};
//--------------------------------
typedef void (*PFunction) ();
Vector<PFunction> toCall;

bool someFlag1 = false;//<-- некий флаг 1
void function1() {     // и ее функция 1
  Serial.println("f1");
}
bool someFlag2 = false;//<-- некий флаг 2
void function2() {     // и ее функция 2
  Serial.println("f2");
}
void DecisionMaker() { //< пихает функции в вектор если флаг активирован
  if (someFlag1)  toCall.push_back(function1);
  if (someFlag2)  toCall.push_back(function2);
}
void Caller() { // Последовательный вызыватель
  for (size_t i = 0; i < toCall.size(); i++) {
    PFunction f = toCall.read(i); f();
  }
  toCall.clear();
}
//--------------------------------

//-----main()---------------------------
void setup() {
}
void loop() {
  mill = millis();
  DecisionMaker();
  Caller();
}
/*Скетч использует 1322 байт (4%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 186 байт (9%) динамической памяти, оставляя 1862 байт для локальных переменных. Максимум: 2048 байт.
*/

ПС: Но мой Менеджер задачи работает. Если кому интересен то он здесь.#165

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

qwone пишет:

Ну метры они такие даже скетчи у них мудренные оказываются . 

Привычка к STL ;) Юзаю простенькую реализацию Vector в проектах, потому и написал пример, основываясь на, так сказать. Потом - это псевдокод, назначение которого - просто продемонстрировать алгоритмику, ничего более. А там - хоть вектор, хоть Queue, хоть Deque, хоть List, хоть чёрт лысый - любой контейнер с нужным поведением, не вопрос.

5N62V
Offline
Зарегистрирован: 25.02.2016

vasya00 пишет:

Здравствуйте, подскажите, пожалуйста, реально ли организовать следующее:
нужна функция, которая будет подавать импульс длительностью 300 мс, при этом очень хотелось бы не тормозить выполнение остальной программы, тоесть delay (300) не подходит. Примеры с millis() тут тоже не актуальны, так как они выполняются обычно в loop, где цикл за циклом после выполнения остальных задач проверяется не подошло ли нужное время, а у меня именно отдельная функция.. Как можно реализовать задержку в ней, не прерывая остальных задач?
 

 

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

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

Ага. Потому грамотно задачу описывать не просто в течении 300мсек, а задать погрешность. И если погрешность допустим 0,2мкс - то аппаратно, 20мкс - то из прерывания, а если 20мсек - то из цикла как выше пишут. Правда с учетом что они там явно переигрывают, и просто код

void loop() {
    if (someFlag1) function1();
  if (someFlag2) function2();
 ...
}

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

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

vasya00
Offline
Зарегистрирован: 30.05.2016

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

DIYMan пишет:

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

а вот как бы было у меня.  Без всяких флагов. 

/*
    Name:       PulseOnceTest.ino
    Created:	28.05.2018 8:56:54
*/
#include "Arduino.h"
#include "TTimerList.h"

void PulseOn(uint32_t ams);
void PulseOff(void);

extern TTimerList TimerList;

THandle hOnceTimer=INVALID_HANDLE;

byte OutPin;


void PulseOn(uint32_t ams) {  // ams - длительность импульса в миллисекундах на пине OutPin
	if (hOnceTimer == INVALID_HANDLE) {
		hOnceTimer = TimerList.Add(ams, PulseOff);   //  взводим таймер
		digitalWrite(OutPin, HIGH);       // включаем OutPin
	}
}

void PulseOff(void) {
	if (hOnceTimer > INVALID_HANDLE) {
		digitalWrite(OutPin, LOW);       // выключаем OutPin
		TimerList.Delete(hOnceTimer);    // удаляем таймер
		hOnceTimer = INVALID_HANDLE;
	}
}



void setup()
{
	OutPin = 13;
	pinMode(OutPin, OUTPUT);

	delay(1000);

	PulseOn(500);  // включаем один раз светлодиот на 13-м пине на 500 миллисекунд

}

void loop()
{

}

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Ну и самый "железный" способ, -попросить таймер поднять и опустить через заданное время свою аппаратную ногу (9пин):

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

void loop(){
if (digitalRead(8)==HIGH) {mig();} 
}

void mig(){
TCCR1B=0; TCNT1=0;
OCR1A=4686;//300mS timeOut
TCCR1A=(1<<COM1A0)|(1<<COM1A1); //pin Up after compare
TCCR1C=1<<FOC1A; //force compare
TCCR1A=(1<<COM1A1);//pin Down after compare
TCCR1B=(1<<CS12)|(1<<CS10)|(1<<WGM12); // CTC, div1024
}

Пример эксклюзивный, в интернетах такого применения нет :)

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

DetSimen пишет:

а вот как бы было у меня.  Без всяких флагов. 

Строго говоря, у тоби есть флаг - хэндл таймера ;) 

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

Ну да. Согласен. 

ТС, вариант от dimax наиболее изящный. Изучай. 

vasya00
Offline
Зарегистрирован: 30.05.2016

Спасибо, большое, накидали много вариантов, "железный" вариант от dimax выглядит круто, но очень сложно, с регистрами и прочим мне пока не разобраться, к сожалению, тут даже не знаю как это читать...Впринципе понял вариант DetSimen. Только подскажите, про INVALID_HANDLE? THandle может принимать такое значение? Ведь это проверка, на то что таймер запущен. Впринципе можно обойтись и без нее, или реализовать иначе. Просто компиллятор ругается на это. 'INVALID_HANDLE' was not declared in this scope. А так, это отличный вариант, посылаем импульс и тут же взводим таймер на его отключение, и впринципе больше не заботимся об этом, работаем дальше. Сейчас пробую разобратся с кодом DIYMan и qwone там конечно намного сложнее с хранением функций в векторе много непонятного, но разобратся будет полезно, спасибо!

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

надо аптамушта скачать 2 файла атсюда 

https://github.com/DetSimen/Arduino-

и бросить в директорию с проектиком

vasya00
Offline
Зарегистрирован: 30.05.2016

DetSimen это само собой, потключенную библиотеку я увидел, ему я так понял не нравится само значение INVALID_HANDLE , я тоже думал что THandle это должно быть целое число.. Без проверки на INVALID_HANDLE все отлично работает..

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

тогда добавь или в h файл  или в свой

const THandle INVALID_HANDLE = -1;

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

vasya00
Offline
Зарегистрирован: 30.05.2016

Да спасибо! Посмотрел библиотеку, она реализует тот же "железный" способ как и у dimax? Менеджер задач осознать пока не получилось..

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

Да, те же таймеры в обертке. 

 

vasya00
Offline
Зарегистрирован: 30.05.2016

Кстати на новую версию компиллятор ругается const THandle INVALID_HANDLE = -1, вообще ведь можно обойтись без этого? Ведь эта проверка, на выполнение задачи не обязательна?

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

А какими словами ругается?

Проверка нужна. Можно тогда везде вместо INVALID_HANDLE вручную подставить -1

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

Все, на сегодня. Зеленый змий зовет с ним бароца. 

vasya00
Offline
Зарегистрирован: 30.05.2016

Желаю побед! Ошибка под катом

Arduino: 1.8.5 (Windows Store 1.8.10.0) (Windows 10), Плата:"Arduino/Genuino Uno"

In file included from sketch\TTimerList.cpp:2:0:

TTimerList.h:5: error: 'THandle' does not name a type

 const THandle INVALID_HANDLE = -1;

       ^

sketch\TTimerList.cpp: In member function 'THandle TTimerList::Add(PVoidFunc, long int)':

TTimerList.cpp:99: error: 'INVALID_HANDLE' was not declared in this scope

  return INVALID_HANDLE;                                   // если нет - вернем код ашыпки (-1) 

         ^

exit status 1
'THandle' does not name a type

Этот отчёт будет иметь больше информации с
включенной опцией Файл -> Настройки ->
"Показать подробный вывод во время компиляции"

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

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

Ошибку поправил, выложил. 

Проверка затем, что таймеры работают асинхронно с loop(). Предположим, вызвал ты гдета PulseOn() и пока проходят 300 миллисекунд до PulseOff() никто не мешает вызвать PulseOn() еще раз, например по ошибке.   И что в этом случае произойдет?  Хэндл то у таймера один.  Таймер конечно добавится еще один, но предыдущий хэндл перепишется новым, а старый перейдет в раздел потерянных.  Причем, ранее добавленный таймер и дальше будет работать, мешая новому, только доступа до него уже не будет, чтобы остановить и удалить, так как старый хэндл затёрт.  Поэтому добавление таймера происходит только если предыдущий удален.  

Паняна?