diff --git a/src/core/gpio.zig b/src/core/gpio.zig index 4008b89..44e3729 100644 --- a/src/core/gpio.zig +++ b/src/core/gpio.zig @@ -13,6 +13,10 @@ pub const Mode = enum { pub const State = enum(u1) { low = 0, high = 1, + + pub fn value(self: State) u1 { + return @enumToInt(self); + } }; pub const Drive = enum(u1) { diff --git a/src/core/microzig.zig b/src/core/microzig.zig index 7d82417..8df10fc 100644 --- a/src/core/microzig.zig +++ b/src/core/microzig.zig @@ -46,13 +46,30 @@ pub const debug = @import("debug.zig"); pub const mmio = @import("mmio.zig"); -/// The microzig panic handler. Will disable interrupts and loop endlessly. -/// Export this symbol from your main file to enable microzig: -/// ``` -/// const micro = @import("microzig"); -/// pub const panic = micro.panic; -/// ``` -pub fn panic(message: []const u8, maybe_stack_trace: ?*std.builtin.StackTrace) noreturn { +// Allow app to override the panic handler +pub const panic = if (@hasDecl(app, "panic")) + app.panic +else + microzig_panic; + +// Conditionally export log_level if the app has it defined. +usingnamespace if (@hasDecl(app, "log_level")) + struct { + pub const log_level = app.log_level; + } +else + struct {}; + +// Conditionally export log() if the app has it defined. +usingnamespace if (@hasDecl(app, "log")) + struct { + pub const log = app.log; + } +else + struct {}; + +/// The microzig default panic handler. Will disable interrupts and loop endlessly. +pub fn microzig_panic(message: []const u8, maybe_stack_trace: ?*std.builtin.StackTrace) noreturn { // utilize logging functions std.log.err("microzig PANIC: {s}", .{message}); diff --git a/src/drivers/quadrature.zig b/src/drivers/quadrature.zig new file mode 100644 index 0000000..c76ecab --- /dev/null +++ b/src/drivers/quadrature.zig @@ -0,0 +1,50 @@ +const micro = @import("microzig"); + +pub const Event = enum { + /// No change since the last decoding happened + idle, + /// The quadrature signal incremented a step. + increment, + /// The quadrature signal decremented a step. + decrement, + /// The quadrature signal skipped a sequence point and entered a invalid state. + @"error", +}; + +pub fn Decoder(comptime pin_a: type, comptime pin_b: type) type { + return struct { + const Self = @This(); + + last_a: micro.gpio.State, + last_b: micro.gpio.State, + + pub fn init() Self { + pin_a.init(); + pin_b.init(); + return Self{ + .last_a = pin_a.read(), + .last_b = pin_b.read(), + }; + } + + pub fn tick(self: *Self) Event { + var a = pin_a.read(); + var b = pin_b.read(); + defer self.last_a = a; + defer self.last_b = b; + + const enable = a.value() ^ b.value() ^ self.last_a.value() ^ self.last_b.value(); + const direction = a.value() ^ self.last_b.value(); + + if (enable != 0) { + if (direction != 0) { + return .increment; + } else { + return .decrement; + } + } else { + return .idle; + } + } + }; +} diff --git a/src/main.zig b/src/main.zig index 721a405..406283a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -181,3 +181,12 @@ const pkgs = struct { .path = .{ .path = root_path ++ "core/import-package.zig" }, }; }; + +/// Generic purpose drivers shipped with microzig +pub const drivers = struct { + pub const quadrature = std.build.Pkg{ + .name = "microzig.quadrature", + .path = .{ .path = root_path ++ "drivers/quadrature.zig" }, + .dependencies = &.{pkgs.microzig}, + }; +}; diff --git a/src/modules/cpus/avr/avr5.zig b/src/modules/cpus/avr/avr5.zig index cf79b74..659157d 100644 --- a/src/modules/cpus/avr/avr5.zig +++ b/src/modules/cpus/avr/avr5.zig @@ -52,27 +52,10 @@ pub const vector_table = blk: { const new_insn = if (has_interrupts) overload: { if (@hasDecl(microzig.app.interrupts, field.name)) { const handler = @field(microzig.app.interrupts, field.name); - const calling_convention = switch (@typeInfo(@TypeOf(@field(microzig.app.interrupts, field.name)))) { - .Fn => |info| info.calling_convention, - else => @compileError("Declarations in 'interrupts' namespace must all be functions. '" ++ field.name ++ "' is not a function"), - }; - - const exported_fn = switch (calling_convention) { - .Unspecified => struct { - fn wrapper() callconv(.Signal) void { - if (calling_convention == .Unspecified) // TODO: workaround for some weird stage1 bug - @call(.{ .modifier = .always_inline }, handler, .{}); - } - }.wrapper, - .Signal => handler, - .Interrupt => handler, - else => @compileError("Calling conventions for interrupts must be 'Interrupt', 'Signal', or unspecified. The signal calling convention leaves global interrupts disabled during the ISR, where the interrupt calling conventions enables global interrupts for nested ISRs."), - }; - - const exported_name = "microzig_isr_" ++ field.name; - const options = .{ .name = exported_name, .linkage = .Strong }; - @export(exported_fn, options); - break :overload "jmp " ++ exported_name; + + const isr = makeIsrHandler(field.name, handler); + + break :overload "jmp " ++ isr.exported_name; } else { break :overload "jmp microzig_unhandled_vector"; } @@ -90,6 +73,31 @@ pub const vector_table = blk: { break :blk T._start; }; +fn makeIsrHandler(comptime name: []const u8, comptime func: anytype) type { + const calling_convention = switch (@typeInfo(@TypeOf(func))) { + .Fn => |info| info.calling_convention, + else => @compileError("Declarations in 'interrupts' namespace must all be functions. '" ++ name ++ "' is not a function"), + }; + + switch (calling_convention) { + .Unspecified, .Signal, .Interrupt => {}, + else => @compileError("Calling conventions for interrupts must be 'Interrupt', 'Signal', or unspecified. The signal calling convention leaves global interrupts disabled during the ISR, where the interrupt calling conventions enables global interrupts for nested ISRs."), + } + + return struct { + pub const exported_name = "microzig_isr_" ++ name; + + pub fn isr_vector() callconv(.Signal) void { + @call(.{ .modifier = .always_inline }, func, .{}); + } + + comptime { + const options = .{ .name = exported_name, .linkage = .Strong }; + @export(isr_vector, options); + } + }; +} + pub const startup_logic = struct { export fn microzig_unhandled_vector() callconv(.Naked) noreturn { @panic("Unhandled interrupt");