Add UART line inversion support (#5522)

* feat(machine): add UART line inversion support.

Add InvertTX and InvertRX to UARTConfig to allow enabling hardware
line inversion on supported targets. Added hardware implementation for
RP2 (RP2040, RP2350), STM32 (newer families), SAM (SAMD51, SAME5x),
and ESP (ESP32, ESP32-C3, ESP32-C6).

* refactor(machine): Refactor UART inversion with pin setter helpers.

RP2: Extract setOutOver/setInOver methods on Pin, removing inline
IO control register manipulation from UART configure.

SAM: Switch to SetCTRLA_TXINV/SetCTRLA_RXINV methods, dropping
the unused device/sam import and raw SetBits/ClearBits calls.

---------

Co-authored-by: Konstantin Sharlaimov <ksharlaimov@inavflight.com>
This commit is contained in:
Konstantin Sharlaimov
2026-07-17 10:45:50 +02:00
committed by GitHub
parent 7994d2e912
commit ea003da13f
13 changed files with 109 additions and 4 deletions
+3
View File
@@ -621,6 +621,9 @@ func (uart *UART) Configure(config UARTConfig) error {
//sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ;
uart.Bus.CTRLB.SetBits(sam.SERCOM_USART_CTRLB_TXEN | sam.SERCOM_USART_CTRLB_RXEN)
// Configure RX/TX inversion
uart.setInversion(config)
// Enable USART1 port.
// sercom->USART.CTRLA.bit.ENABLE = 0x1u;
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_CTRLA_ENABLE)
+3
View File
@@ -1102,6 +1102,9 @@ func (uart *UART) Configure(config UARTConfig) error {
//sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ;
uart.Bus.CTRLB.SetBits(sam.SERCOM_USART_INT_CTRLB_TXEN | sam.SERCOM_USART_INT_CTRLB_RXEN)
// Configure RX/TX inversion
uart.setInversion(config)
// Enable USART1 port.
// sercom->USART.CTRLA.bit.ENABLE = 0x1u;
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_INT_CTRLA_ENABLE)
+10
View File
@@ -332,10 +332,20 @@ func (uart *UART) Configure(config UARTConfig) {
if config.RX != NoPin {
config.RX.configure(PinConfig{Mode: PinInputPullup}, uart.TXRXSignal)
if config.InvertRX {
inFunc(uart.TXRXSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX)<<esp.GPIO_FUNC_IN_SEL_CFG_IN_SEL_Pos | esp.GPIO_FUNC_IN_SEL_CFG_IN_INV_SEL)
} else {
inFunc(uart.TXRXSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX)<<esp.GPIO_FUNC_IN_SEL_CFG_IN_SEL_Pos)
}
}
if config.TX != NoPin {
config.TX.configure(PinConfig{Mode: PinOutput}, uart.TXRXSignal)
if config.InvertTX {
config.TX.outFunc().Set(uart.TXRXSignal | esp.GPIO_FUNC_OUT_SEL_CFG_INV_SEL)
} else {
config.TX.outFunc().Set(uart.TXRXSignal)
}
}
if config.RTS != NoPin {
+10 -2
View File
@@ -447,9 +447,17 @@ func (uart *UART) setupPins(config UARTConfig, regs registerSet) {
config.TX.Configure(PinConfig{Mode: PinInputPullup})
// link TX with GPIO signal X (technical reference manual 5.10) (this is not interrupt signal!)
config.TX.outFunc().Set(regs.gpioMatrixSignal)
if config.InvertTX {
config.TX.outFunc().Set(regs.gpioMatrixSignal | esp.GPIO_FUNC_OUT_SEL_CFG_INV_SEL)
} else {
config.TX.outFunc().Set(regs.gpioMatrixSignal)
}
// link RX with GPIO signal X and route signals via GPIO matrix (GPIO_SIGn_IN_SEL 0x40)
inFunc(regs.gpioMatrixSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX))
if config.InvertRX {
inFunc(regs.gpioMatrixSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX) | esp.GPIO_FUNC_IN_SEL_CFG_IN_INV_SEL)
} else {
inFunc(regs.gpioMatrixSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX))
}
}
func (uart *UART) configureInterrupt(intrMapReg *volatile.Register32) { // Disable all UART interrupts
+10 -2
View File
@@ -460,9 +460,17 @@ func (uart *UART) setupPins(config UARTConfig, regs registerSet) {
config.TX.Configure(PinConfig{Mode: PinInputPullup})
// link TX with GPIO signal X (technical reference manual, GPIO matrix)
config.TX.outFunc().Set(regs.gpioMatrixSignal)
if config.InvertTX {
config.TX.outFunc().Set(regs.gpioMatrixSignal | esp.GPIO_FUNC_OUT_SEL_CFG_INV_SEL)
} else {
config.TX.outFunc().Set(regs.gpioMatrixSignal)
}
// link RX with GPIO signal X and route signals via GPIO matrix
inFunc(regs.gpioMatrixSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX))
if config.InvertRX {
inFunc(regs.gpioMatrixSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX) | esp.GPIO_FUNC_IN_SEL_CFG_IN_INV_SEL)
} else {
inFunc(regs.gpioMatrixSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(config.RX))
}
}
func (uart *UART) configureInterrupt(intrMapReg *volatile.Register32) {
+10
View File
@@ -141,6 +141,16 @@ func (p Pin) setSchmitt(trigger bool) {
p.padCtrl().ReplaceBits(boolToBit(trigger)<<rp.PADS_BANK0_GPIO0_SCHMITT_Pos, rp.PADS_BANK0_GPIO0_SCHMITT_Msk, 0)
}
// setOutOver sets the output override (OUTOVER field) for the pin.
func (p Pin) setOutOver(over uint32) {
p.ioCtrl().ReplaceBits(over<<rp.IO_BANK0_GPIO0_CTRL_OUTOVER_Pos, rp.IO_BANK0_GPIO0_CTRL_OUTOVER_Msk, 0)
}
// setInOver sets the input override (INOVER field) for the pin.
func (p Pin) setInOver(over uint32) {
p.ioCtrl().ReplaceBits(over<<rp.IO_BANK0_GPIO0_CTRL_INOVER_Pos, rp.IO_BANK0_GPIO0_CTRL_INOVER_Msk, 0)
}
// setFunc will set pin function to fn.
func (p Pin) setFunc(fn pinFunc) {
// Set input enable, Clear output disable
+10
View File
@@ -52,9 +52,19 @@ func (uart *UART) Configure(config UARTConfig) error {
// set GPIO mux to UART for the pins
if config.TX != NoPin {
config.TX.Configure(PinConfig{Mode: PinUART})
if config.InvertTX {
config.TX.setOutOver(rp.IO_BANK0_GPIO0_CTRL_OUTOVER_INVERT)
} else {
config.TX.setOutOver(rp.IO_BANK0_GPIO0_CTRL_OUTOVER_NORMAL)
}
}
if config.RX != NoPin {
config.RX.Configure(PinConfig{Mode: PinUART})
if config.InvertRX {
config.RX.setInOver(rp.IO_BANK0_GPIO0_CTRL_INOVER_INVERT)
} else {
config.RX.setInOver(rp.IO_BANK0_GPIO0_CTRL_INOVER_NORMAL)
}
}
if config.RTS != 0 {
config.RTS.Configure(PinConfig{Mode: PinOutput})
+17
View File
@@ -0,0 +1,17 @@
//go:build sam && (atsamd51 || atsame5x)
package machine
// Configure UART TX/RX line inversion using SVD-generated APIs.
func (uart *UART) setInversion(config UARTConfig) {
if config.InvertTX {
uart.Bus.SetCTRLA_TXINV(1)
} else {
uart.Bus.SetCTRLA_TXINV(0)
}
if config.InvertRX {
uart.Bus.SetCTRLA_RXINV(1)
} else {
uart.Bus.SetCTRLA_RXINV(0)
}
}
+7
View File
@@ -0,0 +1,7 @@
//go:build sam && !atsamd51 && !atsame5x
package machine
// Hardware inversion is not supported on SAMD21.
func (uart *UART) setInversion(config UARTConfig) {
}
+3
View File
@@ -56,6 +56,9 @@ func (uart *UART) Configure(config UARTConfig) {
// Set baud rate
uart.SetBaudRate(config.BaudRate)
// Configure RX/TX inversion
uart.setInversion(config)
// Enable USART port, tx, rx and rx interrupts
uart.Bus.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
+17
View File
@@ -0,0 +1,17 @@
//go:build stm32 && !(stm32f1 || stm32f4)
package machine
// Configure UART TX/RX line inversion using SVD-generated APIs.
func (uart *UART) setInversion(config UARTConfig) {
if config.InvertTX {
uart.Bus.SetCR2_TXINV(1)
} else {
uart.Bus.SetCR2_TXINV(0)
}
if config.InvertRX {
uart.Bus.SetCR2_RXINV(1)
} else {
uart.Bus.SetCR2_RXINV(0)
}
}
@@ -0,0 +1,7 @@
//go:build stm32 && (stm32f1 || stm32f4)
package machine
// Hardware inversion is not supported on F1/F4.
func (uart *UART) setInversion(config UARTConfig) {
}
+2
View File
@@ -13,6 +13,8 @@ type UARTConfig struct {
RX Pin
RTS Pin
CTS Pin
InvertTX bool // Invert TX line (active low becomes active high, etc.)
InvertRX bool // Invert RX line (active low becomes active high, etc.)
}
// NullSerial is a serial version of /dev/null (or null router): it drops