Rolaro:
Yes, thanks, with that i can detect the change of flank, the problem is that micros() function can be too slow to read the duty cicle with high accuracy.
Hi Rolaro,
You are correct = the function micros() always comes back with a value that is a multiple of 4! This imposes a limit of 250KHz on even a hypothetical no-time sketch. But of course the sketch time adds significantly to the overhead. The maximum frequency and duty cycle precision are adversely affected.
However, this code is not ideal. It spends way too much time in the interrupt service routine! Plus it accesses three volatile variables. Volatile variables are read/written slower than non-volatile variables. ISR code can access byte sized arguments (aka "atomic" variables) in a single cycle, so they need not be declared volatile, as they can't change mid-cycle.
The ISR should be super simple. Like set a single atomic variable and exit. Do everything else in the loop() function. The following code does this and reads a frequency on pin 2 (and ground!) up to 13KHz.
// UNO, pwm signal to pin 2 + common ground
// 050518 clh 2630/252 show frequency and duty cycle
const byte inputPin = 2; // INT.0
boolean inputState = false;
boolean lastInputState = false;
unsigned long pulseLength = 0;
unsigned long lastPulseLength = 0;
unsigned long startMicros;
unsigned long timeBetween = 0;
unsigned long lastTimeBetween = 0;
unsigned long stopMicros;
unsigned long previousDisplayMillis = millis();
long displayMillis = 1000L;
void setup() {
pinMode(inputPin, INPUT_PULLUP); // pin <-- 100R <-- vibePin
Serial.begin(115200);
attachInterrupt(digitalPinToInterrupt(inputPin), setInputState, CHANGE);
}
void setInputState() {
// inputState = digitalRead(inputPin);
inputState = PIND & 0b0100;
}
void loop() {
if (inputState != lastInputState) {
if (pulseDone()) {
lastPulseLength = pulseLength;
} else {
lastTimeBetween = timeBetween;
}
lastInputState = inputState;
}
// --------- Run this code every second ------------------
if (millis() - previousDisplayMillis >= displayMillis) {
previousDisplayMillis += displayMillis;
unsigned long period = (lastPulseLength + lastTimeBetween);
long frequency = 1000000L / period;
byte dutyCycle = ((100L * lastPulseLength) / period) + 1;
Serial.print("Frequency: ");
Serial.print(frequency);
Serial.print(" Hz | Duty Cycle: ");
Serial.print(dutyCycle);
Serial.println('%');
}
}
// If pulseDone() is true, the variable pulseLength is valid.
// If false, then timeBetween is valid, but pulseLength is 0.
boolean pulseDone () {
unsigned long nowMicros = micros(); // resolution of micros() is 4µs, so *
if (inputState) { // rising edge of the pulse.
startMicros = nowMicros; // start the microseconds clock
pulseLength = 0UL;
timeBetween = (nowMicros - stopMicros);
return false;
}
else { // falling edge - stop clock
stopMicros = nowMicros; // start the microseconds clock
timeBetween = 0UL;
pulseLength = (nowMicros - startMicros);
return true;
}
}