Помогите нубу с таймером

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

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

 if (millis() > Time1 + 60000) {
    Time1 = millis();
    if (timer1 > 0) {
      timer1--;
    }
  }
Подобных таймеров в основной программе 2 штуки на 60 сек и на 10 сек и я решил добавить ещё один таймер на 1 сек для отсчёта мелких интервалов времени получился вот такой код
#define Btn_pin  4
#define Pin_btn  9
#define Pin_led  13
#define P_led    10
#define Pi_led   11

unsigned long  Time,  Time1, Time2 = 0;
int  timer, timer1 ;

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

  pinMode( Pi_led, OUTPUT);
  pinMode( P_led,  OUTPUT);
  pinMode(Pin_led, OUTPUT);
  pinMode(Pin_btn,  INPUT_PULLUP);
  pinMode(Btn_pin,  INPUT_PULLUP);
}
void loop() {

  if (millis() > Time + 1000) {
    Time = millis();
    zapusk();
    if (timer > 0) {
      timer--;
    }
  }

  if (millis() > Time1 + 60000) {
    Time1 = millis();
    if (timer1 > 0) {
      timer1--;
    }
  }

  if (millis() > Time2 + 10000) {
    Time2 = millis(), detection();
  }

  if ( digitalRead (Pin_btn)  == LOW) {       // Условие запуска
    startengine();
  }
}

void startengine() {
  if ( digitalRead (Btn_pin)  == LOW) {       // Доп проверка
    timer1 = 15 ;
    timer = 25;
    digitalWrite (P_led , HIGH);              // Включаем нагрузку 1
    Serial.println("start");
  }
}

void zapusk() {
  if (timer == 20 ) {
    digitalWrite (Pi_led , HIGH);             // Если прошло определённое время то включаем нагрузку 2
    //  starter == true;
    Serial.println("ACC");
  }

  if (timer == 15 ) {
    digitalWrite (Pin_led , HIGH);            // Если прошло определённое время то включаем нагрузку 3
    Serial.println("starter");
  }

  if (timer == 10 ) {
    digitalWrite (Pin_led , LOW);             // и выключаем её через определённый промежуток времени
    Serial.println("starter off");
  }
}

void stape() {                              // Функция остановки
  digitalWrite (P_led , LOW);
  digitalWrite (Pin_led , LOW);
  digitalWrite (Pi_led , LOW);
  Serial.println("stop");
  timer = 0;
  timer1 = 0;
}

void  detection() {

  if ( digitalRead (Btn_pin)  == LOW) {      // Функция остановки по кнопке
    Serial.println("knock stop");
    stape();
  }

  if (timer1 == 0 ) {                         // Функция остановки по таймеру
    stape();
  }
}

Код работает, но немного напрягает такой момент, если в loop () ввести пример blink это который с delay из примеров ардуино то счётчики начинают чудить. В основной программе при отправке сообщений на сервер и в других местах программы присутствуют короткие Delay, но программа в принципе  работает, отсюда возникают вопросы

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

2) Есть ли смысл заморачиваться с титановыми велосипедами ? 

Заранее благодарю всех откликнувшихся!

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

Не знаю, поможет ли 

https://github.com/DetSimen/Arduino_TimerList

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

Секешфехервар пишет:

если в loop () ввести пример blink это который с delay из примеров ардуино то счётчики начинают чудить. 

Это нормально.

Секешфехервар пишет:

1 ) Правильно ли я делаю . 

Нет, ибо 

Секешфехервар пишет:

других местах программы присутствуют короткие Delay

Если используются замеры времени на миллис, то делэи использовать нельзя. А если используются делэи, то счётчики времени нужно делать на прерываниях таймера.

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

Секешфехервар пишет:
Код работает, но немного напрягает такой момент, если в loop () ввести пример blink это который с delay из примеров ардуино то счётчики начинают чудить.
Так не вводить дрянь в код. И код будет в порядке. Вот Бог создал человека. Но если в кровь человека начать вводить к примеру мочу, люди от этого почему-то дохнут. А Бога этот недостаток в человеке не сильно напрягает. 

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

Дак может и напрягаться на этот счёт некому...

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

DetSimen пишет:

Не знаю, поможет ли 

https://github.com/DetSimen/Arduino_TimerList


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

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

Если используются замеры времени на миллис, то делэи использовать нельзя. А если используются делэи, то счётчики времени нужно делать на прерываниях таймера.

[/quote
У меня там весь код такой ( конечно же не у меня, а там откуда я его взял (анатомия автозапуска , возможно видели этот проект на драйве или гитхабе) делэй вперемешку с миллис ) , спасибо за подсказку .В основном коде каждую минуту отправка сообщения на mqtt сервер и при отправке сообщения реализован delay(200) ,получается код виснет часики и так не особо точные (устройство находится в машине ) а с делеями и вовсе шляпа выходит , прискорбно.

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

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

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

Вдогонку:   Никогда так  

if (millis() > Time1 + 60000) {

не делай. 

junior_developer
Offline
Зарегистрирован: 27.11.2017

Да, чтобы избежать глюков, связанных с переполнением millis(), правильно писать так
 

	 
#define interval 1000UL  // например 1 секунда
// "заготовка/шаблон функции" которая периодически выполняет КАКОЕ-ТО-ДЕЙСТВИЕ 
void somePeriodical(unsigned long interval){
 static unsigned long prevTime=0; 
  if(millis()-prevTime>interval){ //если интервал прошел prevTime=millis(); 
 //КАКОЕ-ТО-ДЕЙСТВИЕ; 
 } 
}
junior_developer
Offline
Зарегистрирован: 27.11.2017
Не стал создавать новую тему, так как эта очень похожа. Тоже вытаюсь разобраться с отсчетом времени! Как сделать задержку например на 2 секунды. Алгоритм простой:
 
Есть обычная тактовая кнопка и светодиод
1. В обычном режиме он мигает с частотой 1 герц.
2. При нажатии кнопки начинает мигать 10 раз в секунду
3. После её отпускания мигает также быстро ещё 2 секунды.
3. Затем опять мигает медленно с частотой 1 герц.
 
Обычный delay тут точно не подойдет, millis тоже непонятно, потому что получается нужно как бы считывать 2 интервала одновременно, то есть смотреть не прошел ли интервал переключения светодиода и интервал, когда нужно изменить частоту миганий. 
И ещё кнопку получается нужно опрашивать не 1, а 2 раза на нажатие и отпускание, то есть
если кнопка нажата частота миганий сразу же изменяется. Значит нужно отследить "фронт" импульса точнее(перепад из HIGHT в LOW). Если использовать внутренние подтягивающие резисторы, то нажитие соответствует уровню LOW, а отпускание HIGHT.
То есть получается временный интервал нужно замерять после перепада из LOW в HIGHT? Верно?
Для кнопки можно сделать отдельную функцию что то типа
bool scanButton(byte bt_Pin)
static bt_Flag = 0;
if(digitalRead(bt_Pin) && !bt_Flag)
  {
    bt_Flag = 1;  
    return 1;
  }
if(!digitalRead(bt_Pin) && bt_Flag) 
  {bt_Flag = 0;
    return 0;
  }
Если бы задержка не была нужна, то наверно можно было бы написать как-то так:
unsigned long interval;
if (scanButton(2)) interval = 100;  // кнопка на выводе 2
else interval = 1000;

 

и все.
а как реализовать задержку например на 2 секунды? Подскажите пожалуйста!
Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

DetSimen пишет:

Вдогонку:   Никогда так  

if (millis() > Time1 + 60000) {

не делай. 


Ок а как делать правильно?

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

Вам же написали в #9! Вы совсем не читаете? :)

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

junior_developer пишет:

Да, чтобы избежать глюков, связанных с переполнением millis(), правильно писать так
 

	 
#define interval 1000UL  // например 1 секунда
// "заготовка/шаблон функции" которая периодически выполняет КАКОЕ-ТО-ДЕЙСТВИЕ 
void somePeriodical(unsigned long interval){
 static unsigned long prevTime=0; 
  if(millis()-prevTime>interval){ //если интервал прошел prevTime=millis(); 
 //КАКОЕ-ТО-ДЕЙСТВИЕ; 
 } 
}


Я почему то думал что в варианте который я использую миллис не считает до тех пор пока ему не присвоится значение ~20 секунд , то есть счётчик остановлен , а после запуска начинается обратный отсчёт timer -- .Или я что то непонимаю?

Beginer123
Offline
Зарегистрирован: 23.11.2018

junior_developer пишет:

Не стал создавать новую тему, так как эта очень похожа. Тоже вытаюсь разобраться с отсчетом времени! Как сделать задержку например на 2 секунды. Алгоритм простой:
 
Есть обычная тактовая кнопка и светодиод
1. В обычном режиме он мигает с частотой 1 герц.
2. При нажатии кнопки начинает мигать 10 раз в секунду
3. После её отпускания мигает также быстро ещё 2 секунды.
3. Затем опять мигает медленно с частотой 1 герц.
...
 
1. Стартовое состояние: кнопка отпущена и задержка не включена. в loop() светодиод соответственно начинает мигать "штатно" сам с частотой 1Гц;
2. Там же в loop() проверяем состояние кнопки:
2.1 Кнопка - нажата: включаем таймер задержки, который по завершению устанавливает новую частоту мигания. Таймер самовыключается.
2.2. Кнопка отпущена: включаем второй таймер задержки, который по завершению устанавливает старую частоту мигания. Аналогично - самовыключение и тут тоже.
Собственно все, мне кажется ..
Соответственно, требуется хранить: текущую частоту мигания (интервал); флаг включения первого таймера и флаг включения второго таймера, ну и состояние самой кнопки, дабы не обнаруживать 100500 раз что "нажата"..
 
Можно примерно так:
#define pinLed 13
#define pinBtn  2

#define NORM        500
#define SHORT        50
#define PRESSED    2000
#define UPPED      2000

// Простейший макрос от Архата для имитации конечного автомата от времени:
#define everyMillis(interval, action)               \
{                                                   \
  static unsigned long _t0 = 0;                     \
  if( millis() - _t0 >= (unsigned long)(interval) ) \
  { action }                                        \
}

int8_t btnState=0, isPressed=0, isUpped = 0, pinState=0;
unsigned long current = NORMAL;
void setup(){ pinMode(pinLed, OUTPUT); }

void loop()
{
  // моргаем светодиодом как сказано:
  everyMillis( current, { digitalWrite( pinLed, pinState=1-pinState); });

  // если надо - запускаем таймер "нажата кнопка"
  // пропускаем первое срабатывание - оно будет сразу!
  if( isPressed ){
    everyMillis( PRESSED, {
      if( ++isPressed > 2 ){
        isPressed = 0;
        current = SHORT;
      }
    });
  }
  // аналогично - таймер "кнопка отпущена"
  if( isUpped ){
    everyMillis( UPPED, {
      if( ++isUpped > 2 ){
        isUpped = 0;
        current = NORMAL;
      }
    });
  }
  // опрашиваем кнопку: @TODO можно добавить антидребезг!
  // HIGH - кнопка нажата, LOW - отпущена:
  if( btnState==0 && digitalRead(pinBtn) == HIGH ){
    isUpped = 0;
    isPressed = 1; // нажата, включаем таймер
    btnState = 1;
  }
  if( btnState==1 && digitalRead(pinBtn) == LOW ){
    isPressed = 0;
    isUpped = 1;
    btnState=0;
  }
}

Ну вот, как-то так. Фактически у вас есть конечный автомат "от вермени" (мигалка), который управляет светодиодом (первый everyMillis) он использует текущий заданный интервал мигания.

Затем есть ещё 2 конечных автомата от вермени (тоже по сути мигалки), которые включаются или выключаются флагами, которые управляются кнопкой. И есть опросник кнопки, который должен быть самоблокирующимся, чтобы не срабатывать много раз повторно. Кнопка только управляет автоматами - таймерами, которые саовыключаются после второго срабатывания.

Надеюсь работает, не проверял. :)

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

Beginer123 пишет:

Надеюсь работает

Напрасно надеетесь. Не только не работает, но даже и не компилируется.

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

ЕвгенийП пишет:

Вам же написали в #9! Вы совсем не читаете? :)


Если бы один человек написал так делать не правильно , нужно делать вот так , я б и вопроса не задал.

Beginer123
Offline
Зарегистрирован: 23.11.2018
#define pinLed 13
#define pinBtn  2

#define NORM        500
#define SHORT        50
#define PRESSED    2000
#define UPPED      2000

// Простейший макрос от Архата для имитации конечного автомата от времени:
#define everyMillis(interval, action)               \
{                                                   \
  static unsigned long _t0 = 0;                     \
  if( millis() - _t0 >= (unsigned long)(interval) ) \
  { action }                                        \
}

int8_t btnState=0, isPressed=0, isUpped = 0, pinState=0;
unsigned long current = NORM;
void setup(){ pinMode(pinLed, OUTPUT); }

void loop()
{
  // моргаем светодиодом как сказано:
  everyMillis( current, { digitalWrite( pinLed, pinState=1-pinState); });

  // если надо - запускаем таймер "нажата кнопка"
  // пропускаем первое срабатывание - оно будет сразу!
  if( isPressed ){
    everyMillis( PRESSED, {
      if( ++isPressed > 2 ){
        isPressed = 0;
        current = SHORT;
      }
    });
  }
  // аналогично - таймер "кнопка отпущена"
  if( isUpped ){
    everyMillis( UPPED, {
      if( ++isUpped > 2 ){
        isUpped = 0;
        current = NORM;
      }
    });
  }
  // опрашиваем кнопку: @TODO можно добавить антидребезг!
  // HIGH - кнопка нажата, LOW - отпущена:
  if( btnState==0 && digitalRead(pinBtn) == HIGH ){
    isUpped = 0;
    isPressed = 1; // нажата, включаем таймер
    btnState = 1;
  }
  if( btnState==1 && digitalRead(pinBtn) == LOW ){
    isPressed = 0;
    isUpped = 1;
    btnState=0;
  }
}

Мелкие описки. Теперь - компиляется. :)

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

Акей, пусь будет 1 человек. 

DetSimen пишет:

Вдогонку:   Никогда так  

if (millis() > Time1 + 60000) {

не делай. 

Делай так 

uint32_t now = millis();

if (now-Time1>60000) { 

.

.

Time1 = now;

} 

 

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

Спасибо добрый человек ! Вечерком попробую .

Voodoo Doll
Voodoo Doll аватар
Offline
Зарегистрирован: 18.09.2016

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

void service_jaws(){
  if(!jawsbtnpress && jawsbtn){                                      // initial press, set flag
    jawsbtnpress=1;
    jawsbtntime=millis();
  }
  if(millis()-jawsbtntime>=1000 && jawsbtnpress) digitalWrite(jawsmtr,1);      // hold >1s, move
  if(millis()-jawsbtntime>=1000 && jawsbtnpress && !jawsbtn){                  // release after >1s, stop
    jawsbtnpress=0;
    digitalWrite(jawsmtr,0);
  }
  if(millis()-jawsbtntime>=300 && millis()-jawsbtntime<800 && jawsbtnpress && !jawsbtn){   // release after <1s, invert dir
    jawsbtnpress=0;
    invpol=!invpol;
  }
  digitalWrite(mtrpol,invpol);                                                             // write dir state
  if (invpol){
    digitalWrite(ledUp, HIGH);
    digitalWrite(ledDown, LOW);
  }else{
    digitalWrite(ledDown, HIGH);
    digitalWrite(ledUp, LOW);            
  }
}

Короткое нажатие на кнопку меняет направление, долгое - крутить двигатель. Перекидывание полярности двумя реле, третье - ход.

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

Beginer123 пишет:

Мелкие описки.

А программе без разницы, мелкие там описки или крупные

Beginer123 пишет:

Теперь - компиляется. :)

Но, не работает :(

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

Дико извиняюсь если кто то успел прочитать )) Det, таймер работает.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Секешфехервар пишет:
Дико извиняюсь если кто то успел прочитать )) Det, таймер работает.

и чего бы ему не работать? )))

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

Uint32 _t now = millis (); прописал перед сетапом , я так то электросварщик думаю мне простительно образование 3 класса ))

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

Секешфехервар пишет:
Det, таймер работает.

Да ты шо? О_О  

junior_developer
Offline
Зарегистрирован: 27.11.2017

Наконец-то написал и даже компилируется
 

#define LED_Pin 13
#define BTN_Pin 2
#define interval_slow 500UL
#define interval_fast 50UL
#define ext_interval 2000UL

bool is_Pressed = false;
bool isReleased = false;
bool isNormal = true;
void setup(){ 
pinMode(LED_Pin, OUTPUT); 
pinMode(BTN_Pin, INPUT_PULLUP);
}

void Blink (byte _LED_Pin, unsigned long interval){
static unsigned long prevTime=0;
if (millis - prevTime > interval){
  prevTime = millis;
 digitalWrite(_LED_Pin, !(digitalRead(_LED_Pin)));
  }
}
void loop(){
ButtonScan(BTN_Pin);
if(isNormal) Blink (LED_Pin, interval_slow);
if(is_Pressed) Blink (LED_Pin, interval_fast);
if(isReleased) { 
  static unsigned long prevTime=0;
  if(millis()-prevTime>ext_interval){
    prevTime=millis();
  isReleased = false;
  isNormal= true;
  }
 }
}
void ButtonScan(byte _BTN_Pin){
  static bool BTN_wasPressed = false;
  if (digitalRead(_BTN_Pin)==false && BTN_wasPressed == false){
  BTN_wasPressed = true;
  isNormal = false;
  //isReleased = false;
  is_Pressed = true;
}
    if( digitalRead(_BTN_Pin)== true && BTN_wasPressed == true) {
  BTN_wasPressed = false;
  isNormal = false;
  is_Pressed = false;
  isReleased = true;
  }
}

В loop() там всего несколько строчек получилось
 

void loop(){
ButtonScan(BTN_Pin);
if(isNormal) Blink (LED_Pin, interval_slow);
if(isPressed) Blink (LED_Pin, interval_fast);
if(isReleased) { 
	static unsigned long prevTime=0;
  if(millis()-prevTime>ext_interval){
    prevTime=millis();
	isReleased = false;
	isNormal= true;
  }
 }
}

Всё остальное вынесено в отдельные функции! Ещё пока не проверил, но думаю, что работать будет. И ещё мне пришла в голову мысль, написать как-то так
 

bool progTimer(unsigned long interval){
  static unsigned long prevTime=0;
  if(millis()-prevTime>interval){
    prevTime=millis();
    return true;
	}
  else return false;
}

а потом проверять как-то так

if(progTimer(500)) { если время отсчета например пол-секунды прошло...
...}

В loop тогда вообще всего несколько строчек
 

void loop(){
ButtonScan(BTN_Pin);
if(isNormal) Blink (LED_Pin, interval_slow);   // нормальный режим
if(is_Pressed) Blink (LED_Pin, interval_fast);  // кнопка нажата
if(isReleased) {                            // кнопка отпущена
  if(progTimer(2000)) {   // время, например, 2 секунды прошло
  isReleased = false;     
  isNormal= true;}       // переход в нормальныый режим
 }
}

Только что проверил, - компилируется без ошибок! а как работает пока не проверял.
Кстати заметил, что созданная мною переменная isPressed почему-то подсвечивалась - пришлось её переименовать в is_Pressed!

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

junior_developer пишет:

Всё остальное вынесено в отдельные функции! Ещё пока не проверил, но думаю, что работать будет. И ещё мне пришла в голову мысль, написать как-то так

Для одного интервала - будет. Для нескольких - надо несколько функций писать

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

а всего то надо только внимательно прочитать и спросить, что непонятна.

https://github.com/DetSimen/Arduino_TimerList

Beginer123
Offline
Зарегистрирован: 23.11.2018

ЕвгенийП пишет:

Но, не работает :(

А это уже в раздел "Ищу исполнителя". Там - помогут подправить, не смею отбирать хлеб.

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

Beginer123 пишет:

А это уже в раздел "Ищу исполнителя". Там - помогут подправить, не смею отбирать хлеб.

Т.е. Вы публикуете суперкод, а мне с ним в "Ищу исполнителя"? Как похоже на Вашего Учителя :)

Не, не пойду - амфибиотропная асфиксия :(

Буду страдать и надеяться, что кто-нибудь даром подправит :)

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

Beginer123 пишет:

ЕвгенийП пишет:

Но, не работает :(

А это уже в раздел "Ищу исполнителя". Там - помогут подправить, не смею отбирать хлеб.

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

Voodoo Doll пишет:

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

void service_jaws(){
  if(!jawsbtnpress && jawsbtn){                                      // initial press, set flag
    jawsbtnpress=1;
    jawsbtntime=millis();
  }
  if(millis()-jawsbtntime>=1000 && jawsbtnpress) digitalWrite(jawsmtr,1);      // hold >1s, move
  if(millis()-jawsbtntime>=1000 && jawsbtnpress && !jawsbtn){                  // release after >1s, stop
    jawsbtnpress=0;
    digitalWrite(jawsmtr,0);
  }
  if(millis()-jawsbtntime>=300 && millis()-jawsbtntime<800 && jawsbtnpress && !jawsbtn){   // release after <1s, invert dir
    jawsbtnpress=0;
    invpol=!invpol;
  }
  digitalWrite(mtrpol,invpol);                                                             // write dir state
  if (invpol){
    digitalWrite(ledUp, HIGH);
    digitalWrite(ledDown, LOW);
  }else{
    digitalWrite(ledDown, HIGH);
    digitalWrite(ledUp, LOW);            
  }
}

Короткое нажатие на кнопку меняет направление, долгое - крутить двигатель. Перекидывание полярности двумя реле, третье - ход.


То есть если вмэтот код добавить делэй код не будет блокироваться?

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

А если мы в него рельсу пихнём...

junior_developer
Offline
Зарегистрирован: 27.11.2017

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

Prog_Timer prog_timer_1(2000); // 2 секунды
Prog_Timer prog_timer_2(1000); // 1 секунда
Prog_Timer prog_timer_3(500); // пол секунды 
/ну и т.д.

Пытаюсь разобраться, как правильно. Нужно создать класс, например  Prog_Timer
 

class Prog_Timer {
	public:
	unsigned long interval;
	bool progTimer(unsigned long interval);
	private:
	unsigned long _interval;
}

Значение передавать в частную переменную
 

Prog_Timer::progTimer(unsigned long interval){
	_interval = interval;
}

и использовать её в функции, переписанной так
 

bool Prog_Timer::progTimer(unsigned long _interval){
  static unsigned long prevTime=0;
  if(millis()-prevTime>_interval){
    prevTime=millis();
    return true;
	}
  else return false;

Правильно ли я думаю?

В вызывать функцию и проверять, наверно придется так

if(Prog_Timer.prog_timer_1) { 
.... // какое-то действие
}

 

Voodoo Doll
Voodoo Doll аватар
Offline
Зарегистрирован: 18.09.2016

Секешфехервар пишет:
То есть если вмэтот код добавить делэй код не будет блокироваться?

Нет не правильно, внимательно читаем то что находится рядом с millis(), видим например циферки 800, 300, видим что 300 больше чем а 800 меньше чем, делаем выводы что это "с 300та до 800от", как часы дня в режиме работы, типа как приходишь в собес а там тётенька "вас много а я одна, у меня обед". Люблю эту фразу из совка, особенно люблю читать как тоскующих по Брежневу за неё нещадно увольняют :3

По поводу delay() советую раз запомнить чтобы отложилось: это синхронная функция. Синхронное можно делать когда:

1. точно знаем когда, что. точно не провороним тактовый сигнал

2. прочих асинхронных сигналов нет

Под "асинхронными" здесь подразумеваются любые события, которые не укладываются в обозначенные темпоральные кванты (то бишь синхронная схема читает состояния только раз в надцать наносекунд/минут/лет и никак иначе, вот допустим тактируется 110 - значит обязательно на 0й, на 110й, на 220й и т. д., то есть на 8030й можно (потому что 8030/110) = целое число), на 8000й низя, так как частота строба 110 нс. И всё. Ни левее ни правее надо чтобы было на графике. Поэтому либо на delay полностью всё, либо если надо выдрать из кода delay(), то вся очередь автоматически становится асинхронной, и от всех вхождений delay() надо избавиться. Даже от delay(15) могут возникать выверты.

Слово service_ в названии функции призвано обозначить, что она вписана в loop() и должна прорабатывать (возвращаться назад в loop) как можно быстрее.

All, кто-нибудь, возможно кто-то знает более внятное объяснение как показать человеку разницу между синхронным выполнением процессов и асинхронным. Я хз.

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

Очень интересно ! Но ни хрена не понятно . На самом деле очень мало по форумам так конкретно отвечают , спасибо большое за пояснения , просто я прочитал неблокирующая задержка как код которому все ни по чём . А в своих потугах обещаю избавиться от делеев... По крайней мере от тех которые вызываются регулярно.

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

http://arduino.ru/forum/programmirovanie/vopros-po-ispolzovaniyu-millis вот здесь неплохо расписано , как раз мой случай миллис и автозапуск .

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

Секешфехервар пишет:
я прочитал неблокирующая задержка как код которому все ни по чём . А в своих потугах обещаю избавиться от делеев... По крайней мере от тех которые вызываются регулярно.

Нет, неблокирующие задержки работают только если весь остальной код тоже неблокирующий. Вчитайтесь в слово - это "неблокирующая" задержка. А не "неблокируемая", понимаете разницу? Она сама других не блокирует, но ее заблокировать может любой делей. Поэтому Вам надо избавиться в коде АБСОЛЮТНО ОТ ВСЕХ ДЕЛЕЕВ, а не только от каких-то тех, которые вам больше мешают

junior_developer
Offline
Зарегистрирован: 27.11.2017

Переделал свой код, теперь он выглядит так

#define LED_Pin 0
#define BTN_Pin 4
#define interval_slow 500UL  // мигание медленно
#define interval_fast 50UL  // мигание быстро
#define ext_interval 2000UL // дополнительное время в течении которого мигает светодиод

void setup(){ 
pinMode(LED_Pin, OUTPUT); 
pinMode(BTN_Pin, INPUT_PULLUP);
}

bool progTimer(unsigned long interval){
  static unsigned long prevTime=0;
  if(millis()-prevTime>interval){
    prevTime=millis();
    return false;   // время прошло 
  }
  else return true; // время еще не прошло 
}

void Blink (byte _LED_Pin, unsigned long interval){
static unsigned long prevTime=0;
if (millis() - prevTime > interval){
  prevTime = millis();
 digitalWrite(_LED_Pin, !(digitalRead(_LED_Pin)));
  }
}
byte ButtonScan(byte b_Pin){  // проверяем кнопку активный уровень низкий!
//то есть при нажатии 0 
  static bool prevState=1;    // флаг предыдущего состояния
  if ((digitalRead(b_Pin))&& prevState==1) { // нажатия ещё не было
         return 0;} 
  if ((!(digitalRead(b_Pin)))&& prevState==1) { // произошло нажатие
    prevState=0; return 1;
  }
  if ((!(digitalRead(b_Pin)))&& prevState==0) {// кнопка удерживается
    prevState=0; return 2; 
  }
  if ((digitalRead(b_Pin))&& prevState==0) {  // произошло отпускание
    prevState=1; return 3; 
  }
}


void loop(){
  byte State=ButtonScan(BTN_Pin);
switch(State){
  case 0: {Blink (LED_Pin, interval_slow); break;}
  case 1: {Blink (LED_Pin, interval_fast); break;}
  case 2: {Blink (LED_Pin, interval_fast); break;}
  case 3: {progTimer(ext_interval);
      while (progTimer(ext_interval)){ 
         Blink (LED_Pin, interval_fast);
         }
      Blink (LED_Pin, interval_slow);
      break;
      }
   }
}

Работает, но как-то странно - интервал таймера вроде бы не всегда одинаковый! А по идее должно работать так:
1. После включения  светодиод переключается каждые 500мс (то есть медленно мигает)
2. Если кнопка нажата - переключение каждые 50мс (мигает быстро)
3. Если отпущена - запустить таймер на 2000мс, и когда время прошло, переключиться с режима быстрого мигания на на медленный.
Не знаю, как правильно написать такой кусочек? Так

  case 3: {progTimer(ext_interval);
    while (progTimer(ext_interval)) {
... } // пока время не прошло 
}

или так

case 3: { while (progTimer(ext_interval)){ 
         
  ...   } // пока время не прошло
}

В коде этот фрагмент начинается с 51 строки (она выделена).

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

junior_develope - опять в коде while? - строка 52 ?

Как вы (новички) не поймете. что использование в коде конструкции while(Timer) превращает ваш код в банальный блокирующий делей???

У вас в программе уже есть цикл - это сама процедура loop() . Никаких других while for или do() для работы таймеров на миллис КАТЕГОРИЧЕСКИ НЕ НУЖНО.

 

junior_developer
Offline
Зарегистрирован: 27.11.2017

Да Вы правы! Спасибо!
Если не трудно, подскажите пожалуйста, как тогда здесь можно обойтись без while. Чем его можно заменить?

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

junior_developer пишет:

как тогда здесь можно обойтись без while. Чем его можно заменить?

Для начала вам надо выделить то, что сейчас внутри while - в отдельное состояние. И подумать, что произойдет. если в это время кто-то снова нажмет кнопку. Я бы, например. разделил в отдельные переменные состояние светодиода и состояние кнопки. Так, как сейчас - одна переменная stste - логику прописать будет трудно.

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

а можно вот так переписать (сорри за возможные опечатки - проверить негде)

void loop(){
  static byte State=0;
  if (State != 3) State = ButtonScan(BTN_Pin);
switch(State){
  case 0: {Blink (LED_Pin, interval_slow); break;}
  case 1: {Blink (LED_Pin, interval_fast); break;}
  case 2: {Blink (LED_Pin, interval_fast); break;}
  case 3: {Blink (LED_Pin, interval_fast); 
               if ( ! (progTimer(ext_interval))) State =0;
               break;}
    
   }
}

 

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

b707 пишет:

Секешфехервар пишет:
я прочитал неблокирующая задержка как код которому все ни по чём . А в своих потугах обещаю избавиться от делеев... По крайней мере от тех которые вызываются регулярно.

Нет, неблокирующие задержки работают только если весь остальной код тоже неблокирующий. Вчитайтесь в слово - это "неблокирующая" задержка. А не "неблокируемая", понимаете разницу? Она сама других не блокирует, но ее заблокировать может любой делей. Поэтому Вам надо избавиться в коде АБСОЛЮТНО ОТ ВСЕХ ДЕЛЕЕВ, а не только от каких-то тех, которые вам больше мешают

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

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

Подскажите такой вариант жизнеспособен ? 



В программе есть несколько функций, все они вызываются в разное время
int  timer1 ;
uint16_t mni, des, des2, Time1, timer = 0 ;

void setup() {
  // put your setup code here, to run once:

}

void loop() {

}
uint32_t now = millis ();
if (now - Time1 >= 100) {         // Интервал 100 мсек 
  Time1 = now;
  des++ ;
  des2++ ;
  Serial.println(des2);
  if (timer > 0) {
    timer--;
    zapusk();
  }
  
 if (des == 100) {               // Интервал 10 сек
      detection();
      des = 0;
       des2 = 0;
      
      mni++;
    }
  }
  
  if ( mni == 6) {                // Интервал 60 сек
    mni = 0;
    
      if (timer1 > 0) {
      timer1--;
    }
  }
}
 
  
  void something(){
    здесь выполняется часть кода ;
des2 = 0;
  if (des2 == 2 ){    задержка  200 мсек
  здесь выполняется код после задержки  
  }

 void something1(){
    здесь выполняется часть кода ;
des2 = 0;
  if (des2 == 2 ){    задержка  200 мсек
  здесь выполняется код после задержки  
  }
 void something2(){
    здесь выполняется часть кода ;
des2 = 0;
  if (des2 == 2 ){    задержка  200 мсек
  здесь выполняется код после задержки  
  }
    И так далее и тому подобное навскидку пара десятков 
функций в которых требуется задержка от 200 мсек до 4 секунд
 все эти функции вызываются в разное время

 

Schwarz78
Offline
Зарегистрирован: 19.01.2019

А какие переменные у вас инициализируются, для начала?

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

Секешфехервар пишет:

Подскажите такой вариант жизнеспособен ?

Что касается кода в loop() - почему бы и нет? Насколко я вижу, вы сначала миллисом отмеряете интервал в 100мс, потом накапливаете 100 таких интервалов - получаете 10 секунд, а сделав так 6 раз - получаете минуту. Да, можно делать и так, хотя по-моему проще все три интервала отмерить непосредственно с помощью миллис, без вложенных циклов.

Что касается функций something() в конце программы - если честно, я не понял, к чему они. В коде они нигде не вызываются. И в коде этих функций встречается странный фрагмент:

void something(){
// здесь выполняется часть кода ;
des2 = 0;
if (des2 == 2 ){    задержка  200 мсек

//  здесь выполняется код после задержки 

 }

очевидно, что если в третьей строке des2 =0  - условие в четвертой строке никогда не выполнится....

Секешфехервар
Секешфехервар аватар
Offline
Зарегистрирован: 06.09.2018

Функция здесь и не вызывается это просто пример не буду же я портянку на ~450 строк выкладывать. Странно, но эта хрень работает на макете - вызывается функция счётчик в третьей строке сбрасывается и начинает считать от нуля четвертая строка срабатывает по условию... Не знаю насколько это будет стабильно видимо нужно пробовать.