3
\$\begingroup\$

I little background first..
I have 4 separate wheels in my robot and an encoder on each wheel. I am using an arduino mega 2560 which only has 6 interrupt pins, every code that I've seen either uses 2 interrupt lines per encoder or 1 but then its not that precise.

My idea:
My idea here is that the encoders tick at around 300Hz at maximum speed for my robot so I'm using a timer overflow interrupt to read all of the inputs that encoders are connected to at roughly 1kHz which also gives me plenty of time (1 ms) to check if the inputs changed and do my calculations and then return from the interrupt.

My question:
Is my thinking correct? Will I be able to read properly the encoder position and changes? Is there anything better I could do? Are there any caveats that I should take into account when dealing with this?

Update: From the discussion in the comments and answers I currently have the two following alternatives:

  1. Stick to the polling idea (increase frequency when necessary)
  2. Use a "shared" interrupt available on every pin (didn't know it was possible)
\$\endgroup\$
  • 1
    \$\begingroup\$ If I recall correctly, you can actually get an interrupt from any ATmega pin, the catch is that apart from the "interrupt pins" you will not get a unique interrupt but rather a shared "any of the enabled" one. Still, this might be preferable to periodic polling. \$\endgroup\$ – Chris Stratton Jun 30 '17 at 21:58
  • \$\begingroup\$ Thats what I'm aiming at I want a shared event for all encoders which would allow me to check all of them at once but the problem is that my motors are turning at different speeds (weight distribution, differences between them etc...) so I need to catch all of it, so using a "triggered" interrupt from one of the encoders is not going to work I think. \$\endgroup\$ – leawp Jun 30 '17 at 22:06
  • \$\begingroup\$ The 6 pin limit is for the unique "external" interrupts. But each port's "pin change interrupt" can include any combination of pin(s) on that port as a trigger. \$\endgroup\$ – Chris Stratton Jun 30 '17 at 22:41
  • \$\begingroup\$ Thank you, I didn't fully understand your first comment but I do now. Thanks for your patience. I didn't know about this. \$\endgroup\$ – leawp Jun 30 '17 at 22:44
  • \$\begingroup\$ please add your comment as an answer so I can accept it as the solution to my problem \$\endgroup\$ – leawp Jul 1 '17 at 12:50
0
\$\begingroup\$

If you really poll much faster than the encoder moves then this will work. Whether it is a good idea depends on how fast the encoder can actually move and on how important it is that you don't miss clicks. I certainly hate it when I spin a dial to scroll through a long UI list and it moves slowly backwards, although there is no real harm done. If the encoder is feedback from a motor, missing clicks could cause bigger problems, like mechanical damage.

You could use dedicated logic to make a counter, or you could use a different microcontroller that has a built-in encoder peripheral, but for most purposes polling is probably fine if you do it fast enough.

\$\endgroup\$
  • \$\begingroup\$ Thank you for your answer, I will experiment with polling rate and see how fast I can execute the "read" before next interrupt. \$\endgroup\$ – leawp Jun 30 '17 at 22:13
  • \$\begingroup\$ Of course, you need to factor in the processing time required to evaluate a changed input state, plus the larger programmatic implications of a change, since until that is resolved you can't poll the inputs again. \$\endgroup\$ – WhatRoughBeast Jun 30 '17 at 22:47
0
\$\begingroup\$

Have you considered polling very fast and strobing a pair of 71451s with four separate addresses to get the pair of inputs you need, in round-robin (but very fast) fashion? You could then read one bit off each chip, one per encoder output that is being addressed at this moment.

This will reduce the number of input pins needed to two.

\$\endgroup\$
0
\$\begingroup\$

all are fine

You have to oversample each encoder, meaning sample faster than they are moving. assuming that 300Hz is correct. 1kHz is plenty fast, enough headroom just in case. Assuming a 2 pin encoder? There are only two valid next states from the current state yes? Updated your position based on current state and next state (increment a position or decrement, or whatever your solution is). For each wheel. value stored in a global used by the foreground task

if you can have it so any of the gpio pins changing state causes an interrupt that works too, less interrupt overhead, but if there is any bounce in the state change, you may get multiple interrupts (with bogus values). (oversampling you can also have bounce problems).

You need to be careful with interrupts and your code. Check the compiler output. Rusty on AVR but the registers are 8 bit yes? The loads and stores appear to be 8 bit. So if you have a 16 bit position counter your forground task is reading that counter which means it has to do at least two instructions to get the two parts from the global variable written by the isr and read by the application. Current position is 0x0FFF. You read 0x0F, get an interrupt, position changes to 0x1000, return from interrupt, forground task reads the second half 0x00, and thinks the position is 0x0F00. 255 counts wrong. for one loop of the forground task. So you need to be careful that this doesnt happen, many general ways to avoid it, but specific for this target, I dont remember off hand. or you can do the math such that your position per wheel fits in one byte.

polling, so long as you poll fast enough, that works just fine (well bounce if there is any in your encoder is not fine, lets assume you dont have any). sometimes you have to poll more than once through the loop, make it a function

while(1)
{
  do stuff
  poll_wheels();
  do stuff
  poll_wheels();
  ...
  poll_wheels();
}

or maybe the worst case loop is fast enough

while(1)
{
   poll_wheels();
   do stuff.
}

you need to deal with invalid states in some way, perhaps during development if you ever see an invalid state set a panic variable then just stop motors and everything and or set an led or something like that to visually indicate that this happened. not much you can do but should still have extra code that if the current state of an encoder is not a valid next state to the last state of that encoder, then other than setting the alarm variable/led/bit/counter, dont update the position but use the current state as the last state for the next test. just pretend from a position perspective that no movement happened but are at a new encoder position. if you have three bit encoders or more then you can see if you missed one and jumped two, but if it is just a two bit encoder you cant necessarily do that.

\$\endgroup\$
-1
\$\begingroup\$

Thanks to the suggestion from Chris Stratton I got this to work properly I've used a pin change intterupts.. with proper masking it worked like a charm :)

Thanks to everyone for all the suggestions and expertise.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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