Read Motor Speed Better By Making The RP2040 PIO Do It

A quadrature encoder provides a way to let hardware read movement (and direction) of a shaft, and they can be simple, effective, and inexpensive devices. But [Paulo Marques] observed that when it comes to reading motor speeds with them, what works best at high speeds doesn’t work at low speeds, and vice versa. His solution? PicoEncoder is a library providing a lightweight and robust method of using the Programmable I/O (PIO) hardware on the RP2040 to get better results, even (or especially) from cheap encoders, and do it efficiently.

The results of the sub-step method (blue) resemble a low-pass filter, but is delivered with no delay or CPU burden.

The output of a quadrature encoder is typically two square waves that are out of phase with one another. This data says whether a shaft is moving, and in what direction. When used to measure something like a motor shaft, one can also estimate rotation speed. Count how many steps come from the encoder over a period of time, and use that as the basis to calculate something like revolutions per minute.

[Paulo] points out that one issue with this basic method is that the quality depends a lot on how much data one has to work with. But the slower a motor turns, the less data one gets. To work around this, one can use a different calculation optimized for low speeds, but there’s really no single solution that handles high and low speeds well.

Another issue is that readings at the “edges” of step transitions can have a lot of noise. This can be ignored and assumed to average out, but it’s a source of inaccuracy that gets worse at slower speeds. Finally, while an ideal encoder has individual phases that are exactly 50% duty cycle and exactly 90 degrees out of phase with one another. This is almost never actually the case with cheaper encoders. Again, a source of inaccuracy.

[Paulo]’s solution was to roll his own method with the RP2040’s PIO, using a hybrid approach to effect a “sub-step” quadrature encoder. Compared to simple step counting, PicoEncoder more carefully tracks transitions to avoid problems with noise, and even accounts for phase size differences present in a particular encoder. The result is a much more accurate calculation of motor speed and position without any delays. Most of the work is done by the PIO of the RP2040, which does the low-level work of counting steps and tracking transitions without any CPU time involved. Try it out the next time you need to read a quadrature encoder for a motor!

The PIO is one of the more interesting pieces of functionality in the RP2040 and it’s great to see it used in a such a clever way. As our own Elliot Williams put it when he evaluated the RP2040, the PIO promises never having to bit-bang a solution again.

4 thoughts on “Read Motor Speed Better By Making The RP2040 PIO Do It

  1. Nice use of the PIO. For those unfamiliar with the PIO, it’s a small piece of IP available in Raspberry Pi Pico SOCs that can clock and manipulate a bitstream under the control of its own tiny instruction set. Because it can be combined with DMA engines and the interrupt controller, its operation can be completely independent of an executing program. It’s been used for everything from simple SPI up to generating digital video. If you love bit twiddling and haven’t tried it, go grab the RP2040 or RP2350 datasheet and have a read. It’s addictive.

  2. I will vouch for this library on the 2040- I used it in a personal project with a really high count encoder recently and it’s absolutely zero drama and wonderful. I really appreciate Paulo’s work.

  3. Those PIO things are indeed quite impressive. I did find the Hackaday article a bit confusing. The writeup on the (already linked to) github page is much clearer.

    On a sidenote, does anyone know how the graphs are made? These look quite simple (which I like). It’s always a compromise between the amount of work to do the setup, and how easy it is to get the data into the graph. I once experimented with matplotlib.pyplot (in python) and it was a bit of a mess, partly because of my limited python knowledge, but also because it had to be a multithreaded application (I used “multiprocessing”.

    1. I usually use pyplot. It generally works well enough for me. Multithreaded applications aren’t an issue either. Try reading about animations for details.

      However, if you only want to plot the output from a controller, then Arduino serial plotter is awesome.

      For more serious projects, I use PyQtGraph. It’s really well optimized. But can be difficult to work with

Leave a Reply to AndrewCancel reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.