diff --git a/examples/stmicro/stm32/src/blinky.zig b/examples/stmicro/stm32/src/blinky.zig index 74968ec62..e019050fb 100644 --- a/examples/stmicro/stm32/src/blinky.zig +++ b/examples/stmicro/stm32/src/blinky.zig @@ -40,6 +40,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 c3c8cece0..859bda2a3 100644 --- a/port/stmicro/stm32/build.zig +++ b/port/stmicro/stm32/build.zig @@ -76,6 +76,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/STM32F103.zig b/port/stmicro/stm32/src/hals/STM32F103.zig index 7cfd4868b..f866f633b 100644 --- a/port/stmicro/stm32/src/hals/STM32F103.zig +++ b/port/stmicro/stm32/src/hals/STM32F103.zig @@ -16,6 +16,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 3cd46707b..5dedf2129 100644 --- a/port/stmicro/stm32/src/hals/STM32F303.zig +++ b/port/stmicro/stm32/src/hals/STM32F303.zig @@ -7,7 +7,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 43caee757..b83fd4bfc 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,17 @@ 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; +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 clock = struct { pub const Domain = enum { cpu, @@ -42,51 +50,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, @enumFromInt(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/bdma_v2.zig b/port/stmicro/stm32/src/hals/common/bdma_v2.zig index d645e2a61..3c4c50020 100644 --- a/port/stmicro/stm32/src/hals/common/bdma_v2.zig +++ b/port/stmicro/stm32/src/hals/common/bdma_v2.zig @@ -8,7 +8,6 @@ const dma_common = @import("dma_common.zig"); 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 bf0195a7d..2518e58e4 100644 --- a/port/stmicro/stm32/src/hals/common/enums.zig +++ b/port/stmicro/stm32/src/hals/common/enums.zig @@ -3,36 +3,17 @@ 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 Peripherals = util.create_peripheral_enum(); -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 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 CAN_Type = util.sub_peripheral_enum(Peripherals, .{"CAN"}, null); +pub const SAL_Type = util.sub_peripheral_enum(Peripherals, .{"SAL"}, null); +pub const I2S_Type = util.sub_peripheral_enum(Peripherals, .{"I2S"}, null); pub fn to_peripheral(comptime val: anytype) Peripherals { return switch (@TypeOf(val)) { @@ -42,6 +23,9 @@ pub fn to_peripheral(comptime val: anytype) Peripherals { DMA_Type, TIMGP16_Type, ADC_Type, + CAN_Type, + SAL_Type, + I2S_Type, => @as(Peripherals, @enumFromInt(@intFromEnum(val))), else => @panic("Value must be one of the sur peripheral enum define below"), }; @@ -52,12 +36,25 @@ pub fn get_regs(comptime T: type, comptime val: anytype) *volatile T { return @field(microzig.chip.peripherals, @tagName(periph_enum)); } +fn get_field_index(comptime T: type, comptime val: T) u32 { + inline for (0.., @typeInfo(T).@"enum".fields) |i, field| { + if (field.value == @intFromEnum(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 => @intFromEnum(val) - @intFromEnum(Peripherals.USART1), - I2C_Type => @intFromEnum(val) - @intFromEnum(Peripherals.I2C1), - SPI_Type => @intFromEnum(val) - @intFromEnum(Peripherals.SPI1), - DMA_Type => @intFromEnum(val) - @intFromEnum(Peripherals.DMA1), - else => @panic("Index peripheral is only for multiple index peripherals"), + UART_Type => get_field_index(UART_Type, val), + I2C_Type => get_field_index(I2C_Type, val), + SPI_Type => get_field_index(I2C_Type, val), + DMA_Type => get_field_index(I2C_Type, val), + ADC_Type => get_field_index(I2C_Type, val), + CAN_Type => get_field_index(I2C_Type, val), + SAL_Type => get_field_index(I2C_Type, val), + I2S_Type => get_field_index(I2C_Type, val), + else => @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/gpio_v2.zig b/port/stmicro/stm32/src/hals/common/gpio_v2.zig deleted file mode 100644 index fba4909f6..000000000 --- a/port/stmicro/stm32/src/hals/common/gpio_v2.zig +++ /dev/null @@ -1,177 +0,0 @@ -const std = @import("std"); -const microzig = @import("microzig"); - -const assert = std.debug.assert; -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; -const AFIO = microzig.chip.peripherals.AFIO; - -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(@intFromEnum(gpio) % 16); - return @as(u32, 0b11) << (pin << 1); - } - - pub fn mask(gpio: Pin) u32 { - const pin: u4 = @intCast(@intFromEnum(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(@intFromEnum(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(@intFromEnum(gpio) % 16); - const modMask: u32 = gpio.mask_2bit(); - - port.PUPDR.write_raw((port.PUPDR.raw & ~modMask) | @as(u32, @intFromEnum(bias)) << (pin << 1)); - } - - pub inline fn set_speed(gpio: Pin, speed: OSPEEDR) void { - const port = gpio.get_port(); - const pin: u5 = @intCast(@intFromEnum(gpio) % 16); - const modMask: u32 = gpio.mask_2bit(); - - port.OSPEEDR.write_raw((port.OSPEEDR.raw & ~modMask) | @as(u32, @intFromEnum(speed)) << (pin << 1)); - } - - pub inline fn set_moder(gpio: Pin, moder: MODER) void { - const port = gpio.get_port(); - const pin: u5 = @intCast(@intFromEnum(gpio) % 16); - const modMask: u32 = gpio.mask_2bit(); - - port.MODER.write_raw((port.MODER.raw & ~modMask) | @as(u32, @intFromEnum(moder)) << (pin << 1)); - } - - pub inline fn set_output_type(gpio: Pin, otype: OT) void { - const port = gpio.get_port(); - const pin: u5 = @intCast(@intFromEnum(gpio) % 16); - - port.OTYPER.write_raw((port.OTYPER.raw & ~gpio.mask()) | @as(u32, @intFromEnum(otype)) << pin); - } - - pub inline fn set_alternate_function(gpio: Pin, afr: AF) void { - const port = gpio.get_port(); - const pin: u5 = @intCast(@intFromEnum(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, @intFromEnum(afr)) << ((pin % 8) << 2)); - } - - pub fn from_port(port: Port, pin: u4) Pin { - const value: usize = pin + (@as(usize, 16) * @intFromEnum(port)); - return @enumFromInt(value); - } -}; diff --git a/port/stmicro/stm32/src/hals/common/pins_v2.zig b/port/stmicro/stm32/src/hals/common/pins_v2.zig index 198831cfd..6802d72ec 100644 --- a/port/stmicro/stm32/src/hals/common/pins_v2.zig +++ b/port/stmicro/stm32/src/hals/common/pins_v2.zig @@ -4,7 +4,7 @@ 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; @@ -17,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, @@ -40,12 +94,99 @@ 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(_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 = @intFromEnum(_gpio.pin); + return @as(u32, 0b11) << (pin << 1); + } + + fn mask(_gpio: GPIO_Pin) u32 { + const pin: u4 = @intFromEnum(_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 = @intFromEnum(_gpio.pin); + const modMask: u32 = _gpio.mask_2bit(); + + port.PUPDR.write_raw((port.PUPDR.raw & ~modMask) | @as(u32, @intFromEnum(bias)) << (pin << 1)); + } + + inline fn set_speed(_gpio: GPIO_Pin, speed: OSPEEDR) void { + const port = _gpio.port.get_port(); + const pin: u5 = @intFromEnum(_gpio.pin); + const modMask: u32 = _gpio.mask_2bit(); + + port.OSPEEDR.write_raw((port.OSPEEDR.raw & ~modMask) | @as(u32, @intFromEnum(speed)) << (pin << 1)); + } + + inline fn set_moder(_gpio: GPIO_Pin, moder: MODER) void { + const port = _gpio.port.get_port(); + const pin: u5 = @intFromEnum(_gpio.pin); + const modMask: u32 = _gpio.mask_2bit(); + + port.MODER.write_raw((port.MODER.raw & ~modMask) | @as(u32, @intFromEnum(moder)) << (pin << 1)); + } + + inline fn set_output_type(_gpio: GPIO_Pin, otype: OT) void { + const port = _gpio.port.get_port(); + const pin: u5 = @intFromEnum(_gpio.pin); + + port.OTYPER.write_raw((port.OTYPER.raw & ~_gpio.mask()) | @as(u32, @intFromEnum(otype)) << pin); + } + + 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) @@ -55,8 +196,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(); @@ -85,11 +226,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, @@ -137,10 +278,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, @@ -151,6 +292,9 @@ pub fn Pins(comptime config: GlobalConfiguration) type { comptime { var fields: []const StructField = &.{}; for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (port_field.type != ?Port.Configuration) { + continue; + } if (@field(config, port_field.name)) |port_config| { for (@typeInfo(Port.Configuration).@"struct".fields) |field| { if (@field(port_config, field.name)) |pin_config| { @@ -186,14 +330,19 @@ pub fn Pins(comptime config: GlobalConfiguration) type { } } -pub const Port = enum { - GPIOA, - GPIOB, - GPIOC, - GPIOD, - GPIOE, - GPIOF, - GPIOG, +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, @@ -214,21 +363,35 @@ pub const Port = enum { comptime { const pin_field_count = @typeInfo(Pin).@"enum".fields.len; - const config_field_count = @typeInfo(Configuration).@"struct".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 })); } }; + + pub fn get_port(port: Port) *volatile gpio_v2.GPIO { + switch (@intFromEnum(port)) { + inline 0...@typeInfo(Port).@"enum".fields.len - 1 => |p| { + const port_id = [_]u8{"ABCDEFGHIJK"[p]}; + return @field(peripherals, "GPIO" ++ port_id); + }, + else => unreachable, + } + } }; 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')) 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; @@ -241,23 +404,29 @@ pub const GlobalConfiguration = struct { var ret: Pins(config) = undefined; inline for (@typeInfo(GlobalConfiguration).@"struct".fields) |port_field| { + if (port_field.type != ?Port.Configuration) { + 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".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 = @intFromEnum(@field(Port, port_field.name)); - var pin = gpio.Pin.from_port(@enumFromInt(port), @intFromEnum(@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 8713df969..dc1f3cc61 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)) |_| { @@ -11,22 +15,20 @@ 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 { - var names: [70]std.builtin.Type.EnumField = undefined; +pub fn create_peripheral_enum() type { + var names: []const std.builtin.Type.EnumField = &.{}; + var names_index = 0; const peripheral = @typeInfo(peripherals); @setEvalBranchQuota(10_000); switch (peripheral) { .@"struct" => |data| { for (data.decls) |decls| { - const decl_name = decls.name; - if (match_name(decl_name, bases_name)) { - names[names_index] = std.builtin.Type.EnumField{ - .name = decls.name, - .value = names_index, - }; - names_index += 1; - } + names = names ++ [_]std.builtin.Type.EnumField{.{ + .name = decls.name, + .value = names_index, + }}; + names_index += 1; } }, else => unreachable, @@ -42,16 +44,35 @@ pub fn create_peripheral_enum(comptime bases_name: []const []const u8) type { return @Type(peri_enum); } +fn sorted_fields(comptime fields: []const std.builtin.Type.EnumField) [fields.len]std.builtin.Type.EnumField { + var _fields: [fields.len]std.builtin.Type.EnumField = undefined; + @memcpy(&_fields, fields); + const f: fn (void, std.builtin.Type.EnumField, std.builtin.Type.EnumField) bool = struct { + fn s(_: void, a: std.builtin.Type.EnumField, b: std.builtin.Type.EnumField) bool { + const an = std.mem.trimEnd(u8, a.name, "0123456789"); + const bn = std.mem.trimEnd(u8, b.name, "0123456789"); + const ai = std.fmt.parseInt(u8, a.name[an.len..a.name.len], 10) catch 0; + const bi = std.fmt.parseInt(u8, b.name[bn.len..b.name.len], 10) catch 0; + if (ai == bi) { + return std.mem.order(u8, a.name, b.name) == .lt; + } + return ai < bi; + } + }.s; + std.mem.sortUnstable(std.builtin.Type.EnumField, &_fields, {}, f); + return _fields; +} + pub fn sub_peripheral_enum(comptime T: type, comptime keep_name: []const []const u8, match_type: ?[]const u8) type { const enum_info = @typeInfo(T); var names_index = 0; var names: [10]std.builtin.Type.EnumField = undefined; - @setEvalBranchQuota(10_000); + @setEvalBranchQuota(30_000); switch (enum_info) { .@"enum" => |data| { - for (data.fields) |field| { + for (sorted_fields(data.fields)) |field| { if (match_name(field.name, keep_name)) { if (match_type) |match| { const type_name = @typeName(@TypeOf(@field(peripherals, field.name)));