const std = @import("std"); const gpio = @import("gpio.zig"); const pwm = @import("pwm.zig"); const regs = @import("microzig").chip.registers; const assert = std.debug.assert; const comptimePrint = std.fmt.comptimePrint; const StructField = std.builtin.TypeInfo.StructField; pub const Pin = enum { GPIO0, GPIO1, GPIO2, GPIO3, GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, GPIO13, GPIO14, GPIO15, GPIO16, GPIO17, GPIO18, GPIO19, GPIO20, GPIO21, GPIO22, GPIO23, GPIO24, GPIO25, GPIO26, GPIO27, GPIO28, GPIO29, pub const Configuration = struct { name: ?[]const u8 = null, function: Function = .SIO, direction: ?gpio.Direction = null, drive_strength: ?gpio.DriveStrength = null, pull: ?enum { up, down } = null, slew_rate: ?gpio.SlewRate = null, // input/output enable // schmitt trigger // hysteresis pub fn getDirection(config: Configuration) gpio.Direction { return if (config.direction) |direction| direction else if (config.function.isPwm()) .out else @panic("TODO"); } }; }; pub const Function = enum { SIO, PIO0, PIO1, SPI0_RX, SPI0_CSn, SPI0_SCK, SPI0_TX, SPI1_RX, SPI1_CSn, SPI1_SCK, SPI1_TX, UART0_TX, UART0_RX, UART0_CTS, UART0_RTS, UART1_TX, UART1_RX, UART1_CTS, UART1_RTS, I2C0_SDA, I2C0_SCL, I2C1_SDA, I2C1_SCL, PWM0_A, PWM0_B, PWM1_A, PWM1_B, PWM2_A, PWM2_B, PWM3_A, PWM3_B, PWM4_A, PWM4_B, PWM5_A, PWM5_B, PWM6_A, PWM6_B, PWM7_A, PWM7_B, CLOCK_GPIN0, CLOCK_GPIN1, CLOCK_GPOUT0, CLOCK_GPOUT1, CLOCK_GPOUT2, CLOCK_GPOUT3, USB_OVCUR_DET, USB_VBUS_DET, USB_VBUS_EN, ADC0, ADC1, ADC2, ADC3, pub fn isPwm(function: Function) bool { return switch (function) { .PWM0_A, .PWM0_B, .PWM1_A, .PWM1_B, .PWM2_A, .PWM2_B, .PWM3_A, .PWM3_B, .PWM4_A, .PWM4_B, .PWM5_A, .PWM5_B, .PWM6_A, .PWM6_B, .PWM7_A, .PWM7_B, => true, else => false, }; } pub fn pwmSlice(comptime function: Function) u32 { return switch (function) { .PWM0_A, .PWM0_B => 0, .PWM1_A, .PWM1_B => 1, .PWM2_A, .PWM2_B => 2, .PWM3_A, .PWM3_B => 3, .PWM4_A, .PWM4_B => 4, .PWM5_A, .PWM5_B => 5, .PWM6_A, .PWM6_B => 6, .PWM7_A, .PWM7_B => 7, else => @compileError("not pwm"), }; } pub fn pwmChannel(comptime function: Function) pwm.Channel { return switch (function) { .PWM0_A, .PWM1_A, .PWM2_A, .PWM3_A, .PWM4_A, .PWM5_A, .PWM6_A, .PWM7_A, => .a, .PWM0_B, .PWM1_B, .PWM2_B, .PWM3_B, .PWM4_B, .PWM5_B, .PWM6_B, .PWM7_B, => .b, else => @compileError("not pwm"), }; } }; fn all() [30]u1 { var ret: [30]u1 = undefined; for (ret) |*elem| elem.* = 1; return ret; } fn list(gpio_list: []const u5) [30]u1 { var ret = std.mem.zeroes([30]u1); for (gpio_list) |num| ret[num] = 1; return ret; } fn single(gpio_num: u5) [30]u1 { var ret = std.mem.zeroes([30]u1); ret[gpio_num] = 1; return ret; } const function_table = [@typeInfo(Function).Enum.fields.len][30]u1{ all(), // SIO all(), // PIO0 all(), // PIO1 list(&.{ 0, 4, 16, 20 }), // SPI0_RX list(&.{ 1, 5, 17, 21 }), // SPI0_CSn list(&.{ 2, 6, 18, 22 }), // SPI0_SCK list(&.{ 3, 7, 19, 23 }), // SPI0_TX list(&.{ 8, 12, 24, 28 }), // SPI1_RX list(&.{ 9, 13, 25, 29 }), // SPI1_CSn list(&.{ 10, 14, 26 }), // SPI1_SCK list(&.{ 11, 15, 27 }), // SPI1_TX list(&.{ 0, 11, 16, 28 }), // UART0_TX list(&.{ 1, 13, 17, 29 }), // UART0_RX list(&.{ 2, 14, 18 }), // UART0_CTS list(&.{ 3, 15, 19 }), // UART0_RTS list(&.{ 4, 8, 20, 24 }), // UART1_TX list(&.{ 5, 9, 21, 25 }), // UART1_RX list(&.{ 6, 10, 22, 26 }), // UART1_CTS list(&.{ 7, 11, 23, 27 }), // UART1_RTS list(&.{ 0, 4, 8, 12, 16, 20, 24, 28 }), // I2C0_SDA list(&.{ 1, 5, 9, 13, 17, 21, 25, 29 }), // I2C0_SCL list(&.{ 2, 6, 10, 14, 18, 22, 26 }), // I2C1_SDA list(&.{ 3, 7, 11, 15, 19, 23, 27 }), // I2C1_SCL list(&.{ 0, 16 }), // PWM0_A list(&.{ 1, 17 }), // PWM0_B list(&.{ 2, 18 }), // PWM1_A list(&.{ 3, 19 }), // PWM1_B list(&.{ 4, 20 }), // PWM2_A list(&.{ 5, 21 }), // PWM2_B list(&.{ 6, 22 }), // PWM3_A list(&.{ 7, 23 }), // PWM3_B list(&.{ 8, 24 }), // PWM4_A list(&.{ 9, 25 }), // PWM4_B list(&.{ 10, 26 }), // PWM5_A list(&.{ 11, 27 }), // PWM5_B list(&.{ 12, 28 }), // PWM6_A list(&.{ 13, 29 }), // PWM6_B single(14), // PWM7_A single(15), // PWM7_B single(20), // CLOCK_GPIN0 single(22), // CLOCK_GPIN1 single(21), // CLOCK_GPOUT0 single(23), // CLOCK_GPOUT1 single(24), // CLOCK_GPOUT2 single(25), // CLOCK_GPOUT3 list(&.{ 0, 3, 6, 9, 12, 15, 18, 21, 24, 27 }), // USB_OVCUR_DET list(&.{ 1, 4, 7, 10, 13, 16, 19, 22, 25, 28 }), // USB_VBUS_DET list(&.{ 2, 5, 8, 11, 14, 17, 20, 23, 26, 29 }), // USB_VBUS_EN single(26), // ADC0 single(27), // ADC1 single(28), // ADC2 single(29), // ADC3 }; pub fn GPIO(comptime num: u5, comptime direction: gpio.Direction) type { return switch (direction) { .in => struct { const gpio_num = num; pub inline fn read(self: @This()) u1 { _ = self; @compileError("TODO"); } }, .out => struct { const gpio_num = num; pub inline fn put(self: @This(), value: u1) void { _ = self; gpio.put(gpio_num, value); } pub inline fn toggle(self: @This()) void { _ = self; gpio.toggle(gpio_num); } }, }; } pub fn Pins(comptime config: GlobalConfiguration) type { const count = count: { var ret: usize = 0; inline for (@typeInfo(GlobalConfiguration).Struct.fields) |field| { if (@field(config, field.name)) |pin_config| if (pin_config.function == .SIO or pin_config.function.isPwm()) { ret += 1; }; } break :count ret; }; var i: usize = 0; var fields: [count]StructField = undefined; inline for (@typeInfo(GlobalConfiguration).Struct.fields) |field| { if (@field(config, field.name)) |pin_config| if (pin_config.function == .SIO) { fields[i] = StructField{ .name = pin_config.name orelse field.name, .field_type = GPIO(@enumToInt(@field(Pin, field.name)), pin_config.direction orelse .in), .is_comptime = false, .default_value = null, .alignment = 1, }; i += 1; } else if (pin_config.function.isPwm()) { fields[i] = StructField{ .name = pin_config.name orelse @tagName(pin_config.function), .field_type = pwm.PWM(pin_config.function.pwmSlice(), pin_config.function.pwmChannel()), .is_comptime = false, .default_value = null, .alignment = 1, }; i += 1; }; } return @Type(.{ .Struct = .{ .layout = .Auto, .is_tuple = false, .fields = &fields, .decls = &.{}, }, }); } pub const GlobalConfiguration = struct { GPIO0: ?Pin.Configuration = null, GPIO1: ?Pin.Configuration = null, GPIO2: ?Pin.Configuration = null, GPIO3: ?Pin.Configuration = null, GPIO4: ?Pin.Configuration = null, GPIO5: ?Pin.Configuration = null, GPIO6: ?Pin.Configuration = null, GPIO7: ?Pin.Configuration = null, GPIO8: ?Pin.Configuration = null, GPIO9: ?Pin.Configuration = null, GPIO10: ?Pin.Configuration = null, GPIO11: ?Pin.Configuration = null, GPIO12: ?Pin.Configuration = null, GPIO13: ?Pin.Configuration = null, GPIO14: ?Pin.Configuration = null, GPIO15: ?Pin.Configuration = null, GPIO16: ?Pin.Configuration = null, GPIO17: ?Pin.Configuration = null, GPIO18: ?Pin.Configuration = null, GPIO19: ?Pin.Configuration = null, GPIO20: ?Pin.Configuration = null, GPIO21: ?Pin.Configuration = null, GPIO22: ?Pin.Configuration = null, GPIO23: ?Pin.Configuration = null, GPIO24: ?Pin.Configuration = null, GPIO25: ?Pin.Configuration = null, GPIO26: ?Pin.Configuration = null, GPIO27: ?Pin.Configuration = null, GPIO28: ?Pin.Configuration = null, GPIO29: ?Pin.Configuration = null, comptime { const pin_field_count = @typeInfo(Pin).Enum.fields.len; const config_field_count = @typeInfo(GlobalConfiguration).Struct.fields.len; if (pin_field_count != config_field_count) @compileError(comptimePrint("{} {}", .{ pin_field_count, config_field_count })); } pub fn apply(comptime config: GlobalConfiguration) Pins(config) { comptime var input_gpios: u32 = 0; comptime var output_gpios: u32 = 0; // validate selected function comptime { inline for (@typeInfo(GlobalConfiguration).Struct.fields) |field| if (@field(config, field.name)) |pin_config| { const gpio_num = @enumToInt(@field(Pin, field.name)); if (0 == function_table[@enumToInt(pin_config.function)][gpio_num]) @compileError(comptimePrint("{s} cannot be configured for {}", .{ field.name, pin_config.function })); switch (pin_config.getDirection()) { .in => input_gpios |= 1 << gpio_num, .out => output_gpios |= 1 << gpio_num, } }; } // TODO: ensure only one instance of an input function exists const used_gpios = comptime input_gpios | output_gpios; gpio.reset(); if (used_gpios != 0) { regs.SIO.GPIO_OE_CLR.raw = used_gpios; regs.SIO.GPIO_OUT_CLR.raw = used_gpios; comptime var i: u32 = 0; inline while (i < 32) : (i += 1) if (0 != used_gpios & 1 << i) gpio.setFunction(i, .sio); } if (output_gpios != 0) regs.SIO.GPIO_OE_SET.raw = output_gpios; // TODO: pwm initialization // 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| @field(ret, field.name) = .{}; return ret; } };