Blue pill hal (#28)

* 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 <git@random-projects.net>
wch-ch32v003
fmaggi 1 year ago committed by GitHub
parent 366e58f65c
commit 237890d49e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,6 +5,7 @@ fn root() []const u8 {
return comptime (std.fs.path.dirname(@src().file) orelse "."); return comptime (std.fs.path.dirname(@src().file) orelse ".");
} }
const build_root = root(); const build_root = root();
const KiB = 1024; const KiB = 1024;
//////////////////////////////////////// ////////////////////////////////////////
@ -30,6 +31,9 @@ pub const chips = struct {
.json = .{ .cwd_relative = build_root ++ "/src/chips/STM32F103.json" }, .json = .{ .cwd_relative = build_root ++ "/src/chips/STM32F103.json" },
}, },
}, },
.hal = .{
.source_file = .{ .cwd_relative = build_root ++ "/src/hals/STM32F103/hal.zig" },
},
}; };
pub const stm32f303vc = .{ pub const stm32f303vc = .{

@ -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();
}
};

@ -0,0 +1,3 @@
pub const pins = @import("pins.zig");
pub fn init() void {}

@ -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
}
};
Loading…
Cancel
Save