Проблема конечного автомата
Я пытаюсь записать импульсы от датчика расхода воды с помощью конечного автомата. Моя цель-считать импульсы до тех пор, пока поток не остановится (т. Е. Ротор внутри не остановится). Это будет представлять собой единое чтение. если через 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();
}
@Zaffresky, 👍0
Обсуждение1 ответ
Лучший ответ:
Обычно я делаю это без дополнительной переменной состояния.
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
- Почему необходимо использовать ключевое слово volatile для глобальных переменных при обработке прерываний в ардуино?
- Какие Arduino поддерживают ATOMIC_BLOCK?
- Прерывания внутри класса, связанные с функцией класса
- Альтернатива опросу флага прерывания из основного цикла?
- Передать шаблонную функцию в качестве параметра attachInterrupt
- Используйте ISR внутри библиотеки более элегантно
- Анимация светодиодной ленты с ESP32 не работает при использовании WiFi
- Последующий вопрос о кнопке и переключателе
Счетчик импульсов сбрасывается через 5 секунд
... не сбрасывайте счетчик ... см. Как используется millis() , @jsotolaесть ли у вас менее 255 показаний в одной партии? вы используете "байт", @Juraj
Код будет иметь гораздо лучшую читабельность, если вы используете makros или enum для состояния, так что вы можете иметь что-то вроде
case RESET:
..., @Sim SonУ вас должен быть весь код, относящийся к конечному автомату, в блоке коммутатора.
TelnetStream.println()
может быть в порядке в thw " loop ()", но "pulseCountNew=0" должно быть в " case 4`., @Sim SontriggerCounter
объявляется какbyte
, поэтому он не может стать отрицательным. ОператорыtriggerCounter--
иif(triggerCounter>0)
подвержены ошибкам и, вероятно, делают не то, что вы ожидаете., @Sim Son@SimSon Я не знал о перечислении, но вы правы. Нужно изучить его использование, @Zaffresky
@Juraj Я полагаю, вы говорите о том, что счетчик триггера объявляется как байт типа. Поскольку в состоянии 2 триггер будет уменьшен, я думал, что счетчик не достигнет 255. Но, думая об этом сейчас, я предполагаю, что это может произойти, @Zaffresky
да, всегда можно взять значение, добавить его к итогу и сбросить. Я не знаю, как быстро увеличивается значение в вашем проекте. использование байта хорошо, потому что операции являются атомарными и не требуют блокировки прерываний. но использование большего типа позволяет проводить больше времени в цикле, не проверяя счетчик, и это помогает, если вы делаете сеть., @Juraj