honey the codewitch Ответов: 2

У меня возникла проблема с техникой тайм-аута, которая по какой-то причине не работает


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

То, что я делаю, - это использование millis() который возвращает количество миллисекунд, прошедших с момента загрузки машины. Я сохраняю это в метке времени, а затем в своем loop() функция, которая вызывается для каждой итерации плотного цикла, процессор вращается для меня в основном я вижу, если число разницы между настоящим моментом и моментом, когда была взята временная метка, составляет более 1000 миллисекунд (1 секунда)

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

Может ли кто-нибудь сказать мне, где я ошибся?

(См. раздел "Что вы пробовали?" Раздел для получения дополнительной информации)

Что я уже пробовал:

uint32_t _timestamp;

// gets called when wire is touched
void callback() {
  // millis() won't advance in interrupt routines
  _timestamp = 0; // reset the timeout
  // equiv of _timestamp=millis();
  Serial.println("Callback");

}

// runs once on CPU startup
void setup() {
  Serial.begin(115200);

  // attach interrupt on GPIO15 touchpad
  // with to 40 sensitivity. callback()
  // gets fired when wire is touched
  touchAttachInterrupt(T3, callback, 40);
  // reset the timeout
  _timestamp = millis();
}

// called for each iteration of the CPU's loop
void loop() {
  // for callback():
  if (!_timestamp)
    _timestamp = millis();

  // if more than a second has elapsed write it
  if (millis() - _timestamp > 1000) {
    // and reset the timestamp
    _timestamp = millis();
    Serial.println("Time elapsed");
  }
}


Выше я выделил жирным шрифтом критическую линию, которая, похоже, не работает. Я также пробовал делать _timestamp volatile но безрезультатно.

Последовательный выход (с метками времени) выглядит следующим образом


Это дает
15:29:56.544 -> Time elapsed
15:29:57.537 -> Time elapsed
...
(i'm pressing the touch sensor wire now)
15:29:59.755 -> Callback
15:29:59.755 -> Time elapsed
15:29:59.788 -> Callback
15:29:59.788 -> Time elapsed
15:29:59.821 -> Callback
....
(i've released the touch sensor wire)
15:30:00.186 -> Time elapsed
15:30:01.179 -> Time elapsed
...


Я выделил жирным шрифтом некоторые фрагменты, чтобы подчеркнуть, как быстро это происходит.

но в любом случае я ожидаю чего-то подобного
Time elapsed
Time elapsed 
Time elapsed
(I've held the wire)
Callback
Callback
Callback
Callback
(I've released the wire)
Time elapsed 
Time elapsed

Rick York

Это однопоточный процесс? У меня сложилось впечатление, что это так, но я хотел проверить.

honey the codewitch

Эээ, ишь? Насколько я понимаю, весь код, кажется, чтобы быть в одном потоке, и все же вещь двухъядерный. Кроме того, было сказано, что мне нужно использовать volatile на глобальных vars, доступных в прерываниях, таких как callback, что предполагает какой-то параллельный доступ, я думаю. Я не отмечал _timestamp с volatile выше, но у меня нет никакого результата.

Rick York

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

markkuk

Вы можете попробовать официальный форум ESP32 Arduino: https://esp32.com/viewforum.php?f=19 для получения дополнительной экспертизы.

honey the codewitch

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

2 Ответов

Рейтинг:
18

markkuk

Попробовать это:

#include <atomic>

uint32_t _timestamp;
std::atomic_flag touched;

// gets called when wire is touched
void callback() {
  touched.clear(); // reset the timeout
  Serial.println("Callback");

}

// runs once on CPU startup
void setup() {
  Serial.begin(115200);
  touched.test_and_set();
  // attach interrupt on GPIO15 touchpad
  // with to 40 sensitivity. callback()
  // gets fired when wire is touched
  touchAttachInterrupt(T3, callback, 40);
  // reset the timeout
  _timestamp = millis();
}

// called for each iteration of the CPU's loop
void loop() {
  // for callback():
  if (!touched.test_and_set())
    _timestamp = millis();

  // if more than a second has elapsed write it
  if (millis() - _timestamp > 1000) {
    // and reset the timestamp
    _timestamp = millis();
    Serial.println("Time elapsed");
  }
}


CPallini

5.

honey the codewitch

Я не уверен, что стандартная библиотека доступна на DevKit arduino, но я попробую это сделать. Спасибо.

markkuk

Arduino для ESP32 работает поверх ESP-IDF, поэтому вы получаете компилятор G++, FreeRTOS и другие вещи, недоступные с традиционными платами Arduino на базе AVR.

honey the codewitch

Это здорово знать. Я все еще совершенно новичок в ESP32, а также довольно новичок в модулях Arduino и ESP8266, но до сих пор я, по крайней мере, построил несколько вещей с arduino и ESP8266s.

honey the codewitch

Блестяще! Большое спасибо!

Рейтинг:
0

CPallini

Цитата:
_timestamp = 0; // сброс таймаута
// экв на _timestamp=миллисе();
Ну, вы не сбросили тайм-аут при обратном вызове.
Это замечание неверно. На самом деле, правильный путь-это
_timestamp = millis();


Я действительно пропустил проверку в главном цикле.

Однако ваш код не обрабатывает должным образом возможность возврата процедуры прерывания после такая проверка проводится.


honey the codewitch

Прочитайте код и прочитайте комментарии в коде. millis() не работает в этой рутине

Если вы видите процедуру loop()

// for callback():
  if (!_timestamp)
    _timestamp = millis();


Это действительно сбрасывает тайм-аут. Комментарий правильный.

CPallini

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

honey the codewitch

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

Кроме того, ради этого вопроса рассмотрим код однопоточный.

markkuk

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

honey the codewitch

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

CPallini

Это предположение небезопасно, оно неверно, потому что вероятность того, что millis() будет больше 1000, очень высока.
Кстати, вы знаете, как работают прерывания? марккук правильно, прерывания могут произойти в любое время.

markkuk

Я думаю, что вы неправильно поняли документацию (https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/) , millis() не увеличивается, находясь внутри обратного вызова прерывания, но это не значит, что вы не можете вызвать его, просто он возвращает одно и то же значение, если вы вызываете millis() несколько раз во время одного вызова обратного вызова.

honey the codewitch

Ах да, я перечитал его, и вы правы. Тем не менее, код не должен навредить. 3-е решение было исправлено.