/*
Measuring a Noisy Signal using CHANGE Mode Interrupt
David Lloyd, May 2023.
*/
const uint8_t inputPin = 2;
const uint8_t bufferSize = 8;
const uint16_t stablePeriod = 120;
float bufferA[bufferSize], bufferB[bufferSize];
uint32_t pulsePeriod, pulseWidth, t00, t11, t22;
volatile uint32_t us, stableUs, t0, t1, t2; // isr
uint8_t inputLevel;
bool ready;
float hz, duty, rpm;
// functions
void input_ISR();
float smoothA(float val);
float smoothB(float val);
bool timer(uint32_t ms);
void setup() {
Serial.begin(115200);
pinMode(inputPin, INPUT); //Interrupt
attachInterrupt(digitalPinToInterrupt(inputPin), input_ISR, CHANGE);
}
void loop() {
if (timer(50)) {
if (ready) {
noInterrupts();
t22 = t2;
t11 = t1;
t00 = t0;
t2 = 0;
t1 = 0;
t0 = 0;
ready = false;
interrupts();
pulsePeriod = t22 - t00;
pulseWidth = t11 - t00;
duty = 100.0 * pulseWidth / pulsePeriod;
duty = smoothA(duty);
hz = 1000000.0 / pulsePeriod;
hz = smoothB(hz);
rpm = hz * 60.0;
Serial.print(" Duty = ");
Serial.print(duty, 0);
Serial.print(" Hz = ");
Serial.print(hz, 0);
Serial.print(" RPM = ");
Serial.print(rpm, 0);
Serial.println();
}
}
}
void input_ISR() {
us = micros();
if (us - stableUs > stablePeriod) {
inputLevel = digitalRead(inputPin);
if (inputLevel == HIGH) {
if (!t0 && !t1 && !t2) t0 = us;
else {
if (t0 && t1 && !t2) {
t2 = us;
ready = true;
}
}
} else {
if (t0 && !t1 && !t2) t1 = us;
}
}
stableUs = us;
}
float smoothA(float val) {
float dataSum = 0.0;
float dataAverage = 0.0;
static uint8_t currentIndex = 0;
bufferA[currentIndex] = val;
currentIndex = (currentIndex + 1) % bufferSize;
for (uint8_t j = 0; j < bufferSize; j++) dataSum += bufferA[j];
return dataAverage = dataSum / (float)bufferSize;
}
float smoothB(float val) {
float dataSum = 0.0;
float dataAverage = 0.0;
static uint8_t currentIndex = 0;
bufferB[currentIndex] = val;
currentIndex = (currentIndex + 1) % bufferSize;
for (uint8_t j = 0; j < bufferSize; j++) dataSum += bufferB[j];
return dataAverage = dataSum / (float)bufferSize;
}
bool timer(uint32_t ms) {
volatile uint32_t prevMs, now;
now = millis();
if ((now - prevMs) >= ms) {
prevMs = now;
return true;
}
return false;
}