mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-06 20:13:40 +00:00
7f970a45c2
Don't block forever if there's nothing to receive. On the other hand, process the entire FIFO, not just a single byte. This fixes an issue where the rp2350 would hang after programming through openocd, where the UART0 interrupt would be spuriously pending.
160 lines
3.4 KiB
Go
160 lines
3.4 KiB
Go
//go:build rp2040 || rp2350
|
|
|
|
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
|
|
settings := uint32(rp.UART0_UARTCR_UARTEN |
|
|
rp.UART0_UARTCR_RXE |
|
|
rp.UART0_UARTCR_TXE)
|
|
const bits = rp.UART0_UARTCR_UARTEN | rp.UART0_UARTCR_TXE
|
|
if config.RTS != 0 {
|
|
settings |= rp.UART0_UARTCR_RTSEN
|
|
}
|
|
if config.CTS != 0 {
|
|
settings |= rp.UART0_UARTCR_CTSEN
|
|
}
|
|
|
|
uart.Bus.UARTCR.SetBits(settings)
|
|
|
|
// set GPIO mux to UART for the pins
|
|
if config.TX != NoPin {
|
|
config.TX.Configure(PinConfig{Mode: PinUART})
|
|
}
|
|
if config.RX != NoPin {
|
|
config.RX.Configure(PinConfig{Mode: PinUART})
|
|
}
|
|
if config.RTS != 0 {
|
|
config.RTS.Configure(PinConfig{Mode: PinOutput})
|
|
}
|
|
if config.CTS != 0 {
|
|
config.CTS.Configure(PinConfig{Mode: PinInput})
|
|
}
|
|
|
|
// 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 * CPUFrequency() / 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) {
|
|
gosched()
|
|
}
|
|
|
|
// write data
|
|
uart.Bus.UARTDR.Set(uint32(c))
|
|
return nil
|
|
}
|
|
|
|
func (uart *UART) flush() {
|
|
for uart.Bus.UARTFR.HasBits(rp.UART0_UARTFR_BUSY) {
|
|
gosched()
|
|
}
|
|
}
|
|
|
|
// 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)<<rp.UART0_UARTLCR_H_WLEN_Pos |
|
|
(stopbits-1)<<rp.UART0_UARTLCR_H_STP2_Pos |
|
|
pen | pev))
|
|
|
|
return nil
|
|
}
|
|
|
|
func initUART(uart *UART) {
|
|
var resetVal uint32
|
|
switch {
|
|
case uart.Bus == rp.UART0:
|
|
resetVal = rp.RESETS_RESET_UART0
|
|
case uart.Bus == rp.UART1:
|
|
resetVal = rp.RESETS_RESET_UART1
|
|
}
|
|
|
|
// reset UART
|
|
rp.RESETS.RESET.SetBits(resetVal)
|
|
rp.RESETS.RESET.ClearBits(resetVal)
|
|
for !rp.RESETS.RESET_DONE.HasBits(resetVal) {
|
|
}
|
|
}
|
|
|
|
// handleInterrupt should be called from the appropriate interrupt handler for
|
|
// this UART instance.
|
|
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
|
for !uart.Bus.UARTFR.HasBits(rp.UART0_UARTFR_RXFE) {
|
|
uart.Receive(byte((uart.Bus.UARTDR.Get() & 0xFF)))
|
|
}
|
|
}
|