From 5184cd7228d4c9ea0cc88fb9cbc09dc1de23b269 Mon Sep 17 00:00:00 2001 From: Graziano Misuraca Date: Sun, 30 Aug 2026 11:13:21 -0400 Subject: [PATCH 1/6] ch32v: Revamp the board-level usart setup design Following up on discussion on discord, addressing some concerns. 1. 'Config' vs. 'Setup' 2. Stuff like baud should not be in the board file. It is an 'application level' configuration. 3. The setup function is clunky. Put it into a method. 4. The `@hasDecl` is clunky. Move it into the method. --- examples/wch/ch32v/src/uart_log.zig | 6 ++-- port/wch/ch32v/src/boards/LANA_TNY.zig | 2 +- port/wch/ch32v/src/boards/nanoCH32V203.zig | 5 +++- port/wch/ch32v/src/hals/usart.zig | 35 ++++++++++++---------- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/examples/wch/ch32v/src/uart_log.zig b/examples/wch/ch32v/src/uart_log.zig index 3d5328ac9..f4dd85c9a 100644 --- a/examples/wch/ch32v/src/uart_log.zig +++ b/examples/wch/ch32v/src/uart_log.zig @@ -4,7 +4,7 @@ const hal = microzig.hal; const board = microzig.board; const time = hal.time; -const uart_cfg: hal.usart.UartConfig = if (@hasDecl(board, "uart_config")) board.uart_config else .{}; +const uart = hal.usart.UartSetup.default; pub const panic = microzig.panic; @@ -20,8 +20,8 @@ comptime { pub fn main() !void { board.init(); - hal.usart.setup_uart(uart_cfg); - hal.usart.init_logger(uart_cfg.instance); + uart.setup(.{ .baud_rate = 115200 }); + hal.usart.init_logger(uart.instance); var i: u32 = 0; while (true) : (i += 1) { diff --git a/port/wch/ch32v/src/boards/LANA_TNY.zig b/port/wch/ch32v/src/boards/LANA_TNY.zig index 832767d1e..dc6f8d3f5 100644 --- a/port/wch/ch32v/src/boards/LANA_TNY.zig +++ b/port/wch/ch32v/src/boards/LANA_TNY.zig @@ -21,7 +21,7 @@ pub fn init() void { } /// Default UART: USART2 on PA2 (exposed on the board header) -pub const uart_config: ch32v.usart.UartConfig = .{ +pub const uart_setup: ch32v.usart.UartSetup = .{ .instance = .USART2, .tx_pin = ch32v.gpio.Pin.init(0, 2), // PA2 }; diff --git a/port/wch/ch32v/src/boards/nanoCH32V203.zig b/port/wch/ch32v/src/boards/nanoCH32V203.zig index c69aa5481..2a55160c4 100644 --- a/port/wch/ch32v/src/boards/nanoCH32V203.zig +++ b/port/wch/ch32v/src/boards/nanoCH32V203.zig @@ -25,7 +25,10 @@ pub fn init() void { } /// Default UART: USART1 on PA9 -pub const uart_config: ch32v.usart.UartConfig = .{}; +pub const uart_setup: ch32v.usart.UartSetup = .{ + .instance = .USART1, + .tx_pin = ch32v.gpio.Pin.init(0, 9), // PA9 +}; pub const pin_config = ch32v.pins.GlobalConfiguration{ .GPIOA = .{ diff --git a/port/wch/ch32v/src/hals/usart.zig b/port/wch/ch32v/src/hals/usart.zig index d10cb4282..25ebc8390 100644 --- a/port/wch/ch32v/src/hals/usart.zig +++ b/port/wch/ch32v/src/hals/usart.zig @@ -92,24 +92,27 @@ pub const ReceiveError = error{ const gpio = hal.gpio; -/// Configuration for board-level UART defaults. -/// Boards export a `uart_config` const of this type with their preferred -/// USART instance, TX pin, and serial settings. Examples can use these -/// defaults directly or construct their own `UartConfig`. -pub const UartConfig = struct { - instance: USART = .USART1, - tx_pin: gpio.Pin = gpio.Pin.init(0, 9), // PA9 (USART1 default TX) - config: Config = .{ .baud_rate = 115200 }, +/// Physical-layer UART setup: which USART instance and TX pin to use. +/// Boards export a `uart_setup` const of this type. Application-level +/// settings (baud rate, parity, etc.) are passed separately via `Config`. +pub const UartSetup = struct { + instance: USART, + tx_pin: gpio.Pin, + + /// Throw a compile error if an application tries to use the default, but + /// the board it is being compiled for does not provide one. + pub const default: UartSetup = if (microzig.config.has_board and @hasDecl(microzig.board, "uart_setup")) + microzig.board.uart_setup + else + @compileError("board does not provide a default uart_setup"); + + /// Apply 'setup' and 'config' + pub fn setup(comptime self: UartSetup, comptime config: Config) void { + self.tx_pin.configure_alternate_function(.push_pull, .max_50MHz); + self.instance.apply(config); + } }; -/// Configure a UART from a `UartConfig`: sets up the TX pin as alternate -/// function push-pull, then applies the USART peripheral configuration -/// (clock enable, AFIO remap, baud rate, etc.). -pub fn setup_uart(comptime cfg: UartConfig) void { - cfg.tx_pin.configure_alternate_function(.push_pull, .max_50MHz); - cfg.instance.apply(cfg.config); -} - pub const instance = struct { pub const USART1: USART = .USART1; pub const USART2: USART = .USART2; From a1131c10513530cd0fcc41d4c2ddeee4cc929f36 Mon Sep 17 00:00:00 2001 From: Graziano Misuraca Date: Sun, 30 Aug 2026 14:01:45 -0400 Subject: [PATCH 2/6] s/setup/apply --- examples/wch/ch32v/src/uart_log.zig | 2 +- port/wch/ch32v/src/hals/usart.zig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/wch/ch32v/src/uart_log.zig b/examples/wch/ch32v/src/uart_log.zig index f4dd85c9a..972e3083a 100644 --- a/examples/wch/ch32v/src/uart_log.zig +++ b/examples/wch/ch32v/src/uart_log.zig @@ -20,7 +20,7 @@ comptime { pub fn main() !void { board.init(); - uart.setup(.{ .baud_rate = 115200 }); + uart.apply(.{ .baud_rate = 115200 }); hal.usart.init_logger(uart.instance); var i: u32 = 0; diff --git a/port/wch/ch32v/src/hals/usart.zig b/port/wch/ch32v/src/hals/usart.zig index 25ebc8390..0db191031 100644 --- a/port/wch/ch32v/src/hals/usart.zig +++ b/port/wch/ch32v/src/hals/usart.zig @@ -106,8 +106,8 @@ pub const UartSetup = struct { else @compileError("board does not provide a default uart_setup"); - /// Apply 'setup' and 'config' - pub fn setup(comptime self: UartSetup, comptime config: Config) void { + /// Apply settings + pub fn apply(comptime self: UartSetup, comptime config: Config) void { self.tx_pin.configure_alternate_function(.push_pull, .max_50MHz); self.instance.apply(config); } From f8e6081732d94ff81a809d4cf1d559dde330ee4d Mon Sep 17 00:00:00 2001 From: Graziano Misuraca Date: Thu, 3 Sep 2026 20:07:01 -0400 Subject: [PATCH 3/6] hal doesn't access board --- examples/wch/ch32v/src/uart_log.zig | 2 +- port/wch/ch32v/src/hals/usart.zig | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/examples/wch/ch32v/src/uart_log.zig b/examples/wch/ch32v/src/uart_log.zig index 972e3083a..2b20ab2eb 100644 --- a/examples/wch/ch32v/src/uart_log.zig +++ b/examples/wch/ch32v/src/uart_log.zig @@ -4,7 +4,7 @@ const hal = microzig.hal; const board = microzig.board; const time = hal.time; -const uart = hal.usart.UartSetup.default; +const uart = board.uart_setup; pub const panic = microzig.panic; diff --git a/port/wch/ch32v/src/hals/usart.zig b/port/wch/ch32v/src/hals/usart.zig index 0db191031..7275a5b47 100644 --- a/port/wch/ch32v/src/hals/usart.zig +++ b/port/wch/ch32v/src/hals/usart.zig @@ -92,23 +92,22 @@ pub const ReceiveError = error{ const gpio = hal.gpio; -/// Physical-layer UART setup: which USART instance and TX pin to use. +/// Physical-layer UART setup: which USART instance and pins to use. /// Boards export a `uart_setup` const of this type. Application-level /// settings (baud rate, parity, etc.) are passed separately via `Config`. pub const UartSetup = struct { instance: USART, - tx_pin: gpio.Pin, + tx_pin: ?gpio.Pin = null, + rx_pin: ?gpio.Pin = null, - /// Throw a compile error if an application tries to use the default, but - /// the board it is being compiled for does not provide one. - pub const default: UartSetup = if (microzig.config.has_board and @hasDecl(microzig.board, "uart_setup")) - microzig.board.uart_setup - else - @compileError("board does not provide a default uart_setup"); - - /// Apply settings + /// Apply settings: configure whichever pins are present, then apply + /// the USART peripheral config (clock, baud rate, etc.). pub fn apply(comptime self: UartSetup, comptime config: Config) void { - self.tx_pin.configure_alternate_function(.push_pull, .max_50MHz); + if (self.tx_pin) |tx| tx.configure_alternate_function(.push_pull, .max_50MHz); + if (self.rx_pin) |rx| { + rx.enable_clock(); + rx.set_input_mode(.floating); + } self.instance.apply(config); } }; From 8367c27069e3a128074ae1a93fa0b123dbbe1ebe Mon Sep 17 00:00:00 2001 From: Graziano Misuraca Date: Mon, 7 Sep 2026 08:54:51 -0400 Subject: [PATCH 4/6] fix up, new example --- examples/raspberrypi/rp2xxx/src/uart_echo.zig | 2 +- examples/wch/ch32v/build.zig | 3 ++ examples/wch/ch32v/src/uart_echo.zig | 31 +++++++++++++++++++ port/wch/ch32v/src/boards/LANA_TNY.zig | 2 ++ port/wch/ch32v/src/boards/nanoCH32V203.zig | 2 ++ port/wch/ch32v/src/hals/time.zig | 8 +++++ 6 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 examples/wch/ch32v/src/uart_echo.zig diff --git a/examples/raspberrypi/rp2xxx/src/uart_echo.zig b/examples/raspberrypi/rp2xxx/src/uart_echo.zig index 35f1b9ad7..ae817b4ad 100644 --- a/examples/raspberrypi/rp2xxx/src/uart_echo.zig +++ b/examples/raspberrypi/rp2xxx/src/uart_echo.zig @@ -38,7 +38,7 @@ pub fn main() !void { continue; }; - //tries to write one byte with 100ms timeout + // Try to write one byte with 100ms timeout _ = uart.write_blocking(&data, time.deadline_in_ms(100)) catch { uart.clear_errors(); }; diff --git a/examples/wch/ch32v/build.zig b/examples/wch/ch32v/build.zig index c8f28688b..995f2de11 100644 --- a/examples/wch/ch32v/build.zig +++ b/examples/wch/ch32v/build.zig @@ -37,11 +37,14 @@ pub fn build(b: *std.Build) void { .{ .target = mb.ports.ch32v.chips.ch32v203x6, .name = "blinky_systick_ch32v203", .file = "src/blinky_systick.zig" }, .{ .target = mb.ports.ch32v.boards.ch32v203.nano_ch32v203, .name = "nano_ch32v203_blinky", .file = "src/board_blinky.zig" }, .{ .target = mb.ports.ch32v.boards.ch32v203.nano_ch32v203, .name = "nano_ch32v203_blinky_wfe", .file = "src/blinky_wfe.zig" }, + .{ .target = mb.ports.ch32v.boards.ch32v203.nano_ch32v203, .name = "nano_ch32v203_uart_echo", .file = "src/uart_echo.zig" }, + .{ .target = mb.ports.ch32v.boards.ch32v203.nano_ch32v203, .name = "nano_ch32v203_uart_log", .file = "src/uart_log.zig" }, .{ .target = mb.ports.ch32v.boards.ch32v203.nano_ch32v203, .name = "nano_ch32v203_usb_cdc", .file = "src/usb_cdc.zig" }, .{ .target = mb.ports.ch32v.boards.ch32v203.suzuduino_uno_v1b, .name = "suzuduino_blinky", .file = "src/board_blinky.zig" }, .{ .target = mb.ports.ch32v.boards.ch32v203.suzuduino_uno_v1b, .name = "suzuduino_usb_cdc", .file = "src/usb_cdc.zig" }, .{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_dma", .file = "src/dma.zig" }, .{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_ws2812", .file = "src/ws2812.zig" }, + .{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_uart_echo", .file = "src/uart_echo.zig" }, .{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_uart_log", .file = "src/uart_log.zig" }, .{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_i2c_bus_scan", .file = "src/i2c_bus_scan.zig" }, .{ .target = mb.ports.ch32v.boards.ch32v203.lana_tny, .name = "lana_tny_i2c_eeprom", .file = "src/i2c_eeprom.zig" }, diff --git a/examples/wch/ch32v/src/uart_echo.zig b/examples/wch/ch32v/src/uart_echo.zig new file mode 100644 index 000000000..d41298496 --- /dev/null +++ b/examples/wch/ch32v/src/uart_echo.zig @@ -0,0 +1,31 @@ +const microzig = @import("microzig"); + +const hal = microzig.hal; +const board = microzig.board; +const time = hal.time; + +const uart = board.uart_setup; + +pub const panic = microzig.panic; + +pub const std_options = microzig.std_options(.{}); + +comptime { + _ = microzig.export_startup(); +} + +pub fn main() !void { + board.init(); + + uart.apply(.{ .baud_rate = 115200 }); + + const usart = uart.instance; + + var data: [1]u8 = .{0}; + while (true) { + usart.read_blocking(&data, .no_deadline) catch { + continue; + }; + _ = usart.write_blocking(&data, time.deadline_in_ms(100)) catch {}; + } +} diff --git a/port/wch/ch32v/src/boards/LANA_TNY.zig b/port/wch/ch32v/src/boards/LANA_TNY.zig index dc6f8d3f5..82e09530b 100644 --- a/port/wch/ch32v/src/boards/LANA_TNY.zig +++ b/port/wch/ch32v/src/boards/LANA_TNY.zig @@ -21,9 +21,11 @@ pub fn init() void { } /// Default UART: USART2 on PA2 (exposed on the board header) +/// TODO: Find default rx pin pub const uart_setup: ch32v.usart.UartSetup = .{ .instance = .USART2, .tx_pin = ch32v.gpio.Pin.init(0, 2), // PA2 + .rx_pin = ch32v.gpio.Pin.init(0, 3), // PA3 }; pub const pin_config = ch32v.pins.GlobalConfiguration{ diff --git a/port/wch/ch32v/src/boards/nanoCH32V203.zig b/port/wch/ch32v/src/boards/nanoCH32V203.zig index 2a55160c4..e77c72e1f 100644 --- a/port/wch/ch32v/src/boards/nanoCH32V203.zig +++ b/port/wch/ch32v/src/boards/nanoCH32V203.zig @@ -25,9 +25,11 @@ pub fn init() void { } /// Default UART: USART1 on PA9 +/// TODO: Find default rx pin pub const uart_setup: ch32v.usart.UartSetup = .{ .instance = .USART1, .tx_pin = ch32v.gpio.Pin.init(0, 9), // PA9 + .rx_pin = ch32v.gpio.Pin.init(0, 10), // PA10 }; pub const pin_config = ch32v.pins.GlobalConfiguration{ diff --git a/port/wch/ch32v/src/hals/time.zig b/port/wch/ch32v/src/hals/time.zig index e416616b0..1f14767d4 100644 --- a/port/wch/ch32v/src/hals/time.zig +++ b/port/wch/ch32v/src/hals/time.zig @@ -176,3 +176,11 @@ pub fn delay_us(us: u32) void { asm volatile ("" ::: .{ .memory = true }); } } + +pub fn deadline_in_ms(time_ms: u32) microzig.drivers.time.Deadline { + return .init_relative(get_time_since_boot(), .from_ms(time_ms)); +} + +pub fn deadline_in_us(time_us: u64) microzig.drivers.time.Deadline { + return .init_relative(get_time_since_boot(), .from_us(time_us)); +} From 8236c2e48af933c584cdbdb2cd9d3e82d2cce053 Mon Sep 17 00:00:00 2001 From: Graziano Misuraca Date: Mon, 7 Sep 2026 08:58:51 -0400 Subject: [PATCH 5/6] i --- port/wch/ch32v/src/boards/LANA_TNY.zig | 1 - port/wch/ch32v/src/boards/nanoCH32V203.zig | 1 - 2 files changed, 2 deletions(-) diff --git a/port/wch/ch32v/src/boards/LANA_TNY.zig b/port/wch/ch32v/src/boards/LANA_TNY.zig index 82e09530b..f650728a2 100644 --- a/port/wch/ch32v/src/boards/LANA_TNY.zig +++ b/port/wch/ch32v/src/boards/LANA_TNY.zig @@ -21,7 +21,6 @@ pub fn init() void { } /// Default UART: USART2 on PA2 (exposed on the board header) -/// TODO: Find default rx pin pub const uart_setup: ch32v.usart.UartSetup = .{ .instance = .USART2, .tx_pin = ch32v.gpio.Pin.init(0, 2), // PA2 diff --git a/port/wch/ch32v/src/boards/nanoCH32V203.zig b/port/wch/ch32v/src/boards/nanoCH32V203.zig index e77c72e1f..7630a6aab 100644 --- a/port/wch/ch32v/src/boards/nanoCH32V203.zig +++ b/port/wch/ch32v/src/boards/nanoCH32V203.zig @@ -25,7 +25,6 @@ pub fn init() void { } /// Default UART: USART1 on PA9 -/// TODO: Find default rx pin pub const uart_setup: ch32v.usart.UartSetup = .{ .instance = .USART1, .tx_pin = ch32v.gpio.Pin.init(0, 9), // PA9 From 5d342d46fb684c20216393ddc7771d0e6d8df0fe Mon Sep 17 00:00:00 2001 From: Graziano Misuraca Date: Mon, 7 Sep 2026 08:58:51 -0400 Subject: [PATCH 6/6] address comments --- examples/wch/ch32v/src/uart_log.zig | 6 +++--- port/wch/ch32v/src/boards/LANA_TNY.zig | 2 +- port/wch/ch32v/src/boards/nanoCH32V203.zig | 2 +- port/wch/ch32v/src/hals/usart.zig | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/wch/ch32v/src/uart_log.zig b/examples/wch/ch32v/src/uart_log.zig index 2b20ab2eb..09b1ba843 100644 --- a/examples/wch/ch32v/src/uart_log.zig +++ b/examples/wch/ch32v/src/uart_log.zig @@ -4,7 +4,7 @@ const hal = microzig.hal; const board = microzig.board; const time = hal.time; -const uart = board.uart_setup; +const uart_setup = board.uart_setup; pub const panic = microzig.panic; @@ -20,8 +20,8 @@ comptime { pub fn main() !void { board.init(); - uart.apply(.{ .baud_rate = 115200 }); - hal.usart.init_logger(uart.instance); + uart_setup.apply(.{ .baud_rate = 115200 }); + hal.usart.init_logger(uart_setup.instance); var i: u32 = 0; while (true) : (i += 1) { diff --git a/port/wch/ch32v/src/boards/LANA_TNY.zig b/port/wch/ch32v/src/boards/LANA_TNY.zig index f650728a2..96333ee6a 100644 --- a/port/wch/ch32v/src/boards/LANA_TNY.zig +++ b/port/wch/ch32v/src/boards/LANA_TNY.zig @@ -21,7 +21,7 @@ pub fn init() void { } /// Default UART: USART2 on PA2 (exposed on the board header) -pub const uart_setup: ch32v.usart.UartSetup = .{ +pub const uart_setup: ch32v.usart.Setup = .{ .instance = .USART2, .tx_pin = ch32v.gpio.Pin.init(0, 2), // PA2 .rx_pin = ch32v.gpio.Pin.init(0, 3), // PA3 diff --git a/port/wch/ch32v/src/boards/nanoCH32V203.zig b/port/wch/ch32v/src/boards/nanoCH32V203.zig index 7630a6aab..5a1109406 100644 --- a/port/wch/ch32v/src/boards/nanoCH32V203.zig +++ b/port/wch/ch32v/src/boards/nanoCH32V203.zig @@ -25,7 +25,7 @@ pub fn init() void { } /// Default UART: USART1 on PA9 -pub const uart_setup: ch32v.usart.UartSetup = .{ +pub const uart_setup: ch32v.usart.Setup = .{ .instance = .USART1, .tx_pin = ch32v.gpio.Pin.init(0, 9), // PA9 .rx_pin = ch32v.gpio.Pin.init(0, 10), // PA10 diff --git a/port/wch/ch32v/src/hals/usart.zig b/port/wch/ch32v/src/hals/usart.zig index 7275a5b47..c3118cee9 100644 --- a/port/wch/ch32v/src/hals/usart.zig +++ b/port/wch/ch32v/src/hals/usart.zig @@ -95,14 +95,14 @@ const gpio = hal.gpio; /// Physical-layer UART setup: which USART instance and pins to use. /// Boards export a `uart_setup` const of this type. Application-level /// settings (baud rate, parity, etc.) are passed separately via `Config`. -pub const UartSetup = struct { +pub const Setup = struct { instance: USART, tx_pin: ?gpio.Pin = null, rx_pin: ?gpio.Pin = null, /// Apply settings: configure whichever pins are present, then apply /// the USART peripheral config (clock, baud rate, etc.). - pub fn apply(comptime self: UartSetup, comptime config: Config) void { + pub fn apply(comptime self: Setup, comptime config: Config) void { if (self.tx_pin) |tx| tx.configure_alternate_function(.push_pull, .max_50MHz); if (self.rx_pin) |rx| { rx.enable_clock();