Quadrature Encoder and PWM Object for One to Nine(ish) Motors/Encoders (beta)
Duane Degn
Posts: 10,588
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".
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:
to
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.
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
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".
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.
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.
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.
Can you post some numbers in the thread of the saturation(max) Quad edge rate, and the PWM details (bits,Step-time)
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'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.
I'm very intrigued by your code. I don't understand it yet but it looks very interesting.
Thank you for posting it.
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).
The encoder information doesn't show up until the encoder has had at least one transition.
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.
I attached the simple demo to the top second post of the thread.
This is what the output looks like:
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.
This version displays the speed in several different formats.
Here's what the output looks like.
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.
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
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.
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.
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.
Edit (June 3, 2021): I fixed the link to the other thread but the link doesn't lead to the correct reply.