Skip to content

machine/ra4m1: add SCI UART driver - #6015

Open
Frelsaren wants to merge 1 commit into
tinygo-org:devfrom
Frelsaren:uno-r4-uart
Open

Frelsaren wants to merge 1 commit into
tinygo-org:devfrom
Frelsaren:uno-r4-uart

Conversation

@Frelsaren

@Frelsaren Frelsaren commented Oct 24, 2026 •

Copy link
Copy Markdown

Asynchronous mode with interrupt driven receive. The bootloader leaves
SCI events linked in the ICU, so Configure unlinks them first.

Arduino Uno R4 WiFi: UART1 is SCI9 (USB CDC via ESP32-S3), UART2 is
SCI2 (D0/D1), UART3 is SCI1 (ESP32-S3 AT link). Arduino Uno R4 Minima:
UART1 is SCI2 (D0/D1).

Both targets now use UART1 as the default serial output, initialized
from the runtime at startup.

@Frelsaren Frelsaren changed the title machine/ra4m1: add SCI UART driver for Arduino UNO R4 machine/ra4m1: add SCI UART driver Oct 24, 2026
@dgryski

dgryski commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

I tried this PR on an Arduino Uno R4 WiFi (board target arduino-uno-r4-wifi) and the UART works.

Test program:

package main

import "time"

func main() {
	for {
		println("hello")
		time.Sleep(time.Second)
	}
}

Running tinygo flash -monitor -target=arduino-uno-r4-wifi main.go prints hello once a second, with clean output at the default 115200 baud.

The pin and event numbers also match ArduinoCore-renesas 1.6.0 (P109/P110 on SCI9, ELC events 0x9E, 0xA3, 0xA8, PSEL 0x04/0x05).

One issue from reading the code, which I have not tested on hardware. Configure falls back to UART_TX_PIN and UART_RX_PIN for every UART. On the WiFi board those are D22 and D23. UART2.Configure(UARTConfig{}) would give D22/D23 the SCI2 function select, which breaks UART1 and leaves UART2 on the wrong pins. Each UART should have its own default pins.

@dgryski

dgryski commented Oct 24, 2026

Copy link
Copy Markdown
Member

More testing on an Arduino Uno R4 WiFi (arduino-uno-r4-wifi) with this PR applied. I wrote a set of test programs plus a Python runner that flashes each one, drives the board over the USB serial port with pyserial, and checks the output. The Minima was not tested.

Results:

Test Result
Echo (10 lines, 64 bytes, 512 paced random bytes) pass
RX burst of 4096 bytes while the program is not reading pass. The 128 byte ring buffer keeps the first 128 bytes and drops the rest, and the UART keeps receiving afterward
2000 numbered lines at 115200 pass, in order, in 2.4 s
All 256 byte values in 4 patterns pass
Baud rates 9600, 57600, 230400, 460800 via SetBaudRate pass, 20/20 lines each
Baud rate 921600 fail
Configure called twice, then TX and RX pass
UART3 (SCI1) to the ESP32-S3, sending AT pass, the ESP32 replies OK
3 goroutines printing and allocating while receiving 512 bytes pass, 600 intact lines and 512/512 bytes counted
UART2.Configure(UARTConfig{}) with default pins fail, see below

Issues:

  1. 921600 baud does not work. SetBaudRate(921600) computes N=7, which gives about 857 kbaud, 7% below the target. The host receives only 0x80 bytes. The other rates are within +0.2%. Supporting 921600 needs the bit rate modulator (SEMR.BRME and MDDR), or the supported maximum could be documented as 460800.
  2. Default pins are not per UART. UART2.Configure(UARTConfig{}) on the WiFi board falls back to UART_TX_PIN and UART_RX_PIN, which are D22 and D23. That gives D22/D23 the SCI2 function select. After that call, UART1 output stops completely, so this is confirmed on hardware. Each UART needs its own default TX and RX pins. UART3 works when its pins are passed explicitly (UART3_TX_PIN and UART3_RX_PIN).

Everything else behaved correctly, including RX interrupts, recovery after an RX buffer overflow, reconfiguring, and operation alongside the scheduler and GC.

Asynchronous mode with interrupt driven receive. The bootloader leaves
SCI events linked in the ICU, so Configure unlinks them first.

Arduino Uno R4 WiFi: UART1 is SCI9 (USB CDC via ESP32-S3), UART2 is
SCI2 (D0/D1), UART3 is SCI1 (ESP32-S3 AT link). Arduino Uno R4 Minima:
UART1 is SCI2 (D0/D1).

Both targets now use UART1 as the default serial output, initialized
from the runtime at startup.
@Frelsaren

Copy link
Copy Markdown
Author

I have made some changes covering the issues.
UART now has per-instance default pins which is set in init(), Configure falls back to those instead of UART_TX_PIN and UART_RX_PIN.

I also implemented the bit rate modulator.

UART2.Configure(UARTConfig{}) no longer kills UART1 on the WiFi board, 921600 passes 20/20, and lower rates keep the same N and CKS as before so no change there.

Have not tested Minima, only WiFi board.

@dgryski

dgryski commented Oct 24, 2026

Copy link
Copy Markdown
Member

Retested the updated commit (9c8f3b04) on an Arduino Uno R4 WiFi (arduino-uno-r4-wifi) with the same test programs and Python runner as before. All tests pass now.

Test Result
Echo (10 lines, 64 bytes, 512 paced random bytes) pass
RX burst of 4096 bytes while the program is not reading pass, the 128 byte buffer keeps the first 128 bytes and the UART still receives afterward
2000 numbered lines at 115200 pass
All 256 byte values in 4 patterns pass
Baud rates 9600, 57600, 230400, 460800, 921600 pass, 20/20 lines each
Configure called twice, then TX and RX pass
UART3 (SCI1) to the ESP32-S3, sending AT pass, the ESP32 replied
3 goroutines printing and allocating while receiving 512 bytes pass, 600 intact lines and 512/512 bytes
UART2.Configure(UARTConfig{}) with default pins pass, UART1 keeps working

Both issues from my earlier comment are fixed. 921600 now works (N=6, MDDR=236, about 0.03% rate error). UART1 is unaffected by a default UART2.Configure.

One small point. Configure still treats config.TX == 0 && config.RX == 0 as "use the defaults", so an explicit TX of pin 0 can't be selected. That is the same pattern other ports use, so it may be fine.

The Minima is still untested.

@dgryski dgryski left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Wouldn't mind a second look from someone who deals more with hardware (e.g., @deadprogram).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants