5
$\begingroup$

I have a robot with two wheels/motors and each has a quadrature encoder for odometry. Using the wheel/motor/encoder combo from Pololu, I get 48 transition changes per rotation and my motors give me a max of 400RPM. I've found it seems to miss some of the encoder state changes with the Pololu wheel encoder library.

Would I run into issues or limitations on my Arduino Uno using interrupts to track the quadrature encoders while using PWM to drive my motors through an H-bridge chip?

$\endgroup$
1
  • $\begingroup$ Without seeing your actual code, it would be impossible to answer whether or not interrupts are going to cause issues. $\endgroup$
    – JamesC4S
    Apr 15, 2013 at 5:31

3 Answers 3

2
$\begingroup$

Interrupt service routines should always do the absolute minimum. Remember that while your MCU is servicing an interrupt, it is doing nothing else.

For a simple quadrature encoder, it should be possible to have a pair of ISR's, one triggered by a change on the A channel, the other triggered by a change on the B channel.

Pretty much the only thing these ISRs should do is identify whether that change means the position variable should be incremented or decremented, then update the position and end the ISR and possibly update a state transition time (if you are using this information to better estimate velocity).

See Velocity and acceleration estimation for optical incremental encoders by R.J.E. Merry, M.J.G. van de Molengraft, M. Steinbuch

With a pair of standard quadrature encoders, 48 transition changes per rotation and a maximum of 400RPM, your cross channel interrupts shouldn't be more frequent than 640 times a second, which should be well within the capability of a 16MHz processor.

An individual channel may produce more interrupts more frequently than that (for instance when the detector is sat on the edge) but missing those state changes does not have to affect the final position value, and there will always be a clear 1.5ms between that channel stabilising and the other channel activating.

So, as long as you can service every A/B and B/A transition, it doesn't matter if you occasionally fail to process an A/A or B/B transition as long as the state of both A and B is taken into account at the next A/B or B/A transition.

Note that if you don't need to calculate the time for your A-B and B-A transition (to calculate velocity more responsively with low resolution encoders) then you would probably be batter off having a fixed 640Hz/1.5ms timer based interrupt service routine that looks at both A and B channels, as suggested by Tim Wescott.

$\endgroup$
2
$\begingroup$

When I'm processing quadrature encoder input in an ISR, I prefer to have a simple ISR running on a timer that's guaranteed to run fast enough to catch every state (not state change) of the encoder. Interrupting on every pin transition invites a situation where the processor gets bogged down if a pin starts bouncing. Interrupting at a fixed rate may use up processor resources all the time, but it always uses the same amount of resources, so it doesn't catch you by surprise when you least want it to happen.

For your 48 transitions/rotation, 400RPM motor, that works out to 320 transitions per second: I would set my ISR to interrupt at least twice that fast. It should do nothing but check the current state of the two pins against the previous one, and then increment or decrement the wheel's counter as appropriate based on what it sees. At 640Hz, such an ISR should load the processor very lightly, even if you're doing updates for multiple wheels.

I've built quite a few successful control systems that use this method, so it can't be all bad.

$\endgroup$
0
1
$\begingroup$

Yes you would: no current [Arduino] on the market (that I know of) can effectively track the state changes without missing a few state changes. (Maybe the Due would be better or another board with a higher clock rate.)

Interrupts can help a little, but:

  • I believe it stops everything else on the chip; the motors might stop slightly when the interrupt is triggered.
  • You may have problems if two interrupts fire at the same time
  • After the code is processed, I believe I read it has a little delay before returning to what it was doing.

Interrupts are not the best solution for this problem, but that is the only thing you can do with modern technology. However, if you wanted to still wanted interrupts, you could have another ATmel328 running on the breadboard, with interrupts, that when the main chip sends a message, it can send the number of turns for each wheel since it sent it last. I don't know how it would work when a interrupt is called, but maybe if you used a developed library, such as Arduino's Wire Library, it might fix the kinks when it is called.

Good luck.

$\endgroup$
5
  • 2
    $\begingroup$ When an interrupt service routine is running, no other interrupts are serviced. Up to one interrupt type can be queued and serviced in a pre-determined--by ATmel--order once the current interrupt finishes. There is a one-instruction delay between interrupts in this case. $\endgroup$
    – JamesC4S
    Apr 15, 2013 at 5:32
  • $\begingroup$ Can an Arduino really not service 640 (2x48x400/60) interrupts per second and still have time for other processing too? Isn't that over 25 thousand clock cycles per interrupt at 16MHz? $\endgroup$
    – Mark Booth
    Apr 15, 2013 at 13:20
  • $\begingroup$ @MarkBooth Yes, it should be able to handle so few interrupts, provided the ISRs aren't bloated with unnecessary instructions. $\endgroup$
    – JamesC4S
    Apr 15, 2013 at 16:21
  • $\begingroup$ Thanks @JamesC4S - I know that crafting efficient ISRs is a skill you have to learn, but I assumed that you would have to be really inefficient not to be able to cope with interrupts at that rate on an embedded CPU. $\endgroup$
    – Mark Booth
    Apr 15, 2013 at 16:31
  • $\begingroup$ @MarkBooth You're right again. However, a really good example are people who try to do relatively slow things like floating point math or Serial.print()s inside the ISR. [And with Arduino 1.0, it doesn't work since Serial.print() is now interrupt driven.] It's best to set a flag or increment a counter, then get out. $\endgroup$
    – JamesC4S
    Apr 15, 2013 at 16:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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