Pages: [1]   Go Down
Author Topic: Incorrectly calculating elapsed time between interrupts.  (Read 1851 times)
0 Members and 1 Guest are viewing this topic.
Kansas City, MO
Offline Offline
Newbie
*
Karma: 0
Posts: 9
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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:
#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?
« Last Edit: July 12, 2013, 02:43:44 pm by THEtheChad » Logged

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 538
Posts: 27147
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

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

assignment vs comparison, get you every time.
Logged

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.

Kansas City, MO
Offline Offline
Newbie
*
Karma: 0
Posts: 9
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

East Anglia (UK)
Offline Offline
Faraday Member
**
Karma: 114
Posts: 4267
May all of your blinks be without delay()
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
  unsigned long delta = elapsed[i-1];
When i = 0 which element of the array will the value of delta be taken from ?
Logged

Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Leeds, UK
Offline Offline
Edison Member
*
Karma: 80
Posts: 1726
Once the magic blue smoke is released, it won't go back in!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Ring-1 by the looks of things:
Code:
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])
« Last Edit: July 12, 2013, 02:51:35 pm by Tom Carpenter » Logged

~Tom~

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 538
Posts: 27147
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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;
Logged

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.

Kansas City, MO
Offline Offline
Newbie
*
Karma: 0
Posts: 9
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 538
Posts: 27147
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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 );

Logged

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.

Kansas City, MO
Offline Offline
Newbie
*
Karma: 0
Posts: 9
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

There's already a pull-up resistor on the line.
Logged

USA
Offline Offline
Jr. Member
**
Karma: 4
Posts: 92
If you can't fix it with a hammer, it must be an electrical problem.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged


United Kingdom
Offline Offline
Tesla Member
***
Karma: 224
Posts: 6619
Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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.

Logged

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.

Pages: [1]   Go Up
Jump to: