# Reading consecutive two pulse periods continuously

I'm controlling the DC motor/fan speed by PWM through varying analog input. Using Uno or Nano.

The loop is as follows:

``````void loop()
{
analogWrite(pwmPin9, map(val, 0, 1024, 0 , 255));
}
``````

Above analogPin0 value is varied by a poti.

But I also want to output the rpm on an LCD. To do that I need to measure the period between two consecutive pulses as shown below:

How could I measure continuously "period between two consecutive pulses"? Should I use interrupt? Does anybody have any experience with that?

• About what time range are we speaking here for the period? Jul 23, 2019 at 11:30
• One pulse period from minimum 4ms up to 100ms Jul 23, 2019 at 11:35
• why do you need to measure the signal? ... you know exactly the value of the parameter that you are passing to the analogWrite() function Jul 23, 2019 at 17:36
• I dont know the exact rpm just the duty cycle Jul 23, 2019 at 17:57
• did you forget to tell us about a revolution sensor? ... it is unclear how you expect to determine the fan RPM from the pulse width of the power supply voltage Jul 23, 2019 at 19:27

I see 2 ways to go here:

1. You could attach a simple interrupt for rising edge to the pin with `attachInterrupt()` and count the rising edges with a variable. If the third rising edge is sensed, you have 1 period. To measure the time you can save the starting time of the first rising edge to a variable with `micros()` (which gives you the time since startup in microseconds). At the third rising edge save the time difference between start and end to another variable (declared volatile): `micros()-start_time`. This variable can then be displayed in your `loop()` function. Be sure to only copy the variable to a local variable in this part and deactivate interrupts during this operation with `noInterrupts()` (reactivate interrupts after that with `interrupts()`. Also you should use an atomic flag variable (atomic meaning, that writing or reading this variable cannot be interrupted. For example if you use `byte` or any other 1byte variable) to show the `loop()` function, that there is new data to display. Only if that's the case, the code in the `loop()` function would process the data.

2. If you want to go fancy or if you want to measure higher speeds (lower periods), You can use the hardware of Timer1 as counter. You give the Timer1 your signal as clock input. On every edge the timer value get's incremented by 1. So when the timer value is 5, one period has passed. You can use the timer in compare match mode, so that it will generate an interrupt, if a special value (here 5) is in the timers value register. In that ISR (Interrupt Service Routine) you would record the current time with `micros()`. On compare match the timer will generate the interrupt, set the value to zero again (if configured right) and again start to count. So the period would be the time between 2 consecutive time measurements. Displaying in the `loop()` function can be handled similar to the principle above.

It might also be good to directly take a mean of the period (to filter out noise in the period) by counting, until multiple periods have gone by. In the first case you could count to 11 to get 5 periods. Then divide the time difference by 5 to get 1 period. For the second case you can set the Timer1 match register to 21 to get 5 periods. Here also divide the time by 5 to get 1 period.

Since you seem to be a beginner, I would suggest the first way: Using `attachInterrupt()`. The second way is more if you want to dig into the microcontrollers registers and the descriptions in the datasheet.

The first way could look something like this:

``````const int pin = 2;

volatile byte counter = 0;
volatile unsigned long start_time;
volatile unsigned long period;
volatile byte data_flag = 0;

void speed_ISR(){
counter++;
if(counter==3){
unsigned long now = micros();
period = now - start_time;
data_flag = 1;
start_time = now;
counter = 1;
}
}

void setup(){
pinMode(pin, INPUT);
attachInterrupt(digitalPinToInterrupt(pin), speed_ISR, RISING);
}

void loop(){
if(data_flag){
noInterrupts();
unsigned long local_period = period;
data_flag = 0;
interrupts();
// Now display the period on the LCD her
} else {
// Print zero to the LCD
}
}
``````

The code gives you the opportunity to write zero to the LCD, when there is no pulse input (hence no data --> data_flag has to be zero). This may be uncomfortable to read on low pulse frequencies, if the LCD doesn't need longer to display the results as the next pulse will be measured, as it might display zero and the next measurement in fast succession. You could either use `millis()` to set `data_flag` to zero after a specified time (instead of resetting it near the LCD code), or you can limit the display rate like the blinking is done in the `BlinkWithoutDelay` example, that comes with the Arduino IDE.

Note that this code is not tested.

• I get an error as : expected primary expression before "==" token. this is at the line if(byte==3){ Jul 23, 2019 at 12:40
• will byte be counter? Jul 23, 2019 at 12:41
• And also having "'NoInterrupts' was not declared in this scope " Jul 23, 2019 at 12:45
• Ok, typing errors. Yes, the if statement in the ISR should be with counter. Also it is `noInterrupts()`, not `notInterrupts`. Sorry, I have changed my answer Jul 23, 2019 at 12:47
• And there are missing 2 semicolons in the ISR. I hope now it is structually sound Jul 23, 2019 at 12:48

An interrupt off of one of the timers sounds like the easiest way to go. To get a measurement precision of, say 3% at 4ms means sampling every (4000us * .03) = 120us (8.333 KHz). If your code has little else to do, you might get away with clock-watching - `micros()` ticks every 4us - but you'll need to keep anything else you do between reads of `micros()` to under 120us, and half of that might be a good target figure.

In this answer I described a general non-blocking procedure for managing several tasks in a timely way. If you chose such a design, one task might manage the potentiometer and analog output, another one the RPM measurement, and another the LCD. The ISR would do nothing but set a flag.

The RPM & LCD tasks can run at a relatively low rate as they're intended for human consumption only. They may have to work piece-wise, though, to meet their time restrictions.