From 87e48c105791187ae1bbd096ae55029de2b072a3 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Wed, 9 Jun 2021 19:11:42 +0200 Subject: [PATCH] machine/rp2040: implement UART0/UART1, can be used on all rp2040 boards Signed-off-by: deadprogram --- src/machine/board_nano-rp2040.go | 6 -- src/machine/machine_rp2040.go | 34 +++++++- src/machine/machine_rp2040_gpio.go | 3 + src/machine/machine_rp2040_uart.go | 133 +++++++++++++++++++++++++++++ src/machine/uart.go | 19 ++++- src/runtime/runtime_rp2040.go | 5 ++ 6 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 src/machine/machine_rp2040_uart.go diff --git a/src/machine/board_nano-rp2040.go b/src/machine/board_nano-rp2040.go index 2863f672..ca85596a 100644 --- a/src/machine/board_nano-rp2040.go +++ b/src/machine/board_nano-rp2040.go @@ -47,12 +47,6 @@ const ( LED = GPIO6 ) -// UART1 pins -const ( - UART_TX_PIN Pin = GPIO0 - UART_RX_PIN Pin = GPIO1 -) - // I2C pins const ( SDA_PIN Pin = GPIO12 diff --git a/src/machine/machine_rp2040.go b/src/machine/machine_rp2040.go index 7f0a71c4..f7580967 100644 --- a/src/machine/machine_rp2040.go +++ b/src/machine/machine_rp2040.go @@ -4,7 +4,7 @@ package machine import ( "device/rp" - _ "unsafe" + "runtime/interrupt" ) const ( @@ -82,3 +82,35 @@ func machineInit() { func ticks() uint64 { return timer.timeElapsed() } + +// UART pins +const ( + UART_TX_PIN = UART0_TX_PIN + UART_RX_PIN = UART0_RX_PIN + UART0_TX_PIN = GPIO0 + UART0_RX_PIN = GPIO1 + UART1_TX_PIN = GPIO8 + UART1_RX_PIN = GPIO9 +) + +// UART on the RP2040 +var ( + UART0 = &_UART0 + _UART0 = UART{ + Buffer: NewRingBuffer(), + Bus: rp.UART0, + } + + UART1 = &_UART1 + _UART1 = UART{ + Buffer: NewRingBuffer(), + Bus: rp.UART1, + } +) + +var Serial = UART0 + +func init() { + UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt) + UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt) +} diff --git a/src/machine/machine_rp2040_gpio.go b/src/machine/machine_rp2040_gpio.go index 62c6c335..b994988e 100644 --- a/src/machine/machine_rp2040_gpio.go +++ b/src/machine/machine_rp2040_gpio.go @@ -67,6 +67,7 @@ const ( PinInputPulldown PinInputPullup PinAnalog + PinUART ) // set drives the pin high @@ -152,6 +153,8 @@ func (p Pin) Configure(config PinConfig) { case PinAnalog: p.setFunc(fnNULL) p.pulloff() + case PinUART: + p.setFunc(fnUART) } } diff --git a/src/machine/machine_rp2040_uart.go b/src/machine/machine_rp2040_uart.go new file mode 100644 index 00000000..e2b5f733 --- /dev/null +++ b/src/machine/machine_rp2040_uart.go @@ -0,0 +1,133 @@ +// +build rp2040 + +package machine + +import ( + "device/rp" + "runtime/interrupt" +) + +// UART on the RP2040. +type UART struct { + Buffer *RingBuffer + Bus *rp.UART0_Type + Interrupt interrupt.Interrupt +} + +// Configure the UART. +func (uart *UART) Configure(config UARTConfig) error { + initUART(uart) + + // Default baud rate to 115200. + if config.BaudRate == 0 { + config.BaudRate = 115200 + } + + // Use default pins if pins are not set. + if config.TX == 0 && config.RX == 0 { + // use default pins + config.TX = UART_TX_PIN + config.RX = UART_RX_PIN + } + + uart.SetBaudRate(config.BaudRate) + + // default to 8-1-N + uart.SetFormat(8, 1, ParityNone) + + // Enable the UART, both TX and RX + uart.Bus.UARTCR.SetBits(rp.UART0_UARTCR_UARTEN | + rp.UART0_UARTCR_RXE | + rp.UART0_UARTCR_TXE) + + // set GPIO mux to UART for the pins + config.TX.Configure(PinConfig{Mode: PinUART}) + config.RX.Configure(PinConfig{Mode: PinUART}) + + // Enable RX IRQ. + uart.Interrupt.SetPriority(0x80) + uart.Interrupt.Enable() + + // setup interrupt on receive + uart.Bus.UARTIMSC.Set(rp.UART0_UARTIMSC_RXIM) + + return nil +} + +// SetBaudRate sets the baudrate to be used for the UART. +func (uart *UART) SetBaudRate(br uint32) { + div := 8 * 125 * MHz / br + + ibrd := div >> 7 + var fbrd uint32 + + switch { + case ibrd == 0: + ibrd = 1 + fbrd = 0 + case ibrd >= 65535: + ibrd = 65535 + fbrd = 0 + default: + fbrd = ((div & 0x7f) + 1) / 2 + } + + // set PL011 baud divisor registers + uart.Bus.UARTIBRD.Set(ibrd) + uart.Bus.UARTFBRD.Set(fbrd) + + // PL011 needs a (dummy) line control register write. + // See https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_uart/uart.c#L93-L95 + uart.Bus.UARTLCR_H.SetBits(0) +} + +// WriteByte writes a byte of data to the UART. +func (uart *UART) WriteByte(c byte) error { + // wait until buffer is not full + for uart.Bus.UARTFR.HasBits(rp.UART0_UARTFR_TXFF) { + } + + // write data + uart.Bus.UARTDR.Set(uint32(c)) + return nil +} + +// SetFormat for number of data bits, stop bits, and parity for the UART. +func (uart *UART) SetFormat(databits, stopbits uint8, parity UARTParity) error { + var pen, pev uint8 + if parity != ParityNone { + pen = rp.UART0_UARTLCR_H_PEN + } + if parity == ParityEven { + pev = rp.UART0_UARTLCR_H_EPS + } + uart.Bus.UARTLCR_H.SetBits(uint32((databits-5)<