From a6adc1d9da8fd834e2d0cd5d82288e7324490391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Quei=C3=9Fner?= Date: Fri, 1 Apr 2022 10:30:13 +0200 Subject: [PATCH] Implements a generic gpio button driver with basic debouncing. (#41) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Felix "xq" Queißner --- src/drivers/button.zig | 67 ++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 6 ++++ 2 files changed, 73 insertions(+) create mode 100644 src/drivers/button.zig diff --git a/src/drivers/button.zig b/src/drivers/button.zig new file mode 100644 index 0000000..be519db --- /dev/null +++ b/src/drivers/button.zig @@ -0,0 +1,67 @@ +const std = @import("std"); +const micro = @import("microzig"); + +pub const Event = enum { + /// Nothing has changed. + idle, + + /// The button was pressed. Will only trigger once per press. + /// Use `Button.isPressed()` to check if the button is currently held. + pressed, + + /// The button was released. Will only trigger once per release. + /// Use `Button.isPressed()` to check if the button is currently held. + released, +}; + +pub fn Button( + /// The GPIO pin the button is connected to. Will be initialized when calling Button.init + comptime gpio: type, + /// The active state for the button. Use `.high` for active-high, `.low` for active-low. + comptime active_state: micro.gpio.State, + /// Optional filter depth for debouncing. If `null` is passed, 16 samples are used to debounce the button, + /// otherwise the given number of samples is used. + comptime filter_depth: ?comptime_int, +) type { + return struct { + const Self = @This(); + const DebounceFilter = std.meta.Int(.unsigned, filter_depth orelse 16); + + debounce: DebounceFilter, + state: micro.gpio.State, + + pub fn init() Self { + gpio.init(); + return Self{ + .debounce = 0, + .state = gpio.read(), + }; + } + + /// Polls for the button state. Returns the change event for the button if any. + pub fn poll(self: *Self) Event { + const state = gpio.read(); + const active_unfiltered = (state == active_state); + + const previous_debounce = self.debounce; + self.debounce <<= 1; + if (active_unfiltered) { + self.debounce |= 1; + } + + if (active_unfiltered and previous_debounce == 0) { + return .pressed; + } else if (!active_unfiltered and self.debounce == 0 and previous_debounce != 0) { + return .released; + } else { + return .idle; + } + } + + /// Returns `true` when the button is pressed. + /// Will only be updated when `poll` is regularly called. + pub fn isPressed(self: *Self) bool { + return (self.debounce != 0); + } + }; +} diff --git a/src/main.zig b/src/main.zig index 406283a..8b565a8 100644 --- a/src/main.zig +++ b/src/main.zig @@ -189,4 +189,10 @@ pub const drivers = struct { .path = .{ .path = root_path ++ "drivers/quadrature.zig" }, .dependencies = &.{pkgs.microzig}, }; + + pub const button = std.build.Pkg{ + .name = "microzig.button", + .path = .{ .path = root_path ++ "drivers/button.zig" }, + .dependencies = &.{pkgs.microzig}, + }; };