Предусмотреть однократную задержку в выполнении функции
- Войдите на сайт для отправки комментариев
Сб, 26/05/2018 - 01:55
Здравствуйте, подскажите, пожалуйста, реально ли организовать следующее:
нужна функция, которая будет подавать импульс длительностью 300 мс, при этом очень хотелось бы не тормозить выполнение остальной программы, тоесть delay (300) не подходит. Примеры с millis() тут тоже не актуальны, так как они выполняются обычно в loop, где цикл за циклом после выполнения остальных задач проверяется не подошло ли нужное время, а у меня именно отдельная функция.. Как можно реализовать задержку в ней, не прерывая остальных задач?
Спасибо!
void Swich::swich () {
digitalWrite (lamp_pin, HIGH);
delay (300);
digitalWrite (lamp_pin, LOW);
}
Вас жестоко обманули. Миллис работает и в отдельной функции: В начале функции ставим иф с проверкой интервала и в каждом проходе заглядываем туда. Когда интервал достигнут, выполнили функцию и задали новый. В лупе и остальных функциях не должно быть делеев и тяжелых циклов.
Да, спасибо, организую так, буду заглядывать туда в каждом проходе, просто до этого, попадал в свою функцию c millis() один раз по условию, при следующих следующих проходах, после выполнения моей функции условие уже не соблюдалось и в эту функцию уже не попадал. Думал вдруг можно как то паролельно работать с этим..
Думал вдруг можно как то паролельно работать с этим..
Можно и параллельно, только всё равно придётся куда-то заглядывать. Как вариант централизованного "заглядывания":
1. Допустим, все функции имеют сигнатуру void Function();
2. Заводим список указателей на функции:
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(); }Таким образом - мы отделили механизм принятия решений от собственно вызова функций, что позволяет при желании настроить разное поведение их вызова. В механике принятия решений прослеживаются начатки по вызову шлюза к различным состояниям системы. В механике вызова функций можно добавить анализ состояния системы после каждого вызова функции, добавив ещё один промежуточный интерфейс (как раз тот самый шлюз к состояниям системы) - таким образом, обеспечивается довольно гибкое поведение системы. При этом сами функции делают только свою работу.
Механизм довольно легко расширяется, добавляются связанные с функциями параметры, цепочки вызовов и т.п., при желании.
Ни на что не претендую - просто как вариант давно известной архитектуры.
Ну метры они такие даже скетчи у них мудренные оказываются . Так переписал- компилируется. но там еще обкатывать и обкатывать.
/**/ 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
Ну метры они такие даже скетчи у них мудренные оказываются .
Привычка к STL ;) Юзаю простенькую реализацию Vector в проектах, потому и написал пример, основываясь на, так сказать. Потом - это псевдокод, назначение которого - просто продемонстрировать алгоритмику, ничего более. А там - хоть вектор, хоть Queue, хоть Deque, хоть List, хоть чёрт лысый - любой контейнер с нужным поведением, не вопрос.
Ага. Потому грамотно задачу описывать не просто в течении 300мсек, а задать погрешность. И если погрешность допустим 0,2мкс - то аппаратно, 20мкс - то из прерывания, а если 20мсек - то из цикла как выше пишут. Правда с учетом что они там явно переигрывают, и просто код
void loop() { if (someFlag1) function1(); if (someFlag2) function2(); ... }ничем не хуже для двух функций. Для большего массив функций завести и циклом вызывать.
Как луп крутится конечно важно, но для вывода через прерывание таймера тоже есть важные подробности: как часто и долго обрабатываются другие прерывания и как часто и надолго ли прерывания запрещаются. Вобщем универсального подхода нет.
Спасибо! За советы, суть уловил. Выглядит как то что нужно, есть конечно ряд нового для меня, попытаюсь разобратся.
а вот как бы было у меня. Без всяких флагов.
/* 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() { }Ну и самый "железный" способ, -попросить таймер поднять и опустить через заданное время свою аппаратную ногу (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 }Пример эксклюзивный, в интернетах такого применения нет :)
а вот как бы было у меня. Без всяких флагов.
Строго говоря, у тоби есть флаг - хэндл таймера ;)
Ну да. Согласен.
ТС, вариант от dimax наиболее изящный. Изучай.
Спасибо, большое, накидали много вариантов, "железный" вариант от dimax выглядит круто, но очень сложно, с регистрами и прочим мне пока не разобраться, к сожалению, тут даже не знаю как это читать...Впринципе понял вариант DetSimen. Только подскажите, про INVALID_HANDLE? THandle может принимать такое значение? Ведь это проверка, на то что таймер запущен. Впринципе можно обойтись и без нее, или реализовать иначе. Просто компиллятор ругается на это. 'INVALID_HANDLE' was not declared in this scope. А так, это отличный вариант, посылаем импульс и тут же взводим таймер на его отключение, и впринципе больше не заботимся об этом, работаем дальше. Сейчас пробую разобратся с кодом DIYMan и qwone там конечно намного сложнее с хранением функций в векторе много непонятного, но разобратся будет полезно, спасибо!
надо аптамушта скачать 2 файла атсюда
https://github.com/DetSimen/Arduino-
и бросить в директорию с проектиком
DetSimen это само собой, потключенную библиотеку я увидел, ему я так понял не нравится само значение INVALID_HANDLE , я тоже думал что THandle это должно быть целое число.. Без проверки на INVALID_HANDLE все отлично работает..
тогда добавь или в h файл или в свой
const THandle INVALID_HANDLE = -1;
в биб-ке я утром уже определение поправил, можешь еще раз скачать файлеги и заменить старые
Да спасибо! Посмотрел библиотеку, она реализует тот же "железный" способ как и у dimax? Менеджер задач осознать пока не получилось..
Да, те же таймеры в обертке.
Кстати на новую версию компиллятор ругается const THandle INVALID_HANDLE = -1, вообще ведь можно обойтись без этого? Ведь эта проверка, на выполнение задачи не обязательна?
А какими словами ругается?
Проверка нужна. Можно тогда везде вместо INVALID_HANDLE вручную подставить -1
Все, на сегодня. Зеленый змий зовет с ним бароца.
Желаю побед! Ошибка под катом
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 Этот отчёт будет иметь больше информации с включенной опцией Файл -> Настройки -> "Показать подробный вывод во время компиляции"Потом растолкукйте, пожалуйста, зачем проверка? В вашем примере, впринципе невозможен вариант повторного включения уже включенного таймера, как и отключение отключенного, он линейный и все последовательно включается и отключается, я правильно понимаю?
Ошибку поправил, выложил.
Проверка затем, что таймеры работают асинхронно с loop(). Предположим, вызвал ты гдета PulseOn() и пока проходят 300 миллисекунд до PulseOff() никто не мешает вызвать PulseOn() еще раз, например по ошибке. И что в этом случае произойдет? Хэндл то у таймера один. Таймер конечно добавится еще один, но предыдущий хэндл перепишется новым, а старый перейдет в раздел потерянных. Причем, ранее добавленный таймер и дальше будет работать, мешая новому, только доступа до него уже не будет, чтобы остановить и удалить, так как старый хэндл затёрт. Поэтому добавление таймера происходит только если предыдущий удален.
Паняна?