From 237890d49ee795110a63df2c45bdd6f6a0029a72 Mon Sep 17 00:00:00 2001 From: fmaggi <61335294+fmaggi@users.noreply.github.com> Date: Mon, 25 Sep 2023 12:14:35 -0300 Subject: [PATCH] Blue pill hal (#28) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rework for MicroZig Gen 2 * gpio hal for blue pill * added a comment * better interface * Started implementing pins interface * commented unused stuff for now * forgot setting pin mode * fixed somethings I forgot * made global config have fields instead of decls * fixed a bug * Lots of bugs * bugs bugs and more bugs * bugs bunny * forgot ? * i * sizes * so weird * weird size * const issues * const issues * always const issues * what is happeningn * weird issues * finally got a led to turn on! * Rework for MicroZig Gen 2 * Rebased onto master --------- Co-authored-by: Felix "xq" Queißner --- build.zig | 4 + src/hals/STM32F103/gpio.zig | 160 ++++++++++++++++++++++++ src/hals/STM32F103/hal.zig | 3 + src/hals/STM32F103/pins.zig | 237 ++++++++++++++++++++++++++++++++++++ 4 files changed, 404 insertions(+) create mode 100644 src/hals/STM32F103/gpio.zig create mode 100644 src/hals/STM32F103/hal.zig create mode 100644 src/hals/STM32F103/pins.zig diff --git a/build.zig b/build.zig index 7a0aac0..4105782 100644 --- a/build.zig +++ b/build.zig @@ -5,6 +5,7 @@ fn root() []const u8 { return comptime (std.fs.path.dirname(@src().file) orelse "."); } const build_root = root(); + const KiB = 1024; //////////////////////////////////////// @@ -30,6 +31,9 @@ pub const chips = struct { .json = .{ .cwd_relative = build_root ++ "/src/chips/STM32F103.json" }, }, }, + .hal = .{ + .source_file = .{ .cwd_relative = build_root ++ "/src/hals/STM32F103/hal.zig" }, + }, }; pub const stm32f303vc = .{ diff --git a/src/hals/STM32F103/gpio.zig b/src/hals/STM32F103/gpio.zig new file mode 100644 index 0000000..811a80c --- /dev/null +++ b/src/hals/STM32F103/gpio.zig @@ -0,0 +1,160 @@ +const std = @import("std"); +const assert = std.debug.assert; + +const microzig = @import("microzig"); +pub const peripherals = microzig.chip.peripherals; + +const GPIOA = peripherals.GPIOA; +const GPIOB = peripherals.GPIOB; +const GPIOC = peripherals.GPIOC; +const GPIOD = peripherals.GPIOD; +const GPIOE = peripherals.GPIOE; +const GPIOF = peripherals.GPIOF; +const GPIOG = peripherals.GPIOG; + +const GPIO = @TypeOf(GPIOA); + +const log = std.log.scoped(.gpio); + +pub const Function = enum {}; + +pub const Mode = union(enum) { + input: InputMode, + output: OutputMode, +}; + +pub const InputMode = enum(u2) { + analog, + floating, + pull, + reserved, +}; + +pub const OutputMode = enum(u2) { + general_purpose_push_pull, + general_purpose_open_drain, + alternate_function_push_pull, + alternate_function_open_drain, +}; + +pub const Speed = enum(u2) { + reserved, + max_10MHz, + max_2MHz, + max_50MHz, +}; + +pub const IrqLevel = enum(u2) { + low, + high, + fall, + rise, +}; + +pub const IrqCallback = fn (gpio: u32, events: u32) callconv(.C) void; + +pub const Enabled = enum { + disabled, + enabled, +}; + +pub const Pull = enum { + up, + down, +}; + +// NOTE: With this current setup, every time we want to do anythting we go through a switch +// Do we want this? +pub const Pin = packed struct(u8) { + number: u4, + port: u3, + padding: u1, + + pub fn init(port: u3, number: u4) Pin { + return Pin{ + .number = number, + .port = port, + .padding = 0, + }; + } + inline fn write_pin_config(gpio: Pin, config: u32) void { + const port = gpio.get_port(); + if (gpio.number <= 7) { + const offset = @as(u5, gpio.number) << 2; + port.CRL.raw &= ~(@as(u32, 0b1111) << offset); + port.CRL.raw |= config << offset; + } else { + const offset = (@as(u5, gpio.number) - 8) << 2; + port.CRH.raw &= ~(@as(u32, 0b1111) << offset); + port.CRH.raw |= config << offset; + } + } + + fn mask(gpio: Pin) u16 { + return @as(u16, 1) << gpio.number; + } + + // NOTE: Im not sure I like this + // We could probably calculate an offset from GPIOA? + pub fn get_port(gpio: Pin) GPIO { + return switch (gpio.port) { + 0 => GPIOA, + 1 => GPIOB, + 2 => GPIOC, + 3 => GPIOD, + 4 => GPIOE, + 5 => GPIOF, + 6 => GPIOG, + 7 => @panic("The STM32 only has ports 0..6 (A..G)"), + }; + } + + pub inline fn set_mode(gpio: Pin, mode: Mode) void { + switch (mode) { + .input => |in| gpio.set_input_mode(in), + .output => |out| gpio.set_output_mode(out, .max_2MHz), + } + } + + pub inline fn set_input_mode(gpio: Pin, mode: InputMode) void { + const m_mode = @as(u32, @intFromEnum(mode)); + const config: u32 = m_mode << 2; + gpio.write_pin_config(config); + } + + pub inline fn set_output_mode(gpio: Pin, mode: OutputMode, speed: Speed) void { + const s_speed = @as(u32, @intFromEnum(speed)); + const m_mode = @as(u32, @intFromEnum(mode)); + const config: u32 = s_speed + (m_mode << 2); + gpio.write_pin_config(config); + } + + pub inline fn set_pull(gpio: Pin, pull: Pull) void { + var port = gpio.get_port(); + switch (pull) { + .up => port.BSRR.raw = gpio.mask(), + .down => port.BRR.raw = gpio.mask(), + } + } + + pub inline fn read(gpio: Pin) u1 { + const port = gpio.get_port(); + return if (port.IDR.raw & gpio.mask() != 0) + 1 + else + 0; + } + + pub inline fn put(gpio: Pin, value: u1) void { + var port = gpio.get_port(); + switch (value) { + 0 => port.BSRR.raw = gpio.mask() << 16, + 1 => port.BSRR.raw = gpio.mask(), + } + } + + pub inline fn toggle(gpio: Pin) void { + var port = gpio.get_port(); + port.ODR.raw ^= gpio.mask(); + } +}; diff --git a/src/hals/STM32F103/hal.zig b/src/hals/STM32F103/hal.zig new file mode 100644 index 0000000..fafa677 --- /dev/null +++ b/src/hals/STM32F103/hal.zig @@ -0,0 +1,3 @@ +pub const pins = @import("pins.zig"); + +pub fn init() void {} diff --git a/src/hals/STM32F103/pins.zig b/src/hals/STM32F103/pins.zig new file mode 100644 index 0000000..021dd6c --- /dev/null +++ b/src/hals/STM32F103/pins.zig @@ -0,0 +1,237 @@ +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 RCC = microzig.chip.peripherals.RCC; + +const gpio = @import("gpio.zig"); +// const pwm = @import("pwm.zig"); +// const adc = @import("adc.zig"); +// const resets = @import("resets.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: ?[]const u8 = null, + // function: Function = .SIO, + mode: ?gpio.Mode = null, + speed: ?gpio.Speed = null, + pull: ?gpio.Pull = null, + // input/output enable + // schmitt trigger + // hysteresis + + pub fn get_mode(comptime config: Configuration) gpio.Mode { + return if (config.mode) |mode| + mode + // else if (comptime config.function.is_pwm()) + // .out + // else if (comptime config.function.is_uart_tx()) + // .out + // else if (comptime config.function.is_uart_rx()) + // .in + // else if (comptime config.function.is_adc()) + // .in + else + @panic("TODO"); + } + }; +}; + +pub fn GPIO(comptime port: u3, comptime num: u4, comptime mode: gpio.Mode) type { + return switch (mode) { + .input => struct { + const pin = gpio.Pin.init(port, num); + + pub inline fn read(self: @This()) u1 { + _ = self; + return pin.read(); + } + }, + .output => struct { + const pin = gpio.Pin.init(port, num); + + pub inline fn put(self: @This(), value: u1) void { + _ = self; + pin.put(value); + } + + pub inline fn toggle(self: @This()) void { + _ = self; + pin.toggle(); + } + }, + }; +} + +pub fn Pins(comptime config: GlobalConfiguration) type { + comptime { + var fields: []const StructField = &.{}; + 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| { + var pin_field = StructField{ + .is_comptime = false, + .default_value = null, + + // initialized below: + .name = undefined, + .type = undefined, + .alignment = undefined, + }; + + pin_field.name = pin_config.name orelse field.name; + pin_field.type = GPIO(@intFromEnum(@field(Port, port_field.name)), @intFromEnum(@field(Pin, field.name)), pin_config.mode orelse .{ .input = .{.floating} }); + pin_field.alignment = @alignOf(field.type); + + fields = fields ++ &[_]StructField{pin_field}; + } + } + } + } + + return @Type(.{ + .Struct = .{ + .layout = .Auto, + .is_tuple = false, + .fields = fields, + .decls = &.{}, + }, + }); + } +} + +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 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) { + inline for (@typeInfo(GlobalConfiguration).Struct.fields) |port_field| { + if (@field(config, port_field.name)) |port_config| { + comptime var input_gpios: u16 = 0; + comptime var output_gpios: u16 = 0; + comptime { + inline for (@typeInfo(Port.Configuration).Struct.fields) |field| + if (@field(port_config, field.name)) |pin_config| { + const gpio_num = @intFromEnum(@field(Pin, field.name)); + + switch (pin_config.get_mode()) { + .input => input_gpios |= 1 << gpio_num, + .output => output_gpios |= 1 << gpio_num, + } + }; + } + + // TODO: ensure only one instance of an input function exists + const used_gpios = comptime input_gpios | output_gpios; + + if (used_gpios != 0) { + const offset = @intFromEnum(@field(Port, port_field.name)) + 2; + const bit = @as(u32, 1 << offset); + RCC.APB2ENR.raw |= bit; + // Delay after setting + _ = RCC.APB2ENR.raw & bit; + } + + inline for (@typeInfo(Port.Configuration).Struct.fields) |field| { + if (@field(port_config, field.name)) |pin_config| { + var pin = gpio.Pin.init(@intFromEnum(@field(Port, port_field.name)), @intFromEnum(@field(Pin, field.name))); + pin.set_mode(pin_config.mode.?); + } + } + + if (input_gpios != 0) { + inline for (@typeInfo(Port.Configuration).Struct.fields) |field| + if (@field(port_config, field.name)) |pin_config| { + var pin = gpio.Pin.init(@intFromEnum(@field(Port, port_field.name)), @intFromEnum(@field(Pin, field.name))); + const pull = pin_config.pull orelse continue; + if (comptime pin_config.get_mode() != .input) + @compileError("Only input pins can have pull up/down enabled"); + + pin.set_pull(pull); + }; + } + } + } + + // fields in the Pins(config) type should be zero sized, so we just + // default build them all (wasn't sure how to do that cleanly in + // `Pins()` + var ret: Pins(config) = undefined; + inline for (@typeInfo(Pins(config)).Struct.fields) |field| { + if (field.default_value) |default_value| { + @field(ret, field.name) = @as(*const field.field_type, @ptrCast(default_value)).*; + } else { + @field(ret, field.name) = .{}; + } + } + return ret; + // validate selected function + } +};