Summary of ACCURATE CYCLE COUNTING ON RP2040 MICROPYTHON
The article describes Jeremy Bentham’s RP2040-based vehicle speedometer ported to MicroPython, using PWM edge counting plus DMA to measure vehicle speed. It discusses challenges of accurate timing in MicroPython, solutions for fast and slow signals (edge counting and time-interval methods), and wrapping hardware access via pico_devices.py using uctypes. Code is available on GitHub.
Parts used in the RP2040-based vehicle speedometer:
- RP2040 microcontroller (Pico or compatible board)
- Wheel or vehicle speed sensor providing pulse output
- Wiring/connectors to sensor and board
- Power supply for RP2040 and sensor
- Optional enclosure or mounting hardware
- MicroPython firmware on RP2040
- pico_devices.py software module
- DMA peripheral (on RP2040, used via software)
- PWM peripheral (on RP2040, used via software)
The RP2040 is a gorgeous little chip with a well-defined datasheet and a fantastic price tag. Two SDKs are even offered: one based on C and the other MicroPython. More experienced MCU wranglers will likely reach for the C variant, but Python does bring a certain speed when banging out a quick project or proof of concept. Perhaps that’s why [Jeremy Bentham] ported his RP2040-based vehicle speedometer to MicroPython.

The two things that make that difficult are that MicroPython tries to be pretty generic, which means some hackery is needed to talk to the low-level hardware, and that MicroPython doesn’t have a reputation for accurate cycle counting. In this case, the low-level hardware is the PWM peripheral. He details the underlying mechanism in more detail in the C version. On the RP2040, the PWM module can count pulse edges on an input. However, you must start and stop it accurately to calculate the amount of time captured. From there, it’s just edges divided by time. For this, the DMA system is pulled in. A DMA request can be triggered once the PWM counter rolls over. The other PWM channel acts as a timer, and when the timer expires, the DMA request turns off the counter. This works great for fast signals but is inaccurate for slow signals (below 1kHz). So, a reciprocal or time-interval system is included, where the time between edges is captured instead of counting the number of edges in a period,
What’s interesting here is how the hardware details are wrapped neatly into pico_devices.py. The uctypes module from MicroPython allows access to MMIO devices such as DMA and PWM. The code is available on GitHub. Of course, [Jeremy] is no stranger to hacking around on the RP2040, as he has previously rolled his own WiFi driver for the Pico W.
Source: ACCURATE CYCLE COUNTING ON RP2040 MICROPYTHON
- Can MicroPython be used to implement an RP2040-based vehicle speedometer?
Yes. Jeremy Bentham ported his RP2040 vehicle speedometer to MicroPython using PWM, DMA, and additional timing methods. - How does the RP2040 measure speed in this project?
Speed is measured by counting PWM input edges over a time period and dividing edges by time, with an alternate time-interval method for slow signals. - What role does DMA play in the MicroPython implementation?
DMA is triggered on PWM counter rollover and is used to turn off the counter when a timer expires, enabling accurate capture of edges over the measured period. - How are slow signals handled in this speedometer?
For slow signals below about 1 kHz, a reciprocal or time-interval system captures the time between edges instead of edge counting. - How does the project access low-level RP2040 hardware from MicroPython?
It uses the uctypes module and a pico_devices.py wrapper to access MMIO devices such as DMA and PWM. - Is the MicroPython implementation as accurate as the C version?
MicroPython requires additional hackery for accurate cycle counting; the project uses DMA and PWM tricks to achieve accurate timing comparable to the C approach for most signal speeds. - Where can the code for this project be found?
The code is available on GitHub, as mentioned in the article.
