rpifour
Posts: 14
Joined: Thu Sep 26, 2019 6:15 pm

Dynamic UART pin assignments?

Tue Nov 07, 2023 7:09 pm

I'd like to read three serial interfaces with a single Pico W. (So using UART0 and UART1 does not suffice.) I do NOT need to read all of them at the same time, so I thought it should be possible to reassign the pins:

Code: Select all

// read from first pin pair
uart = UART(0, baudrate=2400, tx=Pin(0), rx=Pin(1))
uart.readline()

// reassign, first method, to second pin pair
del uart
uart = UART(0, baudrate=2400, tx=Pin(12), rx=Pin(13))
uart.readline()

// reassign, second method, to second pin pair
uart.init(tx=Pin(12), rx=Pin(13))
uart.readline()
etc.

This code executes without error, but after reassignment, no data is received anymore (even when reassigning to the original pins again).

If I power off and on the Pico W and I start with pins 12/13 and the change to pins 0/1, I have similar results, just that pins 12/13 are now working and 0/1 not.

In my understanding this change should be working. What am I doing wrong?
Last edited by rpifour on Wed Nov 08, 2023 5:02 am, edited 1 time in total.

fdufnews
Posts: 447
Joined: Fri Oct 07, 2011 5:37 pm

Re: Dynamic UART pin assignments?

Tue Nov 07, 2023 8:30 pm

There is a note in MicroPython Documentation that maybe of interest.
Note: It is possible to call init() multiple times on the same object in order to reconfigure UART on the fly.
That allows using single UART peripheral to serve different devices attached to different GPIO pins. Only one
device can be served at a time in that case. Also do not call deinit() as it will prevent calling init() again.
Maybe del has the same effect as deinit()

rpifour
Posts: 14
Joined: Thu Sep 26, 2019 6:15 pm

Re: Dynamic UART pin assignments?

Wed Nov 08, 2023 5:01 am

Thanks, I also saw this note, but in my understanding it should not apply to the second method. The original posting probably was not clear, but I tried init() independently of del: Reboot of Pico, constructor of UART called, init() called. This also does not work.

BillTodd
Posts: 79
Joined: Sat Apr 17, 2021 11:01 am

Re: Dynamic UART pin assignments?

Wed Nov 08, 2023 1:58 pm

How about using a PIO as an extra UART?

rpifour
Posts: 14
Joined: Thu Sep 26, 2019 6:15 pm

Re: Dynamic UART pin assignments?

Wed Nov 08, 2023 5:50 pm

@BillTodd: Fair enough, but this would entail quite a bit of timing-sensitive coding as well as decoding signals to recreate the bytes. I have done this on a different platform in a different language, and I would definitely like to avoid this. Especially if it can be done with less than a handful of lines of Python.

hippy
Posts: 15878
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Dynamic UART pin assignments?

Wed Nov 08, 2023 6:33 pm

PIO UART RX is fairly easy to create with the conversion to 8-bit bytes included. You only need one PIO program and can run it three times with whatever pins you want, not just the dedicated RXD pins.

I would personally do it this way rather than using any actual UART. It's probably no harder than trying to get dynamic pins working and without any hidden gotchas.

Untested, but could well work, and at least gives you the basic idea. You probably want to change the pin numbers -

Code: Select all

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

RX0_PIN = 1
RX1_PIN = 5
RX2_PIN = 9

RX_BAUD = 2400

@asm_pio(set_init     = PIO.IN_HIGH,
         in_shiftdir  = PIO.SHIFT_RIGHT,
         fifo_join    = PIO.JOIN_RX)
def PioRx8N1():
    wait( 0, pin, 0)
    set(x, 7)                   [10]
    label("rxloop")
    in_(pins, 1)
    jmp(x_dec, "rxloop")        [6]
    push()

rx0Pin = Pin(RX0_PIN, Pin.IN, Pin.PULL_UP)
rx1Pin = Pin(RX1_PIN, Pin.IN, Pin.PULL_UP)
rx2Pin = Pin(RX2_PIN, Pin.IN, Pin.PULL_UP)

rx0 = StateMachine(0, PioRx8N1, freq=RX_BAUD * 8, in_base=rx0Pin)
rx1 = StateMachine(1, PioRx8N1, freq=RX_BAUD * 8, in_base=rx1Pin)
rx2 = StateMachine(2, PioRx8N1, freq=RX_BAUD * 8, in_base=rx2Pin)

rx0.active(1)
rx1.active(1)
rx2.active(1)

def Read(N, rxN):
    while rxN.rx_fifo():
        b = rxN.get() >> 24
        print("RX{}  <  {:>02X}  {:>08b}".format(N, b, b))

while True:
    Read(0, rx0)
    Read(1, rx1)
    Read(2, rx2)

rpifour
Posts: 14
Joined: Thu Sep 26, 2019 6:15 pm

Re: Dynamic UART pin assignments?

Sat Nov 11, 2023 3:51 pm

@hippy: Thanks for the code sample. Unfortunately, it does not work out of the box for me. I get the idea, though I am not really keen on doing embedded RP2040 assembly. With no existing knowledge of the hardware, I guess this would take me quite some time to get up to speed.

hippy
Posts: 15878
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Dynamic UART pin assignments?

Sat Nov 11, 2023 8:27 pm

rpifour wrote:
Sat Nov 11, 2023 3:51 pm
@hippy: Thanks for the code sample. Unfortunately, it does not work out of the box for me.
That's a shame. I added some code to check it was working and that works for me. I can send bytes to each UART receiver and each will receive what is sent.

WARNING - Disconnect external hardware connections to the pins being used before running the code as this will set the receive input pins to outputs to avoid having to add loop-back links to the board to test -

Code: Select all

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

RX0_PIN = 1
RX1_PIN = 5
RX2_PIN = 9

RX_BAUD = 2400

@asm_pio(set_init     = PIO.IN_HIGH,
         in_shiftdir  = PIO.SHIFT_RIGHT,
         fifo_join    = PIO.JOIN_RX)
def PioRx8N1():
    wait( 0, pin, 0)
    set(x, 7)                   [10]
    label("rxloop")
    in_(pins, 1)
    jmp(x_dec, "rxloop")        [6]
    push()

rx0Pin = Pin(RX0_PIN, Pin.IN, Pin.PULL_UP)
rx1Pin = Pin(RX1_PIN, Pin.IN, Pin.PULL_UP)
rx2Pin = Pin(RX2_PIN, Pin.IN, Pin.PULL_UP)

rx0 = StateMachine(0, PioRx8N1, freq=RX_BAUD * 8, in_base=rx0Pin)
rx1 = StateMachine(1, PioRx8N1, freq=RX_BAUD * 8, in_base=rx1Pin)
rx2 = StateMachine(2, PioRx8N1, freq=RX_BAUD * 8, in_base=rx2Pin)

rx0.active(1)
rx1.active(1)
rx2.active(1)

# --- Added for debugging below

@asm_pio(sideset_init = PIO.OUT_HIGH,
         out_init     = PIO.OUT_HIGH,
         out_shiftdir = PIO.SHIFT_RIGHT,
         fifo_join    = PIO.JOIN_TX)
def PioTx8N1():
    pull()            .side(1)  [7]
    set(x, 7)         .side(0)  [7]
    label("txloop")
    out(pins, 1)
    jmp(x_dec, "txloop")        [6]

tx0Pin = Pin(RX0_PIN, Pin.OUT, value=1)
tx1Pin = Pin(RX1_PIN, Pin.OUT, value=1)
tx2Pin = Pin(RX2_PIN, Pin.OUT, value=1)

tx0 = StateMachine(3, PioTx8N1, freq=RX_BAUD * 8, sideset_base=tx0Pin, out_base=tx0Pin)
tx1 = StateMachine(4, PioTx8N1, freq=RX_BAUD * 8, sideset_base=tx1Pin, out_base=tx1Pin)
tx2 = StateMachine(5, PioTx8N1, freq=RX_BAUD * 8, sideset_base=tx2Pin, out_base=tx2Pin)

tx0.active(1)
tx1.active(1)
tx2.active(1)

def Write(N, txN, c):
    b = ord(c)
    print("TX{}  >  {:>02X}  {:>08b}  {}".format(N, b, b, chr(b)))
    txN.put(b)

# --- Added for debugging above

def Read(N, rxN):
    while rxN.rx_fifo():
      b = rxN.get() >> 24
      print("RX{}  <  {:>02X}  {:>08b}  {}".format(N, b, b, chr(b)))

while True:
  # --- Added for debugging below
  import time
  time.sleep(1)
  print("")
  Write(0, tx0, "A")
  Write(1, tx1, "B")
  Write(2, tx2, "C")
  # --- Added for debugging above
  Read(0, rx0)
  Read(1, rx1)
  Read(2, rx2)

Code: Select all

pi@Pi3B:~ $ mptool run PioUartRxTimesThree_test.py
Executing 'PioUartRxTimesThree_test.py' on the device

TX0  >  41  01000001  A
TX1  >  42  01000010  B
TX2  >  43  01000011  C
RX0  <  41  01000001  A
RX1  <  42  01000010  B
RX2  <  43  01000011  C

rpifour
Posts: 14
Joined: Thu Sep 26, 2019 6:15 pm

Re: Dynamic UART pin assignments?

Fri Dec 08, 2023 6:22 pm

My final solution was to create a UART with the first set of pins, do the needed comms. Delete the UART. Create a new UART with a different set of pins, do the needed comms. Delete the UART again. And so on.

This works fine with the slow changes that I need. I did not do any checks on the runtime of each cycle, especially the overhead incurred by doing this instead of calling init() with different pins.

Return to “MicroPython”