According to the rp2040 chip data sheet, peripheral inputs and outputs can be inverted. Micropython also supports inline assembly, so that’s the hack we use here enable input inversion.
The idea is to set up the UART port first, then set up the GPIO inversion. If the inversion is set up first, it’s likely that the UART setup will overwrite the inversion.
- setup UART port
- invert UART input in GPIO
Reversing the above order might work, and would be an interesting experiment.
Disclaimer: This is my first foray into ARM assembler, so beware the newbie curse…
Update Fixed some assembler syntax errors (replaced + with , for offsets, removed # for literals)
Following example is starting point, from section 10.2 of this link
http://docs.micropython.org/en/latest/pyboard/tutorial/assembler.html
@micropython.asm_thumbdef led_on():
movwt(r0, stm.GPIOA)
movw(r1, 1 << 13)
strh(r1, [r0, stm.GPIO_BSRRL])
Reference for ARM Thumb inline assembler
https://micropython-docs-esp32.readthedocs.io/en/esp32_doc/reference/asm_thumb2_index.html
Set GPIO pin to inverting input
# r0 has GPIP pin # (0-29) to invert input
@micropython.asm_thumb
def inv_gpio_in(r0):
; multiply pin * 8 ; offset to regs for that pin
add(r0, r0, r0)
add(r0, r0, r0)
add(r0, r0, r0)
add(r0, r0, 4) ; offset to control register
movwt(r1, 0x40014000) ; base address of GPIO registers
add(r0, r0, r1) ; r0 = final address of pin regs start
ldr(r1, [r0, 4]) ; get control reg value
; Only want to change bits 17:16 in GPIO ctrl reg,
; so retain all but those bits
; NOTE: bits 17:16 should already be clear, but we do so here
; for completeness
movwt(r2, 0x30000) ; mask for bits 17:16
bic(r1, r2) ; clear bits 17:16
; set bits 17:16 = 01 to invert GPIO input
movwt(r2, 0x10000) ; mask for bits 17:16
orr(r1, r2)
str(r1, [r0, 4]) ; save new control reg value
mov(r0, r1) ; return the new control reg value
Less assembler, more python
The above assembler code demonstrates why assembler is used only when necessary. The programmer must manage every little detail.
Really, all that’s needed is the ability to peek (read) and poke (write) memory locations. Since we know the addresses of the GPIO control registers (see below references), writing code to invert the input pin is much shorted in python. Here it is, using peek/poke to access the the control register.
Note: This could be done with a 16-bit read & write to only access the upper half of the control register. But since the endianess wasn’t clear to me I changed it to access the full word, eliminating any question.
Little & big endian code included below, but commented out. The endianess could be determined empirically by picking an endianess and writing a 16 bit value to a register, then reading it back as a 32 bit value and see which half was updated by the 16 bit write.
# python version of inverting gpio in, using peek/poke
def inv_gpio_in( pin_num):
gpio_ctrl_addr = 0x40014000 + pin_num*8 + 4;
mem_poke32(gpio_ctrl_addr, (mem_peek32(gpio_ctrl_addr) & 0xFFFCFFFF) | 0x10000
; little endian 16 bit access
;mem_poke16(gpio_ctrl_addr+2, (mem_peek16(gpio_ctrl_addr+2) & 0xFFFC) | 0x1
; big endian 16 bit access
;mem_poke16(gpio_ctrl_addr, (mem_peek16(gpio_ctrl_addr) & 0xFFFC) | 0x1
8, 16, 32 bit memory peek/read & poke/write functions
The following micropython functions can be used to access 8, 16, or 32 bit memory values
# read 32 bits from memory
# r0 = address
@micropython.asm_thumb
def mem_peek32(r0):
ldr(r0, [r0, 0])
# write 32 bits to memory
# r0 = address
# r1 = data
@micropython.asm_thumb
def mem_poke32(r0, r1):
str(r1, [r0, 0])
# read 16 bits from memory
# r0 = address
@micropython.asm_thumb
def mem_peek16(r0):
ldrh(r0, [r0, 0])
# write 16 bits to memory
# r0 = address
# r1 = data
@micropython.asm_thumb
def mem_poke16(r0, r1):
strh(r1, [r0, 0])
# read 8 bits from memory
# r0 = address
@micropython.asm_thumb
def mem_peek16(r0):
ldrb(r0, [r0, 0])
# write 8 bits to memory
# r0 = address
# r1 = data
@micropython.asm_thumb
def mem_poke16(r0, r1):
strb(r1, [r0, 0])
RPi 2040 GPIO registers
Following is from RP2040 data sheet
The inversion of GPIO input or outputs is controlled by GPIOx_CTRL register, were x=GPIO pin number.
GPIO Register address mapping
This is the memory map for GPIOs 0-29 in the user IO bank