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![Smile :)]()
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:
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.
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 machineStatistics: Posted by seventz — Sun Apr 06, 2025 11:40 pm