Go Down

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

##### Jul 12, 2013, 09:33 pmLast 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]
`16681664166416641644164416441636163616361648164816481648166416641668========>1404========>240========>240164016401644164416441644166816681664========>1404========>1404========>2401636163616481648164816641664166416641668166816681644164416441640164016401644164416441644166816681672`

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 10volatile 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?

#1
##### Jul 12, 2013, 09:38 pm
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.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

#2
##### Jul 12, 2013, 09:44 pm
Whoops! That wasn't the current code. I updated my post.

#### UKHeliBob

#3
##### Jul 12, 2013, 09:46 pm
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 pmLast 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~

#5
##### Jul 12, 2013, 09:48 pm
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.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

#6
##### Jul 12, 2013, 09:54 pm
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.

#7
##### Jul 12, 2013, 10:03 pm
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.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

#8
##### Jul 12, 2013, 10:28 pm
There's already a pull-up resistor on the line.

#### JimEli

#9
##### Jul 12, 2013, 10:34 pm
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

#10
##### Jul 12, 2013, 11:59 pm
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