Add RP2040 PIO UART implementation for split keyboards
This commit is contained in:
		@@ -41,6 +41,7 @@ split = Split(
 | 
			
		||||
    data_pin2=None,  # Second uart pin to allow 2 way communication
 | 
			
		||||
    target_left=True,  # Assumes that left will be the one on USB. Set to folse if it will be the right
 | 
			
		||||
    uart_flip=True,  # Reverses the RX and TX pins if both are provided
 | 
			
		||||
    use_pio=False,  # Use RP2040 PIO implementation of UART. Required if you want to use other pins than RX/TX
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										90
									
								
								kmk/handlers/pio_uart.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								kmk/handlers/pio_uart.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
# Original source of these examples: https://github.com/adafruit/Adafruit_CircuitPython_PIOASM/tree/main/examples
 | 
			
		||||
# SPDX-FileCopyrightText: 2021 Jeff Epler, written for Adafruit Industries
 | 
			
		||||
# SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
import rp2pio
 | 
			
		||||
import adafruit_pioasm
 | 
			
		||||
 | 
			
		||||
tx_code = adafruit_pioasm.assemble(
 | 
			
		||||
    """
 | 
			
		||||
.program uart_tx
 | 
			
		||||
.side_set 1 opt
 | 
			
		||||
; An 8n1 UART transmit program.
 | 
			
		||||
; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
 | 
			
		||||
  pull side 1 [7] ; Assert stop bit, or stall with line in idle state
 | 
			
		||||
  set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks
 | 
			
		||||
bitloop: ; This loop will run 8 times (8n1 UART)
 | 
			
		||||
  out pins, 1 ; Shift 1 bit from OSR to the first OUT pin
 | 
			
		||||
  jmp x-- bitloop [6] ; Each loop iteration is 8 cycles.
 | 
			
		||||
"""
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
rx_code = adafruit_pioasm.assemble(
 | 
			
		||||
    """
 | 
			
		||||
.program uart_rx_mini
 | 
			
		||||
 | 
			
		||||
; Minimum viable 8n1 UART receiver. Wait for the start bit, then sample 8 bits
 | 
			
		||||
; with the correct timing.
 | 
			
		||||
; IN pin 0 is mapped to the GPIO used as UART RX.
 | 
			
		||||
; Autopush must be enabled, with a threshold of 8.
 | 
			
		||||
 | 
			
		||||
    wait 0 pin 0        ; Wait for start bit
 | 
			
		||||
    set x, 7 [10]       ; Preload bit counter, delay until eye of first data bit
 | 
			
		||||
bitloop:                ; Loop 8 times
 | 
			
		||||
    in pins, 1          ; Sample data
 | 
			
		||||
    jmp x-- bitloop [6] ; Each iteration is 8 cycles
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PIO_UART:
 | 
			
		||||
    def __init__(self, *, tx, rx, baudrate=9600):
 | 
			
		||||
        if tx:
 | 
			
		||||
            self.tx_pio = rp2pio.StateMachine(
 | 
			
		||||
                tx_code,
 | 
			
		||||
                first_out_pin=tx,
 | 
			
		||||
                first_sideset_pin=tx,
 | 
			
		||||
                frequency=8 * baudrate,
 | 
			
		||||
                initial_sideset_pin_state=1,
 | 
			
		||||
                initial_sideset_pin_direction=1,
 | 
			
		||||
                initial_out_pin_state=1,
 | 
			
		||||
                initial_out_pin_direction=1,
 | 
			
		||||
                sideset_enable=True,
 | 
			
		||||
            )
 | 
			
		||||
        if rx:
 | 
			
		||||
            self.rx_pio = rp2pio.StateMachine(
 | 
			
		||||
                rx_code,
 | 
			
		||||
                first_in_pin=rx,
 | 
			
		||||
                frequency=8 * baudrate,
 | 
			
		||||
                auto_push=True,
 | 
			
		||||
                push_threshold=8,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def timeout(self):
 | 
			
		||||
        return 0
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def baudrate(self):
 | 
			
		||||
        return self.tx_pio.frequency // 8
 | 
			
		||||
 | 
			
		||||
    @baudrate.setter
 | 
			
		||||
    def baudrate(self, frequency):
 | 
			
		||||
        self.tx_pio.frequency = frequency * 8
 | 
			
		||||
        self.rx_pio.frequency = frequency * 8
 | 
			
		||||
 | 
			
		||||
    def write(self, buf):
 | 
			
		||||
        return self.tx_pio.write(buf)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def in_waiting(self):
 | 
			
		||||
        return self.rx_pio.in_waiting
 | 
			
		||||
 | 
			
		||||
    def read(self, n):
 | 
			
		||||
        b = bytearray(n)
 | 
			
		||||
        n = self.rx_pio.readinto(b)
 | 
			
		||||
        return b[:n]
 | 
			
		||||
 | 
			
		||||
    def readinto(self, buf):  # pylint: disable=unused-argument
 | 
			
		||||
        return self.rx_pio.readinto(buf)  # pylint: disable=undefined-variable
 | 
			
		||||
@@ -36,6 +36,7 @@ class Split(Module):
 | 
			
		||||
        data_pin2=None,
 | 
			
		||||
        target_left=True,
 | 
			
		||||
        uart_flip=True,
 | 
			
		||||
        use_pio=False,
 | 
			
		||||
        debug_enabled=False,
 | 
			
		||||
    ):
 | 
			
		||||
        self._is_target = True
 | 
			
		||||
@@ -49,6 +50,7 @@ class Split(Module):
 | 
			
		||||
        self.data_pin2 = data_pin2
 | 
			
		||||
        self.target_left = target_left
 | 
			
		||||
        self.uart_flip = uart_flip
 | 
			
		||||
        self._use_pio = use_pio
 | 
			
		||||
        self._uart = None
 | 
			
		||||
        self._uart_interval = uart_interval
 | 
			
		||||
        self._debug_enabled = debug_enabled
 | 
			
		||||
@@ -75,6 +77,11 @@ class Split(Module):
 | 
			
		||||
            self._advertising = False
 | 
			
		||||
            self._psave_enable = False
 | 
			
		||||
 | 
			
		||||
        if self._use_pio:
 | 
			
		||||
            from kmk.handlers.pio_uart import PIO_UART
 | 
			
		||||
 | 
			
		||||
            self.PIO_UART = PIO_UART
 | 
			
		||||
 | 
			
		||||
    def during_bootup(self, keyboard):
 | 
			
		||||
        # Set up name for target side detection and BLE advertisment
 | 
			
		||||
        name = str(getmount('/').label)
 | 
			
		||||
@@ -116,13 +123,19 @@ class Split(Module):
 | 
			
		||||
 | 
			
		||||
        if self.split_type == SplitType.UART and self.data_pin is not None:
 | 
			
		||||
            if self._is_target or not self.uart_flip:
 | 
			
		||||
                self._uart = busio.UART(
 | 
			
		||||
                    tx=self.data_pin2, rx=self.data_pin, timeout=self._uart_interval
 | 
			
		||||
                )
 | 
			
		||||
                if self._use_pio:
 | 
			
		||||
                    self._uart = self.PIO_UART(tx=self.data_pin2, rx=self.data_pin)
 | 
			
		||||
                else:
 | 
			
		||||
                    self._uart = busio.UART(
 | 
			
		||||
                        tx=self.data_pin2, rx=self.data_pin, timeout=self._uart_interval
 | 
			
		||||
                    )
 | 
			
		||||
            else:
 | 
			
		||||
                self._uart = busio.UART(
 | 
			
		||||
                    tx=self.data_pin, rx=self.data_pin2, timeout=self._uart_interval
 | 
			
		||||
                )
 | 
			
		||||
                if self._use_pio:
 | 
			
		||||
                    self._uart = self.PIO_UART(tx=self.data_pin, rx=self.data_pin2)
 | 
			
		||||
                else:
 | 
			
		||||
                    self._uart = busio.UART(
 | 
			
		||||
                        tx=self.data_pin, rx=self.data_pin2, timeout=self._uart_interval
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
        # Attempt to sanely guess a coord_mapping if one is not provided.
 | 
			
		||||
        if not keyboard.coord_mapping:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user