0

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()
{    
  val = analogRead(analogPin0);  // read the analog input pin            
  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:

enter image description here

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? – chrisl Jul 23 at 11:30
  • One pulse period from minimum 4ms up to 100ms – atmnt Jul 23 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 – jsotola Jul 23 at 17:36
  • I dont know the exact rpm just the duty cycle – atmnt Jul 23 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 – jsotola Jul 23 at 19:27
1

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){ – atmnt Jul 23 at 12:40
  • will byte be counter? – atmnt Jul 23 at 12:41
  • And also having "'NoInterrupts' was not declared in this scope " – atmnt Jul 23 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 – chrisl Jul 23 at 12:47
  • And there are missing 2 semicolons in the ISR. I hope now it is structually sound – chrisl Jul 23 at 12:48
1

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.