Не работает delay

vicitacal
Offline
Зарегистрирован: 13.04.2017

Здравствуйте! Понадобилось сделать аппарат, перемещающий крышку каждые 24 часа и работающий автономно. Для уменьшения энергопотребления использую библиотеку LowPower, а для пробуждения по таймеру MsTimer2, для отключения реле(потребляет ток даже когда не движется) - транзистор на 6 поо По непонятным причинам, в функции feed не срабатывает задержка, а без нее серво привод просто не успевает переместится до выключения и перехода в сон.

/*
  Created 2017
  by AlexGyver
  AlexGyver Home Labs Inc.
*/
//--------------------НАСТРОЙКИ----------------------
#define open_angle 60   // угол 1
#define close_angle 128 // угол 0

#define debug 1         // вывод информации в порт для отладки
//--------------------НАСТРОЙКИ----------------------

// ---ПОДКЛЮЧЕНИЕ---
#define MOSFETpin 6
#define servoPin 5
#define OpenTime 1000
// ---ПОДКЛЮЧЕНИЕ---

#include <MsTimer2.h> //библиотека таймера
#include "LowPower.h" // библиотека сна
#include <Servo.h>    // используем библиотеку для работы с сервоприводом
Servo servo;        

boolean open_flag;

void START(){
  digitalWrite(MOSFETpin, 1);     // подать питание на серво
  delay(1);
  servo.write(open_angle);
  delay(1000);                    // ждать серво
  open_flag = 1;
  digitalWrite(MOSFETpin, 0);     // отключить серво
  if (debug) Serial.println("ready");
}


void feed() {
  digitalWrite(MOSFETpin, 1);        // подать питание на серво
  if (debug) Serial.println("on");
  if (open_flag) {
  if (debug) Serial.println("close");
    servo.write(close_angle);
    open_flag = 0;                   // флаг что крышка в положении 0
  } else {
  if (debug) Serial.println("open");
    servo.write(open_angle);
    open_flag = 1;                   // флаг что крышка в положении 1
  }
    delay(OpenTime);
  if (debug) Serial.println("off");
    digitalWrite(MOSFETpin, 0);  ; 
    sleep();
}

void sleep(){
  if (debug) Serial.println("power_down");
  delay(1);
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
}

void setup() {
  if (debug) Serial.begin(9600);
  servo.attach(servoPin);         // серво на 5 порту
  pinMode(MOSFETpin, OUTPUT);     // пин транзистора в режиме выхода
  //MsTimer2::set(86400000, feed);
  MsTimer2::set(10000, feed); 
  MsTimer2::start();
  START();
  sleep();
}

void loop() {

}


 

vicitacal
Offline
Зарегистрирован: 13.04.2017

Извените, опечаточка:

 

для отключения серво-привода (потребляет ток даже когда не движется) - транзистор на 6 порту.

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

Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. http://arduino.ru/Reference/AttachInterrupt

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

Если совсем никак в обработчике прерывания без делай обойтись не можете, замените на delayMicroseconds - этот работает.

Pyotr
Offline
Зарегистрирован: 12.03.2014

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

Если совсем никак в обработчике прерывания без делай обойтись не можете, замените на delayMicroseconds - этот работает.

или написать свою функцию delay(), которая будет работать в обработчике прерывания.
Например

const byte led = 13;

void setup() {                
  pinMode(led, OUTPUT);  
  cli();   
}

void loop() {
  digitalWrite(led, HIGH);  
  delayMy(10);
  digitalWrite(led, LOW);   
  delayMy(990);
}

void delayMy(word ms){
  byte tcnt0 = TCNT0;
  byte tic = 250;//для 16МГц и предделителя ТС0=64, 1 тик=4мкс
  while(ms>0){
    while(TCNT0 == tcnt0){}
    tcnt0 += tic;
    while(TCNT0 != tcnt0){}
    ms--;
  }
}

 

vicitacal
Offline
Зарегистрирован: 13.04.2017

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

Если совсем никак в обработчике прерывания без делай обойтись не можете, замените на delayMicroseconds - этот работает.

Нет, delayMicroseconds тоже не работает. Всё то же самое, что и с обычным delay. Может, это не совсем то прерывание, которое есть в стандартной библиотеке.

vicitacal
Offline
Зарегистрирован: 13.04.2017

Pyotr пишет:

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

Если совсем никак в обработчике прерывания без делай обойтись не можете, замените на delayMicroseconds - этот работает.

или написать свою функцию delay(), которая будет работать в обработчике прерывания.
Например

const byte led = 13;

void setup() {                
  pinMode(led, OUTPUT);  
  cli();   
}

void loop() {
  digitalWrite(led, HIGH);  
  delayMy(10);
  digitalWrite(led, LOW);   
  delayMy(990);
}

void delayMy(word ms){
  byte tcnt0 = TCNT0;
  byte tic = 250;//для 16МГц и предделителя ТС0=64, 1 тик=4мкс
  while(ms>0){
    while(TCNT0 == tcnt0){}
    tcnt0 += tic;
    while(TCNT0 != tcnt0){}
    ms--;
  }
}

 

У меня тоже была мысль тормозить всё бональным выполнением долгой операции. Но, почему то, это тоже не работает. Как будто задержка выполняется в другом месте. Я вызвал функцию на 49 строке, а в ком порте выводится что серва включена, затем задержка и после этого сразу посылается сигнал на серву и она отключается без уже задержки. Вот уж не знаю как так.

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

vicitacal пишет:

Нет, delayMicroseconds тоже не работает. 

delayMicroseconds  в прерывании работает. Если у Вас что-то не работает, ищите причину в другом. (кстати, надеюсь, Вы не забыли задержки на 1000 умножить?)

nik182
Offline
Зарегистрирован: 04.05.2015

Да работает, но с длительностью void delayMicroseconds(unsigned int us); 65 милисекунд максимум. Если на 1000 множить, то очень быстро кончается разрядность и задержки опять становятся короткими.

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

Так множить-то с умом надо! Без ума оно всегда так :(

sadman41
Offline
Зарегистрирован: 19.10.2016
delayMicroseconds()
 
Currently, the largest value that will produce an accurate delay is 16383. This could change in future Arduino releases. For delays longer than a few thousand microseconds, you should use delay() instead.
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, так вызывайте несколько раз. Я же говорю, с умом надо это делать.

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

Можно мне не вызывать несколько раз? Я же просто проинформировал.

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

Вот лично Вам никак низзя! Три штрафных вызова, как минимум!

vicitacal
Offline
Зарегистрирован: 13.04.2017

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

vicitacal пишет:

Нет, delayMicroseconds тоже не работает. 

delayMicroseconds  в прерывании работает. Если у Вас что-то не работает, ищите причину в другом. (кстати, надеюсь, Вы не забыли задержки на 1000 умножить?)

Пишу по факту. У меня не работает простой код, который активируется библиотекой MsTimer2. Какие ещё могут быть причины? Там же вывод текста в ком порт до и после задержки, который в реальности выводятся одновременно. На 1000 не забыл умножить и даже 10 нулей ставил - результат тот же.

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

А как вы понимаете одновременность? 16мс - это не так много, как вы думаете.

vicitacal
Offline
Зарегистрирован: 13.04.2017

Относительно того, что я ввожу - это одновременно. Между сообщениями явно в сотни раз меньше 2 секунд. И при чём тут 16мс?

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

vicitacal пишет:

Пишу по факту. У меня не работает простой код,даже 10 нулей ставил - результат тот же.

Вы пишете не по факту, а по своей интерпретации того. что видите, которая (интерпретация) очевидно ошибочная.

vicitacal пишет:

У меня не работает простой код

Так покажите его, чем языком-то болтать.

vicitacal пишет:

даже 10 нулей ставил 

Вот и я о том же. Вам тут уже полфорума пытались объяснить, что так нельзя, а Вы всё ставите.

Не работает не delayMicroseconds, а Ваш "простой код".

 

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

vicitacal пишет:

Относительно того, что я ввожу - это одновременно. Между сообщениями явно в сотни раз меньше 2 секунд. И при чём тут 16мс?

Всё смешалось - люди, кони...

Ваш MSTimer запускает приаттаченную функцию из ISR? Если да, то никакой delay() там работать не будет. Работать будет delayMicroseconds(), а он позволяет задержать ход времени не более, чем 16.x мс. 

К тому же, если вы ознакомились с библиотекой Servo.h, то знаете, что она сама подвешивается на прерывание таймера. А прерывания не работают, покуда вы находитесь в обработчике другого прерывания. И я буду удивлен, если ваша серва без дополнительных извращений вдруг поедет по команде, поданной изнутри ISR.

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

vicitacal
Offline
Зарегистрирован: 13.04.2017

Так код всё это время в шапке висел. Продублирую:

//--------------------НАСТРОЙКИ----------------------
#define open_angle 60   // угол 1
#define close_angle 128 // угол 0

#define debug 1         // вывод информации в порт для отладки
//--------------------НАСТРОЙКИ----------------------

// ---ПОДКЛЮЧЕНИЕ---
#define MOSFETpin 6
#define servoPin 5
#define OpenTime 1000000
// ---ПОДКЛЮЧЕНИЕ---

#include <MsTimer2.h> //библиотека таймера
#include "LowPower.h" // библиотека сна
#include <Servo.h>    // используем библиотеку для работы с сервоприводом
Servo servo;        

boolean open_flag;

void START(){
  digitalWrite(MOSFETpin, 1);     // подать питание на серво
  delay(1);
  servo.write(open_angle);
  delay(1000);                    // ждать серво
  open_flag = 1;
  digitalWrite(MOSFETpin, 0);     // отключить серво
  if (debug) Serial.println("ready");
}


void feed() {
  digitalWrite(MOSFETpin, 1);        // подать питание на серво
  if (debug) Serial.println("on");
  if (open_flag) {
  if (debug) Serial.println("close");
    servo.write(close_angle);
    open_flag = 0;                   // флаг что крышка в положении 0
  } else {
  if (debug) Serial.println("open");
    servo.write(open_angle);
    open_flag = 1;                   // флаг что крышка в положении 1
  }
    delayMicroseconds(OpenTime);
  if (debug) Serial.println("off");
    digitalWrite(MOSFETpin, 0);  ; 
    sleep();
}

void sleep(){
  if (debug) Serial.println("power_down");
  delay(1);
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
}

void setup() {
  if (debug) Serial.begin(9600);
  servo.attach(servoPin);         // серво на 5 порту
  pinMode(MOSFETpin, OUTPUT);     // пин транзистора в режиме выхода
  //MsTimer2::set(86400000, feed);
  MsTimer2::set(10000, feed); 
  MsTimer2::start();
  START();
  sleep();
}

void loop() {

}

С этой же библиотекой и с таким же принципом пробуждения уже писал другую прошивку и она без проблем работала. 

 

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

Ну и что вы хотите от форума - чтобы вам функцию переписали или IDE пофиксили?

vicitacal
Offline
Зарегистрирован: 13.04.2017

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

sadman41 пишет:

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

Как это так вручную?

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

Вот если вас разбудить утром. Что вы будете делать. Разумеется ходить и не спать. Разбудили Ардуину, вот пусть в loop ардуина и пашет. А если вы умудритесь на работе кемарнуть, то ваш напарник вас разбудет. Потому что когда один спит.Другой пашет в двое, да и "жаба" у напарника тоже есть.

vicitacal
Offline
Зарегистрирован: 13.04.2017

Да пусть пашет где угодно, только вот спит она очень крепко и MsTimer её не всегда будит. А уж как перенести всё в loop вообще не представляю.

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

Читал? http://arduino.ru/forum/programmirovanie/kak-realizovat-periodicheskii-wakeup-iz-sleep-mode

Вот вместо Serial.println("sleep");  и ставь свой код. А потом delay().

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

vicitacal пишет:

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

sadman41 пишет:

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

Как это так вручную?

Напишите маленький скетч, где будет такое действие, например: for (i=0; i < 5; i++) { digitalWrite(5, HIGH); delayMicroseconds(900); digitalWrite(5, LOW); delayMicroseconds(16000); }

На пин 5 повесьте серву. Запустите скетч. Дергается серва? Теперь меняйте 900 на что-то из диапазона 544...2400.

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

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

vicitacal пишет:

Так код всё это время в шапке висел. Продублирую:

Неправда. В шапке висел с делэями, а не микросендсами.

А вот теперь смотрим на Ваш код и видим, что не такой он просто, как Вам кажется.

Вам тут полфорума раз пять говорили, что delayMicroseconds НЕ БЫВАЕТ больше чем на 16383 мкс. А Вы что делаете? Фигачите её на миллион! Вы вообще читаете, что Вам пишут?

Испрвляйте строку 11 на 10000, а строку 44 на такую

for (int i=0; i<100; delayMicroseconds(OpenTime), i++);

vicitacal
Offline
Зарегистрирован: 13.04.2017

Ура! Заработало! Всем большое спасибо! 

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

Испрвляйте строку 11 на 10000, а строку 44 на такую

for (int i=0; i<100; delayMicroseconds(OpenTime), i++);

 Вот так сделать не получилось. Конфликтовала библиотека серво и библиотерка прирываний по таймеру и работало всё не адекватно. Сделал "ручное" управление сервой и убрал её библиотеку.

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

vicitacal пишет:

 Вот так сделать не получилось. Конфликтовала библиотека ....

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

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

Но справедливости ради подчеркну, что совать всю эту бороду в ISR - недостойно джентельмена. Всё должно висеть в лупе или в сетапе с бесконечным циклом унутре.

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

sadman41 пишет:

недостойно джентельмена

https://youtu.be/v1-lr5plK_A?t=3176

— Сэр не слышит.
— А может быть, он вовсе не сэр?
— Отдай лодку, болван! О! Услышал…