Complete Gd32f103 Support (#203)

* Implement gpio hal like to the stm32f103.
* Add blinky test
wch-ch32v003
Daniele Basile 3 months ago committed by GitHub
parent 301ac942e7
commit 43b2a539c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -9,7 +9,7 @@ fn path(comptime suffix: []const u8) std.Build.LazyPath {
}
const hal = .{
.root_source_file = path("/src/hals/GD32VF103.zig"),
.root_source_file = path("/src/hals/GD32VF103/hal.zig"),
};
pub const chips = struct {

@ -0,0 +1,158 @@
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 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.CTL0.raw &= ~(@as(u32, 0b1111) << offset);
port.CTL0.raw |= config << offset;
} else {
const offset = (@as(u5, gpio.number) - 8) << 2;
port.CTL1.raw &= ~(@as(u32, 0b1111) << offset);
port.CTL1.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 => @panic("The STM32 only has ports 0..6 (A..G)"),
6 => @panic("The STM32 only has ports 0..6 (A..G)"),
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.BOP.raw = gpio.mask(),
.down => port.BC.raw = gpio.mask(),
}
}
pub inline fn read(gpio: Pin) u1 {
const port = gpio.get_port();
return if (port.ISTAT.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.OCTL.raw &= ~gpio.mask(),
1 => port.OCTL.raw |= gpio.mask(),
}
}
pub inline fn toggle(gpio: Pin) void {
var port = gpio.get_port();
port.OCTL.raw ^= gpio.mask();
}
};

@ -0,0 +1,7 @@
pub const pins = @import("pins.zig");
pub const clock_frequencies = .{
.cpu = 8_000_000, // 8 MHz
};
pub fn init() void {}

@ -0,0 +1,233 @@
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 RCU = microzig.chip.peripherals.RCU;
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: ?[:0]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,
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,
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 {
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);
RCU.APB2EN.raw |= bit;
// Delay after setting
_ = RCU.APB2EN.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
}
};

@ -0,0 +1,62 @@
const micro = @import("microzig");
const peripherals = micro.chip.peripherals;
const UART3 = peripherals.UART3;
const UART4 = peripherals.UART4;
pub const uart = struct {
pub const DataBits = enum(u2) {
five = 0,
six = 1,
seven = 2,
eight = 3,
};
pub const StopBits = enum(u1) {
one = 0,
two = 1,
};
pub const Parity = enum(u2) {
odd = 0,
even = 1,
mark = 2,
space = 3,
};
};
pub fn Uart(comptime index: usize, comptime pins: micro.uart.Pins) type {
if (pins.tx != null or pins.rx != null)
@compileError("TODO: custom pins are not currently supported");
return struct {
const UARTn = switch (index) {
0 => UART3,
1 => UART4,
else => @compileError("GD32VF103 has 2 UARTs available."),
};
const Self = @This();
pub fn init(config: micro.uart.Config) !Self {
_ = config;
return Self{};
}
pub fn can_write(self: Self) bool {
_ = self;
return false;
}
pub fn tx(self: Self, ch: u8) void {
_ = ch;
while (!self.can_write()) {} // Wait for Previous transmission
}
pub fn can_read(self: Self) bool {
_ = self;
return false;
}
pub fn rx(self: Self) u8 {
while (!self.can_read()) {} // Wait till the data is received
return 1; // Read received data
}
};
}

@ -6,6 +6,7 @@ const available_examples = [_]Example{
.{ .target = gd32.chips.gd32vf103xb, .name = "gd32vf103xb", .file = "src/empty.zig" },
.{ .target = gd32.chips.gd32vf103x8, .name = "gd32vf103x8", .file = "src/empty.zig" },
.{ .target = gd32.boards.sipeed.longan_nano, .name = "sipeed-longan_nano", .file = "src/empty.zig" },
.{ .target = gd32.boards.sipeed.longan_nano, .name = "sipeed-longan_nano_blinky", .file = "src/blinky.zig" },
};
pub fn build(b: *std.Build) void {
@ -32,7 +33,7 @@ pub fn build(b: *std.Build) void {
microzig.install_firmware(b, firmware, .{});
// For debugging, we also always install the firmware as an ELF file
microzig.install_firmware(b, firmware, .{ .format = .elf });
microzig.install_firmware(b, firmware, .{ .format = .bin });
}
}

@ -0,0 +1,30 @@
const std = @import("std");
const microzig = @import("microzig");
const gd32 = microzig.hal;
const pin_config = gd32.pins.GlobalConfiguration{
.GPIOC = .{
.PIN13 = .{ .name = "led", .mode = .{ .output = .general_purpose_push_pull } },
},
.GPIOA = .{
.PIN1 = .{ .name = "green", .mode = .{ .output = .general_purpose_push_pull } },
.PIN2 = .{ .name = "blue", .mode = .{ .output = .general_purpose_push_pull } },
},
};
pub fn main() !void {
const pins = pin_config.apply();
pins.green.put(0);
pins.blue.put(1);
while (true) {
var i: u32 = 0;
while (i < 800_000) {
asm volatile ("nop");
i += 1;
}
pins.led.toggle();
pins.green.toggle();
pins.blue.toggle();
}
}
Loading…
Cancel
Save