Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 8609

MicroPython • Accurate Edge Counter with RP2040 PIO — Pulse Counting on Demand

$
0
0
Hello everyone!

I wanted to share a little PIO project I recently finished — maybe it'll be useful to someone, or spark discussion.

I wanted to count every edge (high->low and low->high) on a single GPIO pin, and be able to read the count on demand from Python. This was part of a project where I needed to monitor a digital signal’s activity in real-time (up to 100 Hz, TACH signal of a computer fan). The motivation was to keep the counting in the background, so that the main program can go stutter-free :)

I tried first a simple way of waiting for the pin state to change with wait() PIO instructions, but this made the machine halt if the fan was not spinning, and I couldn't have that! So instead I went with a constant loop that checks for state change, so that it is always available to give a reading.

The key goals:
  • Count both rising and falling edges.
  • Keep the counting fully in PIO for speed.
  • Let Python request the current count, which gets pushed from the ISR to Python via FIFO.
  • After each request, the counter resets.
With some slight modifications to the logic, you can change it to read only rising or only falling edges! If anyone's interested in that, let me know! Or if you have any questions as to how this works. I am putting a small demonstration code down there. Buttons are connected to GPIO pins 0 and 1 so that they pull to ground when pressed. Button 0 increases the count, button 1 triggers a request of the current count.

Code:

from machine import Pinfrom rp2 import PIO, StateMachine, asm_pio@asm_pio()def pioRoutine():    mov(isr,null) #clear the ISR    in_(pins,1) #read the initial pin state into ISR        label("restart_count")    set(y,0) #setting counter Y to zero at the beginning    label("loop")    mov(x,isr) #restore or initialize X            jmp(pin,"pin_hi") #check the state of the pin and compare to previous (stored in X)    jmp(not_x,"skip_increment")    set(x,0)    jmp("increment")            label("pin_hi")    jmp(not_x,"lo_to_hi")    jmp("skip_increment")    label("lo_to_hi")    set(x,1)        label("increment")    mov(y,invert(y)) #incrementing the counter (~y, y--, ~y gives y++)    jmp(y_dec,"continue")    label("continue")    mov(y,invert(y))    label("skip_increment")    mov(isr,x) #store x in ISR temporarily    set(x,0) # sets 0 for comparison. Request must be different than 0.    pull(noblock) #not blocking pull. Writes X(=0 now) to OSR if no request was made    mov(x,osr) #store request (or lackthereof) in x    jmp(not_x,"loop") #loop back if X is zero (no request was made)    mov(x,isr) #restore x from ISR (we need ISR now to push the counter Y)    mov(isr,y) #place the counter Y in ISR to be pushed    push() #pyshes the content of ISR (=Y) into RXFIFO    mov(isr,x) #store x in ISR again    irq(rel(0))    jmp("restart_count")    btn0 = Pin(0, Pin.IN, pull=Pin.PULL_UP) #this button increases the counter (monitored by PIO)btn1 = Pin(1, Pin.IN, pull=Pin.PULL_UP) #this button requests the counterdef irq_button(pin):    sm.put(1)def irq_handler(sm):    print(sm.get())btn1.irq(handler=irq_button, trigger=Pin.IRQ_FALLING)sm = StateMachine(0, pioRoutine, in_base=btn0, jmp_pin=btn0)sm.irq(irq_handler)sm.active(1) #enable the state machine

Statistics: Posted by seventz — Sun Apr 06, 2025 11:40 pm



Viewing all articles
Browse latest Browse all 8609

Trending Articles