Thursday, January 5, 2017

STM32 Cortex M3 Series - [STM32F103C8T6] - #2 - Encoder Interface

Before I begin kindly keep my GitHub code as reference throughout the tutorial. I'm currently working on encoder interface to generate odometry data for my Autonomous UGV and UAV project. This can be done in various ways by encoders of various types. The one I'm having is this 300RPM motor with a quadrature encoder circuitry embedded to it's shaft. Here's the motor I'm talking about from Robokits:

Figure 1. The Quadrature Encoder Motor

The encoder circuitry has +5, and GND as logic inputs and two channel outputs A and B giving timely square outputs.
(NOTE: Apparently I spent around two full days for the lack of a datasheet on the website trying to figure out why the outputs aren't coming only to realise later that the pins need to be pulled high with a 10k resistor. )
What seems so easy to do on paper is to simply get the interrupts from both the channel for a higher resolution and provide conditional loops in the interrupt routines to get the values and send the values back to main loop. 
WRONG. Never do this. This is bad coding. The prominent reasons for this are:

  1. The encoder readings change very fast. There is a possibility that it just might skip some values give an error reading(channel_A_state=channel_B_state=0 or 1) and count can be a wrong one or an illegal one.
  2. Checking for a high or low value in an Interrupt Service Routine is a bad choice too. The ISR's should be as small as possible. This is because, the core has been interrupted and you need to get back to look into other tasks too.

What I would like you to point towards is this stackexchange post answer which points out to the use of using decisions in getting values of the interrupt values:
You should have ZERO ifs. No decisions. Store your AB state, i.e., 00 or 01, then append your next state, i.e 0001 means AB went from 00 to 01,thus B changed from 0 to 1. Make this a +1. If starting from 00, and you change to 10, then call this a -1. Build a 16 element array of all possible transitions holding the number that needs to be added to your count if it occurs, noting that some are illegal and need to be handled.
The counting is essentially on the works of the graycode.  So we are going to create a graycode and check it against an array of legal and illegal states. Here's the algorithm:

  1. Create an array of the possible states in the graycode with something like this:

    int8_t states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0}
  2. Also create a graycode containing the (final_state= prev_AB_state+current_AB_state). Left shift the previous AB state by 2 and append the current AB state to the final state.(If this is confusing to you, see the full code on my git.)
  3. Here's how a graycode looks:

So if for e.g ,
  • The final_state is 0001, it means the prev_AB_state=00 and it has changed to 01. This would be given a legal state of -1, or anti-clockwise rotation
  • The final_state is 0010, it means the prev_AB_state=00 and it has changed to 10. This would be given a legal state of 1, or clockwise rotation.
  • Now if both channels A and B are changing then we term is as an illegal state as both cannot change the state together.
I would soon post a video on the same and my next post would be on setting up OpenOCD debugging and Linux Eclipse CDT setup.


No comments:

Post a Comment