diff --git a/src/core/uart.zig b/src/core/uart.zig index f84d2dc..13b1485 100644 --- a/src/core/uart.zig +++ b/src/core/uart.zig @@ -2,8 +2,8 @@ const std = @import("std"); const micro = @import("microzig.zig"); const chip = @import("chip"); -pub fn Uart(comptime index: usize) type { - const SystemUart = chip.Uart(index); +pub fn Uart(comptime index: usize, comptime pins: Pins) type { + const SystemUart = chip.Uart(index, pins); return struct { const Self = @This(); @@ -63,7 +63,14 @@ pub fn Uart(comptime index: usize) type { }; } -/// A UART configuration. The config defaults to the *8N1* setting, so "8 data bits, no parity, 1 stop bit" which is the +/// The pin configuration. This is used to optionally configure specific pins to be used with the chosen UART. +/// This makes sense only with microcontrollers supporting multiple pins for a UART peripheral. +pub const Pins = struct { + tx: ?type = null, + rx: ?type = null, +}; + +/// A UART configuration. The config defaults to the *8N1* setting, so "8 data bits, no parity, 1 stop bit" which is the /// most common serial format. pub const Config = struct { /// TODO: Make this optional, to support STM32F303 et al. auto baud-rate detection? diff --git a/src/modules/boards/stm32f3discovery/stm32f3discovery.zig b/src/modules/boards/stm32f3discovery/stm32f3discovery.zig index 995e8a5..56d249e 100644 --- a/src/modules/boards/stm32f3discovery/stm32f3discovery.zig +++ b/src/modules/boards/stm32f3discovery/stm32f3discovery.zig @@ -25,7 +25,7 @@ pub const pin_map = .{ }; pub fn debugWrite(string: []const u8) void { - const uart1 = micro.Uart(1).getOrInit(.{ + const uart1 = micro.Uart(1, .{}).getOrInit(.{ .baud_rate = 9600, .data_bits = .eight, .parity = null, diff --git a/src/modules/chips/atmega328p/atmega328p.zig b/src/modules/chips/atmega328p/atmega328p.zig index 9375a6d..0a24481 100644 --- a/src/modules/chips/atmega328p/atmega328p.zig +++ b/src/modules/chips/atmega328p/atmega328p.zig @@ -94,8 +94,11 @@ pub const uart = struct { }; }; -pub fn Uart(comptime index: usize) type { +pub fn Uart(comptime index: usize, comptime pins: micro.uart.Pins) type { if (index != 0) @compileError("Atmega328p only has a single uart!"); + if (pins.tx != null or pins.rx != null) + @compileError("Atmega328p has fixed pins for uart!"); + return struct { const Self = @This(); diff --git a/src/modules/chips/lpc1768/lpc1768.zig b/src/modules/chips/lpc1768/lpc1768.zig index 93a88a4..9239317 100644 --- a/src/modules/chips/lpc1768/lpc1768.zig +++ b/src/modules/chips/lpc1768/lpc1768.zig @@ -115,7 +115,10 @@ pub const uart = struct { }; }; -pub fn Uart(comptime index: usize) type { +pub fn Uart(comptime index: usize, comptime pins: micro.uart.Pins) type { + if (pins.tx != null or pins.rx != null) + @compileError("TODO: custom pins are not currently supported"); + return struct { const UARTn = switch (index) { 0 => regs.UART0, diff --git a/src/modules/chips/stm32f303/stm32f303.zig b/src/modules/chips/stm32f303/stm32f303.zig index 4b8ff37..35bc5ac 100644 --- a/src/modules/chips/stm32f303/stm32f303.zig +++ b/src/modules/chips/stm32f303/stm32f303.zig @@ -132,8 +132,10 @@ pub const uart = struct { }; }; -pub fn Uart(comptime index: usize) type { +pub fn Uart(comptime index: usize, comptime pins: micro.uart.Pins) type { if (!(index == 1)) @compileError("TODO: only USART1 is currently supported"); + if (pins.tx != null or pins.rx != null) + @compileError("TODO: custom pins are not currently supported"); return struct { parity_read_mask: u8, diff --git a/src/modules/chips/stm32f407/stm32f407.zig b/src/modules/chips/stm32f407/stm32f407.zig index 17f4039..724e410 100644 --- a/src/modules/chips/stm32f407/stm32f407.zig +++ b/src/modules/chips/stm32f407/stm32f407.zig @@ -154,29 +154,85 @@ pub const uart = struct { even = 0, odd = 1, }; + + const PinDirection = std.meta.FieldEnum(micro.uart.Pins); + + /// Checks if a pin is valid for a given uart index and direction + pub fn isValidPin(comptime pin: type, comptime index: usize, comptime direction: PinDirection) bool { + const pin_name = pin.name; + + return switch (direction) { + .tx => switch (index) { + 1 => std.mem.eql(u8, pin_name, "PA9") or std.mem.eql(u8, pin_name, "PB6"), + 2 => std.mem.eql(u8, pin_name, "PA2") or std.mem.eql(u8, pin_name, "PD5"), + 3 => std.mem.eql(u8, pin_name, "PB10") or std.mem.eql(u8, pin_name, "PC10") or std.mem.eql(u8, pin_name, "PD8"), + 4 => std.mem.eql(u8, pin_name, "PA0") or std.mem.eql(u8, pin_name, "PC10"), + 5 => std.mem.eql(u8, pin_name, "PC12"), + 6 => std.mem.eql(u8, pin_name, "PC6") or std.mem.eql(u8, pin_name, "PG14"), + else => unreachable, + }, + // Valid RX pins for the UARTs + .rx => switch (index) { + 1 => std.mem.eql(u8, pin_name, "PA10") or std.mem.eql(u8, pin_name, "PB7"), + 2 => std.mem.eql(u8, pin_name, "PA3") or std.mem.eql(u8, pin_name, "PD6"), + 3 => std.mem.eql(u8, pin_name, "PB11") or std.mem.eql(u8, pin_name, "PC11") or std.mem.eql(u8, pin_name, "PD9"), + 4 => std.mem.eql(u8, pin_name, "PA1") or std.mem.eql(u8, pin_name, "PC11"), + 5 => std.mem.eql(u8, pin_name, "PD2"), + 6 => std.mem.eql(u8, pin_name, "PC7") or std.mem.eql(u8, pin_name, "PG9"), + else => unreachable, + }, + }; + } }; -pub fn Uart(comptime index: usize) type { +pub fn Uart(comptime index: usize, comptime pins: micro.uart.Pins) type { if (index < 1 or index > 6) @compileError("Valid USART index are 1..6"); const usart_name = std.fmt.comptimePrint("USART{d}", .{index}); - // TODO: support alternative pins - const pins = switch (index) { - 1 => .{ .tx = micro.Pin("PA9"), .rx = micro.Pin("PA10") }, - 2 => .{ .tx = micro.Pin("PA2"), .rx = micro.Pin("PA3") }, - 3 => .{ .tx = micro.Pin("PD8"), .rx = micro.Pin("PD9") }, - 4 => .{ .tx = micro.Pin("PC10"), .rx = micro.Pin("PC11") }, - 5 => .{ .tx = micro.Pin("PC12"), .rx = micro.Pin("PD2") }, - 6 => .{ .tx = micro.Pin("PC6"), .rx = micro.Pin("PC7") }, + const tx_pin = + if (pins.tx) |tx| + if (uart.isValidPin(tx, index, .tx)) + tx + else + @compileError(std.fmt.comptimePrint("Tx pin {s} is not valid for UART{}", .{ tx.name, index })) + else switch (index) { + // Provide default tx pins if no pin is specified + 1 => micro.Pin("PA9"), + 2 => micro.Pin("PA2"), + 3 => micro.Pin("PB10"), + 4 => micro.Pin("PA0"), + 5 => micro.Pin("PC12"), + 6 => micro.Pin("PC6"), else => unreachable, }; - const tx_gpio = micro.Gpio(pins.tx, .{ + + const rx_pin = + if (pins.rx) |rx| + if (uart.isValidPin(rx, index, .rx)) + rx + else + @compileError(std.fmt.comptimePrint("Rx pin {s} is not valid for UART{}", .{ rx.name, index })) + else switch (index) { + // Provide default rx pins if no pin is specified + 1 => micro.Pin("PA10"), + 2 => micro.Pin("PA3"), + 3 => micro.Pin("PB11"), + 4 => micro.Pin("PA1"), + 5 => micro.Pin("PD2"), + 6 => micro.Pin("PC7"), + else => unreachable, + }; + + // USART1..3 are AF7, USART 4..6 are AF8 + const alternate_function = if (index <= 3) .af7 else .af8; + + const tx_gpio = micro.Gpio(tx_pin, .{ .mode = .alternate_function, - .alternate_function = .af7, + .alternate_function = alternate_function, }); - const rx_gpio = micro.Gpio(pins.rx, .{ + const rx_gpio = micro.Gpio(rx_pin, .{ .mode = .alternate_function, - .alternate_function = .af7, + .alternate_function = alternate_function, }); return struct { diff --git a/tests/uart-sync.zig b/tests/uart-sync.zig index 696095b..83378a9 100644 --- a/tests/uart-sync.zig +++ b/tests/uart-sync.zig @@ -3,13 +3,25 @@ const micro = @import("microzig"); // Configures the led_pin and uart index const cfg = if (micro.config.has_board) switch (micro.config.board_name) { - .@"mbed LPC1768" => .{ .led_pin = micro.Pin("LED-1"), .uart_idx = 1 }, - .@"STM32F3DISCOVERY" => .{ .led_pin = micro.Pin("LD3"), .uart_idx = 1 }, - .@"STM32F4DISCOVERY" => .{ .led_pin = micro.Pin("LD5"), .uart_idx = 2 }, + .@"mbed LPC1768" => .{ + .led_pin = micro.Pin("LED-1"), + .uart_idx = 1, + .pins = .{}, + }, + .@"STM32F3DISCOVERY" => .{ + .led_pin = micro.Pin("LD3"), + .uart_idx = 1, + .pins = .{}, + }, + .@"STM32F4DISCOVERY" => .{ + .led_pin = micro.Pin("LD5"), + .uart_idx = 2, + .pins = .{ .tx = micro.Pin("PA2"), .rx = micro.Pin("PA3") }, + }, else => @compileError("unknown board"), } else switch (micro.config.chip_name) { - .@"NXP LPC1768" => .{ .led_pin = micro.Pin("P1.18"), .uart_idx = 1 }, + .@"NXP LPC1768" => .{ .led_pin = micro.Pin("P1.18"), .uart_idx = 1, .pins = .{} }, else => @compileError("unknown chip"), }; @@ -20,7 +32,7 @@ pub fn main() !void { }); led.init(); - var uart = micro.Uart(cfg.uart_idx).init(.{ + var uart = micro.Uart(cfg.uart_idx, cfg.pins).init(.{ .baud_rate = 9600, .stop_bits = .one, .parity = null,