diff --git a/build.zig b/build.zig index 1c34f1f..32230f7 100644 --- a/build.zig +++ b/build.zig @@ -21,6 +21,7 @@ pub fn build(b: *std.build.Builder) void { const all_tests = [_]Test{ Test{ .name = "minimal", .source = "tests/minimal.zig" }, Test{ .name = "blinky", .source = "tests/blinky.zig" }, + Test{ .name = "uart-sync", .source = "tests/uart-sync.zig" }, }; const filter = b.option(std.Target.Cpu.Arch, "filter-target", "Filters for a certain cpu target"); diff --git a/src/core/clock.zig b/src/core/clock.zig new file mode 100644 index 0000000..538e492 --- /dev/null +++ b/src/core/clock.zig @@ -0,0 +1,20 @@ +const std = @import("std"); +const root = @import("root"); + +/// Is `true` when microzig has a clock frequency available. +pub const has_clock = @hasDecl(root, "cpu_frequency"); + +/// Returns `true` when the frequency can change at runtime. +pub const is_dynamic = has_clock and !@typeInfo(@TypeOf(&root.cpu_frequency)).Pointer.is_const; + +/// Ensures that microzig has a clock available. This will @compileError when no clock is available, otherwise, it will be a no-op. +pub fn ensure() void { + if (!has_clock) + @compileError("microzig requires the clock frequency to perform this operation. Please export a const or var cpu_frequency from your root file that contains the cpu frequency in hertz!"); +} + +/// Returns the current cpu frequency in hertz. +pub fn get() callconv(.Inline) u32 { + ensure(); + return root.cpu_frequency; +} diff --git a/src/core/microzig.zig b/src/core/microzig.zig index f3d23ad..2ad2514 100644 --- a/src/core/microzig.zig +++ b/src/core/microzig.zig @@ -15,12 +15,18 @@ pub const cpu = chip.cpu; /// Module that helps with interrupt handling. pub const interrupts = @import("interrupts.zig"); +/// Module that provides clock related functions +pub const clock = @import("clock.zig"); + const gpio = @import("gpio.zig"); pub const Gpio = gpio.Gpio; const pin = @import("pin.zig"); pub const Pin = pin.Pin; +const uart = @import("uart.zig"); +pub const Uart = uart.Uart; + /// The microzig panic handler. Will disable interrupts and loop endlessly. /// Export this symbol from your main file to enable microzig: /// ``` @@ -72,7 +78,7 @@ export fn microzig_main() noreturn { // - Compute maximum size on the type of "err" // - Do not emit error names when std.builtin.strip is set. var msg: [64]u8 = undefined; - @panic(std.fmt.bufPrint(&msg, "main() returned error {s}", .{@tagName(err)}) catch @panic("main() returned error.")); + @panic(std.fmt.bufPrint(&msg, "main() returned error {s}", .{@errorName(err)}) catch @panic("main() returned error.")); }; } else { main(); diff --git a/src/core/mmio.zig b/src/core/mmio.zig index d433007..d779928 100644 --- a/src/core/mmio.zig +++ b/src/core/mmio.zig @@ -1,3 +1,4 @@ + const std = @import("std"); pub fn mmio(addr: usize, comptime size: u8, comptime PackedT: type) *volatile MMIO(size, PackedT) { diff --git a/src/core/uart.zig b/src/core/uart.zig new file mode 100644 index 0000000..e400010 --- /dev/null +++ b/src/core/uart.zig @@ -0,0 +1,73 @@ +const std = @import("std"); +const micro = @import("microzig.zig"); +const chip = @import("chip"); + +/// 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. +pub const Config = struct { + baud_rate: u32, + stop_bits: StopBits = .one, + parity: Parity = .none, + data_bits: DataBits = .@"8", +}; + +const DataBits = enum { + @"5", + @"6", + @"7", + @"8", + @"9", +}; + +const StopBits = enum { one, two }; + +const Parity = enum { + none, + even, + odd, + mark, + space, +}; + +pub fn Uart(comptime index: usize) type { + return struct { + const Self = @This(); + + /// Initializes the UART with the given config and returns a handle to the uart. + pub fn init(config: Config) Self { + micro.clock.ensure(); + return Self{}; + } + + pub fn reader(self: Self) Reader { + return Reader{ .context = self }; + } + + pub fn writer(self: Self) Writer { + return Writer{ .context = self }; + } + + const ReadError = error{ + /// The input buffer received a byte while the receive fifo is already full. + /// Devices with no fifo fill overrun as soon as a second byte arrives. + Overrun, + /// A byte with an invalid parity bit was received. + ParityError, + /// The stop bit of our byte was not valid. + FramingError, + /// The break interrupt error will happen when RXD is logic zero for + /// the duration of a full byte. + BreakInterrupt, + }; + const WriteError = error{}; + pub const Reader = std.io.Reader(Self, ReadError, readSome); + pub const Writer = std.io.Writer(Self, WriteError, writeSome); + + fn readSome(self: Self, buffer: []u8) ReadError!usize { + return 0; + } + fn writeSome(self: Self, buffer: []const u8) WriteError!usize { + return 0; + } + }; +} diff --git a/src/modules/chips/lpc1768/lpc1768.zig b/src/modules/chips/lpc1768/lpc1768.zig index 843979f..dbead2e 100644 --- a/src/modules/chips/lpc1768/lpc1768.zig +++ b/src/modules/chips/lpc1768/lpc1768.zig @@ -61,14 +61,3 @@ pub const gpio = struct { } } }; - -// pub const GPIO = extern struct { -// dir: u32, // 0x00 -// pad: [3]u32, -// mask: u32, // 0x10 -// pin: u32, // 0x14, -// set: u32, // 0x18, -// clr: u32, // 0x1C, -// }; - -// pub const gpio_base = 0x2009_C000; diff --git a/tests/blinky.zig b/tests/blinky.zig index 3dece30..d5614c7 100644 --- a/tests/blinky.zig +++ b/tests/blinky.zig @@ -6,13 +6,13 @@ pub const panic = micro.panic; // Configures the led_pin to a hardware pin const led_pin = switch (@import("builtin").cpu.arch) { .avr => if (micro.config.has_board) - micro.Pin("D13") + micro.Pin("D13") // Use D13 from Arduino Nano else - micro.Pin("PB5"), + micro.Pin("PB5"), // Use PB5 on raw ATmega328p .arm => if (micro.config.has_board) - micro.Pin("LED-1") + micro.Pin("LED-1") // Use LED-1 from mbed LPC1768 else - micro.Pin("P1.18"), + micro.Pin("P1.18"), // Use P1.18 on raw LPC1768 else => @compileError("Unsupported platform!"), }; diff --git a/tests/uart-sync.zig b/tests/uart-sync.zig new file mode 100644 index 0000000..3734e0e --- /dev/null +++ b/tests/uart-sync.zig @@ -0,0 +1,31 @@ +const micro = @import("microzig"); + +// this will instantiate microzig and pull in all dependencies +pub const panic = micro.panic; + +// Configures the led_pin to a hardware pin +const uart_txd_pin = micro.Pin("P0.15"); +const uart_rxd_pin = micro.Pin("P0.16"); + +pub const cpu_frequency: u32 = 10_000_000; // 10 MHz + +pub fn main() !void { + var debug_port = micro.Uart(0).init(.{ + .baud_rate = 9600, + .stop_bits = .one, + .parity = .none, // { none, even, odd, mark, space } + .data_bits = .@"8", // 5, 6, 7, 8, or 9 data bits + }); + + var out = debug_port.writer(); + var in = debug_port.reader(); + + try out.writeAll("Please enter a sentence:\r\n"); + + while (true) { + try out.writeAll("> "); + var line_buffer: [64]u8 = undefined; + const line = (try in.readUntilDelimiterOrEof(&line_buffer, '\r')).?; + try out.writeAll(line); + } +}