Arduino застрял в PT_WAIT_UNTIL

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

lastTimeBlink = millis();                                       
PT_WAIT_UNTIL(pt, millis() - lastTimeBlink > 1000);

Почему он там застревает? Весь код загружается сюда: https://pastebin.com/jRsuvn54

(Комментарии на немецком языке)

, 👍0

Обсуждение

На самом деле не так уж полезно включать только короткий фрагмент кода для решения подобной проблемы., @jwh20

Я добавил весь код целиком, @hypertext

Примеры библиотеки Protothread, похоже, используют для этого таймер., @Gerben


1 ответ


Лучший ответ:

1

Изо всех сил старайтесь дать что-то вроде ответа.

Вы сказали:

PT_WAIT_UNTIL(pt, millis() - lastTimeBlink> 1000);

Это не буквально представлено в вашем коде, поэтому я беру ваш вопрос для ссылки :

int dist2 = dist1.toInt();
...
PT_WAIT_UNTIL(pt, millis() - lastTimeBlink > (dist2 * 1710));

или аналогичный раздел.

Когда вы выполняете этот PT_WAIT_UNTIL, dist2 содержит любое значение, которое вы инициализировали в int dist2 = dist1.toInt() ; Предполагая, что условие не выполнено, вы выходите из runCommand() с помощью PT_WAIT_UNTIL,возвращающего PT_WAITING ;

#define PT_WAIT_UNTIL(pt, condition) \
  do {                               \
    LC_SET((pt)->lc);                \
    if(!(condition)) {               \
      return PT_WAITING;             \
    }                                \
  } while(0)

LC_SET здесь, в PT_WAIT_UNTIL , используется для создания метки, к которой можно перейти позже:

#define LC_SET(s)                                \
   do {                                          \
     LC_CONCAT(LC_LABEL, __LINE__):              \
     (s) = &&LC_CONCAT(LC_LABEL, __LINE__);      \
   } while(0)

Позже, когда вы позже вызываете runCommand для возобновления потока, вы выполняете LC_RESUME через PT_BEGIN(pt);

#define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc)
...
#define LC_RESUME(s)        \
  do {                      \
    if(s != NULL) {         \
      goto *s;              \
    }                       \
  } while(0)

goto *s здесь в конечном счете переходит к метке, созданной с помощью PT_WAIT_UNTIL.

Теперь вы вернулись в PT_WAIT_UNTIL, проверяя условие строкой if(!(condition)). То есть вы проверяете millis() - lastTimeBlink> (dist2 * 1710) Однако то, что вы, вероятно (подробнее об этом ниже) делаете здесь, - это чтение мусора из стека. dist2 был инициализирован при предыдущем выполнении runCommand, и его эффективное время жизни закончилось, когда вы вернулись в PT_WAIT_UNTIL при этом предыдущем выполнении. При этом новом выполнении dist2 неинициализируется, потому что вы перепрыгнули через его инициализацию. Если вы включите уровень предупреждения в файле / Настройках, вы должны увидеть что-то для этого.

В этот момент может произойти множество вещей, большинство из которых не очень хороши. Я не могу точно сказать, что оптимизатор компилятора сделает с этим кодом, но меня это не сильно удивило бы. Вполне вероятно, просто знакомясь с оптимизирующими компиляторами и тем, как выглядит ваш код, что он генерирует код для чтения dist2. Это может звучать как данность, но это не так, когда вы делаете что-то неопределенное. Возможно, dist2 имеет значение из предыдущего запуска, когда вы выполняете эту функцию в цикле. Но он также может иметь значение, которое заставляет dist2 * 1710 оценивать продолжительность почти до 50 дней, заставляет PT_WAIT_UNTIL возвращаться из runCommand до тех пор, пока вы тестируете.

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

,

Причина, по которой я поставил 1000 вместо (dist2* 1710) в вопросе, заключается просто в том, что это не работает в любом случае. Вычисление (dist2 * 1710) работает отлично. С этим нет никаких проблем., @hypertext

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

В любом случае спасибо, так как ваш ответ помог мне понять это., @hypertext

Я ценю, что это помогло, но если вы обнаружите, что проблема на самом деле заключается в чем-то другом, вы можете написать свой собственный ответ на вашу проблему и отметить * это * как принятое. Я думаю, что через два дня вы сможете сами отметить свои ответы как принятые. Вы можете проголосовать за то, чтобы этот вопрос был полезным, не объявляя его * ответом *., @timemage