Capacitive Touch Sensor using PIO - how to set jmp_pin? [solved]

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
Post Reply
cebersp
Posts: 30
Joined: Mon Feb 08, 2021 12:07 pm

Capacitive Touch Sensor using PIO - how to set jmp_pin? [solved]

Post by cebersp » Tue Feb 16, 2021 5:21 pm

Edit: See post 3 for working code!

Hi, I would like to do a cap-sensor again but with PIO for better resolution.
viewtopic.php?f=5&t=9829

Two GPIOs are used. One (16) is output and coupled via a 1 Megaohms resistor to the metal pad. The second (17) is coupled directly to the metal pad. The time needed to charge the capacity of the pad is measured. With increased capacity a longer time is needed.


I need to jump on pin state. Something like

jmp(pin, "target_label")

Where can I find the docu about PIO assembler?
Thank you very much!
Christof

Code: Select all

title= "pioTouchA"
# from Example of using PIO for PWM, and fading the brightness of an LED

from machine import Pin
from rp2 import PIO, StateMachine, asm_pio
from time import sleep

@asm_pio(sideset_init=PIO.OUT_LOW)
def cap_prog():
    mov(y, osr) .side(1) # ISR must be preloaded with max
    #mov(x, osr) .side(1) # ISR must be preloaded with max
    
    label("test_pin") # count down y, until inPin is high
    nop()
    jmp(pin, "is_set") # does not work!
    #nop() [5]
    jmp(y_dec, "test_pin") # tested
    
    label("is_set")        
    mov(isr, y) .side(0) # pin low
    push(block)

    


class PIOCAP:
    def __init__(self, sm_id, outPin, inPin, max_count, count_freq):
        self._sm = StateMachine(sm_id, cap_prog, freq=2 * count_freq, sideset_base=Pin(outPin), jmp_pin=Pin(inPin))
        # Use exec() to load max count into ISR
        self._sm.put(max_count)
        self._sm.exec("pull()")
        #self._sm.exec("mov(isr, osr)")
        self._sm.active(1)
        self._max_count = max_count



# Pin 16 is output
#cap = PIOCAP(0, 16, 17, max_count=(1 << 16) - 1, count_freq=10_000_000)
cap = PIOCAP(0, 16, 17, max_count=(1000), count_freq=10_000_000)

while True:
    print(cap._sm.get())
    sleep(0.01)

Last edited by cebersp on Tue Feb 16, 2021 7:28 pm, edited 4 times in total.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: PIO how to set jmp_pin?

Post by dhylands » Tue Feb 16, 2021 7:03 pm

The documentation for the PIO assembler can be found in the RP2040 datasheet. https://datasheets.raspberrypi.org/rp20 ... asheet.pdf starting at page 324.

I haven't seen any documentation on the Micropython assembler syntax. I've use the examples found in the micropython/examples/rp2 directory, and https://hackspace.raspberrypi.org/books ... ython-pico has some examples/verbage in Appendix C (page 130)

I've also found looking at: https://github.com/micropython/micropyt ... les/rp2.py to be useful. If you're looking for jmp modifiers (like !x is really not_x) or mov modifiers (like invert) you'll find them in this file.

It also has the assembler functions (i.e. jmp/mov/wait/...) and you can see what arguments all of the functions expect.

There is an example of jmp(pin, "target") in the pio_uart_rx.py file: https://github.com/micropython/micropyt ... _rx.py#L58

cebersp
Posts: 30
Joined: Mon Feb 08, 2021 12:07 pm

Re: PIO how to set jmp_pin?

Post by cebersp » Tue Feb 16, 2021 7:16 pm

Thank You very much Dave!
Yes I did have a close look into the datasheet.

I learned, how to do it here:
https://www.raspberrypi.org/forums/view ... 6&t=303606

I do not at all understand it, but the following code works now!
I did learn how to get the result from your code.

Christof


Code: Select all

title= "pioTouchB"
# from Example of using PIO for PWM, and fading the brightness of an LED

from machine import Pin
from rp2 import PIO, StateMachine, asm_pio
from time import sleep

maxTime=10000

@asm_pio(sideset_init=PIO.OUT_LOW)
def cap_prog():
    mov(y, osr) .side(1) # OSR must be preloaded with max
    mov(x, osr) .side(1) 
    
    label("test_pin") # count down y, until inPin is high
    jmp(pin, "is_set") # does work
    jmp(y_dec, "test_pin") # tested
        
    label("is_set")
    mov(isr, y) .side(0) # save result pin low
    
    label("delayLow")
    nop()
    jmp(x_dec, "delayLow") # tested    

    


class PIOCAP:
    def __init__(self, sm_id, outPin, inPin, max_count, count_freq):
        self._sm = StateMachine(sm_id, cap_prog, freq=2 * count_freq, sideset_base=Pin(outPin), jmp_pin=Pin(inPin,Pin.IN))
        # Use exec() to load max count into ISR
        self._sm.put(max_count)
        self._sm.exec("pull()")
        #self._sm.exec("mov(isr, osr)")
        self._sm.active(1)
        self._max_count = max_count
        
    def getCap(self):
        self._sm.exec("push()")
        # Since the PIO can only decrement, convert it back into +ve
        return self._sm.get()



# Pin 16 is output
#cap = PIOCAP(0, 16, 17, max_count=(1 << 16) - 1, count_freq=10_000_000)
cap = PIOCAP(0, 16, 17, max_count=(maxTime), count_freq=10_000_000)

while True:
    print(maxTime-cap.getCap())
    sleep(0.01)


rht
Posts: 1
Joined: Fri Aug 19, 2022 7:44 am

Re: Capacitive Touch Sensor using PIO - how to set jmp_pin? [solved]

Post by rht » Fri Aug 19, 2022 8:00 am

Hi,
I have setup a Pico with Christof's code and have a touch switch working. What I want to do now is set up a second state machine so that I have two touch switches (Up and Down) for a timer application.
I have now got this working with the code below. Posting here in case anyone else in interested.
The key is adding the new line for the second state machine:

dn_cap = PIOCAP(1, 18, 19, max_count=(maxTime), count_freq=10_000_000)

with a "1" to designate the second sm, and with the different pin numbers. The other asm code is the same. The only other thing is additional micropython code to use the new value "dn_cap". I found that the touch switches were sensitive to the time,sleep delay values and in the end I commented them out completely.

I assume more state machines could be added in the same way for more touch sensitive switches. But I haven't tested this.

Hope that helps someone. Enjoy.

Code: Select all

# Second core polls the touch sensitive switch inputs
def second_thread():
    led_onboard = machine.Pin(25, machine.Pin.OUT)
    up_on = machine.Pin(13, machine.Pin.OUT)
    dn_on = machine.Pin(12, machine.Pin.OUT)
    max_reading = 0
    
    # Based on https://forum.micropython.org/viewtopic.php?f=21&t=9833&p=55035#p55035
    maxTime=10000

    @asm_pio(sideset_init=PIO.OUT_LOW)
    def cap_prog():
        mov(y, osr) .side(1) # OSR must be preloaded with max
        mov(x, osr) .side(1) 
    
        label("test_pin") # count down y, until inPin is high
        jmp(pin, "is_set") # does work
        jmp(y_dec, "test_pin") # tested
            
        label("is_set")
        mov(isr, y) .side(0) # save result pin low
    
        label("delayLow")
        nop()
        jmp(x_dec, "delayLow") # tested    

    class PIOCAP:
        def __init__(self, sm_id, outPin, inPin, max_count, count_freq):
            self._sm = StateMachine(sm_id, cap_prog, freq=2 * count_freq, sideset_base=Pin(outPin), jmp_pin=Pin(inPin,Pin.IN))
            # Use exec() to load max count into ISR
            self._sm.put(max_count)
            self._sm.exec("pull()")
            #self._sm.exec("mov(isr, osr)")
            self._sm.active(1)
            self._max_count = max_count
        
        def getCap(self):
            self._sm.exec("push()")
            # Since the PIO can only decrement, convert it back into +ve
            return self._sm.get()

    # Pins 16 and 18 are outputs. 17 and 19 are inputs.
    #cap = PIOCAP(0, 16, 17, max_count=(1 << 16) - 1, count_freq=10_000_000)
    up_cap = PIOCAP(0, 16, 17, max_count=(maxTime), count_freq=10_000_000)
    dn_cap = PIOCAP(1, 18, 19, max_count=(maxTime), count_freq=10_000_000)

    while True:
        while (maxTime-up_cap.getCap()) > 200:
            #max_reading = maxTime-up_cap.getCap()
            up_on.value(1)
            led_onboard.value(1)
            #print ("up cap = " + str(maxTime-up_cap.getCap()) )
        #time.sleep_ms(10)
        while (maxTime-dn_cap.getCap()) > 200:
            #max_reading = maxTime-dn_cap.getCap()
            dn_on.value(1)
            led_onboard.value(1)
            #print ("down cap = " + str(maxTime-dn_cap.getCap()) )
        #time.sleep_ms(10)
        up_on.value(0)
        dn_on.value(0)
        led_onboard.value(0)

Post Reply