Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions core/src/core/experimental/uart.zig
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,13 @@ pub const Pins = struct {
rx: ?type = null,
};

/// A UART configuration. The config defaults to the *8N1* setting, so "8 data bits, no parity, 1 stop bit" which is the
/// most common serial format.
/// A UART configuration.
pub const Config = struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change this? Anyways everything in the experimental directory is not used at the moment

/// TODO: Make this optional, to support STM32F303 et al. auto baud-rate detection?
baud_rate: u32,
stop_bits: StopBits = .one,
stop_bits: StopBits,
parity: ?Parity = null,
data_bits: DataBits = .eight,
data_bits: DataBits,
};

// TODO: comptime verify that the enums are valid
Expand Down
45 changes: 27 additions & 18 deletions examples/stmicro/stm32/src/blinky.zig
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,35 @@ pub fn main() !void {
};
break :res .{ pins, all_leds };
} else if (comptime microzig.config.board_name != null and std.mem.eql(u8, microzig.config.board_name.?, "STM32F3DISCOVERY")) {
const pins = (stm32.pins.GlobalConfiguration{ .GPIOE = .{
.PE8 = .{ .mode = .{ .output = .push_pull } },
.PE9 = .{ .mode = .{ .output = .push_pull } },
.PE10 = .{ .mode = .{ .output = .push_pull } },
.PE11 = .{ .mode = .{ .output = .push_pull } },
.PE12 = .{ .mode = .{ .output = .push_pull } },
.PE13 = .{ .mode = .{ .output = .push_pull } },
.PE14 = .{ .mode = .{ .output = .push_pull } },
.PE15 = .{ .mode = .{ .output = .push_pull } },
} }).apply();
const pins = (stm32.pins.GlobalConfiguration{
.GPIOC = .{
.PIN4 = .{ .mode = .{ .alternate_function = .{ .afr = .AF7 } } },
.PIN5 = .{ .mode = .{ .alternate_function = .{ .afr = .AF7 } } },
},
.GPIOE = .{
.PIN8 = .{ .mode = .{ .output = .{ .resistor = .Floating, .o_type = .PushPull } } },
.PIN9 = .{ .mode = .{ .output = .{ .resistor = .Floating, .o_type = .PushPull } } },
.PIN10 = .{ .mode = .{ .output = .{ .resistor = .Floating, .o_type = .PushPull } } },
.PIN11 = .{ .mode = .{ .output = .{ .resistor = .Floating, .o_type = .PushPull } } },
.PIN12 = .{ .mode = .{ .output = .{ .resistor = .Floating, .o_type = .PushPull } } },
.PIN13 = .{ .mode = .{ .output = .{ .resistor = .Floating, .o_type = .PushPull } } },
.PIN14 = .{ .mode = .{ .output = .{ .resistor = .Floating, .o_type = .PushPull } } },
.PIN15 = .{ .mode = .{ .output = .{ .resistor = .Floating, .o_type = .PushPull } } },
},
}).apply();
const all_leds = .{
pins.PE8,
pins.PE9,
pins.PE10,
pins.PE11,
pins.PE12,
pins.PE13,
pins.PE14,
pins.PE15,
pins.PIN8,
pins.PIN9,
pins.PIN10,
pins.PIN11,
pins.PIN12,
pins.PIN13,
pins.PIN14,
pins.PIN15,
};

microzig.board.debug_write("test");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove this function please? Seems like it's broken anyways? 😆 Instead go for std.log and the uart logger if you need logging.


break :res .{ pins, all_leds };
} else {
@compileError("blinky is not (yet?) implemented for this target");
Expand Down
33 changes: 21 additions & 12 deletions port/stmicro/stm32/src/boards/STM32F3DISCOVERY.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub const microzig = @import("microzig");

pub const cpu_frequency = 8_000_000;
pub const rcc = microzig.hal.rcc;
pub const pins = microzig.hal.pins;

pub const pin_map = .{
// circle of LEDs, connected to GPIOE bits 8..15
Expand All @@ -23,15 +23,24 @@ pub const pin_map = .{
.LD6 = "PE15",
};

pub fn debug_write(string: []const u8) void {
const uart1 = microzig.core.experimental.Uart(1, .{}).get_or_init(.{
.baud_rate = 9600,
.data_bits = .eight,
.parity = null,
.stop_bits = .one,
}) catch unreachable;
pub fn init() void {
rcc.enable_hse(8_000_000);
rcc.enable_pll(.HSE, .Div1, .Mul4) catch {
@panic("PLL faile to enable");
};
rcc.select_pll_for_sysclk() catch {
@panic("Faile to select sysclk");
};
}

const writer = uart1.writer();
_ = writer.write(string) catch unreachable;
uart1.internal.txflush();
// Init should come first or the baud_rate would be too fast for the default HSI.
pub fn init_log() void {
_ = (pins.GlobalConfiguration{
.GPIOC = .{
.PIN4 = .{ .mode = .{ .alternate_function = .{ .afr = .AF7 } } },
.PIN5 = .{ .mode = .{ .alternate_function = .{ .afr = .AF7 } } },
},
}).apply();
const uart = try microzig.hal.uart.Uart(.UART1).init(.{ .baud_rate = 460800 });
microzig.hal.uart.init_logger(&uart);
}
197 changes: 7 additions & 190 deletions port/stmicro/stm32/src/hals/STM32F303.zig
Original file line number Diff line number Diff line change
Expand Up @@ -47,33 +47,20 @@ const std = @import("std");
const runtime_safety = std.debug.runtime_safety;

const microzig = @import("microzig");
pub const gpio = @import("STM32F303/gpio.zig");
pub const uart = @import("STM32F303/uart.zig");
pub const rcc = @import("STM32F303/rcc.zig");

const SPI1 = microzig.peripherals.SPI1;
const RCC = microzig.peripherals.RCC;
const USART1 = microzig.peripherals.USART1;
const RCC = microzig.chip.peripherals.RCC;

const GPIOA = microzig.peripherals.GPIOA;
const GPIOB = microzig.peripherals.GPIOB;
const GPIOC = microzig.peripherals.GPIOC;
const GPIOC = microzig.chip.peripherals.GPIOC;
const I2C1 = microzig.peripherals.I2C1;

pub const cpu = @import("cpu");

pub const clock = struct {
pub const Domain = enum {
cpu,
ahb,
apb1,
apb2,
};
};

// Default clock frequencies after reset, see top comment for calculation
pub const clock_frequencies = .{
.cpu = 8_000_000,
.ahb = 8_000_000,
.apb1 = 8_000_000,
.apb2 = 8_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.";

Expand All @@ -98,176 +85,6 @@ fn set_reg_field(reg: anytype, comptime field_name: anytype, value: anytype) voi
reg.write(temp);
}

pub const gpio = struct {
pub fn set_output(comptime pin: type) void {
set_reg_field(RCC.AHBENR, "IOP" ++ pin.gpio_port_name ++ "EN", 1);
set_reg_field(@field(pin.gpio_port, "MODER"), "MODER" ++ pin.suffix, 0b01);
}

pub fn set_input(comptime pin: type) void {
set_reg_field(RCC.AHBENR, "IOP" ++ pin.gpio_port_name ++ "EN", 1);
set_reg_field(@field(pin.gpio_port, "MODER"), "MODER" ++ pin.suffix, 0b00);
}

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 write(comptime pin: type, state: microzig.gpio.State) void {
switch (state) {
.low => set_reg_field(pin.gpio_port.BRR, "BR" ++ pin.suffix, 1),
.high => set_reg_field(pin.gpio_port.BSRR, "BS" ++ pin.suffix, 1),
}
}
};

pub const uart = struct {
pub const DataBits = enum(u4) {
seven = 7,
eight = 8,
};

/// uses the values of USART_CR2.STOP
pub const StopBits = enum(u2) {
one = 0b00,
half = 0b01,
two = 0b10,
one_and_half = 0b11,
};

/// uses the values of USART_CR1.PS
pub const Parity = enum(u1) {
even = 0,
odd = 1,
};
};

pub fn Uart(comptime index: usize, comptime source_pins: microzig.uart.Pins) type {
if (!(index == 1)) @compileError("TODO: only USART1 is currently supported");
if (source_pins.tx != null or source_pins.rx != null)
@compileError("TODO: custom pins are not currently supported");

return struct {
parity_read_mask: u8,

const Self = @This();

pub fn init(config: microzig.uart.Config) !Self {
// The following must all be written when the USART is disabled (UE=0).
if (USART1.CR1.read().UE == 1)
@panic("Trying to initialize USART1 while it is already enabled");
// LATER: Alternatively, set UE=0 at this point? Then wait for something?
// Or add a destroy() function which disables the USART?

// enable the USART1 clock
RCC.APB2ENR.modify(.{ .USART1EN = 1 });
// enable GPIOC clock
RCC.AHBENR.modify(.{ .IOPCEN = 1 });
// set PC4+PC5 to alternate function 7, USART1_TX + USART1_RX
GPIOC.MODER.modify(.{ .MODER4 = 0b10, .MODER5 = 0b10 });
GPIOC.AFRL.modify(.{ .AFRL4 = 7, .AFRL5 = 7 });

// clear USART1 configuration to its default
USART1.CR1.raw = 0;
USART1.CR2.raw = 0;
USART1.CR3.raw = 0;

// set word length
// Per the reference manual, M[1:0] means
// - 00: 8 bits (7 data + 1 parity, or 8 data), probably the chip default
// - 01: 9 bits (8 data + 1 parity)
// - 10: 7 bits (7 data)
// So M1==1 means "7-bit mode" (in which
// "the Smartcard mode, LIN master mode and Auto baud rate [...] are not supported");
// and M0==1 means 'the 9th bit (not the 8th bit) is the parity bit'.
const m1: u1 = if (config.data_bits == .seven and config.parity == null) 1 else 0;
const m0: u1 = if (config.data_bits == .eight and config.parity != null) 1 else 0;
// Note that .padding0 = bit 28 = .M1 (.svd file bug?), and .M == .M0.
USART1.CR1.modify(.{ .padding0 = m1, .M = m0 });

// set parity
if (config.parity) |parity| {
USART1.CR1.modify(.{ .PCE = 1, .PS = @intFromEnum(parity) });
} else USART1.CR1.modify(.{ .PCE = 0 }); // no parity, probably the chip default

// set number of stop bits
USART1.CR2.modify(.{ .STOP = @intFromEnum(config.stop_bits) });

// set the baud rate
// TODO: Do not use the _board_'s frequency, but the _U(S)ARTx_ frequency
// from the chip, which can be affected by how the board configures the chip.
// In our case, these are accidentally the same at chip reset,
// if the board doesn't configure e.g. an HSE external crystal.
// TODO: Do some checks to see if the baud rate is too high (or perhaps too low)
// TODO: Do a rounding div, instead of a truncating div?
const usartdiv = @as(u16, @intCast(@divTrunc(microzig.clock.get().apb1, config.baud_rate)));
USART1.BRR.raw = usartdiv;
// Above, ignore the BRR struct fields DIV_Mantissa and DIV_Fraction,
// those seem to be for another chipset; .svd file bug?
// TODO: We assume the default OVER8=0 configuration above.

// enable USART1, and its transmitter and receiver
USART1.CR1.modify(.{ .UE = 1 });
USART1.CR1.modify(.{ .TE = 1 });
USART1.CR1.modify(.{ .RE = 1 });

// For code simplicity, at cost of one or more register reads,
// we read back the actual configuration from the registers,
// instead of using the `config` values.
return read_from_registers();
}

pub fn get_or_init(config: microzig.uart.Config) !Self {
if (USART1.CR1.read().UE == 1) {
// UART1 already enabled, don't reinitialize and disturb things;
// instead read and use the actual configuration.
return read_from_registers();
} else return init(config);
}

fn read_from_registers() Self {
const cr1 = USART1.CR1.read();
// As documented in `init()`, M0==1 means 'the 9th bit (not the 8th bit) is the parity bit'.
// So we always mask away the 9th bit, and if parity is enabled and it is in the 8th bit,
// then we also mask away the 8th bit.
return Self{ .parity_read_mask = if (cr1.PCE == 1 and cr1.M == 0) 0x7F else 0xFF };
}

pub fn can_write(self: Self) bool {
_ = self;
return switch (USART1.ISR.read().TXE) {
1 => true,
0 => false,
};
}

pub fn tx(self: Self, ch: u8) void {
while (!self.can_write()) {} // Wait for Previous transmission
USART1.TDR.modify(ch);
}

pub fn txflush(_: Self) void {
while (USART1.ISR.read().TC == 0) {}
}

pub fn can_read(self: Self) bool {
_ = self;
return switch (USART1.ISR.read().RXNE) {
1 => true,
0 => false,
};
}

pub fn rx(self: Self) u8 {
while (!self.can_read()) {} // Wait till the data is received
const data_with_parity_bit: u9 = USART1.RDR.read().RDR;
return @as(u8, @intCast(data_with_parity_bit & self.parity_read_mask));
}
};
}

const enable_stm32f303_debug = false;

fn debug_print(comptime format: []const u8, args: anytype) void {
Expand Down
Loading
Loading