Проблема конечного автомата

Я пытаюсь записать импульсы от датчика расхода воды с помощью конечного автомата. Моя цель-считать импульсы до тех пор, пока поток не остановится (т. Е. Ротор внутри не остановится). Это будет представлять собой единое чтение. если через 5 секунд после остановки ротора нет нового импульса/прерывания, выводится общее количество импульсов и счетчик импульсов сбрасывается на ноль для последующих показаний.

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

Не могли бы вы помочь мне понять, что я делаю не так?

#include <Arduino.h>
#include <TelnetStream.h>

const byte pulsePin = 22;
byte volatile triggerCounter = 0;

byte newState = 0;

unsigned long newTime = 0;
unsigned long oldTime = 0;

unsigned long pulseCountNew = 0;

portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;

void IRAM_ATTR handleInterrupt()
{
  portENTER_CRITICAL_ISR(&mux);
  triggerCounter++;
  portEXIT_CRITICAL_ISR(&mux);
}

void stateMachine()
{

  switch (newState)
  {

  case 0 /* Reset */:
    newState = 1;

    break;

  case 1 /* Start */:

    if (triggerCounter > 0)
    {
      newState = 2;
    }
    break;

  case 2 /* Pulse count start */:

    pulseCountNew++;
    triggerCounter--;
    newTime = millis();
    newState = 3;

    break;

  case 3 /* Pulse count stop */:

    if (triggerCounter > 0)
    {
      newState = 2;
    }

    if (newTime - oldTime >= 5000)
    {
      oldTime = newTime;
      newState = 4;
    }
    break;

  case 4 /* Cycle complete */:
    newState = 0;
    TelnetStream.println("State 4");

    break;
  }
}

void setup()
{
  pinMode(pulsePin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(pulsePin), handleInterrupt, FALLING);
  TelnetStream.begin();
}

void loop()
{

  if (newState == 4)
  {

    TelnetStream.println(pulseCountNew);
    pulseCountNew = 0;
  }

  stateMachine();
}

, 👍0

Обсуждение

Счетчик импульсов сбрасывается через 5 секунд ... не сбрасывайте счетчик ... см. Как используется millis() , @jsotola

есть ли у вас менее 255 показаний в одной партии? вы используете "байт", @Juraj

Код будет иметь гораздо лучшую читабельность, если вы используете makros или enum для состояния, так что вы можете иметь что-то вроде case RESET:..., @Sim Son

У вас должен быть весь код, относящийся к конечному автомату, в блоке коммутатора. TelnetStream.println() может быть в порядке в thw " loop ()", но "pulseCountNew=0" должно быть в " case 4`., @Sim Son

triggerCounter объявляется как byte, поэтому он не может стать отрицательным. Операторы triggerCounter-- и if(triggerCounter>0) подвержены ошибкам и, вероятно, делают не то, что вы ожидаете., @Sim Son

@SimSon Я не знал о перечислении, но вы правы. Нужно изучить его использование, @Zaffresky

@Juraj Я полагаю, вы говорите о том, что счетчик триггера объявляется как байт типа. Поскольку в состоянии 2 триггер будет уменьшен, я думал, что счетчик не достигнет 255. Но, думая об этом сейчас, я предполагаю, что это может произойти, @Zaffresky

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


1 ответ


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

0

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

unsigned long readSensor() {
  const unsigned long READING_TIMEOUT_MILLIS = 5000;
  static unsigned long lastReadingMillis;
  static unsigned long lastCounterValue;

  noInterrupts();
  unsigned long counter = triggerCounter;
  interrupts();

  if (counter != lastCounterValue) { // если новое чтение
    lastReadingMillis = millis(); // сброс таймера таймаута
    lastCounterValue = counter; 
  } else if (counter > 0 && (millis() - lastReadingMillis) > READING_TIMEOUT_MILLIS) { // если что-то было подсчитано и тайм-аут
    noInterrupts();
    triggerCounter = 0;
    interrupts();
    lastCounterValue = 0;
    lastReadingMillis = 0;
    return counter;
  }
  return 0;
}

void loop() {
  unsigned long value = readSensor();
  if (value) {
    Serial.println(value);
  }
}

к сожалению, код не тестируется.

,

Я думал, что использование простого конечного автомата будет проще, чем использование нескольких условных операторов. Однако ваш пример гораздо более компактен., @Zaffresky