From 36934307e5f7f6bd1dfd01e24fd2e8b743ac8221 Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Mon, 27 Apr 2026 16:32:58 +0200 Subject: [PATCH 01/15] Update build.zig --- port/stmicro/stm32/build.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/port/stmicro/stm32/build.zig b/port/stmicro/stm32/build.zig index 8dc27a98b..b4ad33f7e 100644 --- a/port/stmicro/stm32/build.zig +++ b/port/stmicro/stm32/build.zig @@ -75,6 +75,10 @@ pub fn init(dep: *std.Build.Dependency) ?Self { .name = "STM32F429IDISCOVERY", .root_source_file = b.path("src/boards/STM32F429IDISCOVERY.zig"), }, + .hal = microzig.HardwareAbstractionLayer{ + .root_source_file = b.path("src/hals/STM32F303.zig"), + .imports = hal_imports, + }, }), }, }; From dae8c6190d74e9fdc2302a79c268f92f16cbff7c Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Mon, 27 Apr 2026 16:35:24 +0200 Subject: [PATCH 02/15] Update build.zig --- port/stmicro/stm32/build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/stmicro/stm32/build.zig b/port/stmicro/stm32/build.zig index b4ad33f7e..28230cdcf 100644 --- a/port/stmicro/stm32/build.zig +++ b/port/stmicro/stm32/build.zig @@ -76,7 +76,7 @@ pub fn init(dep: *std.Build.Dependency) ?Self { .root_source_file = b.path("src/boards/STM32F429IDISCOVERY.zig"), }, .hal = microzig.HardwareAbstractionLayer{ - .root_source_file = b.path("src/hals/STM32F303.zig"), + .root_source_file = b.path("src/hals/STM32F429.zig"), .imports = hal_imports, }, }), From 4433ade537aee9432e35e07eefeae0f59f3e3f5c Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Mon, 27 Apr 2026 16:48:43 +0200 Subject: [PATCH 03/15] Update STM32F429.zig --- port/stmicro/stm32/src/hals/STM32F429.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/port/stmicro/stm32/src/hals/STM32F429.zig b/port/stmicro/stm32/src/hals/STM32F429.zig index ec10ef412..79897147c 100644 --- a/port/stmicro/stm32/src/hals/STM32F429.zig +++ b/port/stmicro/stm32/src/hals/STM32F429.zig @@ -6,14 +6,14 @@ //! default AHB prescaler = /1 (= values 0..7): //! //! ``` -//! RCC.CFGR.modify(.{ .HPRE = 0 }); +//! RCC.CFGR.modify(.{ .HPRE = .Div1 }); //! ``` //! //! so also HCLK = 16 MHz. //! And with the default APB1 prescaler = /1: //! //! ``` -//! RCC.CFGR.modify(.{ .PPRE1 = 0 }); +//! RCC.CFGR.modify(.{ .PPRE1 = .Div1 }); //! ``` //! //! results in PCLK1 = 16 MHz. @@ -22,7 +22,7 @@ const std = @import("std"); const microzig = @import("microzig"); -const peripherals = microzig.peripherals; +const peripherals = microzig.chip.peripherals; const RCC = peripherals.RCC; pub const clock = struct { From 9a6b10e3d7a5163b1f074f4f6a23242de5e85414 Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Mon, 27 Apr 2026 17:19:33 +0200 Subject: [PATCH 04/15] Update STM32F429.zig --- port/stmicro/stm32/src/hals/STM32F429.zig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/port/stmicro/stm32/src/hals/STM32F429.zig b/port/stmicro/stm32/src/hals/STM32F429.zig index 79897147c..aa3786bda 100644 --- a/port/stmicro/stm32/src/hals/STM32F429.zig +++ b/port/stmicro/stm32/src/hals/STM32F429.zig @@ -22,6 +22,7 @@ const std = @import("std"); const microzig = @import("microzig"); +const mmio = microzig.mmio; const peripherals = microzig.chip.peripherals; const RCC = peripherals.RCC; @@ -68,13 +69,16 @@ fn set_reg_field(reg: anytype, comptime field_name: anytype, value: anytype) voi pub const gpio = struct { pub fn set_output(comptime pin: type) void { - set_reg_field(RCC.AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); - set_reg_field(@field(pin.gpio_port, "MODER"), "MODER" ++ pin.suffix, 0b01); + const AHB1ENR: *volatile @TypeOf(RCC.AHB1ENR) = &RCC.AHB1ENR; + const MODER: *volatile @TypeOf(@field(pin.gpio_port, "MODER")) = &@field(pin.gpio_port, "MODER"); + set_reg_field(AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); + set_reg_field(MODER, "MODER[" ++ pin.suffix ++ "]", .Output); } pub fn set_input(comptime pin: type) void { - set_reg_field(RCC.AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); - set_reg_field(@field(pin.gpio_port, "MODER"), "MODER" ++ pin.suffix, 0b00); + const AHB1ENR: *volatile @TypeOf(RCC.AHB1ENR) = &RCC.AHB1ENR; + set_reg_field(AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); + set_reg_field(@field(pin.gpio_port, "MODER"), "MODER[" ++ pin.suffix ++ "]", .Input); } pub fn read(comptime pin: type) microzig.gpio.State { From 07c19d3fad441c129261055562928cdafad657d3 Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Mon, 27 Apr 2026 17:29:42 +0200 Subject: [PATCH 05/15] Update pins_v2.zig --- .../stmicro/stm32/src/hals/common/pins_v2.zig | 310 +++++------------- 1 file changed, 74 insertions(+), 236 deletions(-) diff --git a/port/stmicro/stm32/src/hals/common/pins_v2.zig b/port/stmicro/stm32/src/hals/common/pins_v2.zig index 2663657b8..e3b02378f 100644 --- a/port/stmicro/stm32/src/hals/common/pins_v2.zig +++ b/port/stmicro/stm32/src/hals/common/pins_v2.zig @@ -1,264 +1,102 @@ -const std = @import("std"); -const comptimePrint = std.fmt.comptimePrint; +//! For now we keep all clock settings on the chip defaults. +//! This code should work with all the STM32F42xx line +//! +//! Specifically, TIM6 is running on a 16 MHz clock, +//! HSI = 16 MHz is the SYSCLK after reset +//! default AHB prescaler = /1 (= values 0..7): +//! +//! ``` +//! RCC.CFGR.modify(.{ .HPRE = .Div1 }); +//! ``` +//! +//! so also HCLK = 16 MHz. +//! And with the default APB1 prescaler = /1: +//! +//! ``` +//! RCC.CFGR.modify(.{ .PPRE1 = .Div1 }); +//! ``` +//! +//! results in PCLK1 = 16 MHz. +//! +//! TODO: add more clock calculations when adding Uart +const std = @import("std"); const microzig = @import("microzig"); -const enums = @import("../common/enums.zig"); +const mmio = microzig.mmio; +const peripherals = microzig.chip.peripherals; +const RCC = peripherals.RCC; const Digital_IO = microzig.drivers.base.Digital_IO; -const Direction = Digital_IO.Direction; -const SetDirError = Digital_IO.SetDirError; -const SetBiasError = Digital_IO.SetBiasError; -const WriteError = Digital_IO.WriteError; -const ReadError = Digital_IO.ReadError; const State = Digital_IO.State; -const gpio_v2 = microzig.chip.types.peripherals.gpio_v2; -const PUPDR = gpio_v2.PUPDR; -const rcc = microzig.hal.rcc; - -const gpio = @import("gpio_v2.zig"); - -pub const Pin = enum { - PIN0, - PIN1, - PIN2, - PIN3, - PIN4, - PIN5, - PIN6, - PIN7, - PIN8, - PIN9, - PIN10, - PIN11, - PIN12, - PIN13, - PIN14, - PIN15, - pub const Configuration = struct { - name: ?[:0]const u8 = null, - mode: ?gpio.Mode = null, +pub const clock = struct { + pub const Domain = enum { + cpu, + ahb, + apb1, + apb2, }; }; -pub const InputGPIO = struct { - pin: gpio.Pin, - pub inline fn read(self: @This()) u1 { - const port = self.pin.get_port(); - return if (port.IDR.raw & self.pin.mask() != 0) - 1 - else - 0; - } +// Default clock frequencies after reset, see top comment for calculation +pub const clock_frequencies = .{ + .cpu = 16_000_000, + .ahb = 16_000_000, + .apb1 = 16_000_000, + .apb2 = 16_000_000, }; -pub const OutputGPIO = struct { - pin: gpio.Pin, - - pub inline fn put(self: @This(), value: u1) void { - var port = self.pin.get_port(); - switch (value) { - 0 => port.BSRR.raw = @intCast(self.pin.mask() << 16), - 1 => port.BSRR.raw = self.pin.mask(), - } - } +pub fn parse_pin(comptime spec: []const u8) type { + const invalid_format_msg = "The given pin '" ++ spec ++ "' has an invalid format. Pins must follow the format \"P{Port}{Pin}\" scheme."; - pub inline fn low(self: @This()) void { - self.put(0); - } + if (spec[0] != 'P') + @compileError(invalid_format_msg); + if (spec[1] < 'A' or spec[1] > 'K') + @compileError(invalid_format_msg); - pub inline fn high(self: @This()) void { - self.put(1); - } + const pin_number: comptime_int = std.fmt.parseInt(u4, spec[2..], 10) catch @compileError(invalid_format_msg); - pub inline fn toggle(self: @This()) void { - var port = self.pin.get_port(); - port.ODR.raw ^= self.pin.mask(); - } -}; - -pub const AlternateFunction = struct { - // Empty on perpose it should not be used as a GPIO. -}; - -const Analog = struct { - pin: gpio.Pin, -}; - -pub const Digital_IO_Pin = struct { - pin: gpio.Pin, - const vtable: Digital_IO.VTable = .{ - .set_direction_fn = Digital_IO_Pin.set_direction_fn, - .set_bias_fn = Digital_IO_Pin.set_bias_fn, - .write_fn = Digital_IO_Pin.write_fn, - .read_fn = Digital_IO_Pin.read_fn, + return struct { + /// 'A'...'K' + const gpio_port_name = spec[1..2]; + const gpio_port = @field(peripherals, "GPIO" ++ gpio_port_name); + const suffix = std.fmt.comptimePrint("{d}", .{pin_number}); }; - pub fn set_direction_fn(ptr: *anyopaque, dir: Direction) SetDirError!void { - const self: *@This() = @ptrCast(@alignCast(ptr)); - switch (dir) { - .input => self.pin.set_moder(.Input), - .output => self.pin.set_moder(.Output), - } - } - pub fn set_bias_fn(ptr: *anyopaque, maybe_bias: ?State) SetBiasError!void { - const self: *@This() = @ptrCast(@alignCast(ptr)); - - const pupdr: PUPDR = if (maybe_bias) |bias| switch (bias) { - .low => .PullDown, - .high => .PullUp, - } else .Floating; - self.pin.set_bias(pupdr); - } - pub fn write_fn(ptr: *anyopaque, state: State) WriteError!void { - const self: *@This() = @ptrCast(@alignCast(ptr)); - var port = self.pin.get_port(); - switch (state) { - .low => port.BSRR.raw = @intCast(self.pin.mask() << 16), - .high => port.BSRR.raw = self.pin.mask(), - } - } - pub fn read_fn(ptr: *anyopaque) ReadError!State { - const self: *@This() = @ptrCast(@alignCast(ptr)); - const port = self.pin.get_port(); - return if (port.IDR.raw & self.pin.mask() != 0) - .high - else - .low; - } - - pub fn digital_io(ptr: *@This()) Digital_IO { - return .{ - .ptr = ptr, - .vtable = &vtable, - }; - } -}; +} -pub fn GPIO(comptime mode: gpio.Mode) type { - return switch (mode) { - .input => InputGPIO, - .output => OutputGPIO, - .alternate_function => AlternateFunction, - .analog => Analog, - .digital_io => Digital_IO_Pin, - }; +fn set_reg_field(reg: anytype, comptime field_name: anytype, value: anytype) void { + var temp = reg.read(); + @field(temp, field_name) = value; + reg.write(temp); } -pub fn Pins(comptime config: GlobalConfiguration) type { - var count: usize = 0; - for (@typeInfo(GlobalConfiguration).@"struct".field_names) |port_field_name| { - if (@field(config, port_field_name)) |port_config| { - for (@typeInfo(Port.Configuration).@"struct".field_names) |field_name| { - if (@field(port_config, field_name) != null) { - count += 1; - } - } - } +pub const gpio = struct { + pub fn set_output(comptime pin: type) void { + const AHB1ENR: *volatile @TypeOf(RCC.AHB1ENR) = &RCC.AHB1ENR; + const MODER: *volatile @TypeOf(@field(pin.gpio_port, "MODER")) = &@field(pin.gpio_port, "MODER"); + set_reg_field(AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); + set_reg_field(MODER, "MODER[" ++ pin.suffix ++ "]", .Output); } - var field_names: [count][]const u8 = undefined; - var field_types: [count]type = undefined; - var field_attrs: [count]std.builtin.Type.Struct.FieldAttributes = undefined; - var i: usize = 0; - for (@typeInfo(GlobalConfiguration).@"struct".field_names) |port_field_name| { - if (@field(config, port_field_name)) |port_config| { - for (@typeInfo(Port.Configuration).@"struct".field_names) |field_name| { - if (@field(port_config, field_name)) |pin_config| { - const default_name = "P" ++ port_field_name[4..5] ++ field_name[3..]; - field_names[i] = pin_config.name orelse default_name; - field_types[i] = GPIO(pin_config.mode orelse .{ .input = .{.floating} }); - field_attrs[i] = .{}; - i += 1; - } - } - } + pub fn set_input(comptime pin: type) void { + const AHB1ENR: *volatile @TypeOf(RCC.AHB1ENR) = &RCC.AHB1ENR; + const MODER: *volatile @TypeOf(@field(pin.gpio_port, "MODER")) = &@field(pin.gpio_port, "MODER"); + set_reg_field(AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); + set_reg_field(MODER, "MODER[" ++ pin.suffix ++ "]", .Input); } - return @Struct(.auto, null, &field_names, &field_types, &field_attrs); -} - -pub const Port = enum { - GPIOA, - GPIOB, - GPIOC, - GPIOD, - GPIOE, - GPIOF, - GPIOG, - pub const Configuration = struct { - PIN0: ?Pin.Configuration = null, - PIN1: ?Pin.Configuration = null, - PIN2: ?Pin.Configuration = null, - PIN3: ?Pin.Configuration = null, - PIN4: ?Pin.Configuration = null, - PIN5: ?Pin.Configuration = null, - PIN6: ?Pin.Configuration = null, - PIN7: ?Pin.Configuration = null, - PIN8: ?Pin.Configuration = null, - PIN9: ?Pin.Configuration = null, - PIN10: ?Pin.Configuration = null, - PIN11: ?Pin.Configuration = null, - PIN12: ?Pin.Configuration = null, - PIN13: ?Pin.Configuration = null, - PIN14: ?Pin.Configuration = null, - PIN15: ?Pin.Configuration = null, - - comptime { - const pin_field_count = @typeInfo(Pin).@"enum".field_names.len; - const config_field_count = @typeInfo(Configuration).@"struct".field_names.len; - if (pin_field_count != config_field_count) - @compileError(comptimePrint("{} {}", .{ pin_field_count, config_field_count })); - } - }; -}; - -pub const GlobalConfiguration = struct { - GPIOA: ?Port.Configuration = null, - GPIOB: ?Port.Configuration = null, - GPIOC: ?Port.Configuration = null, - GPIOD: ?Port.Configuration = null, - GPIOE: ?Port.Configuration = null, - GPIOF: ?Port.Configuration = null, - GPIOG: ?Port.Configuration = null, - - comptime { - const port_field_count = @typeInfo(Port).@"enum".field_names.len; - const config_field_count = @typeInfo(GlobalConfiguration).@"struct".field_names.len; - if (port_field_count != config_field_count) - @compileError(comptimePrint("{} {}", .{ port_field_count, config_field_count })); + pub fn read(comptime pin: type) microzig.gpio.State { + const idr_reg = pin.gpio_port.IDR; + const reg_value = @field(idr_reg.read(), "IDR" ++ pin.suffix); // TODO extract to getRegField()? + return @as(microzig.gpio.State, @enumFromInt(reg_value)); } - pub fn apply(comptime config: GlobalConfiguration) Pins(config) { - var ret: Pins(config) = undefined; - - inline for (@typeInfo(GlobalConfiguration).@"struct".field_names) |port_field_name| { - if (@field(config, port_field_name)) |_| { - rcc.enable_clock(@field(enums.Peripherals, port_field_name)); - } - } - - inline for (@typeInfo(GlobalConfiguration).@"struct".field_names) |port_field_name| { - if (@field(config, port_field_name)) |port_config| { - inline for (@typeInfo(Port.Configuration).@"struct".field_names) |field_name| { - if (@field(port_config, field_name)) |pin_config| { - const port = @backingInt(@field(Port, port_field_name)); - var pin = gpio.Pin.from_port(@fromBackingInt(port), @backingInt(@field(Pin, field_name))); - pin.write_pin_config(pin_config.mode.?); - const default_name = "P" ++ port_field_name[4..5] ++ field_name[3..]; - - switch (pin_config.mode orelse .input) { - .input => @field(ret, pin_config.name orelse default_name) = InputGPIO{ .pin = pin }, - .output => @field(ret, pin_config.name orelse default_name) = OutputGPIO{ .pin = pin }, - .analog => @field(ret, pin_config.name orelse default_name) = Analog{}, - .alternate_function => @field(ret, pin_config.name orelse default_name) = AlternateFunction{}, - .digital_io => @field(ret, pin_config.name orelse default_name) = Digital_IO_Pin{ .pin = pin }, - } - } - } - } + pub fn write(comptime pin: type, state: State) void { + const BSRR: *volatile @TypeOf(pin.gpio_port.BSRR) = &pin.gpio_port.BSRR; + switch (state) { + .low => set_reg_field(BSRR, "BR[" ++ pin.suffix ++ "]", 1), + .high => set_reg_field(BSRR, "BS[" ++ pin.suffix ++ "]", 1), } - - return ret; } }; From e6d40f6e3746091fe5edb07eb0f29eb94b108dc0 Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Mon, 27 Apr 2026 19:58:47 +0200 Subject: [PATCH 06/15] Update STM32F429.zig --- port/stmicro/stm32/src/hals/STM32F429.zig | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/port/stmicro/stm32/src/hals/STM32F429.zig b/port/stmicro/stm32/src/hals/STM32F429.zig index aa3786bda..9d87a77a6 100644 --- a/port/stmicro/stm32/src/hals/STM32F429.zig +++ b/port/stmicro/stm32/src/hals/STM32F429.zig @@ -26,6 +26,10 @@ const mmio = microzig.mmio; const peripherals = microzig.chip.peripherals; const RCC = peripherals.RCC; +const Digital_IO = microzig.drivers.base.Digital_IO; + +const State = Digital_IO.State; + pub const clock = struct { pub const Domain = enum { cpu, @@ -77,8 +81,9 @@ pub const gpio = struct { pub fn set_input(comptime pin: type) void { const AHB1ENR: *volatile @TypeOf(RCC.AHB1ENR) = &RCC.AHB1ENR; + const MODER: *volatile @TypeOf(@field(pin.gpio_port, "MODER")) = &@field(pin.gpio_port, "MODER"); set_reg_field(AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); - set_reg_field(@field(pin.gpio_port, "MODER"), "MODER[" ++ pin.suffix ++ "]", .Input); + set_reg_field(MODER, "MODER[" ++ pin.suffix ++ "]", .Input); } pub fn read(comptime pin: type) microzig.gpio.State { @@ -87,10 +92,11 @@ pub const gpio = struct { return @as(microzig.gpio.State, @fromBackingInt(reg_value)); } - pub fn write(comptime pin: type, state: microzig.gpio.State) void { + pub fn write(comptime pin: type, state: State) void { + const BSRR: *volatile @TypeOf(pin.gpio_port.BSRR) = &pin.gpio_port.BSRR; switch (state) { - .low => set_reg_field(pin.gpio_port.BSRR, "BR" ++ pin.suffix, 1), - .high => set_reg_field(pin.gpio_port.BSRR, "BS" ++ pin.suffix, 1), + .low => set_reg_field(BSRR, "BR[" ++ pin.suffix ++ "]", 1), + .high => set_reg_field(BSRR, "BS[" ++ pin.suffix ++ "]", 1), } } }; From 594e94adffae1a92ceccfd6c49b097e1a5f5a3db Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Mon, 27 Apr 2026 19:59:41 +0200 Subject: [PATCH 07/15] Merge gpio_v2 and pins_v2 They were tightly coupled together, but had different types for the same concept. This introduced unneeded enum casts from and to int, and added complexity. --- .../stmicro/stm32/src/hals/common/gpio_v2.zig | 174 ------- .../stmicro/stm32/src/hals/common/pins_v2.zig | 464 +++++++++++++++--- 2 files changed, 390 insertions(+), 248 deletions(-) delete mode 100644 port/stmicro/stm32/src/hals/common/gpio_v2.zig diff --git a/port/stmicro/stm32/src/hals/common/gpio_v2.zig b/port/stmicro/stm32/src/hals/common/gpio_v2.zig deleted file mode 100644 index 04cd99574..000000000 --- a/port/stmicro/stm32/src/hals/common/gpio_v2.zig +++ /dev/null @@ -1,174 +0,0 @@ -const microzig = @import("microzig"); - -pub const peripherals = microzig.chip.peripherals; - -const gpio_v2 = microzig.chip.types.peripherals.gpio_v2; -const GPIO = gpio_v2.GPIO; -const MODER = gpio_v2.MODER; -const PUPDR = gpio_v2.PUPDR; -const OSPEEDR = gpio_v2.OSPEEDR; -const OT = gpio_v2.OT; - -pub const Port = enum { - A, - B, - C, - D, - E, - F, - G, -}; - -pub const Mode = union(enum) { - input: InputMode, - output: OutputMode, - analog: AnalogMode, - alternate_function: AlternateFunction, - digital_io: Digital_IO, -}; - -pub const Digital_IO = struct {}; - -pub const InputMode = struct { - resistor: PUPDR, -}; - -pub const OutputMode = struct { - resistor: PUPDR, - o_type: OT, - o_speed: OSPEEDR = .LowSpeed, -}; - -pub const AnalogMode = struct { - resistor: PUPDR = .Floating, -}; - -pub const AF = enum(u4) { - AF0, - AF1, - AF2, - AF3, - AF4, - AF5, - AF6, - AF7, - AF8, - AF9, - AF10, - AF11, - AF12, - AF13, - AF14, - AF15, -}; - -pub const AlternateFunction = struct { - afr: AF, - resistor: PUPDR = .Floating, - o_type: OT = .PushPull, - o_speed: OSPEEDR = .HighSpeed, -}; - -// This is mostly internal to hal for writing configuration. -// Public implementation is provided in the pins.zig file. -pub const Pin = enum(usize) { - _, - - pub inline fn write_pin_config(gpio: Pin, mode: Mode) void { - switch (mode) { - .input => |imode| { - gpio.set_moder(MODER.Input); - gpio.set_bias(imode.resistor); - }, - .output => |omode| { - gpio.set_moder(MODER.Output); - gpio.set_output_type(omode.o_type); - gpio.set_bias(omode.resistor); - gpio.set_speed(omode.o_speed); - }, - .analog => |amode| { - gpio.set_moder(MODER.Analog); - gpio.set_bias(amode.resistor); - }, - .alternate_function => |afmode| { - gpio.set_moder(MODER.Alternate); - gpio.set_bias(afmode.resistor); - gpio.set_speed(afmode.o_speed); - gpio.set_output_type(afmode.o_type); - gpio.set_alternate_function(afmode.afr); - }, - .digital_io => { - // Nothing for now - }, - } - } - - pub fn mask_2bit(gpio: Pin) u32 { - const pin: u5 = @intCast(@backingInt(gpio) % 16); - return @as(u32, 0b11) << (pin << 1); - } - - pub fn mask(gpio: Pin) u32 { - const pin: u4 = @intCast(@backingInt(gpio) % 16); - return @as(u32, 1) << pin; - } - - //NOTE: should invalid pins panic or just be ignored? - pub fn get_port(gpio: Pin) *volatile GPIO { - const port: usize = @divFloor(@backingInt(gpio), 16); - switch (port) { - 0 => return if (@hasDecl(peripherals, "GPIOA")) peripherals.GPIOA else @panic("Invalid Pin"), - 1 => return if (@hasDecl(peripherals, "GPIOB")) peripherals.GPIOB else @panic("Invalid Pin"), - 2 => return if (@hasDecl(peripherals, "GPIOC")) peripherals.GPIOC else @panic("Invalid Pin"), - 3 => return if (@hasDecl(peripherals, "GPIOD")) peripherals.GPIOD else @panic("Invalid Pin"), - 4 => return if (@hasDecl(peripherals, "GPIOE")) peripherals.GPIOE else @panic("Invalid Pin"), - 5 => return if (@hasDecl(peripherals, "GPIOF")) peripherals.GPIOF else @panic("Invalid Pin"), - 6 => return if (@hasDecl(peripherals, "GPIOG")) peripherals.GPIOG else @panic("Invalid Pin"), - else => @panic("The STM32 only has ports 0..6 (A..G)"), - } - } - - pub inline fn set_bias(gpio: Pin, bias: PUPDR) void { - const port = gpio.get_port(); - const pin: u5 = @intCast(@backingInt(gpio) % 16); - const modMask: u32 = gpio.mask_2bit(); - - port.PUPDR.write_raw((port.PUPDR.raw & ~modMask) | @as(u32, @backingInt(bias)) << (pin << 1)); - } - - pub inline fn set_speed(gpio: Pin, speed: OSPEEDR) void { - const port = gpio.get_port(); - const pin: u5 = @intCast(@backingInt(gpio) % 16); - const modMask: u32 = gpio.mask_2bit(); - - port.OSPEEDR.write_raw((port.OSPEEDR.raw & ~modMask) | @as(u32, @backingInt(speed)) << (pin << 1)); - } - - pub inline fn set_moder(gpio: Pin, moder: MODER) void { - const port = gpio.get_port(); - const pin: u5 = @intCast(@backingInt(gpio) % 16); - const modMask: u32 = gpio.mask_2bit(); - - port.MODER.write_raw((port.MODER.raw & ~modMask) | @as(u32, @backingInt(moder)) << (pin << 1)); - } - - pub inline fn set_output_type(gpio: Pin, otype: OT) void { - const port = gpio.get_port(); - const pin: u5 = @intCast(@backingInt(gpio) % 16); - - port.OTYPER.write_raw((port.OTYPER.raw & ~gpio.mask()) | @as(u32, @backingInt(otype)) << pin); - } - - pub inline fn set_alternate_function(gpio: Pin, afr: AF) void { - const port = gpio.get_port(); - const pin: u5 = @intCast(@backingInt(gpio) % 16); - const afrMask: u32 = @as(u32, 0b1111) << ((pin % 8) << 2); - const register = if (pin > 7) &port.AFR[1] else &port.AFR[0]; - register.write_raw((register.raw & ~afrMask) | @as(u32, @backingInt(afr)) << ((pin % 8) << 2)); - } - - pub fn from_port(port: Port, pin: u4) Pin { - const value: usize = pin + (@as(usize, 16) * @backingInt(port)); - return @fromBackingInt(value); - } -}; diff --git a/port/stmicro/stm32/src/hals/common/pins_v2.zig b/port/stmicro/stm32/src/hals/common/pins_v2.zig index e3b02378f..d4315f626 100644 --- a/port/stmicro/stm32/src/hals/common/pins_v2.zig +++ b/port/stmicro/stm32/src/hals/common/pins_v2.zig @@ -1,102 +1,418 @@ -//! For now we keep all clock settings on the chip defaults. -//! This code should work with all the STM32F42xx line -//! -//! Specifically, TIM6 is running on a 16 MHz clock, -//! HSI = 16 MHz is the SYSCLK after reset -//! default AHB prescaler = /1 (= values 0..7): -//! -//! ``` -//! RCC.CFGR.modify(.{ .HPRE = .Div1 }); -//! ``` -//! -//! so also HCLK = 16 MHz. -//! And with the default APB1 prescaler = /1: -//! -//! ``` -//! RCC.CFGR.modify(.{ .PPRE1 = .Div1 }); -//! ``` -//! -//! results in PCLK1 = 16 MHz. -//! -//! TODO: add more clock calculations when adding Uart - const std = @import("std"); +const assert = std.debug.assert; +const comptimePrint = std.fmt.comptimePrint; +const StructField = std.builtin.Type.StructField; + const microzig = @import("microzig"); -const mmio = microzig.mmio; -const peripherals = microzig.chip.peripherals; -const RCC = peripherals.RCC; +const enums = @import("../common/enums.zig"); const Digital_IO = microzig.drivers.base.Digital_IO; +const Direction = Digital_IO.Direction; +const SetDirError = Digital_IO.SetDirError; +const SetBiasError = Digital_IO.SetBiasError; +const WriteError = Digital_IO.WriteError; +const ReadError = Digital_IO.ReadError; const State = Digital_IO.State; -pub const clock = struct { - pub const Domain = enum { - cpu, - ahb, - apb1, - apb2, +const gpio_v2 = microzig.chip.types.peripherals.gpio_v2; +const PUPDR = gpio_v2.PUPDR; +const MODER = gpio_v2.MODER; +const OSPEEDR = gpio_v2.OSPEEDR; +const OT = gpio_v2.OT; +const AFIO = microzig.chip.peripherals.AFIO; + +const rcc = microzig.hal.rcc; +const peripherals = microzig.chip.peripherals; + +pub const Mode = union(enum) { + input: InputMode, + output: OutputMode, + analog: AnalogMode, + alternate_function: AlternateFunctionMode, + digital_io: Digital_IOMode, +}; + +const Digital_IOMode = struct {}; + +const InputMode = struct { + resistor: PUPDR, +}; + +const OutputMode = struct { + resistor: PUPDR, + o_type: OT, + o_speed: OSPEEDR = .LowSpeed, +}; + +const AnalogMode = struct { + resistor: PUPDR = .Floating, +}; + +const AF = enum(u4) { + AF0, + AF1, + AF2, + AF3, + AF4, + AF5, + AF6, + AF7, + AF8, + AF9, + AF10, + AF11, + AF12, + AF13, + AF14, + AF15, +}; + +pub const AlternateFunctionMode = struct { + afr: AF, + resistor: PUPDR = .Floating, + o_type: OT = .PushPull, + o_speed: OSPEEDR = .HighSpeed, +}; + +pub const Pin = enum { + PIN0, + PIN1, + PIN2, + PIN3, + PIN4, + PIN5, + PIN6, + PIN7, + PIN8, + PIN9, + PIN10, + PIN11, + PIN12, + PIN13, + PIN14, + PIN15, + pub const Configuration = struct { + name: ?[:0]const u8 = null, + mode: ?Mode = null, }; }; -// Default clock frequencies after reset, see top comment for calculation -pub const clock_frequencies = .{ - .cpu = 16_000_000, - .ahb = 16_000_000, - .apb1 = 16_000_000, - .apb2 = 16_000_000, +const GPIO_Pin = struct { + pin: Pin, + port: Port, + + inline fn write_pin_config(_gpio: GPIO_Pin, mode: Mode) void { + switch (mode) { + .input => |imode| { + _gpio.set_moder(MODER.Input); + _gpio.set_bias(imode.resistor); + }, + .output => |omode| { + _gpio.set_moder(MODER.Output); + _gpio.set_output_type(omode.o_type); + _gpio.set_bias(omode.resistor); + _gpio.set_speed(omode.o_speed); + }, + .analog => |amode| { + _gpio.set_moder(MODER.Analog); + _gpio.set_bias(amode.resistor); + }, + .alternate_function => |afmode| { + _gpio.set_moder(MODER.Alternate); + _gpio.set_bias(afmode.resistor); + _gpio.set_speed(afmode.o_speed); + _gpio.set_output_type(afmode.o_type); + _gpio.set_alternate_function(afmode.afr); + }, + .digital_io => { + // Nothing for now + }, + } + } + + fn mask_2bit(_gpio: GPIO_Pin) u32 { + const pin: u4 = @backingInt(_gpio.pin); + return @as(u32, 0b11) << (pin << 1); + } + + fn mask(_gpio: GPIO_Pin) u32 { + const pin: u4 = @backingInt(_gpio.pin); + return @as(u32, 1) << pin; + } + + //NOTE: should invalid pins panic or just be ignored? + fn get_port(_gpio: GPIO_Pin) *volatile gpio_v2.GPIO { + return _gpio.port.get_port(); + } + + inline fn set_bias(_gpio: GPIO_Pin, bias: PUPDR) void { + const port = _gpio.port.get_port(); + const pin: u4 = @backingInt(_gpio.pin); + const modMask: u32 = _gpio.mask_2bit(); + + port.PUPDR.write_raw((port.PUPDR.raw & ~modMask) | @as(u32, @backingInt(bias)) << (pin << 1)); + } + + inline fn set_speed(_gpio: GPIO_Pin, speed: OSPEEDR) void { + const port = _gpio.port.get_port(); + const pin: u5 = @backingInt(_gpio.pin); + const modMask: u32 = _gpio.mask_2bit(); + + port.OSPEEDR.write_raw((port.OSPEEDR.raw & ~modMask) | @as(u32, @backingInt(speed)) << (pin << 1)); + } + + inline fn set_moder(_gpio: GPIO_Pin, moder: MODER) void { + const port = _gpio.port.get_port(); + const pin: u5 = @backingInt(_gpio.pin); + const modMask: u32 = _gpio.mask_2bit(); + + port.MODER.write_raw((port.MODER.raw & ~modMask) | @as(u32, @backingInt(moder)) << (pin << 1)); + } + + inline fn set_output_type(_gpio: GPIO_Pin, otype: OT) void { + const port = _gpio.port.get_port(); + const pin: u5 = @backingInt(_gpio.pin); + + port.OTYPER.write_raw((port.OTYPER.raw & ~_gpio.mask()) | @as(u32, @backingInt(otype)) << pin); + } + + fn from_port(port: Port, pin: Pin) GPIO_Pin { + return .{ + .port = port, + .pin = pin, + }; + } }; -pub fn parse_pin(comptime spec: []const u8) type { - const invalid_format_msg = "The given pin '" ++ spec ++ "' has an invalid format. Pins must follow the format \"P{Port}{Pin}\" scheme."; +pub const Input_GPIO = struct { + pin: GPIO_Pin, + pub inline fn read(self: @This()) u1 { + const port = self.pin.get_port(); + return if (port.IDR.raw & self.pin.mask() != 0) + 1 + else + 0; + } +}; - if (spec[0] != 'P') - @compileError(invalid_format_msg); - if (spec[1] < 'A' or spec[1] > 'K') - @compileError(invalid_format_msg); +pub const Output_GPIO = struct { + pin: GPIO_Pin, - const pin_number: comptime_int = std.fmt.parseInt(u4, spec[2..], 10) catch @compileError(invalid_format_msg); + pub inline fn put(self: @This(), value: u1) void { + var port = self.pin.get_port(); + switch (value) { + 0 => port.BSRR.raw = @intCast(self.pin.mask() << 16), + 1 => port.BSRR.raw = self.pin.mask(), + } + } + + pub inline fn low(self: @This()) void { + self.put(0); + } + + pub inline fn high(self: @This()) void { + self.put(1); + } + + pub inline fn toggle(self: @This()) void { + var port = self.pin.get_port(); + port.ODR.raw ^= self.pin.mask(); + } +}; + +pub const AlternateFunction = struct { + // Empty on perpose it should not be used as a GPIO. +}; + +const Analog = struct { + pin: GPIO_Pin, +}; - return struct { - /// 'A'...'K' - const gpio_port_name = spec[1..2]; - const gpio_port = @field(peripherals, "GPIO" ++ gpio_port_name); - const suffix = std.fmt.comptimePrint("{d}", .{pin_number}); +pub const Digital_IO_Pin = struct { + pin: GPIO_Pin, + const vtable: Digital_IO.VTable = .{ + .set_direction_fn = Digital_IO_Pin.set_direction_fn, + .set_bias_fn = Digital_IO_Pin.set_bias_fn, + .write_fn = Digital_IO_Pin.write_fn, + .read_fn = Digital_IO_Pin.read_fn, }; -} + pub fn set_direction_fn(ptr: *anyopaque, dir: Direction) SetDirError!void { + const self: *@This() = @ptrCast(@alignCast(ptr)); + switch (dir) { + .input => self.pin.set_moder(.Input), + .output => self.pin.set_moder(.Output), + } + } + pub fn set_bias_fn(ptr: *anyopaque, maybe_bias: ?State) SetBiasError!void { + const self: *@This() = @ptrCast(@alignCast(ptr)); + + const pupdr: PUPDR = if (maybe_bias) |bias| switch (bias) { + .low => .PullDown, + .high => .PullUp, + } else .Floating; + self.pin.set_bias(pupdr); + } + pub fn write_fn(ptr: *anyopaque, state: State) WriteError!void { + const self: *@This() = @ptrCast(@alignCast(ptr)); + var port = self.pin.get_port(); + switch (state) { + .low => port.BSRR.raw = @intCast(self.pin.mask() << 16), + .high => port.BSRR.raw = self.pin.mask(), + } + } + pub fn read_fn(ptr: *anyopaque) ReadError!State { + const self: *@This() = @ptrCast(@alignCast(ptr)); + const port = self.pin.get_port(); + return if (port.IDR.raw & self.pin.mask() != 0) + .high + else + .low; + } + + pub fn digital_io(ptr: *@This()) Digital_IO { + return .{ + .ptr = ptr, + .vtable = &vtable, + }; + } +}; -fn set_reg_field(reg: anytype, comptime field_name: anytype, value: anytype) void { - var temp = reg.read(); - @field(temp, field_name) = value; - reg.write(temp); +pub fn GPIO(comptime mode: Mode) type { + return switch (mode) { + .input => Input_GPIO, + .output => Output_GPIO, + .alternate_function => AlternateFunction, + .analog => Analog, + .digital_io => Digital_IO_Pin, + }; } -pub const gpio = struct { - pub fn set_output(comptime pin: type) void { - const AHB1ENR: *volatile @TypeOf(RCC.AHB1ENR) = &RCC.AHB1ENR; - const MODER: *volatile @TypeOf(@field(pin.gpio_port, "MODER")) = &@field(pin.gpio_port, "MODER"); - set_reg_field(AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); - set_reg_field(MODER, "MODER[" ++ pin.suffix ++ "]", .Output); +pub fn Pins(comptime config: GlobalConfiguration) type { + var count: usize = 0; + for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (@field(config, port_field.name)) |port_config| { + for (@typeInfo(Port.Configuration).@"struct".fields) |field| { + if (@field(port_config, field.name) != null) { + count += 1; + } + } + } } - pub fn set_input(comptime pin: type) void { - const AHB1ENR: *volatile @TypeOf(RCC.AHB1ENR) = &RCC.AHB1ENR; - const MODER: *volatile @TypeOf(@field(pin.gpio_port, "MODER")) = &@field(pin.gpio_port, "MODER"); - set_reg_field(AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); - set_reg_field(MODER, "MODER[" ++ pin.suffix ++ "]", .Input); + var field_names: [count][]const u8 = undefined; + var field_types: [count]type = undefined; + var field_attrs: [count]std.builtin.Type.StructField.Attributes = undefined; + var i: usize = 0; + for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (@field(config, port_field.name)) |port_config| { + for (@typeInfo(Port.Configuration).@"struct".fields) |field| { + if (@field(port_config, field.name)) |pin_config| { + const default_name = "P" ++ port_field.name[4..5] ++ field.name[3..]; + field_names[i] = pin_config.name orelse default_name; + field_types[i] = GPIO(pin_config.mode orelse .{ .input = .{.floating} }); + field_attrs[i] = .{}; + i += 1; + } + } + } + } + + return @Struct(.auto, null, &field_names, &field_types, &field_attrs); +} + +pub const Port = enum { + GPIOA, + GPIOB, + GPIOC, + GPIOD, + GPIOE, + GPIOF, + GPIOG, + pub const Configuration = struct { + PIN0: ?Pin.Configuration = null, + PIN1: ?Pin.Configuration = null, + PIN2: ?Pin.Configuration = null, + PIN3: ?Pin.Configuration = null, + PIN4: ?Pin.Configuration = null, + PIN5: ?Pin.Configuration = null, + PIN6: ?Pin.Configuration = null, + PIN7: ?Pin.Configuration = null, + PIN8: ?Pin.Configuration = null, + PIN9: ?Pin.Configuration = null, + PIN10: ?Pin.Configuration = null, + PIN11: ?Pin.Configuration = null, + PIN12: ?Pin.Configuration = null, + PIN13: ?Pin.Configuration = null, + PIN14: ?Pin.Configuration = null, + PIN15: ?Pin.Configuration = null, + + comptime { + const pin_field_count = @typeInfo(Pin).@"enum".fields.len; + const config_field_count = @typeInfo(Configuration).@"struct".fields.len; + if (pin_field_count != config_field_count) + @compileError(comptimePrint("{} {}", .{ pin_field_count, config_field_count })); + } + }; + + //NOTE: should invalid pins panic or just be ignored? + pub fn get_port(port: Port) *volatile gpio_v2.GPIO { + switch (@backingInt(port)) { + inline 0...7 - 1 => |_port| { + const port_name = [_]u8{"ABCDEFGHIJK"[_port]}; + return @field(peripherals, "GPIO" ++ port_name); + }, + else => @panic("STM32s only have ports 0..10 (A..K)"), + } } +}; + +pub const GlobalConfiguration = struct { + GPIOA: ?Port.Configuration = null, + GPIOB: ?Port.Configuration = null, + GPIOC: ?Port.Configuration = null, + GPIOD: ?Port.Configuration = null, + GPIOE: ?Port.Configuration = null, + GPIOF: ?Port.Configuration = null, + GPIOG: ?Port.Configuration = null, - pub fn read(comptime pin: type) microzig.gpio.State { - const idr_reg = pin.gpio_port.IDR; - const reg_value = @field(idr_reg.read(), "IDR" ++ pin.suffix); // TODO extract to getRegField()? - return @as(microzig.gpio.State, @enumFromInt(reg_value)); + comptime { + const port_field_count = @typeInfo(Port).@"enum".fields.len; + const config_field_count = @typeInfo(GlobalConfiguration).@"struct".fields.len; + if (port_field_count != config_field_count) + @compileError(comptimePrint("{} {}", .{ port_field_count, config_field_count })); } - pub fn write(comptime pin: type, state: State) void { - const BSRR: *volatile @TypeOf(pin.gpio_port.BSRR) = &pin.gpio_port.BSRR; - switch (state) { - .low => set_reg_field(BSRR, "BR[" ++ pin.suffix ++ "]", 1), - .high => set_reg_field(BSRR, "BS[" ++ pin.suffix ++ "]", 1), + pub fn apply(comptime config: GlobalConfiguration) Pins(config) { + var ret: Pins(config) = undefined; + + inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (@field(config, port_field.name)) |_| { + rcc.enable_clock(@field(enums.Peripherals, port_field.name)); + } } + + inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (@field(config, port_field.name)) |port_config| { + inline for (@typeInfo(Port.Configuration).@"struct".fields) |field| { + if (@field(port_config, field.name)) |pin_config| { + const port = @field(Port, port_field.name); + var pin = GPIO_Pin.from_port(port, @field(Pin, field.name)); + pin.write_pin_config(pin_config.mode.?); + const default_name = "P" ++ port_field.name[4..5] ++ field.name[3..]; + + switch (pin_config.mode orelse .input) { + .input => @field(ret, pin_config.name orelse default_name) = Input_GPIO{ .pin = pin }, + .output => @field(ret, pin_config.name orelse default_name) = Output_GPIO{ .pin = pin }, + .analog => @field(ret, pin_config.name orelse default_name) = Analog{}, + .alternate_function => @field(ret, pin_config.name orelse default_name) = AlternateFunction{}, + .digital_io => @field(ret, pin_config.name orelse default_name) = Digital_IO_Pin{ .pin = pin }, + } + } + } + } + } + + return ret; } }; From 24e802ce2368a29c17008c665c3ffae1d476e6a9 Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Wed, 29 Apr 2026 11:49:08 +0200 Subject: [PATCH 08/15] Make pins_v2.zig adaptable to multiple chips Previously pins_v2 was only available on the F303 and L47x, and only implemented for 7 GPIO ports on the later. This makes the pins api available to any number of pins (soft limited to 11 due to comptime LUT size). Importer can provide the available number of ports. This also makes the api available for stm32f429 --- .../stmicro/stm32/src/hals/STM32F303/pins.zig | 4 +- port/stmicro/stm32/src/hals/STM32F429.zig | 97 ++- .../stmicro/stm32/src/hals/STM32L47X/pins.zig | 4 +- .../stmicro/stm32/src/hals/common/pins_v2.zig | 820 ++++++++++-------- 4 files changed, 502 insertions(+), 423 deletions(-) diff --git a/port/stmicro/stm32/src/hals/STM32F303/pins.zig b/port/stmicro/stm32/src/hals/STM32F303/pins.zig index f2fef522f..77b7a6f97 100644 --- a/port/stmicro/stm32/src/hals/STM32F303/pins.zig +++ b/port/stmicro/stm32/src/hals/STM32F303/pins.zig @@ -1,2 +1,4 @@ -const PinCommon = @import("../common/pins_v2.zig"); +const PinCommon = @import("../common/pins_v2.zig").get_pins(.{ + .portcount = 7, +}); pub const GlobalConfiguration = PinCommon.GlobalConfiguration; diff --git a/port/stmicro/stm32/src/hals/STM32F429.zig b/port/stmicro/stm32/src/hals/STM32F429.zig index 9d87a77a6..9420fca1b 100644 --- a/port/stmicro/stm32/src/hals/STM32F429.zig +++ b/port/stmicro/stm32/src/hals/STM32F429.zig @@ -30,6 +30,10 @@ const Digital_IO = microzig.drivers.base.Digital_IO; const State = Digital_IO.State; +pub const pins = @import("./common/pins_v2.zig").get_pins(.{ + .portcount = 11, +}); + pub const clock = struct { pub const Domain = enum { cpu, @@ -47,56 +51,63 @@ pub const clock_frequencies = .{ .apb2 = 16_000_000, }; -pub fn parse_pin(comptime spec: []const u8) type { - const invalid_format_msg = "The given pin '" ++ spec ++ "' has an invalid format. Pins must follow the format \"P{Port}{Pin}\" scheme."; - - if (spec[0] != 'P') - @compileError(invalid_format_msg); - if (spec[1] < 'A' or spec[1] > 'K') - @compileError(invalid_format_msg); - - const pin_number: comptime_int = std.fmt.parseInt(u4, spec[2..], 10) catch @compileError(invalid_format_msg); - - return struct { - /// 'A'...'K' - const gpio_port_name = spec[1..2]; - const gpio_port = @field(peripherals, "GPIO" ++ gpio_port_name); - const suffix = std.fmt.comptimePrint("{d}", .{pin_number}); - }; -} +// TODO: There should be a common rcc with stuff like this, just like pins_v2.zig +pub const rcc = struct { + const util = @import("common/util.zig"); + const _rcc = microzig.chip.peripherals.RCC; -fn set_reg_field(reg: anytype, comptime field_name: anytype, value: anytype) void { - var temp = reg.read(); - @field(temp, field_name) = value; - reg.write(temp); -} + // Any peripheral that must be enable in RCC. + pub const Peripherals = util.create_peripheral_enum(&.{ + "GPIO", + }); -pub const gpio = struct { - pub fn set_output(comptime pin: type) void { - const AHB1ENR: *volatile @TypeOf(RCC.AHB1ENR) = &RCC.AHB1ENR; - const MODER: *volatile @TypeOf(@field(pin.gpio_port, "MODER")) = &@field(pin.gpio_port, "MODER"); - set_reg_field(AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); - set_reg_field(MODER, "MODER[" ++ pin.suffix ++ "]", .Output); + ///configure the power and clock registers before enabling the RTC + ///this function also can be called from `rtc.enable()` + pub fn enable_rtc(on: bool) void { + _rcc.BDCR.modify(.{ .RTCEN = @intFromBool(on) }); } - pub fn set_input(comptime pin: type) void { - const AHB1ENR: *volatile @TypeOf(RCC.AHB1ENR) = &RCC.AHB1ENR; - const MODER: *volatile @TypeOf(@field(pin.gpio_port, "MODER")) = &@field(pin.gpio_port, "MODER"); - set_reg_field(AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); - set_reg_field(MODER, "MODER[" ++ pin.suffix ++ "]", .Input); + pub fn set_clock(comptime peri: Peripherals, state: u1) void { + const peri_name = @tagName(peri); + const field = peri_name ++ "EN"; + if (util.match_name(peri_name, &.{"RTC"})) { + enable_rtc(state != 0); + return; + } + const rcc_register_name = comptime if (util.match_name(peri_name, &.{ + "OTGHSULPI", + "OTGHS", + "ETHMACPTP", + "ETHMACRX", + "ETHMACTX", + "ETHMAC", + "DMA2D", + "DMA2", + "DMA1", + "CCMDATARAM", + "BKPSRAM", + "CRC", + "GPIOK", + "GPIOJ", + "GPIOI", + "GPIOH", + "GPIOG", + "GPIOF", + "GPIOE", + "GPIOD", + "GPIOC", + "GPIOB", + "GPIOA", + })) "AHB1ENR" else "AHB1ENR"; + + @field(_rcc, rcc_register_name).modify_one(field, state); } - pub fn read(comptime pin: type) microzig.gpio.State { - const idr_reg = pin.gpio_port.IDR; - const reg_value = @field(idr_reg.read(), "IDR" ++ pin.suffix); // TODO extract to getRegField()? - return @as(microzig.gpio.State, @fromBackingInt(reg_value)); + pub fn enable_clock(comptime peri: Peripherals) void { + set_clock(peri, 1); } - pub fn write(comptime pin: type, state: State) void { - const BSRR: *volatile @TypeOf(pin.gpio_port.BSRR) = &pin.gpio_port.BSRR; - switch (state) { - .low => set_reg_field(BSRR, "BR[" ++ pin.suffix ++ "]", 1), - .high => set_reg_field(BSRR, "BS[" ++ pin.suffix ++ "]", 1), - } + pub fn disable_clock(comptime peri: Peripherals) void { + set_clock(peri, 0); } }; diff --git a/port/stmicro/stm32/src/hals/STM32L47X/pins.zig b/port/stmicro/stm32/src/hals/STM32L47X/pins.zig index f2fef522f..eb69d0ce0 100644 --- a/port/stmicro/stm32/src/hals/STM32L47X/pins.zig +++ b/port/stmicro/stm32/src/hals/STM32L47X/pins.zig @@ -1,2 +1,4 @@ -const PinCommon = @import("../common/pins_v2.zig"); +const PinCommon = @import("../common/pins_v2.zig").get_pins(.{ + .portcount = 8, +}); pub const GlobalConfiguration = PinCommon.GlobalConfiguration; diff --git a/port/stmicro/stm32/src/hals/common/pins_v2.zig b/port/stmicro/stm32/src/hals/common/pins_v2.zig index d4315f626..d3f6efc55 100644 --- a/port/stmicro/stm32/src/hals/common/pins_v2.zig +++ b/port/stmicro/stm32/src/hals/common/pins_v2.zig @@ -1,418 +1,482 @@ -const std = @import("std"); -const assert = std.debug.assert; -const comptimePrint = std.fmt.comptimePrint; -const StructField = std.builtin.Type.StructField; - -const microzig = @import("microzig"); -const enums = @import("../common/enums.zig"); - -const Digital_IO = microzig.drivers.base.Digital_IO; -const Direction = Digital_IO.Direction; -const SetDirError = Digital_IO.SetDirError; -const SetBiasError = Digital_IO.SetBiasError; -const WriteError = Digital_IO.WriteError; -const ReadError = Digital_IO.ReadError; - -const State = Digital_IO.State; - -const gpio_v2 = microzig.chip.types.peripherals.gpio_v2; -const PUPDR = gpio_v2.PUPDR; -const MODER = gpio_v2.MODER; -const OSPEEDR = gpio_v2.OSPEEDR; -const OT = gpio_v2.OT; -const AFIO = microzig.chip.peripherals.AFIO; - -const rcc = microzig.hal.rcc; -const peripherals = microzig.chip.peripherals; - -pub const Mode = union(enum) { - input: InputMode, - output: OutputMode, - analog: AnalogMode, - alternate_function: AlternateFunctionMode, - digital_io: Digital_IOMode, +pub const config = struct { + portcount: usize, }; -const Digital_IOMode = struct {}; +pub fn get_pins(comptime cfg: config) type { + return struct { + const std = @import("std"); + const assert = std.debug.assert; + const comptimePrint = std.fmt.comptimePrint; + const StructField = std.builtin.Type.StructField; + + const microzig = @import("microzig"); + + const Digital_IO = microzig.drivers.base.Digital_IO; + const Direction = Digital_IO.Direction; + const SetDirError = Digital_IO.SetDirError; + const SetBiasError = Digital_IO.SetBiasError; + const WriteError = Digital_IO.WriteError; + const ReadError = Digital_IO.ReadError; + + const State = Digital_IO.State; + + const gpio_v2 = microzig.chip.types.peripherals.gpio_v2; + const PUPDR = gpio_v2.PUPDR; + const MODER = gpio_v2.MODER; + const OSPEEDR = gpio_v2.OSPEEDR; + const OT = gpio_v2.OT; + const AFIO = microzig.chip.peripherals.AFIO; + + const rcc = microzig.hal.rcc; + const peripherals = microzig.chip.peripherals; + + pub const Mode = union(enum) { + input: InputMode, + output: OutputMode, + analog: AnalogMode, + alternate_function: AlternateFunctionMode, + digital_io: Digital_IO_Mode, + }; -const InputMode = struct { - resistor: PUPDR, -}; + const Digital_IO_Mode = struct {}; -const OutputMode = struct { - resistor: PUPDR, - o_type: OT, - o_speed: OSPEEDR = .LowSpeed, -}; + const InputMode = struct { + resistor: PUPDR, + }; -const AnalogMode = struct { - resistor: PUPDR = .Floating, -}; + const OutputMode = struct { + resistor: PUPDR, + o_type: OT, + o_speed: OSPEEDR = .LowSpeed, + }; -const AF = enum(u4) { - AF0, - AF1, - AF2, - AF3, - AF4, - AF5, - AF6, - AF7, - AF8, - AF9, - AF10, - AF11, - AF12, - AF13, - AF14, - AF15, -}; + const AnalogMode = struct { + resistor: PUPDR = .Floating, + }; -pub const AlternateFunctionMode = struct { - afr: AF, - resistor: PUPDR = .Floating, - o_type: OT = .PushPull, - o_speed: OSPEEDR = .HighSpeed, -}; + const AF = enum(u4) { + AF0, + AF1, + AF2, + AF3, + AF4, + AF5, + AF6, + AF7, + AF8, + AF9, + AF10, + AF11, + AF12, + AF13, + AF14, + AF15, + }; -pub const Pin = enum { - PIN0, - PIN1, - PIN2, - PIN3, - PIN4, - PIN5, - PIN6, - PIN7, - PIN8, - PIN9, - PIN10, - PIN11, - PIN12, - PIN13, - PIN14, - PIN15, - pub const Configuration = struct { - name: ?[:0]const u8 = null, - mode: ?Mode = null, - }; -}; + pub const AlternateFunctionMode = struct { + afr: AF, + resistor: PUPDR = .Floating, + o_type: OT = .PushPull, + o_speed: OSPEEDR = .HighSpeed, + }; -const GPIO_Pin = struct { - pin: Pin, - port: Port, - - inline fn write_pin_config(_gpio: GPIO_Pin, mode: Mode) void { - switch (mode) { - .input => |imode| { - _gpio.set_moder(MODER.Input); - _gpio.set_bias(imode.resistor); - }, - .output => |omode| { - _gpio.set_moder(MODER.Output); - _gpio.set_output_type(omode.o_type); - _gpio.set_bias(omode.resistor); - _gpio.set_speed(omode.o_speed); - }, - .analog => |amode| { - _gpio.set_moder(MODER.Analog); - _gpio.set_bias(amode.resistor); - }, - .alternate_function => |afmode| { - _gpio.set_moder(MODER.Alternate); - _gpio.set_bias(afmode.resistor); - _gpio.set_speed(afmode.o_speed); - _gpio.set_output_type(afmode.o_type); - _gpio.set_alternate_function(afmode.afr); - }, - .digital_io => { - // Nothing for now - }, - } - } - - fn mask_2bit(_gpio: GPIO_Pin) u32 { - const pin: u4 = @backingInt(_gpio.pin); - return @as(u32, 0b11) << (pin << 1); - } - - fn mask(_gpio: GPIO_Pin) u32 { - const pin: u4 = @backingInt(_gpio.pin); - return @as(u32, 1) << pin; - } - - //NOTE: should invalid pins panic or just be ignored? - fn get_port(_gpio: GPIO_Pin) *volatile gpio_v2.GPIO { - return _gpio.port.get_port(); - } - - inline fn set_bias(_gpio: GPIO_Pin, bias: PUPDR) void { - const port = _gpio.port.get_port(); - const pin: u4 = @backingInt(_gpio.pin); - const modMask: u32 = _gpio.mask_2bit(); - - port.PUPDR.write_raw((port.PUPDR.raw & ~modMask) | @as(u32, @backingInt(bias)) << (pin << 1)); - } - - inline fn set_speed(_gpio: GPIO_Pin, speed: OSPEEDR) void { - const port = _gpio.port.get_port(); - const pin: u5 = @backingInt(_gpio.pin); - const modMask: u32 = _gpio.mask_2bit(); - - port.OSPEEDR.write_raw((port.OSPEEDR.raw & ~modMask) | @as(u32, @backingInt(speed)) << (pin << 1)); - } - - inline fn set_moder(_gpio: GPIO_Pin, moder: MODER) void { - const port = _gpio.port.get_port(); - const pin: u5 = @backingInt(_gpio.pin); - const modMask: u32 = _gpio.mask_2bit(); - - port.MODER.write_raw((port.MODER.raw & ~modMask) | @as(u32, @backingInt(moder)) << (pin << 1)); - } - - inline fn set_output_type(_gpio: GPIO_Pin, otype: OT) void { - const port = _gpio.port.get_port(); - const pin: u5 = @backingInt(_gpio.pin); - - port.OTYPER.write_raw((port.OTYPER.raw & ~_gpio.mask()) | @as(u32, @backingInt(otype)) << pin); - } - - fn from_port(port: Port, pin: Pin) GPIO_Pin { - return .{ - .port = port, - .pin = pin, + pub const Pin = enum { + PIN0, + PIN1, + PIN2, + PIN3, + PIN4, + PIN5, + PIN6, + PIN7, + PIN8, + PIN9, + PIN10, + PIN11, + PIN12, + PIN13, + PIN14, + PIN15, + pub const Configuration = struct { + name: ?[:0]const u8 = null, + mode: ?Mode = null, + }; }; - } -}; -pub const Input_GPIO = struct { - pin: GPIO_Pin, - pub inline fn read(self: @This()) u1 { - const port = self.pin.get_port(); - return if (port.IDR.raw & self.pin.mask() != 0) - 1 - else - 0; - } -}; + const GPIO_Pin = struct { + pin: Pin, + port: Port, + + inline fn write_pin_config(_gpio: GPIO_Pin, mode: Mode) void { + switch (mode) { + .input => |imode| { + _gpio.set_moder(MODER.Input); + _gpio.set_bias(imode.resistor); + }, + .output => |omode| { + _gpio.set_moder(MODER.Output); + _gpio.set_output_type(omode.o_type); + _gpio.set_bias(omode.resistor); + _gpio.set_speed(omode.o_speed); + }, + .analog => |amode| { + _gpio.set_moder(MODER.Analog); + _gpio.set_bias(amode.resistor); + }, + .alternate_function => |afmode| { + _gpio.set_moder(MODER.Alternate); + _gpio.set_bias(afmode.resistor); + _gpio.set_speed(afmode.o_speed); + _gpio.set_output_type(afmode.o_type); + _gpio.set_alternate_function(afmode.afr); + }, + .digital_io => { + // Nothing for now + }, + } + } -pub const Output_GPIO = struct { - pin: GPIO_Pin, + fn mask_2bit(_gpio: GPIO_Pin) u32 { + const pin: u4 = @backingInt(_gpio.pin); + return @as(u32, 0b11) << (pin << 1); + } - pub inline fn put(self: @This(), value: u1) void { - var port = self.pin.get_port(); - switch (value) { - 0 => port.BSRR.raw = @intCast(self.pin.mask() << 16), - 1 => port.BSRR.raw = self.pin.mask(), - } - } + fn mask(_gpio: GPIO_Pin) u32 { + const pin: u4 = @backingInt(_gpio.pin); + return @as(u32, 1) << pin; + } - pub inline fn low(self: @This()) void { - self.put(0); - } + //NOTE: should invalid pins panic or just be ignored? + fn get_port(_gpio: GPIO_Pin) *volatile gpio_v2.GPIO { + return _gpio.port.get_port(); + } - pub inline fn high(self: @This()) void { - self.put(1); - } + inline fn set_bias(_gpio: GPIO_Pin, bias: PUPDR) void { + const port = _gpio.port.get_port(); + const pin: u4 = @backingInt(_gpio.pin); + const modMask: u32 = _gpio.mask_2bit(); - pub inline fn toggle(self: @This()) void { - var port = self.pin.get_port(); - port.ODR.raw ^= self.pin.mask(); - } -}; + port.PUPDR.write_raw((port.PUPDR.raw & ~modMask) | @as(u32, @backingInt(bias)) << (pin << 1)); + } -pub const AlternateFunction = struct { - // Empty on perpose it should not be used as a GPIO. -}; + inline fn set_speed(_gpio: GPIO_Pin, speed: OSPEEDR) void { + const port = _gpio.port.get_port(); + const pin: u5 = @backingInt(_gpio.pin); + const modMask: u32 = _gpio.mask_2bit(); -const Analog = struct { - pin: GPIO_Pin, -}; + port.OSPEEDR.write_raw((port.OSPEEDR.raw & ~modMask) | @as(u32, @backingInt(speed)) << (pin << 1)); + } -pub const Digital_IO_Pin = struct { - pin: GPIO_Pin, - const vtable: Digital_IO.VTable = .{ - .set_direction_fn = Digital_IO_Pin.set_direction_fn, - .set_bias_fn = Digital_IO_Pin.set_bias_fn, - .write_fn = Digital_IO_Pin.write_fn, - .read_fn = Digital_IO_Pin.read_fn, - }; - pub fn set_direction_fn(ptr: *anyopaque, dir: Direction) SetDirError!void { - const self: *@This() = @ptrCast(@alignCast(ptr)); - switch (dir) { - .input => self.pin.set_moder(.Input), - .output => self.pin.set_moder(.Output), - } - } - pub fn set_bias_fn(ptr: *anyopaque, maybe_bias: ?State) SetBiasError!void { - const self: *@This() = @ptrCast(@alignCast(ptr)); - - const pupdr: PUPDR = if (maybe_bias) |bias| switch (bias) { - .low => .PullDown, - .high => .PullUp, - } else .Floating; - self.pin.set_bias(pupdr); - } - pub fn write_fn(ptr: *anyopaque, state: State) WriteError!void { - const self: *@This() = @ptrCast(@alignCast(ptr)); - var port = self.pin.get_port(); - switch (state) { - .low => port.BSRR.raw = @intCast(self.pin.mask() << 16), - .high => port.BSRR.raw = self.pin.mask(), - } - } - pub fn read_fn(ptr: *anyopaque) ReadError!State { - const self: *@This() = @ptrCast(@alignCast(ptr)); - const port = self.pin.get_port(); - return if (port.IDR.raw & self.pin.mask() != 0) - .high - else - .low; - } + inline fn set_moder(_gpio: GPIO_Pin, moder: MODER) void { + const port = _gpio.port.get_port(); + const pin: u5 = @backingInt(_gpio.pin); + const modMask: u32 = _gpio.mask_2bit(); + + port.MODER.write_raw((port.MODER.raw & ~modMask) | @as(u32, @backingInt(moder)) << (pin << 1)); + } - pub fn digital_io(ptr: *@This()) Digital_IO { - return .{ - .ptr = ptr, - .vtable = &vtable, + inline fn set_output_type(_gpio: GPIO_Pin, otype: OT) void { + const port = _gpio.port.get_port(); + const pin: u5 = @backingInt(_gpio.pin); + + port.OTYPER.write_raw((port.OTYPER.raw & ~_gpio.mask()) | @as(u32, @backingInt(otype)) << pin); + } + + fn from_port(port: Port, pin: Pin) GPIO_Pin { + return .{ + .port = port, + .pin = pin, + }; + } }; - } -}; -pub fn GPIO(comptime mode: Mode) type { - return switch (mode) { - .input => Input_GPIO, - .output => Output_GPIO, - .alternate_function => AlternateFunction, - .analog => Analog, - .digital_io => Digital_IO_Pin, - }; -} + pub const Input_GPIO = struct { + pin: GPIO_Pin, + pub inline fn read(self: @This()) u1 { + const port = self.pin.get_port(); + return if (port.IDR.raw & self.pin.mask() != 0) + 1 + else + 0; + } + }; + + pub const Output_GPIO = struct { + pin: GPIO_Pin, -pub fn Pins(comptime config: GlobalConfiguration) type { - var count: usize = 0; - for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { - if (@field(config, port_field.name)) |port_config| { - for (@typeInfo(Port.Configuration).@"struct".fields) |field| { - if (@field(port_config, field.name) != null) { - count += 1; + pub inline fn put(self: @This(), value: u1) void { + var port = self.pin.get_port(); + switch (value) { + 0 => port.BSRR.raw = @intCast(self.pin.mask() << 16), + 1 => port.BSRR.raw = self.pin.mask(), } } - } - } - - var field_names: [count][]const u8 = undefined; - var field_types: [count]type = undefined; - var field_attrs: [count]std.builtin.Type.StructField.Attributes = undefined; - var i: usize = 0; - for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { - if (@field(config, port_field.name)) |port_config| { - for (@typeInfo(Port.Configuration).@"struct".fields) |field| { - if (@field(port_config, field.name)) |pin_config| { - const default_name = "P" ++ port_field.name[4..5] ++ field.name[3..]; - field_names[i] = pin_config.name orelse default_name; - field_types[i] = GPIO(pin_config.mode orelse .{ .input = .{.floating} }); - field_attrs[i] = .{}; - i += 1; + + pub inline fn low(self: @This()) void { + self.put(0); + } + + pub inline fn high(self: @This()) void { + self.put(1); + } + + pub inline fn toggle(self: @This()) void { + var port = self.pin.get_port(); + port.ODR.raw ^= self.pin.mask(); + } + }; + + pub const AlternateFunction = struct { + // Empty on perpose it should not be used as a GPIO. + }; + + const Analog = struct { + pin: GPIO_Pin, + }; + + pub const Digital_IO_Pin = struct { + pin: GPIO_Pin, + const vtable: Digital_IO.VTable = .{ + .set_direction_fn = Digital_IO_Pin.set_direction_fn, + .set_bias_fn = Digital_IO_Pin.set_bias_fn, + .write_fn = Digital_IO_Pin.write_fn, + .read_fn = Digital_IO_Pin.read_fn, + }; + pub fn set_direction_fn(ptr: *anyopaque, dir: Direction) SetDirError!void { + const self: *@This() = @ptrCast(@alignCast(ptr)); + switch (dir) { + .input => self.pin.set_moder(.Input), + .output => self.pin.set_moder(.Output), } } - } - } + pub fn set_bias_fn(ptr: *anyopaque, maybe_bias: ?State) SetBiasError!void { + const self: *@This() = @ptrCast(@alignCast(ptr)); + + const pupdr: PUPDR = if (maybe_bias) |bias| switch (bias) { + .low => .PullDown, + .high => .PullUp, + } else .Floating; + self.pin.set_bias(pupdr); + } + pub fn write_fn(ptr: *anyopaque, state: State) WriteError!void { + const self: *@This() = @ptrCast(@alignCast(ptr)); + var port = self.pin.get_port(); + switch (state) { + .low => port.BSRR.raw = @intCast(self.pin.mask() << 16), + .high => port.BSRR.raw = self.pin.mask(), + } + } + pub fn read_fn(ptr: *anyopaque) ReadError!State { + const self: *@This() = @ptrCast(@alignCast(ptr)); + const port = self.pin.get_port(); + return if (port.IDR.raw & self.pin.mask() != 0) + .high + else + .low; + } - return @Struct(.auto, null, &field_names, &field_types, &field_attrs); -} + pub fn digital_io(ptr: *@This()) Digital_IO { + return .{ + .ptr = ptr, + .vtable = &vtable, + }; + } + }; -pub const Port = enum { - GPIOA, - GPIOB, - GPIOC, - GPIOD, - GPIOE, - GPIOF, - GPIOG, - pub const Configuration = struct { - PIN0: ?Pin.Configuration = null, - PIN1: ?Pin.Configuration = null, - PIN2: ?Pin.Configuration = null, - PIN3: ?Pin.Configuration = null, - PIN4: ?Pin.Configuration = null, - PIN5: ?Pin.Configuration = null, - PIN6: ?Pin.Configuration = null, - PIN7: ?Pin.Configuration = null, - PIN8: ?Pin.Configuration = null, - PIN9: ?Pin.Configuration = null, - PIN10: ?Pin.Configuration = null, - PIN11: ?Pin.Configuration = null, - PIN12: ?Pin.Configuration = null, - PIN13: ?Pin.Configuration = null, - PIN14: ?Pin.Configuration = null, - PIN15: ?Pin.Configuration = null, - - comptime { - const pin_field_count = @typeInfo(Pin).@"enum".fields.len; - const config_field_count = @typeInfo(Configuration).@"struct".fields.len; - if (pin_field_count != config_field_count) - @compileError(comptimePrint("{} {}", .{ pin_field_count, config_field_count })); + pub fn GPIO(comptime mode: Mode) type { + return switch (mode) { + .input => Input_GPIO, + .output => Output_GPIO, + .alternate_function => AlternateFunction, + .analog => Analog, + .digital_io => Digital_IO_Pin, + }; } - }; - //NOTE: should invalid pins panic or just be ignored? - pub fn get_port(port: Port) *volatile gpio_v2.GPIO { - switch (@backingInt(port)) { - inline 0...7 - 1 => |_port| { - const port_name = [_]u8{"ABCDEFGHIJK"[_port]}; - return @field(peripherals, "GPIO" ++ port_name); - }, - else => @panic("STM32s only have ports 0..10 (A..K)"), - } - } -}; + pub fn Pins(comptime config: GlobalConfiguration) type { + var count: usize = 0; + for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (@field(config, port_field.name)) |port_config| { + for (@typeInfo(Port.Configuration).@"struct".fields) |field| { + if (@field(port_config, field.name) != null) { + count += 1; + } + } + } + } -pub const GlobalConfiguration = struct { - GPIOA: ?Port.Configuration = null, - GPIOB: ?Port.Configuration = null, - GPIOC: ?Port.Configuration = null, - GPIOD: ?Port.Configuration = null, - GPIOE: ?Port.Configuration = null, - GPIOF: ?Port.Configuration = null, - GPIOG: ?Port.Configuration = null, - - comptime { - const port_field_count = @typeInfo(Port).@"enum".fields.len; - const config_field_count = @typeInfo(GlobalConfiguration).@"struct".fields.len; - if (port_field_count != config_field_count) - @compileError(comptimePrint("{} {}", .{ port_field_count, config_field_count })); - } - - pub fn apply(comptime config: GlobalConfiguration) Pins(config) { - var ret: Pins(config) = undefined; - - inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { - if (@field(config, port_field.name)) |_| { - rcc.enable_clock(@field(enums.Peripherals, port_field.name)); + var field_names: [count][]const u8 = undefined; + var field_types: [count]type = undefined; + var field_attrs: [count]std.builtin.Type.StructField.Attributes = undefined; + var i: usize = 0; + for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (@field(config, port_field.name)) |port_config| { + for (@typeInfo(Port.Configuration).@"struct".fields) |field| { + if (@field(port_config, field.name)) |pin_config| { + const default_name = "P" ++ port_field.name[4..5] ++ field.name[3..]; + field_names[i] = pin_config.name orelse default_name; + field_types[i] = GPIO(pin_config.mode orelse .{ .input = .{.floating} }); + field_attrs[i] = .{}; + i += 1; + } + } + } } + + return @Struct(.auto, null, &field_names, &field_types, &field_attrs); } - inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { - if (@field(config, port_field.name)) |port_config| { - inline for (@typeInfo(Port.Configuration).@"struct".fields) |field| { - if (@field(port_config, field.name)) |pin_config| { - const port = @field(Port, port_field.name); - var pin = GPIO_Pin.from_port(port, @field(Pin, field.name)); - pin.write_pin_config(pin_config.mode.?); - const default_name = "P" ++ port_field.name[4..5] ++ field.name[3..]; - - switch (pin_config.mode orelse .input) { - .input => @field(ret, pin_config.name orelse default_name) = Input_GPIO{ .pin = pin }, - .output => @field(ret, pin_config.name orelse default_name) = Output_GPIO{ .pin = pin }, - .analog => @field(ret, pin_config.name orelse default_name) = Analog{}, - .alternate_function => @field(ret, pin_config.name orelse default_name) = AlternateFunction{}, - .digital_io => @field(ret, pin_config.name orelse default_name) = Digital_IO_Pin{ .pin = pin }, + const PortConfig = struct { + PIN0: ?Pin.Configuration = null, + PIN1: ?Pin.Configuration = null, + PIN2: ?Pin.Configuration = null, + PIN3: ?Pin.Configuration = null, + PIN4: ?Pin.Configuration = null, + PIN5: ?Pin.Configuration = null, + PIN6: ?Pin.Configuration = null, + PIN7: ?Pin.Configuration = null, + PIN8: ?Pin.Configuration = null, + PIN9: ?Pin.Configuration = null, + PIN10: ?Pin.Configuration = null, + PIN11: ?Pin.Configuration = null, + PIN12: ?Pin.Configuration = null, + PIN13: ?Pin.Configuration = null, + PIN14: ?Pin.Configuration = null, + PIN15: ?Pin.Configuration = null, + + // TODO: Add enabled option to enable it from the start + comptime { + const pin_field_count = @typeInfo(Pin).@"enum".fields.len; + const config_field_count = @typeInfo(PortConfig).@"struct".fields.len; + if (pin_field_count != config_field_count) + @compileError(comptimePrint("{} {}", .{ pin_field_count, config_field_count })); + } + }; + + const _Port = enum { + //NOTE: should invalid pins panic or just be ignored? + fn get_port(port: Port) *volatile gpio_v2.GPIO { + switch (@backingInt(port)) { + inline 0...cfg.portcount - 1 => |_port| { + const port_name = [_]u8{"ABCDEFGHIJK"[_port]}; + return @field(peripherals, "GPIO" ++ port_name); + }, + else => @panic(std.fmt.comptimePrint("STM32s only have ports 0..{any} (A..{s})", .{ cfg.portcount, [_]u8{"ABCDEFGHIJK"[cfg.portcount]} })), + } + } + }; + + const Port7 = enum { + GPIOA, + GPIOB, + GPIOC, + GPIOD, + GPIOE, + GPIOF, + GPIOG, + pub const Configuration = PortConfig; + pub const get_port = _Port.get_port; + }; + + const Port11 = enum { + GPIOA, + GPIOB, + GPIOC, + GPIOD, + GPIOE, + GPIOF, + GPIOG, + GPIOH, + GPIOI, + GPIOJ, + GPIOK, + pub const Configuration = PortConfig; + pub const get_port = _Port.get_port; + }; + + pub const Port = if (cfg.portcount == 7) + Port7 + else + Port11; + + const _GlobalConfiguration = struct { + fn apply(comptime global_config: GlobalConfiguration) Pins(global_config) { + var ret: Pins(global_config) = undefined; + + inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (@field(global_config, port_field.name)) |_| { + rcc.enable_clock(@field(rcc.Peripherals, port_field.name)); + } + } + + inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (@field(global_config, port_field.name)) |port_config| { + inline for (@typeInfo(Port.Configuration).@"struct".fields) |field| { + if (@field(port_config, field.name)) |pin_config| { + const port = @field(Port, port_field.name); + var pin = GPIO_Pin.from_port(port, @field(Pin, field.name)); + pin.write_pin_config(pin_config.mode.?); + const default_name = "P" ++ port_field.name[4..5] ++ field.name[3..]; + + switch (pin_config.mode orelse .input) { + .input => @field(ret, pin_config.name orelse default_name) = Input_GPIO{ .pin = pin }, + .output => @field(ret, pin_config.name orelse default_name) = Output_GPIO{ .pin = pin }, + .analog => @field(ret, pin_config.name orelse default_name) = Analog{}, + .alternate_function => @field(ret, pin_config.name orelse default_name) = AlternateFunction{}, + .digital_io => @field(ret, pin_config.name orelse default_name) = Digital_IO_Pin{ .pin = pin }, + } + } } } } + + return ret; } - } + }; - return ret; - } -}; + const GlobalConfiguration7 = struct { + GPIOA: ?Port.Configuration = null, + GPIOB: ?Port.Configuration = null, + GPIOC: ?Port.Configuration = null, + GPIOD: ?Port.Configuration = null, + GPIOE: ?Port.Configuration = null, + GPIOF: ?Port.Configuration = null, + GPIOG: ?Port.Configuration = null, + + comptime { + const port_field_count = @typeInfo(Port).@"enum".fields.len; + const config_field_count = @typeInfo(GlobalConfiguration).@"struct".fields.len; + if (port_field_count != config_field_count) + @compileError(comptimePrint("{} {}", .{ port_field_count, config_field_count })); + } + pub const apply = _GlobalConfiguration.apply; + }; + + const GlobalConfiguration11 = struct { + GPIOA: ?Port.Configuration = null, + GPIOB: ?Port.Configuration = null, + GPIOC: ?Port.Configuration = null, + GPIOD: ?Port.Configuration = null, + GPIOE: ?Port.Configuration = null, + GPIOF: ?Port.Configuration = null, + GPIOG: ?Port.Configuration = null, + GPIOH: ?Port.Configuration = null, + GPIOI: ?Port.Configuration = null, + GPIOJ: ?Port.Configuration = null, + GPIOK: ?Port.Configuration = null, + + comptime { + const port_field_count = @typeInfo(Port).@"enum".fields.len; + const config_field_count = @typeInfo(GlobalConfiguration).@"struct".fields.len; + if (port_field_count != config_field_count) + @compileError(comptimePrint("{} {}", .{ port_field_count, config_field_count })); + } + pub const apply = _GlobalConfiguration.apply; + }; + + pub const GlobalConfiguration = if (cfg.portcount == 7) + GlobalConfiguration7 + else + GlobalConfiguration11; + }; +} From 7a013420e8b67ea75fa1fec321e9bdd4c0c85dc5 Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Wed, 29 Apr 2026 12:25:45 +0200 Subject: [PATCH 09/15] Add blinky support for STM32F429 DISC1 --- examples/stmicro/stm32/src/blinky.zig | 7 +++++++ port/stmicro/stm32/src/boards/STM32F429IDISCOVERY.zig | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/examples/stmicro/stm32/src/blinky.zig b/examples/stmicro/stm32/src/blinky.zig index 0b159f3d6..e8d4c912e 100644 --- a/examples/stmicro/stm32/src/blinky.zig +++ b/examples/stmicro/stm32/src/blinky.zig @@ -48,6 +48,13 @@ pub fn main() !void { pins.LD10, }; break :res .{ pins, all_leds }; + } else if (comptime microzig.config.board_name != null and std.mem.eql(u8, microzig.config.board_name.?, "STM32F429IDISCOVERY")) { + const pins = board.leds_config.apply(); + const all_leds = .{ + pins.LD3, + pins.LD4, + }; + break :res .{ pins, all_leds }; } else if (comptime microzig.config.board_name != null and std.mem.eql(u8, microzig.config.board_name.?, "STM32L476DISCOVERY")) { const pins = board.leds_config.apply(); const all_leds = .{ diff --git a/port/stmicro/stm32/src/boards/STM32F429IDISCOVERY.zig b/port/stmicro/stm32/src/boards/STM32F429IDISCOVERY.zig index 034295a13..ce1449bf6 100644 --- a/port/stmicro/stm32/src/boards/STM32F429IDISCOVERY.zig +++ b/port/stmicro/stm32/src/boards/STM32F429IDISCOVERY.zig @@ -1,3 +1,7 @@ +pub const microzig = @import("microzig"); + +pub const hal = microzig.hal; + pub const cpu_frequency = 16_000_000; pub const pin_map = .{ @@ -10,3 +14,10 @@ pub const pin_map = .{ // User button .B1 = "PA0", }; + +pub const leds_config = (hal.pins.GlobalConfiguration{ + .GPIOG = .{ + .PIN13 = .{ .name = "LD3", .mode = .{ .output = .{ .resistor = .Floating, .o_type = .PushPull } } }, + .PIN14 = .{ .name = "LD4", .mode = .{ .output = .{ .resistor = .Floating, .o_type = .PushPull } } }, + }, +}); From 22f2b05e40fe720237421db9b664bccbb765fe59 Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Fri, 1 May 2026 13:04:14 +0200 Subject: [PATCH 10/15] Make pins config automatic and more dynamic --- .../stmicro/stm32/src/hals/STM32F303/pins.zig | 4 +- port/stmicro/stm32/src/hals/STM32F429.zig | 4 +- .../stmicro/stm32/src/hals/STM32L47X/pins.zig | 4 +- .../stmicro/stm32/src/hals/common/pins_v2.zig | 832 ++++++++---------- port/stmicro/stm32/src/hals/common/util.zig | 4 + 5 files changed, 398 insertions(+), 450 deletions(-) diff --git a/port/stmicro/stm32/src/hals/STM32F303/pins.zig b/port/stmicro/stm32/src/hals/STM32F303/pins.zig index 77b7a6f97..f2fef522f 100644 --- a/port/stmicro/stm32/src/hals/STM32F303/pins.zig +++ b/port/stmicro/stm32/src/hals/STM32F303/pins.zig @@ -1,4 +1,2 @@ -const PinCommon = @import("../common/pins_v2.zig").get_pins(.{ - .portcount = 7, -}); +const PinCommon = @import("../common/pins_v2.zig"); pub const GlobalConfiguration = PinCommon.GlobalConfiguration; diff --git a/port/stmicro/stm32/src/hals/STM32F429.zig b/port/stmicro/stm32/src/hals/STM32F429.zig index 9420fca1b..2b303b027 100644 --- a/port/stmicro/stm32/src/hals/STM32F429.zig +++ b/port/stmicro/stm32/src/hals/STM32F429.zig @@ -30,9 +30,7 @@ const Digital_IO = microzig.drivers.base.Digital_IO; const State = Digital_IO.State; -pub const pins = @import("./common/pins_v2.zig").get_pins(.{ - .portcount = 11, -}); +pub const pins = @import("./common/pins_v2.zig"); pub const clock = struct { pub const Domain = enum { diff --git a/port/stmicro/stm32/src/hals/STM32L47X/pins.zig b/port/stmicro/stm32/src/hals/STM32L47X/pins.zig index eb69d0ce0..f2fef522f 100644 --- a/port/stmicro/stm32/src/hals/STM32L47X/pins.zig +++ b/port/stmicro/stm32/src/hals/STM32L47X/pins.zig @@ -1,4 +1,2 @@ -const PinCommon = @import("../common/pins_v2.zig").get_pins(.{ - .portcount = 8, -}); +const PinCommon = @import("../common/pins_v2.zig"); pub const GlobalConfiguration = PinCommon.GlobalConfiguration; diff --git a/port/stmicro/stm32/src/hals/common/pins_v2.zig b/port/stmicro/stm32/src/hals/common/pins_v2.zig index d3f6efc55..48d2dfd5f 100644 --- a/port/stmicro/stm32/src/hals/common/pins_v2.zig +++ b/port/stmicro/stm32/src/hals/common/pins_v2.zig @@ -1,482 +1,432 @@ -pub const config = struct { - portcount: usize, +const std = @import("std"); +const assert = std.debug.assert; +const comptimePrint = std.fmt.comptimePrint; +const StructField = std.builtin.Type.StructField; + +const microzig = @import("microzig"); +const util = @import("util.zig"); + +const Digital_IO = microzig.drivers.base.Digital_IO; +const Direction = Digital_IO.Direction; +const SetDirError = Digital_IO.SetDirError; +const SetBiasError = Digital_IO.SetBiasError; +const WriteError = Digital_IO.WriteError; +const ReadError = Digital_IO.ReadError; + +const State = Digital_IO.State; + +const gpio_v2 = microzig.chip.types.peripherals.gpio_v2; +const PUPDR = gpio_v2.PUPDR; +const MODER = gpio_v2.MODER; +const OSPEEDR = gpio_v2.OSPEEDR; +const OT = gpio_v2.OT; +const AFIO = microzig.chip.peripherals.AFIO; + +const rcc = microzig.hal.rcc; +const peripherals = microzig.chip.peripherals; + +pub const Mode = union(enum) { + input: InputMode, + output: OutputMode, + analog: AnalogMode, + alternate_function: AlternateFunctionMode, + digital_io: Digital_IO_Mode, }; -pub fn get_pins(comptime cfg: config) type { - return struct { - const std = @import("std"); - const assert = std.debug.assert; - const comptimePrint = std.fmt.comptimePrint; - const StructField = std.builtin.Type.StructField; - - const microzig = @import("microzig"); - - const Digital_IO = microzig.drivers.base.Digital_IO; - const Direction = Digital_IO.Direction; - const SetDirError = Digital_IO.SetDirError; - const SetBiasError = Digital_IO.SetBiasError; - const WriteError = Digital_IO.WriteError; - const ReadError = Digital_IO.ReadError; - - const State = Digital_IO.State; - - const gpio_v2 = microzig.chip.types.peripherals.gpio_v2; - const PUPDR = gpio_v2.PUPDR; - const MODER = gpio_v2.MODER; - const OSPEEDR = gpio_v2.OSPEEDR; - const OT = gpio_v2.OT; - const AFIO = microzig.chip.peripherals.AFIO; - - const rcc = microzig.hal.rcc; - const peripherals = microzig.chip.peripherals; - - pub const Mode = union(enum) { - input: InputMode, - output: OutputMode, - analog: AnalogMode, - alternate_function: AlternateFunctionMode, - digital_io: Digital_IO_Mode, - }; +const Digital_IO_Mode = struct {}; - const Digital_IO_Mode = struct {}; +const InputMode = struct { + resistor: PUPDR, +}; - const InputMode = struct { - resistor: PUPDR, - }; +const OutputMode = struct { + resistor: PUPDR, + o_type: OT, + o_speed: OSPEEDR = .LowSpeed, +}; - const OutputMode = struct { - resistor: PUPDR, - o_type: OT, - o_speed: OSPEEDR = .LowSpeed, - }; +const AnalogMode = struct { + resistor: PUPDR = .Floating, +}; - const AnalogMode = struct { - resistor: PUPDR = .Floating, - }; +const AF = enum(u4) { + AF0, + AF1, + AF2, + AF3, + AF4, + AF5, + AF6, + AF7, + AF8, + AF9, + AF10, + AF11, + AF12, + AF13, + AF14, + AF15, +}; - const AF = enum(u4) { - AF0, - AF1, - AF2, - AF3, - AF4, - AF5, - AF6, - AF7, - AF8, - AF9, - AF10, - AF11, - AF12, - AF13, - AF14, - AF15, - }; +pub const AlternateFunctionMode = struct { + afr: AF, + resistor: PUPDR = .Floating, + o_type: OT = .PushPull, + o_speed: OSPEEDR = .HighSpeed, +}; - pub const AlternateFunctionMode = struct { - afr: AF, - resistor: PUPDR = .Floating, - o_type: OT = .PushPull, - o_speed: OSPEEDR = .HighSpeed, - }; +pub const Pin = enum { + PIN0, + PIN1, + PIN2, + PIN3, + PIN4, + PIN5, + PIN6, + PIN7, + PIN8, + PIN9, + PIN10, + PIN11, + PIN12, + PIN13, + PIN14, + PIN15, + pub const Configuration = struct { + name: ?[:0]const u8 = null, + mode: ?Mode = null, + }; +}; - pub const Pin = enum { - PIN0, - PIN1, - PIN2, - PIN3, - PIN4, - PIN5, - PIN6, - PIN7, - PIN8, - PIN9, - PIN10, - PIN11, - PIN12, - PIN13, - PIN14, - PIN15, - pub const Configuration = struct { - name: ?[:0]const u8 = null, - mode: ?Mode = null, - }; +const GPIO_Pin = struct { + pin: Pin, + port: Port, + + inline fn write_pin_config(_gpio: GPIO_Pin, mode: Mode) void { + switch (mode) { + .input => |imode| { + _gpio.set_moder(MODER.Input); + _gpio.set_bias(imode.resistor); + }, + .output => |omode| { + _gpio.set_moder(MODER.Output); + _gpio.set_output_type(omode.o_type); + _gpio.set_bias(omode.resistor); + _gpio.set_speed(omode.o_speed); + }, + .analog => |amode| { + _gpio.set_moder(MODER.Analog); + _gpio.set_bias(amode.resistor); + }, + .alternate_function => |afmode| { + _gpio.set_moder(MODER.Alternate); + _gpio.set_bias(afmode.resistor); + _gpio.set_speed(afmode.o_speed); + _gpio.set_output_type(afmode.o_type); + _gpio.set_alternate_function(afmode.afr); + }, + .digital_io => { + // Nothing for now + }, + } + } + + fn mask_2bit(_gpio: GPIO_Pin) u32 { + const pin: u4 = @backingInt(_gpio.pin); + return @as(u32, 0b11) << (pin << 1); + } + + fn mask(_gpio: GPIO_Pin) u32 { + const pin: u4 = @backingInt(_gpio.pin); + return @as(u32, 1) << pin; + } + + //NOTE: should invalid pins panic or just be ignored? + fn get_port(_gpio: GPIO_Pin) *volatile gpio_v2.GPIO { + return _gpio.port.get_port(); + } + + inline fn set_bias(_gpio: GPIO_Pin, bias: PUPDR) void { + const port = _gpio.port.get_port(); + const pin: u4 = @backingInt(_gpio.pin); + const modMask: u32 = _gpio.mask_2bit(); + + port.PUPDR.write_raw((port.PUPDR.raw & ~modMask) | @as(u32, @backingInt(bias)) << (pin << 1)); + } + + inline fn set_speed(_gpio: GPIO_Pin, speed: OSPEEDR) void { + const port = _gpio.port.get_port(); + const pin: u5 = @backingInt(_gpio.pin); + const modMask: u32 = _gpio.mask_2bit(); + + port.OSPEEDR.write_raw((port.OSPEEDR.raw & ~modMask) | @as(u32, @backingInt(speed)) << (pin << 1)); + } + + inline fn set_moder(_gpio: GPIO_Pin, moder: MODER) void { + const port = _gpio.port.get_port(); + const pin: u5 = @backingInt(_gpio.pin); + const modMask: u32 = _gpio.mask_2bit(); + + port.MODER.write_raw((port.MODER.raw & ~modMask) | @as(u32, @backingInt(moder)) << (pin << 1)); + } + + inline fn set_output_type(_gpio: GPIO_Pin, otype: OT) void { + const port = _gpio.port.get_port(); + const pin: u5 = @backingInt(_gpio.pin); + + port.OTYPER.write_raw((port.OTYPER.raw & ~_gpio.mask()) | @as(u32, @backingInt(otype)) << pin); + } + + fn from_port(port: Port, pin: Pin) GPIO_Pin { + return .{ + .port = port, + .pin = pin, }; + } +}; - const GPIO_Pin = struct { - pin: Pin, - port: Port, - - inline fn write_pin_config(_gpio: GPIO_Pin, mode: Mode) void { - switch (mode) { - .input => |imode| { - _gpio.set_moder(MODER.Input); - _gpio.set_bias(imode.resistor); - }, - .output => |omode| { - _gpio.set_moder(MODER.Output); - _gpio.set_output_type(omode.o_type); - _gpio.set_bias(omode.resistor); - _gpio.set_speed(omode.o_speed); - }, - .analog => |amode| { - _gpio.set_moder(MODER.Analog); - _gpio.set_bias(amode.resistor); - }, - .alternate_function => |afmode| { - _gpio.set_moder(MODER.Alternate); - _gpio.set_bias(afmode.resistor); - _gpio.set_speed(afmode.o_speed); - _gpio.set_output_type(afmode.o_type); - _gpio.set_alternate_function(afmode.afr); - }, - .digital_io => { - // Nothing for now - }, - } - } - - fn mask_2bit(_gpio: GPIO_Pin) u32 { - const pin: u4 = @backingInt(_gpio.pin); - return @as(u32, 0b11) << (pin << 1); - } - - fn mask(_gpio: GPIO_Pin) u32 { - const pin: u4 = @backingInt(_gpio.pin); - return @as(u32, 1) << pin; - } - - //NOTE: should invalid pins panic or just be ignored? - fn get_port(_gpio: GPIO_Pin) *volatile gpio_v2.GPIO { - return _gpio.port.get_port(); - } - - inline fn set_bias(_gpio: GPIO_Pin, bias: PUPDR) void { - const port = _gpio.port.get_port(); - const pin: u4 = @backingInt(_gpio.pin); - const modMask: u32 = _gpio.mask_2bit(); - - port.PUPDR.write_raw((port.PUPDR.raw & ~modMask) | @as(u32, @backingInt(bias)) << (pin << 1)); - } - - inline fn set_speed(_gpio: GPIO_Pin, speed: OSPEEDR) void { - const port = _gpio.port.get_port(); - const pin: u5 = @backingInt(_gpio.pin); - const modMask: u32 = _gpio.mask_2bit(); - - port.OSPEEDR.write_raw((port.OSPEEDR.raw & ~modMask) | @as(u32, @backingInt(speed)) << (pin << 1)); - } - - inline fn set_moder(_gpio: GPIO_Pin, moder: MODER) void { - const port = _gpio.port.get_port(); - const pin: u5 = @backingInt(_gpio.pin); - const modMask: u32 = _gpio.mask_2bit(); - - port.MODER.write_raw((port.MODER.raw & ~modMask) | @as(u32, @backingInt(moder)) << (pin << 1)); - } - - inline fn set_output_type(_gpio: GPIO_Pin, otype: OT) void { - const port = _gpio.port.get_port(); - const pin: u5 = @backingInt(_gpio.pin); +pub const Input_GPIO = struct { + pin: GPIO_Pin, + pub inline fn read(self: @This()) u1 { + const port = self.pin.get_port(); + return if (port.IDR.raw & self.pin.mask() != 0) + 1 + else + 0; + } +}; - port.OTYPER.write_raw((port.OTYPER.raw & ~_gpio.mask()) | @as(u32, @backingInt(otype)) << pin); - } +pub const Output_GPIO = struct { + pin: GPIO_Pin, - fn from_port(port: Port, pin: Pin) GPIO_Pin { - return .{ - .port = port, - .pin = pin, - }; - } - }; + pub inline fn put(self: @This(), value: u1) void { + var port = self.pin.get_port(); + switch (value) { + 0 => port.BSRR.raw = @intCast(self.pin.mask() << 16), + 1 => port.BSRR.raw = self.pin.mask(), + } + } - pub const Input_GPIO = struct { - pin: GPIO_Pin, - pub inline fn read(self: @This()) u1 { - const port = self.pin.get_port(); - return if (port.IDR.raw & self.pin.mask() != 0) - 1 - else - 0; - } - }; + pub inline fn low(self: @This()) void { + self.put(0); + } - pub const Output_GPIO = struct { - pin: GPIO_Pin, + pub inline fn high(self: @This()) void { + self.put(1); + } - pub inline fn put(self: @This(), value: u1) void { - var port = self.pin.get_port(); - switch (value) { - 0 => port.BSRR.raw = @intCast(self.pin.mask() << 16), - 1 => port.BSRR.raw = self.pin.mask(), - } - } + pub inline fn toggle(self: @This()) void { + var port = self.pin.get_port(); + port.ODR.raw ^= self.pin.mask(); + } +}; - pub inline fn low(self: @This()) void { - self.put(0); - } +pub const AlternateFunction = struct { + // Empty on perpose it should not be used as a GPIO. +}; - pub inline fn high(self: @This()) void { - self.put(1); - } +const Analog = struct { + pin: GPIO_Pin, +}; - pub inline fn toggle(self: @This()) void { - var port = self.pin.get_port(); - port.ODR.raw ^= self.pin.mask(); - } - }; +pub const Digital_IO_Pin = struct { + pin: GPIO_Pin, + const vtable: Digital_IO.VTable = .{ + .set_direction_fn = Digital_IO_Pin.set_direction_fn, + .set_bias_fn = Digital_IO_Pin.set_bias_fn, + .write_fn = Digital_IO_Pin.write_fn, + .read_fn = Digital_IO_Pin.read_fn, + }; + pub fn set_direction_fn(ptr: *anyopaque, dir: Direction) SetDirError!void { + const self: *@This() = @ptrCast(@alignCast(ptr)); + switch (dir) { + .input => self.pin.set_moder(.Input), + .output => self.pin.set_moder(.Output), + } + } + pub fn set_bias_fn(ptr: *anyopaque, maybe_bias: ?State) SetBiasError!void { + const self: *@This() = @ptrCast(@alignCast(ptr)); + + const pupdr: PUPDR = if (maybe_bias) |bias| switch (bias) { + .low => .PullDown, + .high => .PullUp, + } else .Floating; + self.pin.set_bias(pupdr); + } + pub fn write_fn(ptr: *anyopaque, state: State) WriteError!void { + const self: *@This() = @ptrCast(@alignCast(ptr)); + var port = self.pin.get_port(); + switch (state) { + .low => port.BSRR.raw = @intCast(self.pin.mask() << 16), + .high => port.BSRR.raw = self.pin.mask(), + } + } + pub fn read_fn(ptr: *anyopaque) ReadError!State { + const self: *@This() = @ptrCast(@alignCast(ptr)); + const port = self.pin.get_port(); + return if (port.IDR.raw & self.pin.mask() != 0) + .high + else + .low; + } - pub const AlternateFunction = struct { - // Empty on perpose it should not be used as a GPIO. + pub fn digital_io(ptr: *@This()) Digital_IO { + return .{ + .ptr = ptr, + .vtable = &vtable, }; + } +}; - const Analog = struct { - pin: GPIO_Pin, - }; +pub fn GPIO(comptime mode: Mode) type { + return switch (mode) { + .input => Input_GPIO, + .output => Output_GPIO, + .alternate_function => AlternateFunction, + .analog => Analog, + .digital_io => Digital_IO_Pin, + }; +} - pub const Digital_IO_Pin = struct { - pin: GPIO_Pin, - const vtable: Digital_IO.VTable = .{ - .set_direction_fn = Digital_IO_Pin.set_direction_fn, - .set_bias_fn = Digital_IO_Pin.set_bias_fn, - .write_fn = Digital_IO_Pin.write_fn, - .read_fn = Digital_IO_Pin.read_fn, - }; - pub fn set_direction_fn(ptr: *anyopaque, dir: Direction) SetDirError!void { - const self: *@This() = @ptrCast(@alignCast(ptr)); - switch (dir) { - .input => self.pin.set_moder(.Input), - .output => self.pin.set_moder(.Output), +pub fn Pins(comptime config: GlobalConfiguration) type { + var count: usize = 0; + for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (@field(config, port_field.name)) |port_config| { + for (@typeInfo(Port.Configuration).@"struct".fields) |field| { + if (@field(port_config, field.name) != null) { + count += 1; } } - pub fn set_bias_fn(ptr: *anyopaque, maybe_bias: ?State) SetBiasError!void { - const self: *@This() = @ptrCast(@alignCast(ptr)); - - const pupdr: PUPDR = if (maybe_bias) |bias| switch (bias) { - .low => .PullDown, - .high => .PullUp, - } else .Floating; - self.pin.set_bias(pupdr); - } - pub fn write_fn(ptr: *anyopaque, state: State) WriteError!void { - const self: *@This() = @ptrCast(@alignCast(ptr)); - var port = self.pin.get_port(); - switch (state) { - .low => port.BSRR.raw = @intCast(self.pin.mask() << 16), - .high => port.BSRR.raw = self.pin.mask(), + } + } + + var field_names: [count][]const u8 = undefined; + var field_types: [count]type = undefined; + var field_attrs: [count]std.builtin.Type.StructField.Attributes = undefined; + var i: usize = 0; + for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (@field(config, port_field.name)) |port_config| { + for (@typeInfo(Port.Configuration).@"struct".fields) |field| { + if (@field(port_config, field.name)) |pin_config| { + const default_name = "P" ++ port_field.name[4..5] ++ field.name[3..]; + field_names[i] = pin_config.name orelse default_name; + field_types[i] = GPIO(pin_config.mode orelse .{ .input = .{.floating} }); + field_attrs[i] = .{}; + i += 1; } } - pub fn read_fn(ptr: *anyopaque) ReadError!State { - const self: *@This() = @ptrCast(@alignCast(ptr)); - const port = self.pin.get_port(); - return if (port.IDR.raw & self.pin.mask() != 0) - .high - else - .low; - } - - pub fn digital_io(ptr: *@This()) Digital_IO { - return .{ - .ptr = ptr, - .vtable = &vtable, - }; - } - }; - - pub fn GPIO(comptime mode: Mode) type { - return switch (mode) { - .input => Input_GPIO, - .output => Output_GPIO, - .alternate_function => AlternateFunction, - .analog => Analog, - .digital_io => Digital_IO_Pin, - }; } + } - pub fn Pins(comptime config: GlobalConfiguration) type { - var count: usize = 0; - for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { - if (@field(config, port_field.name)) |port_config| { - for (@typeInfo(Port.Configuration).@"struct".fields) |field| { - if (@field(port_config, field.name) != null) { - count += 1; - } - } - } - } + return @Struct(.auto, null, &field_names, &field_types, &field_attrs); +} - var field_names: [count][]const u8 = undefined; - var field_types: [count]type = undefined; - var field_attrs: [count]std.builtin.Type.StructField.Attributes = undefined; - var i: usize = 0; - for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { - if (@field(config, port_field.name)) |port_config| { - for (@typeInfo(Port.Configuration).@"struct".fields) |field| { - if (@field(port_config, field.name)) |pin_config| { - const default_name = "P" ++ port_field.name[4..5] ++ field.name[3..]; - field_names[i] = pin_config.name orelse default_name; - field_types[i] = GPIO(pin_config.mode orelse .{ .input = .{.floating} }); - field_attrs[i] = .{}; - i += 1; - } - } - } - } +pub const Port = enum(u8) { + GPIOA = if (util.has_port('A')) 0 else undefined, + GPIOB = if (util.has_port('B')) 1 else undefined, + GPIOC = if (util.has_port('C')) 2 else undefined, + GPIOD = if (util.has_port('D')) 3 else undefined, + GPIOE = if (util.has_port('E')) 4 else undefined, + GPIOF = if (util.has_port('F')) 5 else undefined, + GPIOG = if (util.has_port('G')) 6 else undefined, + GPIOH = if (util.has_port('H')) 7 else undefined, + GPIOI = if (util.has_port('I')) 8 else undefined, + GPIOJ = if (util.has_port('J')) 9 else undefined, + GPIOK = if (util.has_port('K')) 10 else undefined, + + pub const Configuration = struct { + PIN0: ?Pin.Configuration = null, + PIN1: ?Pin.Configuration = null, + PIN2: ?Pin.Configuration = null, + PIN3: ?Pin.Configuration = null, + PIN4: ?Pin.Configuration = null, + PIN5: ?Pin.Configuration = null, + PIN6: ?Pin.Configuration = null, + PIN7: ?Pin.Configuration = null, + PIN8: ?Pin.Configuration = null, + PIN9: ?Pin.Configuration = null, + PIN10: ?Pin.Configuration = null, + PIN11: ?Pin.Configuration = null, + PIN12: ?Pin.Configuration = null, + PIN13: ?Pin.Configuration = null, + PIN14: ?Pin.Configuration = null, + PIN15: ?Pin.Configuration = null, + + comptime { + const pin_field_count = @typeInfo(Pin).@"enum".fields.len; + const config_field_count = @typeInfo(Port.Configuration).@"struct".fields.len; + if (pin_field_count != config_field_count) + @compileError(comptimePrint("{} {}", .{ pin_field_count, config_field_count })); + } + }; - return @Struct(.auto, null, &field_names, &field_types, &field_attrs); + pub fn get_port(port: Port) *volatile gpio_v2.GPIO { + switch (@backingInt(port)) { + inline 0...@typeInfo(Port).@"enum".fields.len - 1 => |p| { + const port_id = [_]u8{"ABCDEFGHIJK"[p]}; + return @field(peripherals, "GPIO" ++ port_id); + }, + else => unreachable, } + } +}; - const PortConfig = struct { - PIN0: ?Pin.Configuration = null, - PIN1: ?Pin.Configuration = null, - PIN2: ?Pin.Configuration = null, - PIN3: ?Pin.Configuration = null, - PIN4: ?Pin.Configuration = null, - PIN5: ?Pin.Configuration = null, - PIN6: ?Pin.Configuration = null, - PIN7: ?Pin.Configuration = null, - PIN8: ?Pin.Configuration = null, - PIN9: ?Pin.Configuration = null, - PIN10: ?Pin.Configuration = null, - PIN11: ?Pin.Configuration = null, - PIN12: ?Pin.Configuration = null, - PIN13: ?Pin.Configuration = null, - PIN14: ?Pin.Configuration = null, - PIN15: ?Pin.Configuration = null, - - // TODO: Add enabled option to enable it from the start - comptime { - const pin_field_count = @typeInfo(Pin).@"enum".fields.len; - const config_field_count = @typeInfo(PortConfig).@"struct".fields.len; - if (pin_field_count != config_field_count) - @compileError(comptimePrint("{} {}", .{ pin_field_count, config_field_count })); +pub const GlobalConfiguration = struct { + GPIOA: ?if (util.has_port('A')) Port.Configuration else undefined = null, + GPIOB: ?if (util.has_port('B')) Port.Configuration else undefined = null, + GPIOC: ?if (util.has_port('C')) Port.Configuration else undefined = null, + GPIOD: ?if (util.has_port('D')) Port.Configuration else undefined = null, + GPIOE: ?if (util.has_port('E')) Port.Configuration else undefined = null, + GPIOF: ?if (util.has_port('F')) Port.Configuration else undefined = null, + GPIOG: ?if (util.has_port('G')) Port.Configuration else undefined = null, + GPIOH: ?if (util.has_port('H')) Port.Configuration else undefined = null, + GPIOI: ?if (util.has_port('I')) Port.Configuration else undefined = null, + GPIOJ: ?if (util.has_port('J')) Port.Configuration else undefined = null, + GPIOK: ?if (util.has_port('K')) Port.Configuration else undefined = null, + + comptime { + const port_field_count = @typeInfo(Port).@"enum".fields.len; + const config_field_count = @typeInfo(GlobalConfiguration).@"struct".fields.len; + if (port_field_count != config_field_count) + @compileError(comptimePrint("{} {}", .{ port_field_count, config_field_count })); + } + + pub fn apply(comptime config: GlobalConfiguration) Pins(config) { + var ret: Pins(config) = undefined; + + inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (port_field.type != ?Port.Configuration) { + continue; } - }; - - const _Port = enum { - //NOTE: should invalid pins panic or just be ignored? - fn get_port(port: Port) *volatile gpio_v2.GPIO { - switch (@backingInt(port)) { - inline 0...cfg.portcount - 1 => |_port| { - const port_name = [_]u8{"ABCDEFGHIJK"[_port]}; - return @field(peripherals, "GPIO" ++ port_name); - }, - else => @panic(std.fmt.comptimePrint("STM32s only have ports 0..{any} (A..{s})", .{ cfg.portcount, [_]u8{"ABCDEFGHIJK"[cfg.portcount]} })), - } + if (@field(config, port_field.name)) |_| { + rcc.enable_clock(@field(rcc.Peripherals, port_field.name)); } - }; - - const Port7 = enum { - GPIOA, - GPIOB, - GPIOC, - GPIOD, - GPIOE, - GPIOF, - GPIOG, - pub const Configuration = PortConfig; - pub const get_port = _Port.get_port; - }; - - const Port11 = enum { - GPIOA, - GPIOB, - GPIOC, - GPIOD, - GPIOE, - GPIOF, - GPIOG, - GPIOH, - GPIOI, - GPIOJ, - GPIOK, - pub const Configuration = PortConfig; - pub const get_port = _Port.get_port; - }; - - pub const Port = if (cfg.portcount == 7) - Port7 - else - Port11; - - const _GlobalConfiguration = struct { - fn apply(comptime global_config: GlobalConfiguration) Pins(global_config) { - var ret: Pins(global_config) = undefined; - - inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { - if (@field(global_config, port_field.name)) |_| { - rcc.enable_clock(@field(rcc.Peripherals, port_field.name)); - } - } + } - inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { - if (@field(global_config, port_field.name)) |port_config| { - inline for (@typeInfo(Port.Configuration).@"struct".fields) |field| { - if (@field(port_config, field.name)) |pin_config| { - const port = @field(Port, port_field.name); - var pin = GPIO_Pin.from_port(port, @field(Pin, field.name)); - pin.write_pin_config(pin_config.mode.?); - const default_name = "P" ++ port_field.name[4..5] ++ field.name[3..]; - - switch (pin_config.mode orelse .input) { - .input => @field(ret, pin_config.name orelse default_name) = Input_GPIO{ .pin = pin }, - .output => @field(ret, pin_config.name orelse default_name) = Output_GPIO{ .pin = pin }, - .analog => @field(ret, pin_config.name orelse default_name) = Analog{}, - .alternate_function => @field(ret, pin_config.name orelse default_name) = AlternateFunction{}, - .digital_io => @field(ret, pin_config.name orelse default_name) = Digital_IO_Pin{ .pin = pin }, - } - } + inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (port_field.type != ?Port.Configuration) { + continue; + } + if (@field(config, port_field.name)) |port_config| { + inline for (@typeInfo(Port.Configuration).@"struct".fields) |field| { + if (@field(port_config, field.name)) |pin_config| { + const port = @field(Port, port_field.name); + var pin = GPIO_Pin.from_port(port, @field(Pin, field.name)); + pin.write_pin_config(pin_config.mode.?); + const default_name = "P" ++ port_field.name[4..5] ++ field.name[3..]; + + switch (pin_config.mode orelse .input) { + .input => @field(ret, pin_config.name orelse default_name) = Input_GPIO{ .pin = pin }, + .output => @field(ret, pin_config.name orelse default_name) = Output_GPIO{ .pin = pin }, + .analog => @field(ret, pin_config.name orelse default_name) = Analog{}, + .alternate_function => @field(ret, pin_config.name orelse default_name) = AlternateFunction{}, + .digital_io => @field(ret, pin_config.name orelse default_name) = Digital_IO_Pin{ .pin = pin }, } } } - - return ret; - } - }; - - const GlobalConfiguration7 = struct { - GPIOA: ?Port.Configuration = null, - GPIOB: ?Port.Configuration = null, - GPIOC: ?Port.Configuration = null, - GPIOD: ?Port.Configuration = null, - GPIOE: ?Port.Configuration = null, - GPIOF: ?Port.Configuration = null, - GPIOG: ?Port.Configuration = null, - - comptime { - const port_field_count = @typeInfo(Port).@"enum".fields.len; - const config_field_count = @typeInfo(GlobalConfiguration).@"struct".fields.len; - if (port_field_count != config_field_count) - @compileError(comptimePrint("{} {}", .{ port_field_count, config_field_count })); - } - pub const apply = _GlobalConfiguration.apply; - }; - - const GlobalConfiguration11 = struct { - GPIOA: ?Port.Configuration = null, - GPIOB: ?Port.Configuration = null, - GPIOC: ?Port.Configuration = null, - GPIOD: ?Port.Configuration = null, - GPIOE: ?Port.Configuration = null, - GPIOF: ?Port.Configuration = null, - GPIOG: ?Port.Configuration = null, - GPIOH: ?Port.Configuration = null, - GPIOI: ?Port.Configuration = null, - GPIOJ: ?Port.Configuration = null, - GPIOK: ?Port.Configuration = null, - - comptime { - const port_field_count = @typeInfo(Port).@"enum".fields.len; - const config_field_count = @typeInfo(GlobalConfiguration).@"struct".fields.len; - if (port_field_count != config_field_count) - @compileError(comptimePrint("{} {}", .{ port_field_count, config_field_count })); } - pub const apply = _GlobalConfiguration.apply; - }; + } - pub const GlobalConfiguration = if (cfg.portcount == 7) - GlobalConfiguration7 - else - GlobalConfiguration11; - }; -} + return ret; + } +}; diff --git a/port/stmicro/stm32/src/hals/common/util.zig b/port/stmicro/stm32/src/hals/common/util.zig index d4decb755..8125cd2f9 100644 --- a/port/stmicro/stm32/src/hals/common/util.zig +++ b/port/stmicro/stm32/src/hals/common/util.zig @@ -2,6 +2,10 @@ const std = @import("std"); const microzig = @import("microzig"); const peripherals = microzig.chip.peripherals; +pub fn has_port(comptime id: u8) bool { + return @hasDecl(peripherals, "GPIO" ++ &[_]u8{id}); +} + pub fn match_name(heystack: []const u8, needles: []const []const u8) bool { for (needles) |needle| { if (std.mem.indexOf(u8, heystack, needle)) |_| { From 3358e4536ff45dfbcf1c91bed778e2db8f3c4cac Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Wed, 12 Aug 2026 01:37:00 +0200 Subject: [PATCH 11/15] Fix rebase issues --- .../stmicro/stm32/src/hals/common/pins_v2.zig | 60 ++++++++++--------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/port/stmicro/stm32/src/hals/common/pins_v2.zig b/port/stmicro/stm32/src/hals/common/pins_v2.zig index 48d2dfd5f..aa56b85c9 100644 --- a/port/stmicro/stm32/src/hals/common/pins_v2.zig +++ b/port/stmicro/stm32/src/hals/common/pins_v2.zig @@ -290,10 +290,13 @@ pub fn GPIO(comptime mode: Mode) type { pub fn Pins(comptime config: GlobalConfiguration) type { var count: usize = 0; - for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { - if (@field(config, port_field.name)) |port_config| { - for (@typeInfo(Port.Configuration).@"struct".fields) |field| { - if (@field(port_config, field.name) != null) { + for (@typeInfo(GlobalConfiguration).@"struct".field_names, @typeInfo(GlobalConfiguration).@"struct".fiel_types) |port_field_name, port_field_type| { + if (port_field_type != ?Port.Configuration) { + continue; + } + if (@field(config, port_field_name)) |port_config| { + for (@typeInfo(Port.Configuration).@"struct".field_names) |field_name| { + if (@field(port_config, field_name) != null) { count += 1; } } @@ -302,13 +305,16 @@ pub fn Pins(comptime config: GlobalConfiguration) type { var field_names: [count][]const u8 = undefined; var field_types: [count]type = undefined; - var field_attrs: [count]std.builtin.Type.StructField.Attributes = undefined; + var field_attrs: [count]std.builtin.Type.Struct.FieldAttributes = undefined; var i: usize = 0; - for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { - if (@field(config, port_field.name)) |port_config| { - for (@typeInfo(Port.Configuration).@"struct".fields) |field| { - if (@field(port_config, field.name)) |pin_config| { - const default_name = "P" ++ port_field.name[4..5] ++ field.name[3..]; + for (@typeInfo(GlobalConfiguration).@"struct".field_names, @typeInfo(GlobalConfiguration).@"struct".fiel_types) |port_field_name, port_field_type| { + if (port_field_type != ?Port.Configuration) { + continue; + } + if (@field(config, port_field_name)) |port_config| { + for (@typeInfo(Port.Configuration).@"struct".field_names) |field_name| { + if (@field(port_config, field_name)) |pin_config| { + const default_name = "P" ++ port_field_name[4..5] ++ field_name[3..]; field_names[i] = pin_config.name orelse default_name; field_types[i] = GPIO(pin_config.mode orelse .{ .input = .{.floating} }); field_attrs[i] = .{}; @@ -353,8 +359,8 @@ pub const Port = enum(u8) { PIN15: ?Pin.Configuration = null, comptime { - const pin_field_count = @typeInfo(Pin).@"enum".fields.len; - const config_field_count = @typeInfo(Port.Configuration).@"struct".fields.len; + const pin_field_count = @typeInfo(Pin).@"enum".field_names.len; + const config_field_count = @typeInfo(Port.Configuration).@"struct".field_names.len; if (pin_field_count != config_field_count) @compileError(comptimePrint("{} {}", .{ pin_field_count, config_field_count })); } @@ -362,7 +368,7 @@ pub const Port = enum(u8) { pub fn get_port(port: Port) *volatile gpio_v2.GPIO { switch (@backingInt(port)) { - inline 0...@typeInfo(Port).@"enum".fields.len - 1 => |p| { + inline 0...@typeInfo(Port).@"enum".field_names.len - 1 => |p| { const port_id = [_]u8{"ABCDEFGHIJK"[p]}; return @field(peripherals, "GPIO" ++ port_id); }, @@ -385,8 +391,8 @@ pub const GlobalConfiguration = struct { GPIOK: ?if (util.has_port('K')) Port.Configuration else undefined = null, comptime { - const port_field_count = @typeInfo(Port).@"enum".fields.len; - const config_field_count = @typeInfo(GlobalConfiguration).@"struct".fields.len; + const port_field_count = @typeInfo(Port).@"enum".field_names.len; + const config_field_count = @typeInfo(GlobalConfiguration).@"struct".field_names.len; if (port_field_count != config_field_count) @compileError(comptimePrint("{} {}", .{ port_field_count, config_field_count })); } @@ -394,26 +400,26 @@ pub const GlobalConfiguration = struct { pub fn apply(comptime config: GlobalConfiguration) Pins(config) { var ret: Pins(config) = undefined; - inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { - if (port_field.type != ?Port.Configuration) { + inline for (@typeInfo(GlobalConfiguration).@"struct".field_names) |port_field_name| { + if (port_field_name.type != ?Port.Configuration) { continue; } - if (@field(config, port_field.name)) |_| { - rcc.enable_clock(@field(rcc.Peripherals, port_field.name)); + if (@field(config, port_field_name)) |_| { + rcc.enable_clock(@field(rcc.Peripherals, port_field_name)); } } - inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { - if (port_field.type != ?Port.Configuration) { + inline for (@typeInfo(GlobalConfiguration).@"struct".field_names) |port_field_name| { + if (port_field_name.type != ?Port.Configuration) { continue; } - if (@field(config, port_field.name)) |port_config| { - inline for (@typeInfo(Port.Configuration).@"struct".fields) |field| { - if (@field(port_config, field.name)) |pin_config| { - const port = @field(Port, port_field.name); - var pin = GPIO_Pin.from_port(port, @field(Pin, field.name)); + if (@field(config, port_field_name)) |port_config| { + inline for (@typeInfo(Port.Configuration).@"struct".field_names) |field_name| { + if (@field(port_config, field_name)) |pin_config| { + const port = @field(Port, port_field_name); + var pin = GPIO_Pin.from_port(port, @field(Pin, field_name)); pin.write_pin_config(pin_config.mode.?); - const default_name = "P" ++ port_field.name[4..5] ++ field.name[3..]; + const default_name = "P" ++ port_field_name[4..5] ++ field_name[3..]; switch (pin_config.mode orelse .input) { .input => @field(ret, pin_config.name orelse default_name) = Input_GPIO{ .pin = pin }, From 6c1017b1e8168a48ff4e0e7d5d2a56984290f168 Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Wed, 12 Aug 2026 01:56:29 +0200 Subject: [PATCH 12/15] Rename variable --- .../stmicro/stm32/src/hals/common/pins_v2.zig | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/port/stmicro/stm32/src/hals/common/pins_v2.zig b/port/stmicro/stm32/src/hals/common/pins_v2.zig index aa56b85c9..b1a0e94a3 100644 --- a/port/stmicro/stm32/src/hals/common/pins_v2.zig +++ b/port/stmicro/stm32/src/hals/common/pins_v2.zig @@ -102,28 +102,28 @@ const GPIO_Pin = struct { pin: Pin, port: Port, - inline fn write_pin_config(_gpio: GPIO_Pin, mode: Mode) void { + inline fn write_pin_config(p: GPIO_Pin, mode: Mode) void { switch (mode) { .input => |imode| { - _gpio.set_moder(MODER.Input); - _gpio.set_bias(imode.resistor); + p.set_moder(MODER.Input); + p.set_bias(imode.resistor); }, .output => |omode| { - _gpio.set_moder(MODER.Output); - _gpio.set_output_type(omode.o_type); - _gpio.set_bias(omode.resistor); - _gpio.set_speed(omode.o_speed); + p.set_moder(MODER.Output); + p.set_output_type(omode.o_type); + p.set_bias(omode.resistor); + p.set_speed(omode.o_speed); }, .analog => |amode| { - _gpio.set_moder(MODER.Analog); - _gpio.set_bias(amode.resistor); + p.set_moder(MODER.Analog); + p.set_bias(amode.resistor); }, .alternate_function => |afmode| { - _gpio.set_moder(MODER.Alternate); - _gpio.set_bias(afmode.resistor); - _gpio.set_speed(afmode.o_speed); - _gpio.set_output_type(afmode.o_type); - _gpio.set_alternate_function(afmode.afr); + p.set_moder(MODER.Alternate); + p.set_bias(afmode.resistor); + p.set_speed(afmode.o_speed); + p.set_output_type(afmode.o_type); + p.set_alternate_function(afmode.afr); }, .digital_io => { // Nothing for now From 3f495fcec5531521a1d5ce045e68967d81f4c1a5 Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Thu, 13 Aug 2026 00:09:00 +0200 Subject: [PATCH 13/15] Generate the ports enum --- .../stmicro/stm32/src/hals/common/pins_v2.zig | 151 ++++++++++-------- 1 file changed, 81 insertions(+), 70 deletions(-) diff --git a/port/stmicro/stm32/src/hals/common/pins_v2.zig b/port/stmicro/stm32/src/hals/common/pins_v2.zig index b1a0e94a3..b37b874fb 100644 --- a/port/stmicro/stm32/src/hals/common/pins_v2.zig +++ b/port/stmicro/stm32/src/hals/common/pins_v2.zig @@ -143,11 +143,16 @@ const GPIO_Pin = struct { //NOTE: should invalid pins panic or just be ignored? fn get_port(_gpio: GPIO_Pin) *volatile gpio_v2.GPIO { - return _gpio.port.get_port(); + switch (@backingInt(_gpio.port)) { + inline 0...@typeInfo(Port).@"enum".field_names.len - 1 => |p| { + return @field(peripherals, @typeInfo(Port).@"enum".field_names[p]); + }, + else => unreachable, + } } inline fn set_bias(_gpio: GPIO_Pin, bias: PUPDR) void { - const port = _gpio.port.get_port(); + const port = _gpio.get_port(); const pin: u4 = @backingInt(_gpio.pin); const modMask: u32 = _gpio.mask_2bit(); @@ -155,7 +160,7 @@ const GPIO_Pin = struct { } inline fn set_speed(_gpio: GPIO_Pin, speed: OSPEEDR) void { - const port = _gpio.port.get_port(); + const port = _gpio.get_port(); const pin: u5 = @backingInt(_gpio.pin); const modMask: u32 = _gpio.mask_2bit(); @@ -163,7 +168,7 @@ const GPIO_Pin = struct { } inline fn set_moder(_gpio: GPIO_Pin, moder: MODER) void { - const port = _gpio.port.get_port(); + const port = _gpio.get_port(); const pin: u5 = @backingInt(_gpio.pin); const modMask: u32 = _gpio.mask_2bit(); @@ -171,7 +176,7 @@ const GPIO_Pin = struct { } inline fn set_output_type(_gpio: GPIO_Pin, otype: OT) void { - const port = _gpio.port.get_port(); + const port = _gpio.get_port(); const pin: u5 = @backingInt(_gpio.pin); port.OTYPER.write_raw((port.OTYPER.raw & ~_gpio.mask()) | @as(u32, @backingInt(otype)) << pin); @@ -290,12 +295,12 @@ pub fn GPIO(comptime mode: Mode) type { pub fn Pins(comptime config: GlobalConfiguration) type { var count: usize = 0; - for (@typeInfo(GlobalConfiguration).@"struct".field_names, @typeInfo(GlobalConfiguration).@"struct".fiel_types) |port_field_name, port_field_type| { - if (port_field_type != ?Port.Configuration) { + for (@typeInfo(GlobalConfiguration).@"struct".field_names, @typeInfo(GlobalConfiguration).@"struct".field_types) |port_field_name, port_field_type| { + if (port_field_type != ?PortConfiguration) { continue; } if (@field(config, port_field_name)) |port_config| { - for (@typeInfo(Port.Configuration).@"struct".field_names) |field_name| { + for (@typeInfo(PortConfiguration).@"struct".field_names) |field_name| { if (@field(port_config, field_name) != null) { count += 1; } @@ -307,12 +312,12 @@ pub fn Pins(comptime config: GlobalConfiguration) type { var field_types: [count]type = undefined; var field_attrs: [count]std.builtin.Type.Struct.FieldAttributes = undefined; var i: usize = 0; - for (@typeInfo(GlobalConfiguration).@"struct".field_names, @typeInfo(GlobalConfiguration).@"struct".fiel_types) |port_field_name, port_field_type| { - if (port_field_type != ?Port.Configuration) { + for (@typeInfo(GlobalConfiguration).@"struct".field_names, @typeInfo(GlobalConfiguration).@"struct".field_types) |port_field_name, port_field_type| { + if (port_field_type != ?PortConfiguration) { continue; } if (@field(config, port_field_name)) |port_config| { - for (@typeInfo(Port.Configuration).@"struct".field_names) |field_name| { + for (@typeInfo(PortConfiguration).@"struct".field_names) |field_name| { if (@field(port_config, field_name)) |pin_config| { const default_name = "P" ++ port_field_name[4..5] ++ field_name[3..]; field_names[i] = pin_config.name orelse default_name; @@ -327,68 +332,74 @@ pub fn Pins(comptime config: GlobalConfiguration) type { return @Struct(.auto, null, &field_names, &field_types, &field_attrs); } -pub const Port = enum(u8) { - GPIOA = if (util.has_port('A')) 0 else undefined, - GPIOB = if (util.has_port('B')) 1 else undefined, - GPIOC = if (util.has_port('C')) 2 else undefined, - GPIOD = if (util.has_port('D')) 3 else undefined, - GPIOE = if (util.has_port('E')) 4 else undefined, - GPIOF = if (util.has_port('F')) 5 else undefined, - GPIOG = if (util.has_port('G')) 6 else undefined, - GPIOH = if (util.has_port('H')) 7 else undefined, - GPIOI = if (util.has_port('I')) 8 else undefined, - GPIOJ = if (util.has_port('J')) 9 else undefined, - GPIOK = if (util.has_port('K')) 10 else undefined, +fn _port() type { + var result_len: usize = 0; - pub const Configuration = struct { - PIN0: ?Pin.Configuration = null, - PIN1: ?Pin.Configuration = null, - PIN2: ?Pin.Configuration = null, - PIN3: ?Pin.Configuration = null, - PIN4: ?Pin.Configuration = null, - PIN5: ?Pin.Configuration = null, - PIN6: ?Pin.Configuration = null, - PIN7: ?Pin.Configuration = null, - PIN8: ?Pin.Configuration = null, - PIN9: ?Pin.Configuration = null, - PIN10: ?Pin.Configuration = null, - PIN11: ?Pin.Configuration = null, - PIN12: ?Pin.Configuration = null, - PIN13: ?Pin.Configuration = null, - PIN14: ?Pin.Configuration = null, - PIN15: ?Pin.Configuration = null, - - comptime { - const pin_field_count = @typeInfo(Pin).@"enum".field_names.len; - const config_field_count = @typeInfo(Port.Configuration).@"struct".field_names.len; - if (pin_field_count != config_field_count) - @compileError(comptimePrint("{} {}", .{ pin_field_count, config_field_count })); + for (0..11) |i| { + const port_id: u8 = "ABCDEFGHIJK"[i]; + if (util.has_port(port_id)) { + result_len += 1; } - }; + } - pub fn get_port(port: Port) *volatile gpio_v2.GPIO { - switch (@backingInt(port)) { - inline 0...@typeInfo(Port).@"enum".field_names.len - 1 => |p| { - const port_id = [_]u8{"ABCDEFGHIJK"[p]}; - return @field(peripherals, "GPIO" ++ port_id); - }, - else => unreachable, + + var field_names: [result_len][]const u8 = undefined; + var field_values: [result_len]u8 = undefined; + var v: u8 = 0; + for (0..11) |i| { + const port_id: u8 = "ABCDEFGHIJK"[i]; + if (util.has_port(port_id)) { + const x: []const u8 = "GPIO" ++ .{port_id}; + field_names[v] = x; + field_values[v] = v; + v += 1; } } + + return @Enum(u8, .exhaustive, &field_names, &field_values); +} + +pub const Port = _port(); + + +pub const PortConfiguration = struct { + PIN0: ?Pin.Configuration = null, + PIN1: ?Pin.Configuration = null, + PIN2: ?Pin.Configuration = null, + PIN3: ?Pin.Configuration = null, + PIN4: ?Pin.Configuration = null, + PIN5: ?Pin.Configuration = null, + PIN6: ?Pin.Configuration = null, + PIN7: ?Pin.Configuration = null, + PIN8: ?Pin.Configuration = null, + PIN9: ?Pin.Configuration = null, + PIN10: ?Pin.Configuration = null, + PIN11: ?Pin.Configuration = null, + PIN12: ?Pin.Configuration = null, + PIN13: ?Pin.Configuration = null, + PIN14: ?Pin.Configuration = null, + PIN15: ?Pin.Configuration = null, + + comptime { + const pin_field_count = @typeInfo(Pin).@"enum".field_names.len; + const config_field_count = @typeInfo(PortConfiguration).@"struct".field_names.len; + if (pin_field_count != config_field_count) + @compileError(comptimePrint("{} {}", .{ pin_field_count, config_field_count })); + } }; pub const GlobalConfiguration = struct { - GPIOA: ?if (util.has_port('A')) Port.Configuration else undefined = null, - GPIOB: ?if (util.has_port('B')) Port.Configuration else undefined = null, - GPIOC: ?if (util.has_port('C')) Port.Configuration else undefined = null, - GPIOD: ?if (util.has_port('D')) Port.Configuration else undefined = null, - GPIOE: ?if (util.has_port('E')) Port.Configuration else undefined = null, - GPIOF: ?if (util.has_port('F')) Port.Configuration else undefined = null, - GPIOG: ?if (util.has_port('G')) Port.Configuration else undefined = null, - GPIOH: ?if (util.has_port('H')) Port.Configuration else undefined = null, - GPIOI: ?if (util.has_port('I')) Port.Configuration else undefined = null, - GPIOJ: ?if (util.has_port('J')) Port.Configuration else undefined = null, - GPIOK: ?if (util.has_port('K')) Port.Configuration else undefined = null, + GPIOA: ?if (util.has_port('A')) PortConfiguration else struct{} = null, + GPIOB: ?if (util.has_port('B')) PortConfiguration else struct{} = null, + GPIOC: ?if (util.has_port('C')) PortConfiguration else struct{} = null, + GPIOD: ?if (util.has_port('D')) PortConfiguration else struct{} = null, + GPIOE: ?if (util.has_port('E')) PortConfiguration else struct{} = null, + GPIOF: ?if (util.has_port('F')) PortConfiguration else struct{} = null, + GPIOG: ?if (util.has_port('G')) PortConfiguration else struct{} = null, + GPIOH: ?if (util.has_port('H')) PortConfiguration else struct{} = null, + GPIOI: ?if (util.has_port('I')) PortConfiguration else struct{} = null, + GPIOJ: ?if (util.has_port('J')) PortConfiguration else struct{} = null, + GPIOK: ?if (util.has_port('K')) PortConfiguration else struct{} = null, comptime { const port_field_count = @typeInfo(Port).@"enum".field_names.len; @@ -400,8 +411,8 @@ pub const GlobalConfiguration = struct { pub fn apply(comptime config: GlobalConfiguration) Pins(config) { var ret: Pins(config) = undefined; - inline for (@typeInfo(GlobalConfiguration).@"struct".field_names) |port_field_name| { - if (port_field_name.type != ?Port.Configuration) { + inline for (@typeInfo(GlobalConfiguration).@"struct".field_names, @typeInfo(GlobalConfiguration).@"struct".field_types) |port_field_name, port_field_type| { + if (port_field_type != ?PortConfiguration) { continue; } if (@field(config, port_field_name)) |_| { @@ -409,12 +420,12 @@ pub const GlobalConfiguration = struct { } } - inline for (@typeInfo(GlobalConfiguration).@"struct".field_names) |port_field_name| { - if (port_field_name.type != ?Port.Configuration) { + inline for (@typeInfo(GlobalConfiguration).@"struct".field_names, @typeInfo(GlobalConfiguration).@"struct".field_types) |port_field_name, port_field_type| { + if (port_field_type != ?PortConfiguration) { continue; } if (@field(config, port_field_name)) |port_config| { - inline for (@typeInfo(Port.Configuration).@"struct".field_names) |field_name| { + inline for (@typeInfo(PortConfiguration).@"struct".field_names) |field_name| { if (@field(port_config, field_name)) |pin_config| { const port = @field(Port, port_field_name); var pin = GPIO_Pin.from_port(port, @field(Pin, field_name)); From 0e770835abc2d5c31fb8530c9a52833841f5f08c Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Thu, 13 Aug 2026 00:21:36 +0200 Subject: [PATCH 14/15] Fix build issues --- .../stmicro/stm32/src/hals/common/pins_v2.zig | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/port/stmicro/stm32/src/hals/common/pins_v2.zig b/port/stmicro/stm32/src/hals/common/pins_v2.zig index b37b874fb..6d87776e4 100644 --- a/port/stmicro/stm32/src/hals/common/pins_v2.zig +++ b/port/stmicro/stm32/src/hals/common/pins_v2.zig @@ -182,6 +182,14 @@ const GPIO_Pin = struct { port.OTYPER.write_raw((port.OTYPER.raw & ~_gpio.mask()) | @as(u32, @backingInt(otype)) << pin); } + inline fn set_alternate_function(_gpio: GPIO_Pin, afr: AF) void { + const port = _gpio.get_port(); + const pin: u5 = @backingInt(_gpio.pin); + const afrMask: u32 = @as(u32, 0b1111) << ((pin % 8) << 2); + const register = if (pin > 7) &port.AFR[1] else &port.AFR[0]; + register.write_raw((register.raw & ~afrMask) | @as(u32, @backingInt(afr)) << ((pin % 8) << 2)); + } + fn from_port(port: Port, pin: Pin) GPIO_Pin { return .{ .port = port, @@ -342,7 +350,6 @@ fn _port() type { } } - var field_names: [result_len][]const u8 = undefined; var field_values: [result_len]u8 = undefined; var v: u8 = 0; @@ -361,7 +368,6 @@ fn _port() type { pub const Port = _port(); - pub const PortConfiguration = struct { PIN0: ?Pin.Configuration = null, PIN1: ?Pin.Configuration = null, @@ -389,21 +395,28 @@ pub const PortConfiguration = struct { }; pub const GlobalConfiguration = struct { - GPIOA: ?if (util.has_port('A')) PortConfiguration else struct{} = null, - GPIOB: ?if (util.has_port('B')) PortConfiguration else struct{} = null, - GPIOC: ?if (util.has_port('C')) PortConfiguration else struct{} = null, - GPIOD: ?if (util.has_port('D')) PortConfiguration else struct{} = null, - GPIOE: ?if (util.has_port('E')) PortConfiguration else struct{} = null, - GPIOF: ?if (util.has_port('F')) PortConfiguration else struct{} = null, - GPIOG: ?if (util.has_port('G')) PortConfiguration else struct{} = null, - GPIOH: ?if (util.has_port('H')) PortConfiguration else struct{} = null, - GPIOI: ?if (util.has_port('I')) PortConfiguration else struct{} = null, - GPIOJ: ?if (util.has_port('J')) PortConfiguration else struct{} = null, - GPIOK: ?if (util.has_port('K')) PortConfiguration else struct{} = null, + GPIOA: ?if (util.has_port('A')) PortConfiguration else struct {} = null, + GPIOB: ?if (util.has_port('B')) PortConfiguration else struct {} = null, + GPIOC: ?if (util.has_port('C')) PortConfiguration else struct {} = null, + GPIOD: ?if (util.has_port('D')) PortConfiguration else struct {} = null, + GPIOE: ?if (util.has_port('E')) PortConfiguration else struct {} = null, + GPIOF: ?if (util.has_port('F')) PortConfiguration else struct {} = null, + GPIOG: ?if (util.has_port('G')) PortConfiguration else struct {} = null, + GPIOH: ?if (util.has_port('H')) PortConfiguration else struct {} = null, + GPIOI: ?if (util.has_port('I')) PortConfiguration else struct {} = null, + GPIOJ: ?if (util.has_port('J')) PortConfiguration else struct {} = null, + GPIOK: ?if (util.has_port('K')) PortConfiguration else struct {} = null, comptime { const port_field_count = @typeInfo(Port).@"enum".field_names.len; - const config_field_count = @typeInfo(GlobalConfiguration).@"struct".field_names.len; + + var config_field_count: usize = 0; + for (@typeInfo(GlobalConfiguration).@"struct".field_types) |port_field_type| { + if (port_field_type == ?PortConfiguration) { + config_field_count += 1; + } + } + if (port_field_count != config_field_count) @compileError(comptimePrint("{} {}", .{ port_field_count, config_field_count })); } From 5397a8f1bacbd25e9e67db104cc1f1bc96f7e94e Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Thu, 13 Aug 2026 16:25:56 +0200 Subject: [PATCH 15/15] [stm32] Add all peripherals in enums.zig This makes it so that all peripherals always get added, so this should be good for almost any chip. Only thing that needs to be done is potentially add more indexed peripherals. This commit also fixes the peripheral index to work on chips where the peripherals are not coincidentally in order. --- port/stmicro/stm32/src/hals/STM32F103.zig | 1 + port/stmicro/stm32/src/hals/STM32F303.zig | 2 +- port/stmicro/stm32/src/hals/STM32F429.zig | 5 +- .../stmicro/stm32/src/hals/common/bdma_v2.zig | 1 + port/stmicro/stm32/src/hals/common/enums.zig | 93 +++++++++---------- port/stmicro/stm32/src/hals/common/util.zig | 62 +++++++++++-- 6 files changed, 106 insertions(+), 58 deletions(-) diff --git a/port/stmicro/stm32/src/hals/STM32F103.zig b/port/stmicro/stm32/src/hals/STM32F103.zig index cbde20545..6c81fb625 100644 --- a/port/stmicro/stm32/src/hals/STM32F103.zig +++ b/port/stmicro/stm32/src/hals/STM32F103.zig @@ -15,6 +15,7 @@ pub const backup = @import("./STM32F103/backup.zig"); pub const rtc = @import("./STM32F103/rtc.zig"); pub const dma = @import("./STM32F103/dma.zig"); pub const time = @import("./STM32F103/time.zig"); +pub const enums = @import("./common/enums.zig"); const util = @import("./common/util.zig"); //temporary solution diff --git a/port/stmicro/stm32/src/hals/STM32F303.zig b/port/stmicro/stm32/src/hals/STM32F303.zig index 669f746fa..67a11adb8 100644 --- a/port/stmicro/stm32/src/hals/STM32F303.zig +++ b/port/stmicro/stm32/src/hals/STM32F303.zig @@ -4,7 +4,7 @@ pub const i2c = @import("STM32F303/i2c.zig"); pub const spi = @import("STM32F303/spi.zig"); pub const pins = @import("STM32F303/pins.zig"); pub const dma = @import("./STM32F303/dma.zig"); -pub const enums = @import("./common//enums.zig"); +pub const enums = @import("./common/enums.zig"); pub const systick_timer = @import("./common/systick_timer.zig"); pub const systick = @import("./common/systick.zig"); diff --git a/port/stmicro/stm32/src/hals/STM32F429.zig b/port/stmicro/stm32/src/hals/STM32F429.zig index 2b303b027..b83fd4bfc 100644 --- a/port/stmicro/stm32/src/hals/STM32F429.zig +++ b/port/stmicro/stm32/src/hals/STM32F429.zig @@ -26,12 +26,13 @@ const mmio = microzig.mmio; const peripherals = microzig.chip.peripherals; const RCC = peripherals.RCC; +pub const pins = @import("./common/pins_v2.zig"); +pub const enums = @import("./common/enums.zig"); + const Digital_IO = microzig.drivers.base.Digital_IO; const State = Digital_IO.State; -pub const pins = @import("./common/pins_v2.zig"); - pub const clock = struct { pub const Domain = enum { cpu, diff --git a/port/stmicro/stm32/src/hals/common/bdma_v2.zig b/port/stmicro/stm32/src/hals/common/bdma_v2.zig index 456591a6c..dfb1e45b4 100644 --- a/port/stmicro/stm32/src/hals/common/bdma_v2.zig +++ b/port/stmicro/stm32/src/hals/common/bdma_v2.zig @@ -8,6 +8,7 @@ const enums = @import("../common/enums.zig"); pub const Instances = enums.DMA_Type; const hal = microzig.hal; + const DMA_Peripheral = microzig.chip.types.peripherals.bdma_v2.DMA; pub const Error = error{ diff --git a/port/stmicro/stm32/src/hals/common/enums.zig b/port/stmicro/stm32/src/hals/common/enums.zig index 9668f84de..f26bb7da3 100644 --- a/port/stmicro/stm32/src/hals/common/enums.zig +++ b/port/stmicro/stm32/src/hals/common/enums.zig @@ -2,48 +2,32 @@ const util = @import("util.zig"); const microzig = @import("microzig"); // Any peripheral that must be enable in RCC. -pub const Peripherals = util.create_peripheral_enum(&.{ - "DMA", - "USBRAM", - "FLASH", - "CRC", - "SDIO", - "AFIO", - "GPIO", - "ADC", - "TIM", - "SPI", - "USART", - "WWDG", - "UART", - "I2C", - "CAN", - "BKP", - "PWR", - "DAC", - "RTC", - "USB", - "LPUART", -}); - -pub const UART_Type = util.sub_peripheral_enum(Peripherals, &.{ "USART", "UART", "LPUART" }, null); -pub const I2C_Type = util.sub_peripheral_enum(Peripherals, &.{"I2C"}, null); -pub const SPI_Type = util.sub_peripheral_enum(Peripherals, &.{"SPI"}, null); -pub const DMA_Type = util.sub_peripheral_enum(Peripherals, &.{"DMA"}, null); -pub const TIMGP16_Type = util.sub_peripheral_enum(Peripherals, &.{"TIM"}, "TIM_GP16"); -pub const ADC_Type = util.sub_peripheral_enum(Peripherals, &.{"ADC"}, null); +pub const Peripherals = util.create_peripheral_enum(); + +pub const UART_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{ "USART", "UART", "LPUART" }, null); +pub const I2C_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"I2C"}, null); +pub const SPI_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"SPI"}, null); +pub const DMA_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"DMA"}, null); +pub const TIMGP16_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"TIM"}, "TIM_GP16"); +pub const ADC_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"ADC"}, null); +pub const CAN_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"CAN"}, null); +pub const SAL_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"SAL"}, null); +pub const I2S_Type = util.sub_peripheral_enum(Peripherals, &[_][]const u8{"I2S"}, null); + +fn is_peripheral(comptime val: anytype) bool { + inline for ([_]type{ UART_Type, I2C_Type, SPI_Type, DMA_Type, TIMGP16_Type, ADC_Type, CAN_Type, SAL_Type, I2S_Type }) |T| { + if (@TypeOf(val) == T) { + return true; + } + } + return false; +} pub fn to_peripheral(comptime val: anytype) Peripherals { - return switch (@TypeOf(val)) { - UART_Type, - I2C_Type, - SPI_Type, - DMA_Type, - TIMGP16_Type, - ADC_Type, - => @as(Peripherals, @fromBackingInt(@backingInt(val))), - else => @panic("Value must be one of the sur peripheral enum define below"), - }; + if (is_peripheral(val)) { + return @as(Peripherals, @fromBackingInt(@backingInt(val))); + } + @panic("Value must be one of the sur peripheral enum define below"); } pub fn get_regs(comptime T: type, comptime val: anytype) *volatile T { @@ -51,12 +35,27 @@ pub fn get_regs(comptime T: type, comptime val: anytype) *volatile T { return @field(microzig.chip.peripherals, @tagName(periph_enum)); } +fn is_sub_peripheral(comptime val: anytype) bool { + inline for ([_]type{ UART_Type, I2C_Type, SPI_Type, DMA_Type, ADC_Type, CAN_Type, SAL_Type, I2S_Type }) |T| { + if (@TypeOf(val) == T) { + return true; + } + } + return false; +} + +fn get_field_index(comptime T: type, comptime val: T) u32 { + inline for (0.., @typeInfo(T).@"enum".field_values) |i, value| { + if (value == @backingInt(val)) { + return i; + } + } +} + +// This function is basically useless, it doesn't even give the number of the paripheral pub fn base_perihperal_index(comptime val: anytype) u32 { - return switch (@TypeOf(val)) { - UART_Type => @backingInt(val) - @backingInt(Peripherals.USART1), - I2C_Type => @backingInt(val) - @backingInt(Peripherals.I2C1), - SPI_Type => @backingInt(val) - @backingInt(Peripherals.SPI1), - DMA_Type => @backingInt(val) - @backingInt(Peripherals.DMA1), - else => @panic("Index peripheral is only for multiple index peripherals"), - }; + if (is_sub_peripheral(val)) { + return get_field_index(@TypeOf(val), val); + } + @panic("Index peripheral is only for multiple index peripherals"); // TODO: Whats against implementing this for all? } diff --git a/port/stmicro/stm32/src/hals/common/util.zig b/port/stmicro/stm32/src/hals/common/util.zig index 8125cd2f9..8b9468e6b 100644 --- a/port/stmicro/stm32/src/hals/common/util.zig +++ b/port/stmicro/stm32/src/hals/common/util.zig @@ -15,32 +15,56 @@ pub fn match_name(heystack: []const u8, needles: []const []const u8) bool { return false; } -pub fn create_peripheral_enum(comptime bases_name: []const []const u8) type { +pub fn create_peripheral_enum() type { var field_names: []const []const u8 = &.{}; var field_values: []const usize = &.{}; @setEvalBranchQuota(10_000); for (@typeInfo(peripherals).@"struct".decl_names, 0..) |decl_name, i| { - if (match_name(decl_name, bases_name)) { - field_names = field_names ++ .{decl_name}; - field_values = field_values ++ .{i}; - } + field_names = field_names ++ .{decl_name}; + field_values = field_values ++ .{i}; } return @Enum(usize, .exhaustive, field_names, field_values[0..]); } +fn sorted_field_indices(comptime fields: []const [:0]const u8) [fields.len]usize { + var _fields: [fields.len]usize = undefined; + for (0..fields.len) |i| { + _fields[i] = i; + } + + const f: fn (void, usize, usize) bool = struct { + fn s(_: void, a: usize, b: usize) bool { + const aname = fields[a]; + const bname = fields[b]; + const an = std.mem.trimEnd(u8, aname, "0123456789"); + const bn = std.mem.trimEnd(u8, bname, "0123456789"); + const ai = std.fmt.parseInt(u8, aname[an.len..aname.len], 10) catch 0; + const bi = std.fmt.parseInt(u8, bname[bn.len..bname.len], 10) catch 0; + if (ai == bi) { + return std.mem.order(u8, aname, bname) == .lt; + } + return ai < bi; + } + }.s; + std.mem.sortUnstable(usize, &_fields, {}, f); + return _fields; +} + pub fn sub_peripheral_enum(comptime T: type, comptime keep_name: []const []const u8, match_type: ?[]const u8) type { var field_names: []const []const u8 = &.{}; var field_values: []const usize = &.{}; - @setEvalBranchQuota(10_000); + + @setEvalBranchQuota(30_000); const info = @typeInfo(T).@"enum"; - for (info.field_names, info.field_values) |field_name, field_value| { + for (sorted_field_indices(info.field_names)) |i| { + const field_name = info.field_names[i]; + const field_value = info.field_values[i]; if (match_name(field_name, keep_name)) { if (match_type) |match| { const type_name = @typeName(@TypeOf(@field(peripherals, field_name))); _ = std.mem.indexOf(u8, type_name, match) orelse continue; } - field_names = field_names ++ .{field_name}; field_values = field_values ++ .{field_value}; } @@ -49,6 +73,28 @@ pub fn sub_peripheral_enum(comptime T: type, comptime keep_name: []const []const return @Enum(usize, .exhaustive, field_names, field_values[0..]); } +fn num_nonempty_types(comptime size: usize, comptime T: [size]type) usize { + var i: usize = 0; + for (T) |t| { + if (t != @Enum(usize, .exhaustive, &[0][]const u8{}, &[0]usize{})) { + i += 1; + } + } + return i; +} + +pub fn filter_empty_types(comptime size: usize, comptime T: [size]type) [num_nonempty_types(size, T)]type { + var ret: [num_nonempty_types(size, T)]type = undefined; + var i: usize = 0; + for (T) |t| { + if (t != @Enum(usize, .exhaustive, &[0][]const u8{}, &[0]usize{})) { + ret[i] = t; + i += 1; + } + } + return ret; +} + pub fn load_timer_interrupt(handler: *const fn () callconv(.c) void) microzig.cpu.InterruptOptions { var int_op: microzig.cpu.InterruptOptions = .{}; if (@hasField(microzig.cpu.InterruptOptions, "TIM2")) {