Quadrature Encoder and PWM Object for One to Nine(ish) Motors/Encoders (beta)

Duane DegnDuane Degn Posts: 10,002
edited 2015-01-21 - 18:01:12 in Propeller 1
As I mentioned the Open Propeller Project #8 thread, I'm now keeping code I intend to share with the world in my GitHub repository.

I am now longer maintaining two different versions of this code. I previously had a two motor/encoder version and a four motor/encoder version. Now the single version will work with any number of motors and encoders from one to possibly nine.

To tell the object how many motors/encoders you wish to use set the value of "TOTAL_ENCODERS" appropriately. The "TOTAL_ENCODERS" constant is presently located on line # 113 of the file "QuadratureMotors".
TOTAL_ENCODERS = 2

The object presently only works with quadrature encoders. I'm planning on making a modified version for use with single channel encoders.

Warning! The direction the encoders advance is opposite the earlier versions of this code used. I changed the lines:
update        if_z      mov     encoderChange, one      ' decrement value

decvalue      if_nz     mov     encoderChange, negOne   ' increment

to
update        if_z      mov     encoderChange, negOne   ' decrement value

decvalue      if_nz     mov     encoderChange, one      ' increment

I see I forgot to change the name "decvalue" to a more appropriate "incValue". I'll probably make the change the next commit.

If any of you want the encoder values to increment the same direction as the earlier code, you can change the lines above back to the original version.

I added a "ReverseMotor" method to allow one to swap the direction pins without needing to change the physical connections. If HB-25 or other motor controllers using servo pulses are used, the "ReverseMotor" will still have the effect of reversing the motor direction.

I haven't tested the code with all configurations of h-bridge wiring. I also haven't tested many of the methods lately. Please let me know if any of you find any problems.

The code attached to this post is buggy. I don't recommend using it. Use the code from GitHub.

                

Comments

  • Duane DegnDuane Degn Posts: 10,002
    edited 2013-12-10 - 22:06:10
    Two Encoder/Motor Versions of Driver

    I'm using this post to hold the latest two encoder and/or motor version of my drivers.

    First two encoder version added December 12, 2013.

    Edit (December 14, 2013): Updated simple demo to version c. I hadn't removed the unused header from version b. The version b code had be downloaded 4 times.

    Added a more complex two encoder version. "TwoEncoderDemo_131214d".
  • Duane DegnDuane Degn Posts: 10,002
    edited 2013-12-10 - 22:06:28
  • Duane DegnDuane Degn Posts: 10,002
    edited 2013-12-10 - 22:06:48
  • Duane DegnDuane Degn Posts: 10,002
    edited 2013-12-10 - 22:07:08
  • Duane DegnDuane Degn Posts: 10,002
    edited 2013-12-10 - 22:07:27
    Old version archive.

    EncoderDemo131210k doesn't specify encoder direction and has a bunch of other bugs. It's included here as a reference. Don't bother to download it.
    It had been viewed 9 times before I deleted it from the top post.

    Edit (December 14, 2013): Since I'm using this post as an archive, I decided to move my original post (from post #1) down here. I'd like to keep post #1 free as a sort of table of contents to the thread.
    Duane Degn wrote: »
    I've promised several forum members I'd post my quadrature encoder object I've been working on.

    It's still pretty rough. It had previously measured speed by counting transitions; it now measures speed by timing the intervals between transitions. I know this later approach is commonly used but I hadn't thought to use the technique on until OddBot on LMR pointed out the possibility.

    There is still left over code from the previous technique of measuring speed.

    While the PWM portion of the object works, the demo does not provide a way to set motor speeds.

    The object can drive continuous rotation servos if desired. I'm not sure how the resolution compares with the usual servo drivers. It does provide the advantage of not requiring an additional cog for the servo object.

    I'll update the demo to provide PWM support as soon as I can.

    I have many projects which use this code and this will be one of my major projects for a while. I plan to add updated code to this thread as I complete it.

    In order to keep track of which version does what, I plan to use the first six posts of this tread to hold information about the various versions and how I'm using the code in other projects.

    There are a lot of comments in the top object and I'll try to add more documentation about the object to this thread.

    Edit (January 21, 2015): I'm moving more text from post #1 down here. I'm leaving the code attached to post #1 there since it's not availble in my list of attachments.
    I'm using this thread to document my work on several quadrature encoder objects. There are objects for use with encoders only and objects which also output PWM signals while monitoring encoders.

    Edit (December 12, 2013): I moved the original version to post #6. I've attached the latest version here. See post #12 for more information.

    Edit (December 14, 2013): I added a simple demo of an encoder only driver. The encoder only driver monitors two encoders. See post #15 for more information.

    There are two additional demos attached to post #7. The demos attached to post #7 are to the encoder object in the Propeller Tool library.

    I moved the original contents of this first post to post #6 below. I want to use this top post as a sort of table of contents to the thread.

    I've been working on adding PID feed back to my encoder/PWM object. It's not going as smoothly as I'd hoped. One problem I keep running into is where to display all the debug information. The terminal window is getting very crowded.
  • Duane DegnDuane Degn Posts: 10,002
    edited 2013-12-11 - 09:35:33
    Related Links

    Bug in Kye's QEDEngine
    Additional discussion of bug in Xanadu's X1-AR1 Project


    One of the reasons I've posted my encoder/PWM object in this far from finished state was the discovery by Wildatheart of a bug in Kye's quadrature encoder object.

    Here's the thread where the bug is discussed.

    IMO, this is kind of a big deal. Mainly because the author is Kye. I doubt I'm the only one who, when looking in the OBEX and seeing an object written by Kye, figures it's a safe bet the object will be well written and work well. It's kind of disturbing to discover Kye may be mortal.

    You can see the details of what the bug is in the above linked threads. (Please ask questions about Kye's object in the other thread.)

    Erlend asked about alternate quadrature encoder objects. I knew I had used a different object but I couldn't find it in the OBEX. It turned out it was in the Propeller Tool Library (I often find objects there after searching the OBEX in vain).

    I couldn't find any demo code for the QE object so I posted a couple demo programs to the other thread.

    I'll attach these demos here as well. If you want to count encoder transitions, the object in the Propeller Tool Library (let's call it the default object) would be a better choice than the object I posted in post #1.

    If your motor/encoder combination produces lots of transitions, then the default object could be used to calculate speed by keeping track of the change in the total count over time. However if your motor/encoder combo doesn't produce a lot of transitions and you want to control the motor at slow speed, then my object should provide a more accurate speed measurement.

    The other times my object will have an advantage over the default object is if you want the same cog to read the encoders and to produce a PWM signal to the motor(s). As stated in the first post, I don't yet have an interface for adjusting the PWM in the demo program I posted.

    Since my encoder/PWM object tries to do both read encoders and produce PWM signals, it's not going to do either as well as a dedicated object to perform just one of the tasks. If you need high frequency PWM or need to read encoders at high frequency, my object is probably not up to the task.

    The attached demos are to the quadrature encoder object in the Propeller Tool Library.
  • jmgjmg Posts: 13,923
    edited 2013-12-11 - 14:39:12
    Duane Degn wrote: »
    Since my encoder/PWM object tries to do both read encoders and produce PWM signals, it's not going to do either as well as a dedicated object to perform just one of the tasks. If you need high frequency PWM or need to read encoders at high frequency, my object is probably not up to the task.

    Can you post some numbers in the thread of the saturation(max) Quad edge rate, and the PWM details (bits,Step-time)
  • KyeKye Posts: 2,200
    edited 2013-12-11 - 15:27:41
    @Duane Degn - Ha ha, thanks for the compliment!

    I recently worked on a new method to do quadrature encoder stuff, see attached. Maybe you can use the code in what you are doing.
    Nyamekye,
  • Duane DegnDuane Degn Posts: 10,002
    edited 2013-12-11 - 15:38:38
    jmg wrote: »
    Can you post some numbers in the thread of the saturation(max) Quad edge rate, and the PWM details (bits,Step-time)

    I'll post an updated version later this evening. The updated version fixes the lack of direction indication on the speed and also speeds up the code a bit.

    The soon to be released version's main loop takes 480 clock cycles. Once every PWM period there's an added delay as the speed settings are read from hub. I count 84 clock cycles to complete this reading. So at the period transition, the encoders are being read at 141,843Hz (564 tics @ 80MHz clock) .

    The 480 tics of a normal loop give the PWM resolution of 6.0us. I believe the regular servo object offers 1us resolution.

    Since this is only intended to control continuous rotation servos, I think the 6us resolution is probably acceptable.

    Whether or not the object is acceptable from h-bridge motors will depend a lot on the desired frequency one wishes to use with the h-bridge motors.

    The resolution can be increased if using fewer than four motors and the unneeded code for the unused motors and encoders is removed. I plan to make a version for two motors/encoders combos.

    Edit (December 14, 2013): I think my 141KHz is way off. The loop time is very dependent on whether or not an encoder tic is detected. The encoder object should be able to keep up with around 60KHz but I'm not sure of the exact value. I'll and some code to monitor for the longest loop time and find out the exact highest frequency supported.
  • Duane DegnDuane Degn Posts: 10,002
    edited 2013-12-12 - 21:57:42
    Kye wrote: »
    @Duane Degn - Ha ha, thanks for the compliment!

    I recently worked on a new method to do quadrature encoder stuff, see attached. Maybe you can use the code in what you are doing.

    I'm very intrigued by your code. I don't understand it yet but it looks very interesting.

    Thank you for posting it.
  • Duane DegnDuane Degn Posts: 10,002
    edited 2013-12-12 - 22:14:08
    I uploaded an updated version of the encoder/PWM object demo.

    This version allow you to enter the speed of a motor.

    For example to set motor #0 to a speed of 200, I'd type "0" (without the quotes). I would then be prompted to enter the speed from motor #0. I would then enter "200" followed by enter. The motor should then powered with a 20% duty cycle (if resolution is left to the default 1000).

    The pin definitions in this latest demo are different than the pin definitions I used in the first demo posted.

    I'm tried unsuccessfully to make the code simple. Hopefully you all can make sense of it. I plan to add a lot more features to this code so if you don't need to use this object yet, I'd suggest waiting a bit to download a version until I flesh out the features.

    Here's the output from two motors (I've only tested it with two so far).
            name of program = EncoderDemo_131212g
     name of encoder object = FourQuadratureMotors131212b
      name of header object = HeaderVexTreads131212a
    ----------------------------------------
       Menu To enter speed of a motor press the motor ID number 0 through 3
    
    
     Alternately Press One of the Following Keys
      c) Clear screen and display header and menu.
    
    
    ----------------------------------------
    
    
    
    
    
    
    
    
      PWM and Encoder resolution = 480 clock cycles or 6 us
    ----------------------------------------
         totalCount[0] =       6756, transitionTime[0] =     22452 clock cycles, average of 64 readings
              speed[0] =    3563(transitions per second)
        scaledSpeed[0] =     329 Scaled so max transitions per sec (10827) equals full speed of 1000 (current PWM resolution).
     rpm = 47.72       Based on 4480 transitions per revolution.
         totalCount[1] =       5753, transitionTime[1] =     23376 clock cycles, average of 64 readings
              speed[1] =    3422(transitions per second)
        scaledSpeed[1] =     329 Scaled so max transitions per sec (10827) equals full speed of 1000 (current PWM resolution).
     rpm = 45.83       Based on 4480 transitions per revolution.
    ----------------------------------------
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
                 I/O Pin Numbers
          tens:   3           2            1
         units:  1098_7654_3210_9876_5432_1098_7654_3210
     ina state: %1011_1110_0011_0000_0001_1101_0000_0010
    ----------------------------------------
     targetSpeed[0] = 200
     targetSpeed[1] = 200
     targetSpeed[2] = 0
     targetSpeed[3] = 0
    ----------------------------------------
    

    The encoder information doesn't show up until the encoder has had at least one transition.
  • ErlendErlend Posts: 558
    edited 2013-12-14 - 07:00:59
    When I finally got around to try an alternative to Kye's qdec I found out that that's exactly what I had done some months ago. But still, with this one (Luke Haywas' qdec), I get these strange glitches. Sorry Kye, I must instead blame myself - again.

    Erlend
    21st century - when everything changes
    "Better with a DAT and a COG than with a CAT and a DOG"
  • Duane DegnDuane Degn Posts: 10,002
    edited 2013-12-14 - 12:14:05
    Erlend wrote: »
    When I finally got around to try an alternative to Kye's qdec I found out that that's exactly what I had done some months ago. But still, with this one (Luke Haywas' qdec), I get these strange glitches. Sorry Kye, I must instead blame myself - again.

    Erlend

    I'm almost done converting my object to an encoder only object. As I was formatting the output I noticed I has some text overwriting the last few digits of the "totalCount" display. I'm pretty sure my encoder object catches all transitions (not that you said it didn't, I was just beginning to wonder because of the missing digits). I'll have a two encoder version to post very soon (with no motor control). Hopefully it will catch all the transitions for you.
  • Duane DegnDuane Degn Posts: 10,002
    edited 2013-12-14 - 13:39:17
    I tend to add a lot of debug information to my demo programs. This can cause the terminal window to get very cluttered. I decided to strip all but essential information and made a very simple demo of my new two encoder only object.

    I attached the simple demo to the top second post of the thread.

    This is what the output looks like:
         totalCount[0] = 1
    ----------------------------------------
         totalCount[1] = 179
    ----------------------------------------
                 I/O Pin Numbers
          tens:   3           2            1
         units:  1098_7654_3210_9876_5432_1098_7654_3210
     ina state: %1111_1110_0011_0000_0000_0000_0000_1101
    ----------------------------------------
    

    The section under "I/O Pin Numbers" shows the ina state of each I/O pin. In this example P0 - P3 are connected to the encoder output.

    The "I/O Pin Numbers" section lets you watch for encoder transitions and made sure each transition is registered in the "totalCount" variable.

    I'll also be posting a more complicated demo of the two encoder object. The simple demo should be enough to convince someone that each transition is being seen.

    Edit: I decided to keep two encoder versions of the objects in post #2.
  • Duane DegnDuane Degn Posts: 10,002
    edited 2013-12-14 - 14:47:53
    I forgot to remove the unused header object from the two encoder simple demo. If you're one of the four who downloaded version b, you might want to grab version c instead (from post #2).
  • Duane DegnDuane Degn Posts: 10,002
    edited 2013-12-14 - 15:25:48
    I added a more complex demo of the two encoder driver to post #2.

    This version displays the speed in several different formats.

    Here's what the output looks like.
            name of program = TwoEncoderDemo_131214d
     name of encoder object = TwoEncoders131214a
      name of header object = HeaderEncodersOnly131214a
    ----------------------------------------
     Encoder loop time = 224 clock cycles or 2 us
      The debug loop takes 9461936 clock cycles or 118 ms to complete.
    ----------------------------------------
    
    
    
    
         totalCount[0] =        -782 , transitionTime[0] =    336930 clock cycles, average of 16 readings
              speed[0] =        -237 (transitions per second)
        scaledSpeed[0] =         -21  Scaled so max transitions per sec (10827) equals full speed of 1000 (MAX_SCALED_SPEED).
                  rpm =        -3.17    Based on 4480 transitions per revolution.
    ----------------------------------------
         totalCount[1] =         185 , transitionTime[1] =    293082 clock cycles, average of 16 readings
              speed[1] =           0 (transitions per second)
        scaledSpeed[1] =           0  Scaled so max transitions per sec (10827) equals full speed of 1000 (MAX_SCALED_SPEED).
                  rpm =        0.00    Based on 4480 transitions per revolution.
    ----------------------------------------
    
    
                 I/O Pin Numbers
          tens:   3           2            1
         units:  1098_7654_3210_9876_5432_1098_7654_3210
     ina state: %1011_1110_0011_0000_0000_0000_0000_0110
    ----------------------------------------
    

    I've been trying to get some sort of feedback algorithm to work with the four encoder and PWM version of the code with limited success. I'm trying to both added PID control and an easy way to adjust the PID parameters while the motor is running.
    There's a lot of information to display and I'm running out of terminal window space to display it.
  • Mark_TMark_T Posts: 1,981
    edited 2014-03-20 - 16:33:05
    Not sure if this is the most appropriate thread to jump on, but I've just knocked together a quad quadrature
    encoder object that might be of interest (well perhaps not, but its based on WAITPNE which is perhaps
    novel).

    It basically sleeps in WAITPNE until one of the relevant pins changes (it can monitor upto 4 sets of AB
    signals, arbitrary pin assignments) and updates counters. It also maintains error counters for when
    its getting changes coming in too fast, but should be quite nippy anyway.

    Such a structure could be combined with some other actions if they can be arranged to change a pin and
    thus trigger WAITPNE to wake up.

    http://obex.parallax.com/object/737
  • kuronekokuroneko Posts: 3,623
    edited 2014-03-20 - 16:42:07
    @Mark_T: You might want to change the encoder object name, enc is a reserved (PASM) word.
  • Mark_TMark_T Posts: 1,981
    edited 2014-03-20 - 16:52:06
    kuroneko wrote: »
    @Mark_T: You might want to change the encoder object name, enc is a reserved (PASM) word.

    Ah, yes, so it is, but that's only in the example at least! BST doesn't notice. Haven't got Prop tool to hand at the moment.
  • Duane DegnDuane Degn Posts: 10,002
    edited 2014-03-20 - 21:06:29
    Mark_T wrote: »
    Not sure if this is the most appropriate thread to jump on, but I've just knocked together a quad quadrature
    encoder object

    It's fine by me. I'll probably use one of the first few posts of this thread to list some of the various quadrature encoder objects available.

    I look forward to taking a look at your code. Thanks for adding it to the OBEX.
  • Duane DegnDuane Degn Posts: 10,002
    edited 2015-01-21 - 18:01:12
    I've added an updated version of this object to my GitHub account. There's more information about the updated code in the top post.

    This version still has a lot of rough edges. I've been working with this code a lot lately and I'll likely add improvements soon.

    See my comments in post #55 of the OPP #8 thread for information about the new technique of determining motor speed. This technique is probably the main aspect of the code which makes it worth trying out.

    I'm hoping this method of determining speed based on the time between encoder transitions allows for better control when combined with a PID algorithm. I don't have a PID algorithm implemented with this code yet. The Eddie firmware includes some P.I. components.
Sign In or Register to comment.