DC Motor Speed Measurement using Rotary Encoder

I have written the following code to measure the speed of a unidirectional DC Motor.I have attempted to measure the time between successive interrupts in order to calculate speed.

boolean check;
boolean start;
unsigned int angle=5;   //angle moved per encoder tick
unsigned long volatile time1;   
unsigned long volatile time_11;
unsigned long interrupt_time;
void setup() {
  Serial.begin(9600);
  while (!Serial); 
  pinMode(21,INPUT);  //Pin#21=interrupt pin, matched to interrupt#2
  attachInterrupt(digitalPinToInterrupt(2),speed,FALLING);
  time1=0;
  time_11=0;
  interrupt_time=0;
  check=true;
  start=false;
  time_11=millis(); //Start Clock
}

void loop() {
  // put your main code here, to run repeatedly:

  
  if(time1==interrupt_time&&start) //Both times same , hence bool start.
  {
    if(check)
    {
      
      Serial.print(millis());  //Time taken to rise to steady speed.
      check=false;
     }
    Serial.println((angle/time1)*1000);   //Display Speed.
  }
  interrupt_time=time1;
  start=false;
  while(!start)   //Stay here until next interrupt.
  {}
}
void speed(void)
{
  start=true;
  time1=millis()-time_11;
  time_11=millis();
  
 }

I want the code to be as efficient as possible.I have around 100 ticks per 360 degrees from my encoder.Is there any chance of "missing" an interrupt? And also I have a "used" motor.What provisions should I make for that?

You'll need micros(), not millis(), and don't call it twice in the ISR, that's going to double the time
spent in the ISR nearly.

You are measuring period, but often its best to measure speed by measuring frequency, possibly
time-averaging the frequency measurement.

Used motor? Its a motor, it spins round, how would anything be different?

I concur with MarkT. It’s easier to count the ticks in a given time than the time between ticks and you don’t have to calculate a reciprocal to get the speed. In real time programming I’ve used tables for any algebra.

Anthony

Thank you for the feedback! Considering your advice, I have come up with the following code.

unsigned long volatile encodercount;
unsigned long encodercount_previous;
unsigned int angle;
boolean first;
void setup() {
          angle=3;
          encodercount=0;
          encodercount_previous=0;
          Serial.begin(9600);
          while (!Serial); 
          pinMode(21,INPUT);  //Pin#21=interrupt pin, matched to interrupt#2
          attachInterrupt(digitalPinToInterrupt(2),speed,FALLING);
          first=true;
            }

void loop(){
          delay(100);      //0.1second     
          if(encodercount==encodercount_previous&&encodercount!=0)   
          {
            if(first)
            {
              Serial.println(millis());  //Steady State Time Taken 
              first=false;
             }
            Serial.println((encodercount); //Angle Moved in 0.1second.  
           }
          encodercount_previous=encodercount;
          encodercount=0;   
             }
void speed(void)
{
encodercount++;
}

Is this what you guys meant?

Indent your code consistently and add whitespace around operators and it might be readable…

I edited it.Has the code become more readable?

Has the code become more readable?

Yes, thank you.

I see several problems with the code. Your attach interrupt syntax is incorrect. The reference is to pin #
https://www.arduino.cc/en/Reference/AttachInterrupt

pinMode(21,INPUT);  //Pin#21=interrupt pin, matched to interrupt#2
         // attachInterrupt(digitalPinToInterrupt(2),speed,FALLING);

attachInterrupt(digitalPinToInterrupt(21), speed, FALLING);

I think that your logic is flawed in this section because with active counting

if(encodercount==encodercount_previous&&encodercount!=0)

this is not likely to be satisfied.

Additionally, because encodercount is multibyte, you need to disable interrupts briefly and make a "protected" copy of encodercount so that the value can not change while it is being accessed. See Nick Gammons tutorial in interrupts.

You are also better using a millis() timer instead of delay.

Here is a template for making readings every 100 ms.

volatile unsigned long  count = 0;
unsigned long copyCount = 0;

unsigned long lastRead = 0;
unsigned long interval = 100;

void setup()
{
  Serial.begin(115200);
  Serial.println("start...");
  
  //pinMode(2,INPUT_PULLUP);
  //attachInterrupt(0, isrCount, RISING); //interrupt signal to pin 2

     pinMode(21,INPUT);  //Pin#21=interrupt pin, matched to interrupt#2
     attachInterrupt(digitalPinToInterrupt(21),speed,FALLING);
}

void loop()
{
  if (millis() - lastRead >= interval) //read interrupt count every second
  {
    lastRead  += interval;
    // disable interrupts,make copy of count,reenable interrupts
    noInterrupts();
    copyCount = count;
    count = 0;
    interrupts();
 
    Serial.println(copyCount);

  }
}

void isrCount()
{
  count++;
}

Thanks for the immense help!
However, if I were to disable interrupts , would the condition hold?

if(encodercount==encodercount_previous&&encodercount!=0)

The idea was that as soon as I get equal encoder counts in a specific time period,the motor will have attained steady speed.

The idea was that as soon as I get equal encoder counts in a specific time period,the motor will have attained steady speed.

If you actually get the same number of counts, the conditional test should be valid. You may want to have a small window of +/- some number of counts

Use the safe copy of the encoder counts in the test, and for the assignment to encodercount_previous.

if(copy_encodercount==encodercount_previous&&copy_encodercount!=0)
  encodercount_previous=copy_encodercount;

The interrupts are disabled only to read data from the volatile variables into a copy to be used elsewhere in loop.

I'm doing much the same thing using a different approach. I did not use interrupts since I was doing only one thing--measuring rpm.

I simply made a pulse tach wheel with 60 teeth to put on the spindle of my lathe, and arranged an IR slot sensor to detect the teeth as they went by and fed the pulses to the arduino.
Then the drill is to count pulses for one second, calculate rpm and display it. The 60 teeth was the easiest to do with my tooling and machinery and, fortuitously, with 60 teeth, if you count pulses for one second the count is the rpm without further calculation. Of course you can use any other number of teeth and count time and do a bit of arithmetic to get rpm.
The timer is simplicity itself--use the modulo function on the millis(), e.g:

rem=millis()%1000;

This will give a zero remainder (rem) every second and when this happens you do the arithmetic on the count, display the result, clear the counter and start over. Of course you can detect any other remainder and get the same result.

I don't know if this is any use to you but it works like a charm for me.

On the matter of pulses per unit time or time between pulses in my past industrial experience the former is used for high speed and the latter for low speed.

Thank you so much both of you for your invaluable feedback!

I'll probably have the chance to check out both these approaches tomorrow in the lab.This is my first Arduino Project and the final objective is to implement SPEED CONTROL via PID.Right now, I am figuring out the transfer function parameters.

I can't stress how helpful all this has been, and I'll definitely keep posting here if(more like when) I run into more problems.

Which transfer function are you referring to? The motor or some other part of the block diagram?
For the motor there is a fairly rigorous transfer function already developed in which the key components are the volts per radian per second, the armature time constant and the mechanical time constant. This would be used if you are directly applying the voltage to the motor.
Another approach is to treat the motor as a current to torque transducer and feed it from a current source which is inside a speed control loop. Each has its appropriate application.
My own experience is that the PI is the key part and the D is more used to get you out of trouble. We viewed PI as "integral with lead" as this was easier to deal with in the analog world--simply an integrating op-amp with a resistor in series with the integrating capacitor.

Or perhaps you are way ahead of me--my experience was many years ago.

I used the direct voltage to motor method and had the counts and respective times displayed on the serial window.(I had the interval down to 20ms).Subsequently, I noted down the time the motor took to reach steady state speed.This can then be divided by 5 to find the motor Time Constant.
The Transfer Function I am referring to is this one:

Capture.PNG

!(http://This PC/Desktop/Uploads/DC Motor Transfer Function.jpg)

Attached is a more usual dc motor transfer function which takes into account the CEMF feedback inherent in any dc motor. As you can see the denominator is a quadratic so has 2 time constants which may, or may not be oscillatory. If you must design for a very fast regulator this could cause problems. If you can get away with a slow regulator you can always slug it down till the motor time constants are out of the way.

Understand though that the response of the motor to a change in voltage is a function of both motor and load inertia so anything you measure in a physical experiment will depend on the load inertia which may very well be different from the inertia in your application.

In your physical measurement be sure that you keep the voltage step very small so that you do not run into any sort of current limit of the power supply.

What are you using as a power supply--pure dc, single phase rectified dc, 3 phase rectified dc? With single phase rectified dc you will get discontinuous current which introduces another complication.

So good luck

Sorry-- I see my picture did not make it into my reply. I'll have to do some studying.

In the meantime if you could send me an email address I could send it that way. My address is:

kfox17@cogeco.ca

We’ll see if this works

We’ll see if this works

I am sorry for not being more active.

I have been experimenting with different approaches and this is what I’ve come up with.
I counted the number of intervals in 20ms using the following code :

volatile unsigned long  count = 0;
unsigned long copyCount = 0;
boolean first=true;
unsigned long lastRead = 0;
unsigned long interval = 20;
unsigned long interruptstart;
void setup()
{
  Serial.begin(9600);
  pinMode(21,INPUT);  //Pin#21=interrupt pin, matched to interrupt#2
  attachInterrupt(digitalPinToInterrupt(21),isrCount,FALLING);
}

void loop()
{
  if (millis() - lastRead >= interval) //read interrupt count every 0.02 second
  {
    lastRead  += interval;
    noInterrupts();
    copyCount = count;
    count = 0;
    interrupts();
    if(!first)
    { 
      Serial.print("Time = ");
      Serial.println(lastRead-interruptstart);
     // Serial.println("*********************");
    }
      Serial.print("Count = ");
      Serial.println(copyCount);
    //  Serial.print("*********************");
  }
}

void isrCount()
{
  if(first)
  {
    first=false;                      //To keep track of when the voltage is applied.
    interruptstart=millis();
   }
  count++;
}

The problem is that I get a constant number of counts/steady speed twice.Before the motor settles to its final high steady speed, it keeps a lower steady state speed as well.

I have attached the image of the number of counts obtained per 20ms in the attached.The motor first shows constant counts of 120 for a few intervals and then goes to a final count of around 160 a few intervals later.

This has me really bugged.Why am I facing this issue?

I do not know if you are seeing real behavior of the motor or some artifact.

Is the jump in counts always at the same time for all voltages applied?

Have you monitored the voltage with a scope? It's possible you could be seeing the behavior of the power supply. How is the voltage being turned on? Are there any motor drivers being used?

For a 20ms measurement period, I would use micros() rather than millis(). For more timing precision you can use a hardware timer instead of the software interval, but that would be a later refinement.

It is probably not relevant to your issue, but any variable changed within in ISR should be declared volatile.

volatile boolean first=true;
volatile unsigned long interruptstart;

I turn the serial monitor on, and then power the DC supply to the motor.Since, I am not driving the motor with the Arduino and have an external lab supply upto 30 Volts, I am not using any H-Bridge or Transistor configuration.
This irregularity is pretty regular in terms of both counts and time.