Go Down

Topic: Incorrectly calculating elapsed time between interrupts. (Read 2 times) previous topic - next topic

THEtheChad

Jul 12, 2013, 09:33 pm Last Edit: Jul 12, 2013, 09:43 pm by THEtheChad Reason: 1
I'm designing a motor controller and am having issues calculating the velocity. The motor has an encoder that's wired in to one of the arduino's interrupts. I'm calculating my relative velocity by measuring the time between encoder "ticks", which tells me how far the motor has travelled in the given span of time (the measure of my velocity is ticks/microsecond).

Having said that, I've been getting odd values at intermittent intervals. They look something like this (keep in mind the motor is moving at a constant velocity).

Code: [Select]

1668
1664
1664
1664
1644
1644
1644
1636
1636
1636
1648
1648
1648
1648
1664
1664
1668
========>1404
========>240
========>240
1640
1640
1644
1644
1644
1644
1668
1668
1664
========>1404
========>1404
========>240
1636
1636
1648
1648
1648
1664
1664
1664
1664
1668
1668
1668
1644
1644
1644
1640
1640
1640
1644
1644
1644
1644
1668
1668
1672


You'll notice they are all average about the same value, except for the groups of outliers. I'm using the code below to generate this output.

Code: [Select]

#define MAX_POWER 255

//
// Pin definitions
//
#define ENCODER_A 2
#define ENCODER_B 3

#define MOTOR_CURRENT_1 A0
#define MOTOR_CURRENT_2 A1
#define MOTOR_VOLTAGE   A2

#define MOTOR_DIRECTION 10
#define MOTOR_PWM       11
#define MOTOR_FAULT     12
#define MOTOR_ENABLE    13

#define RING 10

volatile uint8_t i = -1;
volatile unsigned long elapsed[RING];
volatile unsigned long tick_prev = 0;

void tick(){
  if(++i == RING) i = 0;

  unsigned long tick_curr = micros();
  elapsed[i] = tick_curr - tick_prev;
  tick_prev = tick_curr;
}

void setup(){

  Serial.begin(115200);

  // Pin setup
  pinMode(MOTOR_DIRECTION, OUTPUT);
  pinMode(MOTOR_PWM      , OUTPUT);
  pinMode(MOTOR_ENABLE   , OUTPUT);
  pinMode(MOTOR_FAULT    , INPUT );
  pinMode(ENCODER_A      , INPUT );
  pinMode(ENCODER_B      , INPUT );

  // Set motor state
  digitalWrite(MOTOR_ENABLE   , HIGH);
  digitalWrite(MOTOR_DIRECTION, LOW);

  attachInterrupt(0, tick, RISING);

  analogWrite(MOTOR_PWM, MAX_POWER);
}

void loop(){
  uint8_t j = i - 1;
  if(j == 255) j = RING - 1;
 
  unsigned long delta = elapsed[j];
 
  if(delta < 1600) Serial.print("========>");
  Serial.println(delta);
}


I'm using a ring buffer to store my values. I'm not disabling interrupts, which is fine, because I'm never reading and writing from the same blocks of memory at the same time. The index is a single byte which I'm assuming can be read and written to in a single operation (therefore, it can not be "interrupted").

You're welcome to scrutinize the methodology and execution of this technique, but ultimately, whether I'm disabling interrupts or not, I'm getting the same sets of odd values.

Does anyone have any idea what might be causing this?

CrossRoads

Probably want to fix this?
if(++i = 4) i = 0;

++i == 4, or
++i >= 4

assignment vs comparison, get you every time.
Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

THEtheChad

Whoops! That wasn't the current code. I updated my post.

UKHeliBob

Code: [Select]
  unsigned long delta = elapsed[i-1];
When i = 0 which element of the array will the value of delta be taken from ?
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Tom Carpenter

#4
Jul 12, 2013, 09:47 pm Last Edit: Jul 12, 2013, 09:51 pm by Tom Carpenter Reason: 1

When i = 0 which element of the array will the value of delta be taken from ?


Ring-1 by the looks of things:
Code: [Select]
if(j == 255) j = RING - 1;


The odd values could potentially be noise on your interrupt pin. Do you have more info about what is connected to the interrupt pin and how?
(Interestingly, if you add up pairs of the odd readings you get 3 correct values -> [1404+240 = 1644])
~Tom~

CrossRoads

I'd still go with >=, just in case you didn't hit 10 (RING) dead on each time.

RING never changes - why not just say 9 here?

if(j == 255) j = RING - 1;
Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

THEtheChad

I fixed all the issues with the ring buffer. I'm not worried about optimizing the code. The buffer is just a means to an end to print the elapsed times, which are the real concern here.

I'm guessing no one's been focusing on that because it's the hardest piece to figure out. There doesn't seem to be anything wrong with the code. It has to be an interrupt issue of some sort. I don't know if it's in the micros() or ..... I just don't know.

CrossRoads

What's the motor encoder connection like? Perhaps enabling the pullup resistor on EncoderA (B does not seem to be used) will help.

  pinMode(ENCODER_A      , INPUT_PULLUP );
  pinMode(ENCODER_B      , INPUT_PULLUP );

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

THEtheChad


JimEli

Just curious, have you tried to install the interrupt without using Arduino's attachInterrupt() function?

Also, there is a known issue with calling micros() with IRQs disabled and the potential for timer0 rollover, however, I don't believe this to be your issue.

dc42

Your loop() code will potentially print each element multiple times to serial, because it doesn't wait until a new value id available before printing a value. Perhaps what you meant to do was something like this:

Code: [Select]

void loop(){
  static uint8_t j = 0;     // static so it only gets initialized once
  if (j != i)
  {
    unsigned long delta = elapsed[j];
    if(delta < 1600) Serial.print("========>");
    Serial.println(delta);
  }
  ++j;
  if(j == RING) j = 0;
}


If you make that change, you will only print each value once (if I have got the code right) and the problem may be easier to diagnose. However, I suspect the problem is noise causing additional interrupts, because in the example you gave, 1404 + 240 = 1644 which is in line with the normal values.

Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Go Up