Мигающий диод при наличии задержки в основном теле программы
- Войдите на сайт для отправки комментариев
Втр, 05/06/2018 - 13:32
Добрый день!
Взял такой листинг, он мне как раз подходит (это будет автополив по таймеру):
// Первый регулятор управляет временем, которое будет литься вода (от 4 до 15 секунд)
#define MAX_FLOWTIME 15 // seconds
#define MIN_FLOWTIME 4 // seconds
// Второй регулятор управляет частотой полива от раза в день до раза в неделю
#define MAX_PERIOD 7 // days
#define MIN_PERIOD 1 // days
#define MAX 675
#define MIN 0
int volumePin = A0; // Пин, к которому подцеплен регулятор, отвечающий за объём поливаемой воды
int periodPin = A1; // Пин, к которому подцепелн регулятор, отвечающий за период между поливами
int pumpPin = 5; // Пин, к которому подсоединено управление насосом
int ledOnline = 13; // Сигнальный LED
int volume;
int period;
// Процедура, включающая насос на время, заданное в volume
void water() {
digitalWrite(pumpPin, HIGH); // включаем насос
delay(volume);
digitalWrite(pumpPin, LOW); // выключаем насос
delay(period);
}
void setup() {
pinMode(pumpPin, OUTPUT);
digitalWrite(pumpPin, LOW);
ledOnline, OUTPUT);
}
void loop() {
// Считываем значения регуляторов (переменных резисторов) и приводим их к заданным пределам
volume = map(analogRead(volumePin), MIN, MAX, MIN_FLOWTIME, MAX_FLOWTIME) * 1000;
period = map(analogRead(periodPin), MIN, MAX, MIN_PERIOD, MAX_PERIOD) * 1000 * 60 * 60 * 24;
water();
}
Но мне нужно что бы дополнительно к этому постоянно мигал сигнальный светодиод допустим раз в секунду, сигналиризующий о том, что устройство работает (например что батарейки не сели).
Вопрос: как мне это сделать, если основной цикл loop исполняется раз в period, а period равен одному дню (или больше, в зависимости от настройки)?
В этом коде period никогда не может быть дольше, чем 37% дня. И volume тоже. На Uno и nano, по крайней мере.
Вопрос: как мне это сделать, если основной цикл loop исполняется раз в period, а period равен одному дню (или больше, в зависимости от настройки)?
Конечно, можно и заставить диод мигать и в рамках вашей задачи(и совсем несложно), однако это будет неправильно в педагогическом смысле :)
По уму правильнее будет переписать скетч, убрав огромные задержки, чтобы ЛУП исполнялся не дольше нескольких десятков мс.
В этом коде period никогда не может быть дольше, чем 37% дня. И volume тоже.
точно :)
Автор, максимальная длительность задержки в вашем коде - 9 часов 6 минут :)
Это все не отменяет моего предыдущего сообщения - код реально бредовый и должен быть выкинут в помойку.
В этом коде period никогда не может быть дольше, чем 37% дня. И volume тоже. На Uno и nano, по крайней мере.
Почему, расскажи поподробнее?
Конечно, можно и заставить диод мигать и в рамках вашей задачи(и совсем несложно), однако это будет неправильно в педагогическом смысле :)
По уму правильнее будет переписать скетч, убрав огромные задержки, чтобы ЛУП исполнялся не дольше нескольких десятков мс.
Так, я тоже чувствую что ждать несколько дней через delay как-то неправильно.
А как переписать, какой алгоритм будет правильный?
А как переписать, какой алгоритм будет правильный?
смотри пример "blink without delay"
Но мне нужно что бы дополнительно к этому постоянно мигал сигнальный светодиод допустим раз в секунду, сигналиризующий о том, что устройство работает (например что батарейки не сели).
Пока будешь осваивать миллис(), можешь воспользоваться "костылем", который заставит мигать лед
volatile unsigned int blinkLED = 10, period = 1000; void setup(){ pinMode(13, OUTPUT); OCR0A = 128;//записываем в регистр совпадения число например 128 //Биты OCIE0B (2) и OCIE0A (1) разрешают прерывания при совпадении с A и B, TIMSK0 |= (1<<OCIE0A); } void loop(){ delay(10000); } // Прерывание вызывается раз в 1024 mkc ISR(TIMER0_COMPA_vect) { static word count = 0; if(count == period) count = 0; if(count == 0) PORTB |= (1<<5); else if(count == blinkLED) PORTB &= ~(1<<5); count++; }человеку, который ещё не понимает типы данных, тогда уж сразу asm и вперёд, долой все if-ы, только je jnz, только хардкор :)
человеку, который ещё не понимает типы данных, тогда уж сразу asm и вперёд, долой все if-ы, только je jnz, только хардкор :)
Ничего страшного)) научится... Пусть сам заменит на digitalWrite(), если надо.
Я свой первый скетч также писал))
На всяк случай для ТС - вкл\выключать полив можно также как и лед, без millis().
Взял такой листинг, он мне как раз подходит (это будет автополив по таймеру):
...
Но мне нужно что бы дополнительно к этому постоянно мигал сигнальный светодиод допустим раз в секунду, сигналиризующий о том, что устройство работает (например что батарейки не сели).
Вопрос: как мне это сделать, если основной цикл loop исполняется раз в period, а period равен одному дню (или больше, в зависимости от настройки)?
Используйте простую кооперативную операционную систему, например, такую. Одна задача поливает, другая мигает светодиодом. Вдобавок к этому можете еще задач запендюрить сколько надо, пусть еще что-нибудь полезное делают.
Если прикручивать мигающий светодиод, то только к программе построенной по такой структуре
смотри пример "blink without delay"
Попробовал всё переписать:
const int ledPin = 13; // номер выхода, подключенного к светодиоду int ledState = LOW; // этой переменной устанавливаем состояние светодиода unsigned long previousMillis = 0; // храним время последнего переключения светодиода int interval = 100; // интервал между включение/выключением светодиода unsigned long currentMillis; const int pumpPin = 5; int pumpState = LOW; unsigned long previousMillis_period = 0; // храним время последнего переключения насоса const int volumePin = A0; // Пин, к которому подцеплен регулятор, отвечающий за объём поливаемой воды const int periodPin = A1; // Пин, к которому подцепелн регулятор, отвечающий за период между поливами int period; // интервал между переключеием насоса. нужно считывать с потенциометра! int volume; // время работы насоса. нужно считывать с потенциометра! // калибровка потенциометров - минимальное и максимальное аналоговое значение const int min = 0; const int max = 675; // и минимум и максимум значений интервала и времени работы насоса const int period_min = 1; // дни const int period_max = 7; const int volume_min = 4; // секунды const int volume_max = 15; void setup() { pinMode(ledPin, OUTPUT); pinMode(pumpPin, OUTPUT); pinMode(volumePin, INPUT); pinMode(periodPin, INPUT); } void loop() { // включение-выключение насоса currentMillis = millis(); volume = map(analogRead(volumePin), min, max, volume_min, volume_max) * 1000; period = map(analogRead(periodPin), min, max, period_min, period_max) * 1000 * 60 * 60 * 24; if(currentMillis - previousMillis_period > period) { previousMillis_period = currentMillis; if (pumpState == LOW) { pumpState = HIGH; delay(volume); // --- сделать задержку без delay - но тут задержка порядка секунд - будет работать и так } else pumpState = LOW; digitalWrite(pumpPin, pumpState); } // моргание диодом currentMillis = millis(); if(currentMillis - previousMillis > interval) { previousMillis = currentMillis; if (ledState == LOW) ledState = HIGH; else ledState = LOW; digitalWrite(ledPin, ledState); } }Верное направление, или совсем не то?
Это насос "будет работать и так". А светодиод не будет. Задержку в 45 строке надо тоже ликвидировать.
Самый простой путь дописать первый листинг - это использование yield, которая вызывается внутри delay уже из коробки. Таким образом, объявив функцию yield - внутри неё можно будет мигать светодиодом вне зависимости от того, что кто-то где-то висит в delay:
typedef enum { bmNop, bmHigh, } BlinkMachine; BlinkMachine blinkState; #define ON_INTERVAL 500 // интервал горения светодиода, в мс #define OFF_INTERVAL 5000 // интервал покоя светодиода bool wantBlinkFlag = true; uint32_t blinkTimer = OFF_INTERVAL; void yield() { if(wantBlinkFlag) { switch(blinkState) { case bmNop: { if(millis() - blinkTimer > OFF_INTERVAL) { blinkTimer = millis(); digitalWrite(LED_BUILTIN, HIGH); blinkState = bmHigh; } } break; case bmHigh: { if(millis() - blinkTimer > ON_INTERVAL) { blinkTimer = millis(); digitalWrite(LED_BUILTIN, LOW); blinkState = bmNop; } } break; } } } void loop() { yield(); delay(60000); }Как вариант, навскидку - без использования аппаратных таймеров и пр. Для мигания светодиодом - более чем достаточно. Функция позднего связывания yield может также использоваться как механизм периодической передачи управления выполнением кода из одной части прошивки в другую, что бывает очень полезно. Собственно, механизм такой передачи уже есть внутри delay - он вызывает функцию yield, внутри которой мы, на простеньком примере выше - делаем нужные нам дела.
/**/ //--------------------------------------------------------- const int ledPin = 13; // номер выхода, подключенного к светодиоду bool ledState; // этой переменной устанавливаем состояние светодиода const unsigned long interval = 100; // интервал между включение/выключением светодиода unsigned long past; void ledON() { ledState = HIGH; past = millis(); digitalWrite(ledPin, HIGH); } void ledOFF() { ledState = LOW; past = millis(); digitalWrite(ledPin, LOW); } void ledInit() { pinMode(ledPin, OUTPUT); ledOFF(); } void ledRun() { if (ledState == LOW && millis() - past >= interval )ledON(); if (ledState == HIGH && millis() - past >= interval )ledOFF(); } //----------насос-------------------------------------- const int pumpPin = 5; const int volumePin = A0; // Пин, к которому подцеплен регулятор, отвечающий за объём поливаемой воды const int periodPin = A1; // Пин, к которому подцепелн регулятор, отвечающий за период между поливами // калибровка потенциометров - минимальное и максимальное аналоговое значение const int min = 0; const int max = 675; // и минимум и максимум значений интервала и времени работы насоса const int period_min = 1; // дни const int period_max = 7; const int volume_min = 4; // секунды const int volume_max = 15; bool pumpState = LOW; unsigned long past2; void PumpON() { pumpState = HIGH; past2 = millis(); digitalWrite(pumpPin, HIGH); } void PumpOFF() { pumpState = LOW; past2 = millis(); digitalWrite(pumpPin, LOW); } void PumpInit() { pinMode(pumpPin, OUTPUT); pinMode(volumePin, INPUT); pinMode(periodPin, INPUT); PumpOFF(); } void PumpRun() { unsigned long volume = map(analogRead(volumePin), min, max, volume_min, volume_max) * 1000; unsigned long period = (unsigned long)map(analogRead(periodPin), min, max, period_min, period_max) * 1000 * 60 * 60 * 24; if (pumpState == LOW && millis() - past2 >= period)PumpON(); if (pumpState == HIGH && millis() - past2 >= volume)PumpOFF(); } //--------------main---------------------------------- void setup() { ledInit(); PumpInit(); } void loop() { ledRun(); PumpRun(); } /*Скетч использует 1616 байт (5%) памяти устройства. Всего доступно 30720 байт. Глобальные переменные используют 19 байт (0%) динамической памяти, оставляя 2029 байт для локальных переменных. Максимум: 2048 байт. */Скетч из первого поста, переделанный для работы с кооперативной ОС
#include "coos.h" // Первый регулятор управляет временем, которое будет литься вода (от 4 до 15 секунд) #define MAX_FLOWTIME 15 // seconds #define MIN_FLOWTIME 4 // seconds // Второй регулятор управляет частотой полива от раза в день до раза в неделю #define MAX_PERIOD 7 // days #define MIN_PERIOD 1 // days #define MAX 675 #define MIN 0 int volumePin = A0; // Пин, к которому подцеплен регулятор, отвечающий за объём поливаемой воды int periodPin = A1; // Пин, к которому подцепелн регулятор, отвечающий за период между поливами int pumpPin = 5; // Пин, к которому подсоединено управление насосом int ledOnline = 13; // Сигнальный LED int led_on; // управление светодиодом int volume; int period; // Задача, включающая насос на время, заданное в volume void water() { COOS_DELAY(0); while(1) { // Считываем значения регуляторов (переменных резисторов) и приводим их к заданным пределам volume = map(analogRead(volumePin), MIN, MAX, MIN_FLOWTIME, MAX_FLOWTIME) * 1000; period = map(analogRead(periodPin), MIN, MAX, MIN_PERIOD, MAX_PERIOD) * 1000 * 60 * 60 * 24; digitalWrite(pumpPin, HIGH); // включаем насос led_on = 1; // можно мигать COOS_DELAY(volume); digitalWrite(pumpPin, LOW); // выключаем насос led_on = 0; // хватит мигать COOS_DELAY(period); } } // Задача, мигающая светодиодом раз в секунду void led_flash() { while(1) { if (led_on) // если можно мигать { digitalWrite(ledOnline, HIGH); // включаем светодиод COOS_DELAY(500); // на полсекунды } digitalWrite(ledOnline, LOW); // выключаем светодиод COOS_DELAY(500); } } void setup() { pinMode(pumpPin, OUTPUT); digitalWrite(pumpPin, LOW); pinMode(ledOnline, OUTPUT); digitalWrite(ledOnline, LOW); coos.register_task(water); coos.register_task(led_flash); } void loop() { coos.run_scheduler(); }Это насос "будет работать и так". А светодиод не будет. Задержку в 45 строке надо тоже ликвидировать.
Получилось как-то так.
const int ledPin = 13; // номер выхода, подключенного к светодиоду int ledState = LOW; // этой переменной устанавливаем состояние светодиода unsigned long previousMillis = 0; // храним время последнего переключения светодиода int interval = 100; // интервал между включение/выключением светодиода int interval_led; unsigned long currentMillis; const int pumpPin = 5; int pumpState = LOW; unsigned long previousMillis_period = 0; // храним время последнего переключения насоса unsigned long previousMillis_volume = 0; const int volumePin = A0; // Пин, к которому подцеплен регулятор, отвечающий за объём поливаемой воды const int periodPin = A1; // Пин, к которому подцепелн регулятор, отвечающий за период между поливами const int ledRegPin = A2; int period; // интервал между переключеием насоса. нужно считывать с потенциометра! int volume; // время работы насоса. нужно считывать с потенциометра! // калибровка потенциометров - минимальное и максимальное аналоговое значение const int min = 0; const int max = 675; // и минимум и максимум значений интервала и времени работы насоса const int period_min = 1; // дни const int period_max = 7; const int volume_min = 4; // секунды const int volume_max = 15; const int led_min = 10; // миллисекунды const int led_max = 1000; void setup() { pinMode(ledPin, OUTPUT); pinMode(pumpPin, OUTPUT); pinMode(volumePin, INPUT); pinMode(periodPin, INPUT); pinMode(ledRegPin, INPUT); } void loop() { // включение-выключение насоса currentMillis = millis(); volume = map(analogRead(volumePin), min, max, volume_min, volume_max) * 1000; period = map(analogRead(periodPin), min, max, period_min, period_max) * 1000 * 60 * 60 * 24; if(currentMillis - previousMillis_period > period) { previousMillis_period = currentMillis; digitalWrite(pumpPin, HIGH); // в начале очедного цикла включаем насос currentMillis = millis(); if(currentMillis - previousMillis_volume > volume) { previousMillis_volume = currentMillis; digitalWrite(pumpPin, LOW); // насос поработал - выключаем } } interval_led = map(analogRead(ledRegPin), min, max, led_min, led_max); // моргание диодом. для контроля у диода тоже есть регулятор в виде резистора currentMillis = millis(); if(currentMillis - previousMillis > interval_led) { previousMillis = currentMillis; if (ledState == LOW) ledState = HIGH; else ledState = LOW; digitalWrite(ledPin, ledState); } }Самый простой путь дописать первый листинг - это использование yield,
Скетч из первого поста, переделанный для работы с кооперативной ОС
Спасибо друзья за примеры, но видимо не пришло ещё понимание - читал несколько раз и код и гугл, не смог ухватить сути... Нужно к этому видимо прийти.
Спасибо друзья за примеры, но видимо не пришло ещё понимание - читал несколько раз и код и гугл, не смог ухватить сути... Нужно к этому видимо прийти.
Это особая уличная магия, примерно как стрелять из пушки корабельной по воробьям =)
Спасибо друзья за примеры, но видимо не пришло ещё понимание - читал несколько раз и код и гугл, не смог ухватить сути...
Суть работы кооперативной ОС до смешного проста. Считайте, что задачи выполняются одновременно, вот и все.
Заботу о том, чтобы они выполнялись как бы одновременно берет на себя ОС. Вам же для этого в теле задачи просто надо вставлять задержки COOS_DELAY(x). Когда этот оператор выполняется, управление предается ядру ОС. По прошествии заданного времени ОС возвращает управление задаче.
Суть работы кооперативной ОС до смешного проста. Считайте, что задачи выполняются одновременно, вот и все.
я думаю, что подобные костыги - а это ведь костыль, очевидно - не следует применять новичкам, пока сами ходить не научатся.
Для столь простого проекта, как у ТС - никакие ОС не нужны в принципе.
Суть работы кооперативной ОС до смешного проста. Считайте, что задачи выполняются одновременно, вот и все.
Вот после таких вот заявлений и появляются люди которые пишут с паузами и не понимают как оно работает и почему не мигает диод.
Ну напишите что ПАУЗЫ выполняются одновременно, а не задачи...
Задачи таки выполняются последовательно...
я думаю, что подобные костыги - а это ведь костыль, очевидно - не следует применять новичкам, пока сами ходить не научатся.
Для столь простого проекта, как у ТС - никакие ОС не нужны в принципе.
Я не считаю это костылем. Вот ProtoThreads - это, действительно, костыль. Или "смекалка темной головы" qwone. А COOS - это вполне полноценная кооперативная ОС, только простая и портативная (читай: портируемая и компактная) до предела.
Задача ТС как раз правильно решается при помощи ОС. А все прочие решения - самые натуральные костыли.
Ну напишите что ПАУЗЫ выполняются одновременно, а не задачи...
Задачи таки выполняются последовательно...
Если нажимаете на "последовательно", то можете указать в какой последовательности? Нет? Ну тогда и не надо пудрить мозги новичкам. Человеку кажется, что они выполняются одновременно, этого достаточно.
Кажется у нас появилась многоядерность в МК с одним ядром...
А когда кажется - крестятся или смотрят точными приборами, выбор от религии зависит =)
Кажется у нас появилась многоядерность в МК с одним ядром...
К сожалению, многоядерные процессоры внесли сумятицу в неокрепшие умы. Забудьте их покамест. На обычном одноядерном процессоре уже десятки лет как можно реализовать и многозадачность, и многопоточность.
К сожалению, многоядерные процессоры внесли сумятицу в неокрепшие умы. Забудьте их покамест. На обычном одноядерном процессоре уже десятки лет как можно реализовать и многозадачность, и многопоточность.
К сожалению многопоточные(одноядерные)* процессоры, и многозадачные ОС внесли сумятицу в неокрепшие умы. На одноядерных процессорах потоки, и задачи выполняются не одновременно, а последовательно.
Не путайте понятия многопоточность\многозадачность и одновременное выполнение инструкций.
* - привет гипертрединг и подобные технологии вносящие в неокрепшие умы то что выполнение двух операций одновременно(паралельно) возможно на одном ядре, если вы считаете что это возможно - советую глубже разобратся в реализации этих технологий и их концепциях.
На одноядерных процессорах потоки, и задачи выполняются не одновременно, а последовательно.
Не путайте понятия многопоточность\многозадачность и одновременное выполнение инструкций.
* - привет гипертрединг и подобные технологии вносящие в неокрепшие умы то что выполнение двух операций одновременно(паралельно) возможно на одном ядре, если вы считаете что это возможно - советую глубже разобратся в реализации этих технологий и их концепциях.
Смотря на каком уровне смотреть. Если на высоком уровне кода потока, то да, всё последовательно. Если начинаем смотреть на уровне ассемблера, который попадает в кэш, и чуть ниже, то всё становится интереснее, и именно исполнение нескольких операций одновременно(параллельно) привели к meltdown и spectre. Как это было - с точки зрения программы - да, результаты выходят из "чёрного ящика" процессора последовательно, но с точки зрения конвейера внутри - он мог исполнить некоторые инструкции, включая лишние, даже вперёд того, а такты-то остаются те же. И гипертрединг тоже, при некоторых определённых условиях и правда способен одновременно исполнять больше одной инструкции, если у проца остаётся свободное место в конвейере и регистрах.
https://ru.wikipedia.org/wiki/%D0%A1%D1%83%D0%BF%D0%B5%D1%80%D1%81%D0%BA%D0%B0%D0%BB%D1%8F%D1%80%D0%BD%D0%BE%D1%81%D1%82%D1%8C
На одноядерных процессорах потоки, и задачи выполняются не одновременно, а последовательно.
На самых простых одноядерных процессорах потоки и задачи, несомненно, выполняются одновременно. Задача (или поток) не обязана дожидаться окончания выполнения другой задачи (или потока). Если вы попробуете приведенный мною скетч, то убедитесь, что там обе задачи работают одновременно. Но при этом процессор, конечно же, не исполняет несколько команд одновременно.
Неокрепшими следует признать умы, нетвердые в знаниях и терминологии, и, как следствие, употребляющие слова произвольно и неуместно. В частности, к обсуждению многозадачности приплетающие многоядерность и/или одновременное выполнение инструкций, как якобы обязательное условие, необходимое для многозадачности; смешивающие в одну кучу одновременное выполнение задач и одновременное выполнение машинных команд. Путаница в неокрепших умах возникает, помимо прочего, из-за трудностей перевода с английского.
multitasking is the concurrent execution of multiple tasks (also known as processes) over a certain period of time.
Concurrent computing is a form of computing in which several computations are executed during overlapping time periods—concurrently—instead of sequentially (one completing before the next starts)
The concept of concurrent computing is frequently confused with the related but distinct concept of parallel computing,[2][3] although both can be described as "multiple processes executing during the same period of time". In parallel computing, execution occurs at the same physical instant: for example, on separate processors of a multi-processor machine, with the goal of speeding up computations—parallel computing is impossible on a (one-core) single processor, as only one computation can occur at any instant (during any single clock cycle).[a] By contrast, concurrent computing consists of process lifetimes overlapping, but execution need not happen at the same instant. The goal here is to model processes in the outside world that happen concurrently, such as multiple clients accessing a server at the same time.
Проблемы перевода:
parallel {прилаг.} RU параллельный, аналогичный, подобный
concurrent {прилаг.} RU параллельный, действующий совместно или одновременно, совпадающий
ТСу не надо мозг перегревать, ТСу нада чтоб у его светлодиот мигал отдельно от лупа. И сё.
Я, видя уровень, даже за свои таймеры ему не втираю, а они на 2 порядка проще этой вашей многозадачности..
Был скетч 41 в строку, стал в 58 строк. Добавилось 17 строк текста. Одна строка вызова либы, две строки регистрации задач и плюс сама задача мигания светодиодом. Ужас как сложно...
А "на два порядка проще" - наверное у вас в одну строку текста умещается, и тоже машинно-независимая реализация? Или надо парить ТС мозги железом?
Дак я и не парю.