Hi arrg, and Deva,
the input-signal which triggers the interrupt is a 50% duty-cycle with 20-30 Hz.
the complete project is a ESP8266 nodeMCU-board working as a control-unit for a ventilator where the rpm can be controlled by a PWM-signal. The ventilator has a tachometer-output one pulse per revolution.
The complete project includes send/receive with ESP-Now and a 128x32 I2C-OLED-display.
I have stripped down the code to the bare minimum ISR and a loop that does report the measured rpm to the serial port with some other values.
In this Version it works as expected. toggling the test-out-put-pin follows the inputsignal.
As the rpm is pretty low I coded two statemachines one inside the isr and one inside loop()
to make rpm-measurement work this way:
the statemachine inside loop() initiates measuring rpm through setting the isr_state to ISR_Start_Counting
so isr starts measuring with a snapshot of micros()
and continues to count up pulses until loop-statemachine sets a flag StopCounting to initiate finishing counting
the isr will do a last count and store a second snapshot of micros()
and then signaling counting has finished
Doing it this way I get a more exact time-difference how long it took to count the pulses.
There seems to be another bug in it.
In some cases it reports double the frequency. I guess this is through "bad timing" between loop() and isr
I'm not fixed to this solution. If anybody has a different solution that can measure low frequencies as 10 to 30 Hz with high precisision I'm opened to it. Maybe set/reset any of the flags just need to be moved "a little bit" but I don't know.
here is the testcode
unsigned long RPM_Timer;
const int PWM_PIN = 5; //D1
const int RPM_pulses_InputPin = 12; //D6
const int TestPin = 14; //D5
const byte RPM_Start_Counting = 1;
const byte RPM_Wait_for_finished = 2;
const byte RPM_finished = 3;
volatile byte RPM_Timer_States = RPM_Start_Counting;
const byte ISR_Wait_for_Start = 1;
const byte ISR_Start_Counting = 2;
const byte ISR_Count = 3;
const byte ISR_Counting_finished = 4;
volatile byte ISR_Count_States = ISR_Wait_for_Start;
volatile int IsrCount;
int MainCount;
volatile unsigned long StartTime;
volatile unsigned long EndTime;
volatile boolean Finished = true;
volatile boolean StopCounting = false;
volatile unsigned long EnterISR;
volatile unsigned long LeaveISR;
volatile unsigned long ISR_ExecutionTime;
float factor_per_minute;
int TimerNr = 0;
void IRAM_ATTR isr_Count_TimeStamps() { //xxy
EnterISR = micros();
noInterrupts();
digitalWrite(TestPin,!digitalRead(TestPin));
switch (ISR_Count_States) {
case ISR_Wait_for_Start:
//IsrCount = 0;
break;
case ISR_Start_Counting:
StartTime = micros();
IsrCount = 1;
ISR_Count_States = ISR_Count;
break;
case ISR_Count:
IsrCount++;
if (StopCounting) {
ISR_Count_States = ISR_Counting_finished;
}
break;
case ISR_Counting_finished:
EndTime = micros();
//IsrCount++;
Finished = true;
StopCounting = false;
ISR_Count_States = ISR_Wait_for_Start;
break;
}
LeaveISR = micros();
ISR_ExecutionTime = LeaveISR - EnterISR;
}
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - expireTime >= TimePeriod )
{
expireTime = currentMillis; // set new expireTime
return true; // more time than TimePeriod) has elapsed since last time if-condition was true
}
else return false; // not expired
}
void setup() {
Serial.begin(74880); // reset-message is sent in 74880 baud
Serial.println();
Serial.println("setup() start");
pinMode(TestPin, OUTPUT);
digitalWrite(TestPin,LOW);
//pinMode(RPM_pulses_InputPin, INPUT_PULLUP);
pinMode(RPM_pulses_InputPin, INPUT);
attachInterrupt(RPM_pulses_InputPin, isr_Count_TimeStamps, FALLING);
pinMode(PWM_PIN,OUTPUT);
}
void loop() {
if (TimePeriodIsOver(RPM_Timer,1000) ) {
// remember this switch-case is only called once
// every period of time set in the last paremater of
// the if-condition
switch (RPM_Timer_States) { //xxy
case RPM_Start_Counting:
if (ISR_Count_States == ISR_Wait_for_Start) {
RPM_Timer_States = RPM_Wait_for_finished;
ISR_Count_States = ISR_Start_Counting;
break;
}
case RPM_Wait_for_finished:
StopCounting = true;
if (Finished) {
StopCounting = false;
factor_per_minute = IsrCount * 1000000;
Serial.print(" factor_per_minute = IsrCount * 1000000;:");
Serial.print(factor_per_minute);
factor_per_minute = factor_per_minute / (EndTime - StartTime) ;
Serial.print(" EndTime - StartTime:");
Serial.print(EndTime - StartTime);
Serial.print(" factor_per_minute:");
Serial.println(factor_per_minute);
RPM_Timer_States = RPM_finished;
break;
}
case RPM_finished:
Finished = false;
RPM_Timer_States = RPM_Start_Counting;
ISR_Count_States = ISR_Wait_for_Start;
break;
}
Serial.print("TimerNr:");
Serial.print(TimerNr++);
Serial.print(" ISR_ExecutionTime:");
Serial.print(ISR_ExecutionTime);
Serial.print(" RPM_Timer_States:");
Serial.print(RPM_Timer_States);
Serial.print(" ISR ISR_Count_States:");
Serial.print(ISR_Count_States);
if (Finished) {
Serial.print(" Finished TRUE");
}
else {
Serial.print(" Finished false");
}
Serial.print(" IsrCount:");
Serial.print(IsrCount);
Serial.println();
}
}
About the short pulses with the testpin:
Next step is to obey my always quoted rule: add one thing at a time to see when does the quick switch-back to other output-level happen.
best regards Stefan