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/build.zig b/port/stmicro/stm32/build.zig index 8dc27a98b..28230cdcf 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/STM32F429.zig"), + .imports = hal_imports, + }, }), }, }; 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 } } }, + }, +}); diff --git a/port/stmicro/stm32/src/hals/STM32F429.zig b/port/stmicro/stm32/src/hals/STM32F429.zig index ec10ef412..2b303b027 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,9 +22,16 @@ const std = @import("std"); const microzig = @import("microzig"); -const peripherals = microzig.peripherals; +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 pins = @import("./common/pins_v2.zig"); + pub const clock = struct { pub const Domain = enum { cpu, @@ -42,51 +49,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); +// 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; - 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}); - }; -} - -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 { - set_reg_field(RCC.AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); - set_reg_field(@field(pin.gpio_port, "MODER"), "MODER" ++ pin.suffix, 0b01); + ///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 { - set_reg_field(RCC.AHB1ENR, "GPIO" ++ pin.gpio_port_name ++ "EN", 1); - set_reg_field(@field(pin.gpio_port, "MODER"), "MODER" ++ pin.suffix, 0b00); + 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: microzig.gpio.State) void { - 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), - } + pub fn disable_clock(comptime peri: Peripherals) void { + set_clock(peri, 0); } }; 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 2663657b8..6d87776e4 100644 --- a/port/stmicro/stm32/src/hals/common/pins_v2.zig +++ b/port/stmicro/stm32/src/hals/common/pins_v2.zig @@ -1,8 +1,10 @@ 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 util = @import("util.zig"); const Digital_IO = microzig.drivers.base.Digital_IO; const Direction = Digital_IO.Direction; @@ -15,9 +17,63 @@ 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 gpio = @import("gpio_v2.zig"); +const Digital_IO_Mode = 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, @@ -38,12 +94,112 @@ pub const Pin = enum { PIN15, pub const Configuration = struct { name: ?[:0]const u8 = null, - mode: ?gpio.Mode = null, + mode: ?Mode = null, }; }; -pub const InputGPIO = struct { - pin: gpio.Pin, +const GPIO_Pin = struct { + pin: Pin, + port: Port, + + inline fn write_pin_config(p: GPIO_Pin, mode: Mode) void { + switch (mode) { + .input => |imode| { + p.set_moder(MODER.Input); + p.set_bias(imode.resistor); + }, + .output => |omode| { + 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| { + p.set_moder(MODER.Analog); + p.set_bias(amode.resistor); + }, + .alternate_function => |afmode| { + 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 + }, + } + } + + 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 { + 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.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.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.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.get_port(); + const pin: u5 = @backingInt(_gpio.pin); + + 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, + .pin = 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) @@ -53,8 +209,8 @@ pub const InputGPIO = struct { } }; -pub const OutputGPIO = struct { - pin: gpio.Pin, +pub const Output_GPIO = struct { + pin: GPIO_Pin, pub inline fn put(self: @This(), value: u1) void { var port = self.pin.get_port(); @@ -83,11 +239,11 @@ pub const AlternateFunction = struct { }; const Analog = struct { - pin: gpio.Pin, + pin: GPIO_Pin, }; pub const Digital_IO_Pin = struct { - pin: gpio.Pin, + 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, @@ -135,10 +291,10 @@ pub const Digital_IO_Pin = struct { } }; -pub fn GPIO(comptime mode: gpio.Mode) type { +pub fn GPIO(comptime mode: Mode) type { return switch (mode) { - .input => InputGPIO, - .output => OutputGPIO, + .input => Input_GPIO, + .output => Output_GPIO, .alternate_function => AlternateFunction, .analog => Analog, .digital_io => Digital_IO_Pin, @@ -147,9 +303,12 @@ pub fn GPIO(comptime mode: gpio.Mode) type { pub fn Pins(comptime config: GlobalConfiguration) type { var count: usize = 0; - for (@typeInfo(GlobalConfiguration).@"struct".field_names) |port_field_name| { + 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; } @@ -161,9 +320,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) |port_field_name| { + 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; @@ -178,53 +340,83 @@ pub fn Pins(comptime config: GlobalConfiguration) type { 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 })); +fn _port() type { + var result_len: usize = 0; + + for (0..11) |i| { + const port_id: u8 = "ABCDEFGHIJK"[i]; + if (util.has_port(port_id)) { + result_len += 1; } - }; + } + + 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: ?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, + 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 })); } @@ -232,24 +424,30 @@ 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| { + 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)) |_| { - rcc.enable_clock(@field(enums.Peripherals, port_field_name)); + rcc.enable_clock(@field(rcc.Peripherals, port_field_name)); } } - inline for (@typeInfo(GlobalConfiguration).@"struct".field_names) |port_field_name| { + 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 = @backingInt(@field(Port, port_field_name)); - var pin = gpio.Pin.from_port(@fromBackingInt(port), @backingInt(@field(Pin, field_name))); + 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) = InputGPIO{ .pin = pin }, - .output => @field(ret, pin_config.name orelse default_name) = OutputGPIO{ .pin = pin }, + .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 }, 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)) |_| {