Включение пина на определенное время

Dmitriysokol
Offline
Зарегистрирован: 03.06.2016

Добрый день, товарищи. разибраюсь с кодом Ардуины, и вот наткнулся на проблему. Прошу вас помочь

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

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

 if(OKNO_AVT && p1<p2)
 
 { 
     if(flagA==1)
        { 
           
             if(millis()-previousMillisA >= 10000)
                { 
                   digitalWrite(oknoPin,LOW);
             
                   previousMillisB=millis();
                }
  
    if(millis()-previousMillisB>=20000)
      {
        previousMillisA=millis();
        digitalWrite(oknoPin,HIGH);
     }
    }  
 }

 

GarryC
Offline
Зарегистрирован: 08.08.2016
int Sost=0;

void StateMachine(void) {
static unsigned long Time;
 switch (Sost) {
  case 0: Sost = 1; Time = millis()+Delay0; break;
  case 1: if (Условие) {if (millis()>=Time) {Sost=2; Time = millis() + Delay1; Включение}}; else {Sost = 0}; break;
  case 2: if (millis()>=Time) {Sost=3; Time=millis()+Delay2; Выключение}; break;
  case 3: if (millis()>=Time) {Sost=0;}; break;
 };
};

Ждем условие на время Delay0, потом на время Delay1 включаем, потом выключаем на время Delay2 и повторяем, как то так.

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

GarryC,

вот здесь 

GarryC пишет:

  Time = millis()+Delay0;
  ...
  if (millis()>=Time) ...

спрятаны грабли, связанные с переходом millis через переполнение.

GarryC
Offline
Зарегистрирован: 08.08.2016

Если верить документации на Ардуино, то раз в 48 суток.

А вообще то Вы правы, правильнее будет использовать макросы Unix для Jiffies, но стоит ли ТАК пугать молодежь?

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

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

GarryC
Offline
Зарегистрирован: 08.08.2016

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

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

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

как может исчезнуть то, чего нет?

ptr
Offline
Зарегистрирован: 28.05.2016

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

спрятаны грабли, связанные с переходом millis через переполнение.

Грабли есть. Но для реального устройства с watch dog и перезагрузкой раз в сутки - их нет )

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

Мой код такой

/* одновибратор
кнопка -> 6 (btm_pin) 0 нажата/ 1 нет
       GND ->  GND
светодиод -> 13 (Led_pin) 1 горит/ 0 нет
       GND ->  GND
*/
const int btm_pin=6  ; // нога кнопки
const int Led_pin=13 ; // нога светодиода
const int interval=2000 ;// интервал после нажатия клавиши 2 сек
uint8_t Led=0 ;
void setup() {
   pinMode(btm_pin, INPUT_PULLUP);
   pinMode(Led_pin, OUTPUT);
   digitalWrite(Led_pin,Led);
 }

void loop() {
  //#1
  static uint32_t future1 = 0 ;
  if (millis() >=future1) {
       digitalWrite(Led_pin,Led=0);
       }
  if(!digitalRead(btm_pin)){
    digitalWrite(Led_pin,Led=1);
    future1=millis()+interval;
    }
}

А вот как лучше будет через 48 решать вам

if (millis() >=future1) или if (millis()- future1>=0)

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

ptr пишет:

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

спрятаны грабли, связанные с переходом millis через переполнение.

Грабли есть. Но для реального устройства с watch dog и перезагрузкой раз в сутки - их нет )

Ну и с перезагрузкой раз в час их тоже нет. Только зачем это огород, когда достаточно "переставить местами слагаемые" и никаких вочдогов не нужно.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

Если верить документации на Ардуино, то раз в 48 суток.

каким боком документация Дуино к факту того что 4294967295 миллисекунд - это где-то там 50 суток?

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

qwone пишет:

Мой код такой

[въезжает на титановом бульдозере велосипеде] алё! гараж, это здесь выставка одновибраторов?


#include "class_noDELAY.h"

noDELAY nD_01;

void setup () {

pinMode(14, OUTPUT); // LED_01
digitalWrite(14, 0);
nD_01.start();

}

void loop() {
nD_01.read(1000);
if (nD_01.tick) {nD_01.stop(); digitalWrite(14, 1);}
}

 

wdrakula
wdrakula аватар
Онлайн
Зарегистрирован: 15.03.2016

Евгений! Вы же как-то в контакте с модераторами, если я правильно понял.

Тема про "переполнение" достала капитально. И главное, что каждый месяц находятся новые адепты секты "48-ого дня".

Нужно в "прикрепленные" вывесить и примерчик, где Вы показали, что можно и со знаком и без знака, все равно. Он у меня где-то лежит. Нашел.

Вы уж поясните неверующим, что и к чему в примере.

Там в #define можно что со знаком, что без знака поставить - все едино.

 

#define  BasicType  long

template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; }

static const BasicType startValueOfMillis = 4294967279ul; // до переполнения осталось 16

BasicType _millis(void) {
  static BasicType cntr = startValueOfMillis;
  return cntr++;
}

void setup() {
  Serial.begin(9600);
  while(!Serial);
}

void loop() {
  static long enterCounter = 0;
  static const BasicType theInterval = 10;
  static BasicType start = startValueOfMillis;
delay(50);

  if (enterCounter++ > 40) 
  {
    
    return;
  }
  
  BasicType currentMillis = _millis();
  if (currentMillis - start >= theInterval) {
    Serial << "*** Event. Counter: " << enterCounter << "\n";
    start = currentMillis;
  }
}

 

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

wdrakula пишет:

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

прошло мимо меня - о каком знаке и беззнаке речь идёт?

пример простой:

// 4294967294 - 4294967284 = 10
// 4294967295 - 4294967285 = 10
// 0          - 4294967286 = 10
// 1          - 4294967287 = 10
// 2          - 4294967288 = 10
// 3          - 4294967289 = 10
// 4          - 4294967290 = 10
// 5          - 4294967291 = 10
// 6          - 4294967292 = 10
// 7          - 4294967293 = 10
// 8          - 4294967294 = 10
// 9          - 4294967295 = 10
// 10         - 0          = 10
// 11         - 1          = 10

 

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

wdrakula пишет:

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

Там в #define можно что со знаком, что без знака поставить - все едино.

#define  BasicType  long

и так будет работать

#define  BasicType  boolean
#define  BasicType  char

но, зачем так делать, если мы работаем с millis(), которая возвращает unsigned long, а не long или что иное?

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

Клапауций 232 пишет:

но, зачем так делать, если мы работаем с millis(), которая возвращает unsigned long, а не long или что иное?

Просто тогда, в контексте той беседы, так получилось. Я посоветовал человеку что почитать, чтобы разобраться с этим, а он ответитл "Теперь понял, всё дело в unsigned", на что я ответил. то мол "таки не понял, разбирайся ещё, т.к. там пофигу signed или unsigned" и продемонстрировал это, чтобы не быть голослловным.

А вне того контекста - да, незачем.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

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

Просто тогда, в контексте той беседы, так получилось. Я посоветовал человеку что почитать, чтобы разобраться с этим, а он ответитл "Теперь понял, всё дело в unsigned", на что я ответил. то мол "таки не понял, разбирайся ещё, т.к. там пофигу signed или unsigned" и продемонстрировал это, чтобы не быть голослловным.

А вне того контекста - да, незачем.

талант запутывать студентов не пропьёшь - теперь к тому, что не понимал ничего, прибавился ещё один, который понял наполовину - теперь он считает, что время может быть как положительным так и отрицательным, но не смотря на это через 48 суток День Сурка не наступает.

GarryC
Offline
Зарегистрирован: 08.08.2016

Попытаюсь прояснить ситуацию. На самом деле все очень просто и подробно описано в книгах по внутреннему устройству Unix.

Первый возможный и совершенно правильный вариант - это запоминать начало отсчета интервала и его величину и сравнивать разницу текущего и запомненного с ожидаемым
( (Текущее-Начало) >{=} Ожидание ).
Никакие переполнения работу не нарушат, но есть разница в используемых типах. Если у нас тип знаковый, то мы не можем заказать интервал длиннее половины возможного, поскольку Ожидание в этом случае будет отрицательным и условие сработает сразу. Если тип беззнаковый, то все намного лучше и мы можем заказать любой интервал, умещающийся в разрядную сетку. Поэтому когда кое-кто заявляет, что данный подход будет работать при любых типах, неплохо бы понимать разницу.
Поскольку порождаемый код для знакового и беззнакового типа идентичен и изменяется только собственно сравнение (анализ бита знака либо бита переполнения) нет никаких оснований выбирать знаковый тип для реализации.

Тем не менее такой подход не закрепился в Unix системах в силу того, что есть альтернативный, обладающий определенными преимуществами. Для его получения преобразуем исходную формулу к виду
( (Текущее-(Начало+Ожидание) ) >{=} 0 ), что приводит к выражению
( (Текущее-Окончание) >{=} 0 ) при условии
Окончание=Начало+Ожидание.
Этот метод полностью эквивалентен первому с точки зрения правильной обработки переполнения, но включает сравнение с нулем, что автоматически приводит к знаковым типам, и при этом мы видим то же самое ограничение - невозможность заказать более половины возможного интервала.
Однако теперь обращение к знаковым типам оправденно, поскольку такой метод имеет преимущество перед исходным
1) мы должны хранить только Окончание - экономим память и
2) процесс обработки включает одно вычитание вместо двух по первому способу - экономим время.
Да, при этом появились дополнительные операции при формировании интервала, но они проводятся только один раз. Учитывая, что заказ большего, чем допустимый, интервала можно отследить при формировании интервала, и было принято подобное решение. В Unix системах определены макросы вида
#define time_after_eq(unknown, known) ((long)(unknown) - (long)(known) >= 0), которые данный подход реализуют.

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

 

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

Никакие переполнения работу не нарушат, но есть разница в используемых типах. Если у нас тип знаковый, то мы не можем заказать интервал длиннее половины возможного, поскольку Ожидание в этом случае будет отрицательным и условие сработает сразу.

снова за рыбу деньги - читаем описалово функции millis()

Возвращаемое значение

Количество миллисекунд с момента начала выполнения программы. (unsigned long)

*привяжи свой груз образования к ноге и спрыгни с моста - здесь обсуждают millis(), а не Unix time.

ptr
Offline
Зарегистрирован: 28.05.2016

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

 никаких вочдогов не нужно.

Watch dog по жизни нужен. Без него надежность работы устройства сильно понижается.

GarryC
Offline
Зарегистрирован: 08.08.2016

Некий Клапауций в своем комментарии утверждал что и тип char будет так же работать, а выяснилось, что не также, вот он и занервничал.

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

Ну и так, для сведения, все изложенное в комменте никоим образом с Unix time не связано, поскольку формирует более тонкие отсчеты, а связано с переменной jiffies и особенностями ее использования в связи с переполнениями. Переполнение же упомянутого атрибута кончно, тоже произойдет, но вряд ли оно должно волновать сегодняшних программистов.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

Некий Клапауций в своем комментарии утверждал что и тип char будет так же работать, а выяснилось, что не также, вот он и занервничал.

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

мой комментарий про char относился к коду, упоминаемому в #14

почему там работает лонг, но не будет работать чар?

какая такая сакральная причина?

чем лонг отличается от чар, кроме диапазона возможных значений?

*с чего бы это мне волноваться? - я не несу ответсвенности за факт наличия в этой реальности типов переменных.

GarryC
Offline
Зарегистрирован: 08.08.2016

Именно диапазоном и отличается. Речь идет о том, что для беззнакового числа диапазон допустимых задержек составляет полную разрядную сетку, а для знаковых - только половину ее, вот и все отличие. Оно не существенно в данной конкретной задаче ввиду значительных размеров диапазона, но оно есть и это надо учитывать.

А разницы между long и char действительно нет, Вы правы, они оба знаковые и в этом смысле одинаково плохи с указанной точки зрения. Да, с точки зрения комментария к приведенному коду, использующему long, Ваше утверждение верно. Единственное, относительно типа boolean я не был бы столь уверен, не видя его реализации, но это уже придирки.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

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

...относительно типа boolean я не был бы столь уверен...

а, мне странно, почему ты урезаешь диапазон вполовину, но не желаешь уменьшить диапазон до единицы. :D

почему можно резать пополам, но нельзя до минимально возможного?

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

GarryC,

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

Сначала Вы в посте №2 написали неправильный код. Затем, в посте №6 предложили какой-то космический вариант решения проблемы которой (проблемы) вообще-то и нет вовсе.

А уж дальше Вас и свосем понесло

GarryC пишет:

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

Говорите за себя. Я вот не знаю, что Вы можете, а что нет, потому за Вас ничего и не утверждаю.  За себя могу сказать, что я могу. Вам код показать? В котором тип знаковый, а задержка больше половины (т.е. отрицательная) или сами проверите?

Изучите всё-таки как устроена арифметика. Тогда поймёте. что дополнитеьный код для того и создан, чтобы процессору было фиолетово знаковый тип или нет - всё работает абсолютно одинаково в обоих случаях.

Был тут уже один знаток, который уверял, что допустимость записи unsigned a = -1; есть косяк компилятора, а когда его ткнули в стандарт языка, стал кричать, что это косяк языка. Не уподобляйтесь ему.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

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

Вам код показать? В котором тип знаковый, а задержка больше половины (т.е. отрицательная) или сами проверите?

почему только больше половины, а не убить наповал жертву знаковых типов переменных и Юникс времени - заюзать весь без остатка диапазон от -2147483648 до 2147483647 ?

очевидно, что диапазон long -2147483648 - 2147483647 равен (внезапно! о, ужос!) unsigned long 4294967295.

как так?

где же наша, обещанная GarryC, половинка, одну из которых мы можем юзать, а вторую не можем?

внезапно, не можем? - нужно проверить:


#include <DigiUSB.h>

// unsigned long 0 - 4294967295
// long -2147483648 - 2147483647

long  m; // начало диапазона переменной.
long nm; // конец диапазона переменной.
int n;
unsigned long interval;

void setup() {

DigiUSB.begin();

m  = -2147483648; // 
nm =  2147483647; // 
n = 0;
while(n < 10){ // делаем 10 шагов назад 
if (DigiUSB.tx_remaining() > 100) { // проверка места в буфере USB.
++n;
--m;
--nm;
interval = nm - m;
print();
}
DigiUSB.delay(1);
}

m  = -2147483648;
nm =  2147483647;
n = 0;
while(n < 10){ // делаем 10 шагов вперёд 
if (DigiUSB.tx_remaining() > 100) { // проверка места в буфере USB.
++n;
++m;
++nm;
interval = nm - m;
print();
}
DigiUSB.delay(1);
}

}

void loop() {DigiUSB.delay(1);}

void print() {
DigiUSB.print(nm);
DigiUSB.print(" - ");
DigiUSB.print(m);
DigiUSB.print(" = ");
DigiUSB.print(interval);
DigiUSB.print('\n');
}

ок. что напечатало:

2147483646 - 2147483647 = 4294967295
2147483645 - 2147483646 = 4294967295
2147483644 - 2147483645 = 4294967295
2147483643 - 2147483644 = 4294967295
2147483642 - 2147483643 = 4294967295
2147483641 - 2147483642 = 4294967295
2147483640 - 2147483641 = 4294967295
2147483639 - 2147483640 = 4294967295
2147483638 - 2147483639 = 4294967295
2147483637 - 2147483638 = 4294967295
-2147483648 - -2147483647 = 4294967295
-2147483647 - -2147483646 = 4294967295
-2147483646 - -2147483645 = 4294967295
-2147483645 - -2147483644 = 4294967295
-2147483644 - -2147483643 = 4294967295
-2147483643 - -2147483642 = 4294967295
-2147483642 - -2147483641 = 4294967295
-2147483641 - -2147483640 = 4294967295
-2147483640 - -2147483639 = 4294967295
-2147483639 - -2147483638 = 4294967295

вот так...

GarryC
Offline
Зарегистрирован: 08.08.2016

Жду кода в котором проверка (Текущее-Начальное)>Задержка будет работать при знаковых типах и значении Задержка отрицательном. Может все таки будет читать то, что написано в посте?

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

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

GarryC
Offline
Зарегистрирован: 08.08.2016

Читаем пост и видим
"Первый возможный и совершенно правильный вариант - это запоминать начало отсчета интервала и его величину и сравнивать разницу текущего и запомненного с ожидаемым ( (Текущее-Начало) >{=} Ожидание ). Никакие переполнения работу не нарушат, но есть разница в используемых типах. Если у нас тип знаковый, то мы не можем заказать интервал длиннее половины возможного, поскольку Ожидание в этом случае будет отрицательным и условие сработает сразу."

Читаем реплику в отчет на этот пост
"Говорите за себя. Я вот не знаю, что Вы можете, а что нет, потому за Вас ничего и не утверждаю.  За себя могу сказать, что я могу. Вам код показать? В котором тип знаковый, а задержка больше половины (т.е. отрицательная) или сами проверите?"

Читаем реплику в ответ на реплику "Жду кода в котором проверка (Текущее-Начальное)>Задержка будет работать при знаковых типах и значении Задержка отрицательном. Может все таки будет читать то, что написано в посте?"

Читаем ответ на реплику в ответ на реплику "Уже пошли передёргивания. Нигде в этом топике такого неравенста не было - появляется впервые."

Смотрим ЕЩЕ раз на первый пост и ВИДИМ это неравенство - я его сделал жирным. Вы по прежнему уверены, что оно появлось впервые? То есть дело в том что Ожидание и Задержка отличаются по написанию? Или Вас смутило Начало и Начальное? Хорошо, я согласен, напишите, пожалуйста, код для выделенного неравенства с знаковыми типами.

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

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

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

GarryC пишет:

Читаем пост и видим
"Первый возможный ...

И снова передёргивания.

Ну, вот, скажите, зачем передёргивать, всё же лежит на виду.

Я писал 

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

Сначала Вы в посте №2 написали неправильный код. Затем, в посте №6 предложили какой-то космический вариант решения проблемы которой (проблемы) вообще-то и нет вовсе.

Теперь, как Вы выражаетесь, "читаем пост" №6, на который я ссылался, и видим совсем не то, что Вы цитируете сейчас:

GarryC пишет:

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

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

И только в посте №20, после того, как Вам уже всё объяснили, Вы начали что-то писать по "совершенно правильные варианты" и в качестве такового взяли готовое решение, которое Вам приводили в посте №14.

Ну, зачем бабушку-то лохматить? Зачем выставлять себя на посмешище? Знаете, как-то становится неинтересно с Вами разговаривать.

GarryC пишет:

А что касается надуманности проблемы, прошу в студию другой код (не совпадающий с приведенным мной)

Ещё раз. Я говорил о проблеме из Вашего поста №6 и явно говорил об этом. А тот код, что привели в посте №20 (вернее, скопировали из поста №14) - против него у меня возражений нет.

GarryC пишет:

Смотрим ЕЩЕ раз на первый пост и ВИДИМ это неравенство

Вы правда не видите, что одно строгое, а другое нет? Или прикидываетесь?
 
GarryC
Offline
Зарегистрирован: 08.08.2016

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

GarryC
Offline
Зарегистрирован: 08.08.2016

Поскольку в посте 4 я сразу упомянул макросы для jiffies, для которых характерен подход, показанный в моем посте 2, связанный с прибавлением и сравнением с результатом, поэтому в посте 6 я писал именно об этой реализации задержек и там перевод в знаковый совершенно необходим и там есть проблема и она отнюдь не космическая. Вы в своем посте 5 обсуждали совсем другой подход, свойственный Ардуино, а я продолжал обсуждать свой, что и вызвало определенное недопонимание.  В посте 20 данный вопрос был рассмотрен весьма подробно, если Вам так и осталось непонятна природа вопроса, ничем не могу помочь.

До сих пор жду подтверждения Вашего заявления" За себя могу сказать, что я могу. Вам код показать? В котором тип знаковый, а задержка больше половины (т.е. отрицательная) или сами проверите?", но, видимо, не дождусь.

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

GarryC пишет:

До сих пор жду подтверждения Вашего заявления" За себя могу сказать, что я могу. Вам код показать? В котором тип знаковый, а задержка больше половины (т.е. отрицательная) или сами проверите?", но, видимо, не дождусь.

Ну, знаете, у меня осталось впечатление, что "Вы всегда правы", поскольку после неправильного кода в посте №2, Вы начали нести какой-то бред в посте №6, а теперь отмазываетесь каким-то UNIX'ом (почему не ОС-360?). Можно подумать, что там арифметика другая. По уму, мне бы прекратить это разговор, потому как я отлично знаю, что последует дальше.

Но, тем не менее, я приведу кусочек кода. Не для Вас - Вы ведь А) всегда правы; и Б) итак всё знаете. Я приведу его для тех, кто пока ещё не всё знает.

Итак, коллеги, первое и главное. Плюньте на этот дурацкий спор и просто используйте беззнаковый long, который выдаёт millis(), только делайте это правильно, как приведено в примере "Блинк без делэй", в постах Лешака, в посте №14 данного топика и вообще, везде у грамотных людей. Никогда не делайте как предложил GarryC в посте №2. И всё у вас будет работать долго и правильно.

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

Итак, в чём собственно проблема?

Если переменные знаковые, то их значения бывают отрицательнми. И это не всегда очевидно. Например, переменная а в данном примере

int8_t a = 150;

будет отрицательной и равной -106. Напечатайте и проверьте.

Но тогда в программе

static const int8_t theInterval = 150;

if (int8_millis() - startTime >= theInterval ) { ...

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

Непонятно зачем может понадобиться такое извращение (знаковые числа в задержках), но чисто из любви к искусству обойти это можно.

Для начала нужно понимать, что (это важно!!!) любые вычисления происходят одинаково (абсолютно!!!) для знаковых и беззнаковых переменных. Разница в нашем случае только в операции сравнения!

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

Ну, самое простое, что Вы должны сразу же заметить - это то, что если вычисления идут одинаково, то нам достаточно сравнивать на равенсто (== вместо >=) - всё отлично сравнится и будет замечательно работать, при условии, что длительность Вашего loop не превышает 1 мс. Поробуйте сами - всё отлично сработает.

#define BasicType	int8_t

template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; }

BasicType _millis(void) {
	static BasicType cntr = 0;
	return cntr++;
}

void setup() {
	Serial.begin(115200);
}

void loop() {
	static long enterCounter = 0;
	static const BasicType theInterval = 150;
	static BasicType start = 0;

	if (enterCounter > 1000) return;
	
	const BasicType currentMillis = _millis();
	const BasicType diff = currentMillis - start;
	if (diff == theInterval) {
		Serial << "*** Event. Counter: " << enterCounter << "\n";
		start = currentMillis;
	}
	enterCounter++;
}
/*
 * РЕЗУЛЬТАТ
*** Event. Counter: 150
*** Event. Counter: 300
*** Event. Counter: 450
*** Event. Counter: 600
*** Event. Counter: 750
*** Event. Counter: 900
*/

Но это плохая идея. Это >= туда не зря пихают. А ну как попадётся долгий loop ... Если таки хочется, чтобы работало и при более долгих loop'ах, то надо просто немного поменять условие. Есть миллион способов это сделать, например, вот так:

#define BasicType	int8_t

template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; }

BasicType _millis(void) {
	static BasicType cntr = 0;
	return cntr++;
}

void setup() {
	Serial.begin(115200);
}

void loop() {
	static long enterCounter = 0;
	static const BasicType theInterval = 150;
	static BasicType start = 0;

	if (enterCounter > 1000) return;
	
	const BasicType currentMillis = _millis();
	const BasicType diff = currentMillis - start;
	const BasicType dd = currentMillis - start - theInterval;
	if ((theInterval < 0 && diff < 0 && dd >= 0) || (theInterval > 0 && diff >= theInterval)) {
		Serial << "*** Event. Counter: " << enterCounter << "\n";
		start = currentMillis;
	}
	enterCounter++;
}

/*
 * РЕЗУЛЬТАТ
*** Event. Counter: 150
*** Event. Counter: 300
*** Event. Counter: 450
*** Event. Counter: 600
*** Event. Counter: 750
*** Event. Counter: 900
*/

Кстати, можете попробовать вместо 150 поставить что-нибудь положительное, например 120 (в обоих примерах) и убедиться. что с положительными тоже работает.

Зачем это нужно? Не знаю. Просто для демонстрации, что такое возможно.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

Жду кода в котором проверка (Текущее-Начальное)>Задержка будет работать при знаковых типах и значении Задержка отрицательном.

так вот же! #29

переведи 4294967295 в тип long и убедись, что оно отрицательное.

GarryC пишет:

Вообще то, для особо продвинутых...

поясни для особо продвинутых, зачем ты упорно настаиваешь, что результат операции с двумя переменными типа long нужно переводить в так же тип long, даже в том случае, когда значение этой операции всегда имеет тип unsigned long?

--------------------------------------

факт:

для описания свойств переменных типа long нужно использовать переменные типа unsigned long, в том числе.

пример: диапазоном значений переменной long является значение типа unsigned long.

--------------------------------------

логика:

даже, если ты желаешь использовать переменные типа long для хранения значения времени, из этого никак не следует, что операции с этими переменными должны иметь аналогичный тип - тип зависит от контекста операций.

--------------------------------------

GarryC
Offline
Зарегистрирован: 08.08.2016

Ну вот я и дождался ).
Сразу скажу что я допустил 2 ошибки - первая была в исходном посте, даже если делаешь набросок кода, не следует писать небрежно, это несомненно так. Тем не менее, если писать правильное условие с преобразованием в знаковый тип, то вполне можно использовать подход с немедленным прибавлением "и все у вас будет работать долго, правильно"  и чуть быстрее.  А вот вторая моя ошибка не столь очевидна - когда я просил код в студию, следовало уточнить, что нужен правильный и разумный код, а не какой-нибудь, поскольку моим оппонентом такой вид кода не подразумевался.

Но это преамбула, а теперь обратимся собственно к коду. Поскольку для решения изначально поставленной задачи (использованию задержки более половины диапазона) необходимо беззнаковое сравнение, а автором кода постулировалась возможность реализации на знаковых типах, перед ним встала проблема беззнакового сравнения знаковых чисел. В принципе, она решается (конечно решается) очень просто, путем явного преобразования и можно было написать
(unsigned BasicType) diff >= (unsigned BasicType) theInterval
, но такой путь слишком явно наводит на мысль о том, что беззнаковое действительно необходимо. Поэтому автором кода было принять решение замаскировать беззнаковое сравнение и сделать его на знаковых операциях. Такое решение (одно из миллиона способов почесать правое ухо левой рукой, просунув ее под коленкой) и приведено в рассматриваемом коде. Обратим на него внимание и подумаем, как мы можем превратить знаковое сравнение в беззнаковое. Для этого нам потребуется рассмотреть различные сочетания операндов, здесь возможны 4 варианта
1) оба сравниваемых числа положительны - можно просто сравнить,
2) первое положительно, второе отрицательно - всегда меньше,
3) первое отрицательно, второе положительно - всегда больше,
4) оба отрицательны - можно просто сравнить.
Вот и истоки столь замысловатого кода, но в нем не реализована 3 вариант, что приводит к неправильному выполнению при пропуске равенства при определенных значениях задержки. Следовало бы добавить еще одно условие
... ((theInterval > 0) && ((diff >= theInterval) || (diff < 0)))
, тогда код становился бы правильным, но разумным его бы это не делало, поскольку разумный (и правильный) написан чуть выше.

Разумеется, можно придумать еще много способов реализации безнакового сравнения, например перевести в float с учетом знака и сравнивать float, наверное, можно придумать еще что-нибудь более экзотичное, но вряд ли существует способ, который докажет, что беззнаковое сравнение не нужно, хотя с интересом жду.

GarryC
Offline
Зарегистрирован: 08.08.2016

"зачем ты упорно настаиваешь, что результат операции с двумя переменными типа long нужно переводить в так же тип long, даже в том случае, когда значение этой операции всегда имеет тип unsigned long?" - Что Я Делаю Не Так, если думаю, что результат операции на long будет иметь тип long, и что характерно, описания языка со мной согласно?

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

Что Я Делаю Не Так, если думаю, что результат операции на long будет иметь тип long, и что характерно, описания языка со мной согласно?

почему ты не думаешь, что результатом операции с long будет boolean?

описание переменной long с тобой не согласно - диапазон переменной возможно описать числом типа unsigned long, и никак иначе.

хватит уже тупить - возможно, ты как-то кем-то образован, но правильно применить свои знания тебе мешает отсутствие логики.

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

Ну, в целом я и ожидал воплей "несчитова!", когда писал

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

я отлично знаю, что последует дальше.

Только вот есть загводка, даже две.

1. Я не преобразовывал к беззнаковому сравнению, обошёлся знаковыми - а требовалось только это и ничего другого. Так, что всё остальное - мимо кассы. Ваше выражение "правильный и разумный код" несколько странно в данном контексте. Код, который правильно решает поставленную задачу правилен и разумен. Т.е. данный код намного правильнее и разумнее того, что привели Вы во втором посте. Если у Вас другие критерии правильности и разумности - это Ваша проблема. То, что он несколько "извращение", так задача была такая.

2. Вы не разобрались в коде. Ваше третье условие учтено, просто Вы по-прежнему демонстрируете незнание устройства арифметики в этом языке. Собака зарыта в 23-ей строке. Собственно 23-я строка это своего рода жульничество, но чтобы это понять нужно знать как устроена арифметика в тонкостях, а не "вообще".

Считаю инцидент исчерпанным.

GarryC
Offline
Зарегистрирован: 08.08.2016

Да, это верно, у меня не достаточно логики, чтобы понять, каким именно образом компилятор может догадаться, как ему нужно модифицировать операцию сравнения в зависимости от полученного значения. Мне казалось, что для беззнакового типа будет сформирована команда BRSH, а для знакового - BGE независимо от количества единиц в результате вычитания и почему то используемый мною компилятор так и делает. Значит, мы оба неправы, но если относительно меня данное предположение вполне может быть справедливо, то относительно компилятора - это сильное утверждение, требующее доказательств.

GarryC
Offline
Зарегистрирован: 08.08.2016

Я догадывался, что Вы не поймете своей ошибки, и рад, что не ошибся. Если Вы сможете внятно объяснить, каким именно образом 23 строка спасет Вас в случае, когда заказан интервал 127 единиц, сравнение равенства пропущено из за внешней задержки и Вы начинаете выполнять ветвь (127>0) && (-1>=127) и получаете верный результат, то Вы меня очень обяжете.

Оказывается, это я продемонстрировал незнание арифметики, как интересно. Боюсь, что с человеком, искренне считающим, что -1>=127 при знаковом сравнении, разговаривать особо не о чем.

GarryC
Offline
Зарегистрирован: 08.08.2016

Мощная реплика, правда на вопрос не отвечает, но ведь это и не важно. Наверное, у меня и у Вас разные компиляторы, тогда дальше разговор становится слегка бессмысленным.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

Да, это верно, у меня не достаточно логики, чтобы понять, каким именно образом компилятор может догадаться, как ему нужно модифицировать операцию сравнения в зависимости от полученного значения. Мне казалось, что для беззнакового типа будет сформирована команда BRSH, а для знакового - BGE независимо от количества единиц в результате вычитания и почему то используемый мною компилятор так и делает. Значит, мы оба неправы, но если относительно меня данное предположение вполне может быть справедливо, то относительно компилятора - это сильное утверждение, требующее доказательств.

надоело с упоротым спорить... уже компиллятор у него типы переменных изменяет в зависимости от действий с этими переменными.

GarryC
Offline
Зарегистрирован: 08.08.2016

Надоело спорить с упоротым, у которого компилятор изменяет типы переменных в зависимости от полученных значений - К&R для понимания.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

Надоело спорить с упоротым, у которого компилятор изменяет типы переменных в зависимости от полученных значений - К&R для понимания.

ну, хватит уже...

формула (Текущее-Начальное)>Задержка содержит три переменных: Текущее, Начальное, Задержка.

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

 

 

GarryC
Offline
Зарегистрирован: 08.08.2016

Никогда я не был столь низкого мнения о своих умственных способностях, как в эти 10 минут. Как же так я очевидно вижу ошибку, понимаю причину ее возникновения и методы устранения и вдруг оказывается, что все это мираж и я не понимаю как работает компилятор языка С. Ведь человек, которому я на ошибку указал, проверил свою программу и убедился в том, что я не прав и никакой ошибки нет. Конечно же, я не тестировал его программу и мое предсталение о наличии ошибки было вызвано исключительно моим пониманием и мысленным экспериментом. Неужели старческий маразм наступил столь неожиданно, ведь вроде ничего не предвещало ... а до пенсии еще далеко и чем я буду зарабатывать на жизнь? Ведь я подробно разъяснил, в чем проблема, причем два раза, ведь не мог мой оппонент просто не понять о чем речь.

Но, собрав всю волю в кулак, решаюсь убедиться в своей умственной неполноценности, переношу предложенный скетч в транслятор и запускаю и ... о ужас ... все действительно работает, хотя я продолжаю считать, что работать не должно. На последних силах начинаю смотреть код и ... забрезжила надежда. Автор почему то дайствительно НИЧЕГО не понял из моих указаний на ошибку и проверяет СОВСЕМ не то, что надо. Слегка модифицируем код и обнаруживаем, что прав таки я и ошибка имеет место быть. Для этого достаточно модифицировать действия запрещенной а РФ организации и в строке 25 поставить
if (currentMillis !=127) { ...
и все становится на свои места. Строку 23 можно и оставить, пропуск срабатывания таймера не влияе ни на что, то есть совсем. Проведение эксперимента оставляем заинтересованному читателю.

Ну и теперь немного выводов:
Смешной Вы парень. Вы бы хоть попробовали запустить мой код с этим интервалом! - нет, не пробовал, грешен.
Ну, ведь я Вам говорю - там трюк-фокус, Вы его не понимаете. - ну не дано мне понимание, как -1 станет >=127 для int8_t.
Ну, вот честное слово, на Вашем месте, прежде чем постить такой пост, я бы проверил, а вдруг там и впрямь фокус, чем чёрт не шутит. - я верю в математику, иногда излишне, но не в таком случае
Проверил бы просто на всякий случай, чтобы не сесть в лужу. - ну верю я в математику.
Но Вы, похоже, любите там сидеть. - оставим без комментариев, Вам виднее
Что, ж извольте заплатить за излишнюю самоуверенность ... - итак, карты на стол, у меня флэш рояль, у Вас что? 
Вы согласны с тем, что правильный результат получен? - НЕТ
Вы согласны с тем, что это тот самый скетч, что был опубликован ранее, и я не жульничаю? - ДА
Я Вас уже обязал? Или пока ещё не очень? - НЕТ
А вот что касается первой части Вашей просьбы - "внятно объяснить" - увольте. - увольняю, нельзя объяснить необъяснимое. Заниматься Вашим обучением я готов только на коммерческой основе. - бесконечно благодарен, но воздержусь.
Я и так уже подсказал Вам в какой именно строке фокус, разумному достаточно - сам разберётся. - именно в ней, о как.
Но Вы, в своей самоуверенности, даже не потрудились проверить как оно работает. - да я положился на свой разум, рад, что он меня не подвел.
Нам с Вами действительно не особо есть о чём разговаривать, т.к. мы в разных весовых категориях. - согласен.
У нас разный уровень знаний и понимания. - совершенно согласен.
Надеюсь, сейчас-то Вы уже согласны с этим? - я уже согласился, но сделаю это еще раз.
А с людьми, которые всё знают я как-то стараюсь дискутировать поменьше. - и правильно делаете, как показал этот несложный пример.
 

 

GarryC
Offline
Зарегистрирован: 08.08.2016

То есть Вы сделали рекомендованные мною исправления с целью продемонстрировать ошибку и у Вас по прежнему все работает?
Я то, устыженный Вашими поучениями, это сделал, чтобы не "сесть в лужу" и так далее по тексту. А Вы это сделали? У меня сильное подозрение, что нет.

Если да, ну значит, у нас разные компиляторы, разные языки С и разные вселенные. В Вашей условие ((127>0) && (-1>127)) может сработать если тремя строками выше применить хитрый фокус-покус, в моей это условие не срабатывает никогда, от слова совсем. Хотя, может быть в Вашей вселенной при определенных условиях может сработать (127<0) && (dd>=0) если dd посчитать некоторым хитрым способом, у меня так не получается.

"Вы, знаете, просто признать свою ошибку - это совсем не стыдно и не плохо. А вот вертеться, как Вы это делаете ... не знаю, ну если Вам нравится быть посмешищем...." - золотые слова, жаль что не по адресу, так что не "прекращайте юродствовать".

 

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

В общем, мне это надоело. Я потёр свои посты и прекращаю эту дискуссию. Всего хорошего!

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

"Вы, знаете, просто признать свою ошибку - это совсем не стыдно и не плохо. А вот вертеться, как Вы это делаете ... не знаю, ну если Вам нравится быть посмешищем...." - золотые слова, жаль что не по адресу, так что не "прекращайте юродствовать".

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

millis() возвращает Количество миллисекунд с момента начала выполнения программы. (unsigned long)

каким здесь боком знаковые типы переменных?

то, что тебе пытались объяснить, что если очень нужно, то можно делать трюки с типами переменных, но объясни каким боком здесь millis() ?

GarryC
Offline
Зарегистрирован: 08.08.2016

"Я потёр свои посты и прекращаю эту дискуссию" - интересный способ признать свою ошибку, жаль, что Вы прибегнули именно к такому.

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

Скорее ЕвгенийП осознал свою ошибку. Надо было прямо вам сказать- Мне не интересует мнение первоклассника о таблице умножения. Вот он на вас потратил очень много времени. И это было его ошибкой.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

GarryC пишет:

"Я потёр свои посты и прекращаю эту дискуссию" - интересный способ признать свою ошибку, жаль, что Вы прибегнули именно к такому.

ты лучше объясни читателям темы, зачем ты набредил про знаковые переменные, придумал проблему и всех победил в войне с несуществующим врагом?

Клапауций 232 пишет:

GarryC пишет:

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

как может исчезнуть то, чего нет?

GarryC
Offline
Зарегистрирован: 08.08.2016

Надеюсь, Вы спросили именно для того, чтобы получить ответ.

Объясняю еще раз. Да, Вы совершенно правы, millis() возвращет тип unsigned long. Используя этот факт, мы можем ждать окончание задержки так, как это принять в Ардуино, то есть запомнить начало отсчета и ждать выполнения условия
(millis()-Начало)>=Задержка
При этом все работает из коробки, никаких других типов не надо, все переменные типа unsigned long, не нужно учитывать переполнение, все хорошо.

Но для вычислений по этой формуле Вам надо знать 2 переменные - Начало и Задержка. Если у вас каждый модуль работает только за себя, то он их знает по умолчанию. Но в Unix системах приняты очереди таймеров (так они называют задержки), когда Вы заказываете задержку и назначаете функцию обработки, которая будет вызвана в свое время. Кстати, такой способ можно реализовать и в Ардуино, и иногда он может быть удобным. При указанном способе ожидания задержки Вам придется хранить 2 переменные в очереди задержек - это и есть недостаток данного способа. Поэтому здесь принят другой способ хранения информации о требуемом моменте времени, а именно при запуске задержки вычисляется unsigned long Окончание=millis()+Задержка и условие изменяется
(millis()-Окончание)>0, но для беззнакового типа данное выражение бессмысленно, поскольку любое беззнековое число больше нуля,  и мы вынуждены сделать приведение
((long)millis()-(long)Окончание)>0. Здесь тоже все работает и можно не беспокоиться о переполнениях, но, если внимательно проанализировать, мы не можем заказать интервал более половины диапазона, потому что он отработается мгновенно. При этом, как Вы видите, мы по прежнему используем millis() с его возвращаемым типом, но для нашего удобства явно его приводим.

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