aboutsummaryrefslogtreecommitdiff
path: root/pw_digital_io/public/pw_digital_io/digital_io.h
diff options
context:
space:
mode:
Diffstat (limited to 'pw_digital_io/public/pw_digital_io/digital_io.h')
-rw-r--r--pw_digital_io/public/pw_digital_io/digital_io.h541
1 files changed, 541 insertions, 0 deletions
diff --git a/pw_digital_io/public/pw_digital_io/digital_io.h b/pw_digital_io/public/pw_digital_io/digital_io.h
new file mode 100644
index 000000000..ba8516b01
--- /dev/null
+++ b/pw_digital_io/public/pw_digital_io/digital_io.h
@@ -0,0 +1,541 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_assert/check.h"
+#include "pw_digital_io/internal/conversions.h"
+#include "pw_function/function.h"
+#include "pw_result/result.h"
+#include "pw_status/status.h"
+#include "pw_status/try.h"
+
+namespace pw::digital_io {
+
+// The logical state of a digital line.
+enum class State : bool {
+ kActive = true,
+ kInactive = false,
+};
+
+// The triggering configuration for an interrupt handler.
+enum class InterruptTrigger : int {
+ // Trigger on transition from kInactive to kActive.
+ kActivatingEdge,
+ // Trigger on transition from kActive to kInactive.
+ kDeactivatingEdge,
+ // Trigger on any state transition between kActive and kInactive.
+ kBothEdges,
+};
+
+// Interrupt handling function. The argument contains the latest known state of
+// the line. It is backend-specific if, when, and how this state is updated.
+using InterruptHandler = ::pw::Function<void(State sampled_state)>;
+
+// A digital I/O line that may support input, output, and interrupts, but makes
+// no guarantees about whether any operations are supported. You must check the
+// various provides_* flags before calling optional methods. Unsupported methods
+// invoke PW_CRASH.
+//
+// All methods are potentially blocking. Unless otherwise specified, access from
+// multiple threads to a single line must be externally synchronized - for
+// example using pw::Borrowable. Unless otherwise specified, none of the methods
+// are safe to call from an interrupt handler. Therefore, this abstraction may
+// not be suitable for bitbanging and other low-level uses of GPIO.
+//
+// Note that the initial state of a line is not guaranteed to be consistent with
+// either the "enabled" or "disabled" state. Users of the API who need to ensure
+// the line is disabled (ex. output not driving the line) should call Disable.
+//
+// This class should almost never be used in APIs directly. Instead, use one of
+// the derived classes that explicitly supports the functionality that your
+// API needs.
+//
+// This class cannot be extended directly. Instead, extend one of the
+// derived classes that explicitly support the functionality that you want to
+// implement.
+//
+class DigitalIoOptional {
+ public:
+ virtual ~DigitalIoOptional() = default;
+
+ // True if input (getting state) is supported.
+ constexpr bool provides_input() const { return config_.input; }
+ // True if output (setting state) is supported.
+ constexpr bool provides_output() const { return config_.output; }
+ // True if interrupt handlers can be registered.
+ constexpr bool provides_interrupt() const { return config_.interrupt; }
+
+ // Get the state of the line.
+ //
+ // This method is not thread-safe and cannot be used in interrupt handlers.
+ //
+ // Returns:
+ //
+ // OK - an active or inactive state.
+ // FAILED_PRECONDITION - The line has not been enabled.
+ // Other status codes as defined by the backend.
+ //
+ Result<State> GetState() { return DoGetState(); }
+
+ // Set the state of the line.
+ //
+ // Callers are responsible to wait for the voltage level to settle after this
+ // call returns.
+ //
+ // This method is not thread-safe and cannot be used in interrupt handlers.
+ //
+ // Returns:
+ //
+ // OK - the state has been set.
+ // FAILED_PRECONDITION - The line has not been enabled.
+ // Other status codes as defined by the backend.
+ //
+ Status SetState(State state) { return DoSetState(state); }
+
+ // Check if the line is in the active state.
+ //
+ // The line is in the active state when GetState() returns State::kActive.
+ //
+ // This method is not thread-safe and cannot be used in interrupt handlers.
+ //
+ // Returns:
+ //
+ // OK - true if the line is in the active state, otherwise false.
+ // FAILED_PRECONDITION - The line has not been enabled.
+ // Other status codes as defined by the backend.
+ //
+ Result<bool> IsStateActive() {
+ PW_TRY_ASSIGN(const State state, GetState());
+ return state == State::kActive;
+ }
+
+ // Sets the line to the active state. Equivalent to SetState(State::kActive).
+ //
+ // Callers are responsible to wait for the voltage level to settle after this
+ // call returns.
+ //
+ // This method is not thread-safe and cannot be used in interrupt handlers.
+ //
+ // Returns:
+ //
+ // OK - the state has been set.
+ // FAILED_PRECONDITION - The line has not been enabled.
+ // Other status codes as defined by the backend.
+ //
+ Status SetStateActive() { return SetState(State::kActive); }
+
+ // Sets the line to the inactive state. Equivalent to
+ // SetState(State::kInactive).
+ //
+ // Callers are responsible to wait for the voltage level to settle after this
+ // call returns.
+ //
+ // This method is not thread-safe and cannot be used in interrupt handlers.
+ //
+ // Returns:
+ //
+ // OK - the state has been set.
+ // FAILED_PRECONDITION - The line has not been enabled.
+ // Other status codes as defined by the backend.
+ //
+ Status SetStateInactive() { return SetState(State::kInactive); }
+
+ // Set an interrupt handler to execute when an interrupt is triggered, and
+ // Configure the condition for triggering the interrupt.
+ //
+ // The handler is executed in a backend-specific context - this may be a
+ // system interrupt handler or a shared notification thread. Do not do any
+ // blocking or expensive work in the handler. The only universally safe
+ // operations are the IRQ-safe functions on pw_sync primitives.
+ //
+ // In particular, it is NOT safe to get the state of a DigitalIo line - either
+ // from this line or any other DigitalIoOptional instance - inside the
+ // handler.
+ //
+ // This method is not thread-safe and cannot be used in interrupt handlers.
+ //
+ // Precondition: no handler is currently set.
+ //
+ // Returns:
+ // OK - the interrupt handler was configured.
+ // INVALID_ARGUMENT - handler is empty.
+ // Other status codes as defined by the backend.
+ //
+ Status SetInterruptHandler(InterruptTrigger trigger,
+ InterruptHandler&& handler) {
+ if (handler == nullptr) {
+ return Status::InvalidArgument();
+ }
+ return DoSetInterruptHandler(trigger, std::move(handler));
+ }
+
+ // Clear the interrupt handler and disable interrupts if enabled.
+ //
+ // This method is not thread-safe and cannot be used in interrupt handlers.
+ //
+ // Returns:
+ // OK - the itnerrupt handler was cleared.
+ // Other status codes as defined by the backend.
+ //
+ Status ClearInterruptHandler() {
+ return DoSetInterruptHandler(InterruptTrigger::kActivatingEdge, nullptr);
+ }
+
+ // Enable interrupts which will trigger the interrupt handler.
+ //
+ // This method is not thread-safe and cannot be used in interrupt handlers.
+ //
+ // Precondition: a handler has been set using SetInterruptHandler.
+ //
+ // Returns:
+ // OK - the interrupt handler was configured.
+ // FAILED_PRECONDITION - The line has not been enabled.
+ // Other status codes as defined by the backend.
+ //
+ Status EnableInterruptHandler() { return DoEnableInterruptHandler(true); }
+
+ // Disable the interrupt handler. This is a no-op if interrupts are disabled.
+ //
+ // This method can be called inside the interrupt handler for this line
+ // without any external synchronization. However, the exact behavior is
+ // backend-specific. There may be queued events that will trigger the handler
+ // again after this call returns.
+ //
+ // Returns:
+ // OK - the interrupt handler was configured.
+ // Other status codes as defined by the backend.
+ //
+ Status DisableInterruptHandler() { return DoEnableInterruptHandler(false); }
+
+ // Enable the line to initialize it into the default state as determined by
+ // the backend. This may enable pull-up/down resistors, drive the line high or
+ // low, etc. The line must be enabled before getting/setting the state
+ // or enabling interrupts.
+ //
+ // Callers are responsible to wait for the voltage level to settle after this
+ // call returns.
+ //
+ // This method is not thread-safe and cannot be used in interrupt handlers.
+ //
+ // Returns:
+ // OK - the line is enabled and ready for use.
+ // Other status codes as defined by the backend.
+ //
+ Status Enable() { return DoEnable(true); }
+
+ // Disable the line to power down any pull-up/down resistors and disconnect
+ // from any voltage sources. This is usually done to save power. Interrupt
+ // handlers are automatically disabled.
+ //
+ // This method is not thread-safe and cannot be used in interrupt handlers.
+ //
+ // Returns:
+ // OK - the line is disabled.
+ // Other status codes as defined by the backend.
+ //
+ Status Disable() { return DoEnable(false); }
+
+ private:
+ friend class DigitalInterrupt;
+ friend class DigitalIn;
+ friend class DigitalInInterrupt;
+ friend class DigitalOut;
+ friend class DigitalOutInterrupt;
+ friend class DigitalInOut;
+ friend class DigitalInOutInterrupt;
+
+ // Private constructor so that only friends can extend us.
+ constexpr DigitalIoOptional(internal::Provides config) : config_(config) {}
+
+ // Implemented by derived classes to provide different functionality.
+ // See the documentation of the public functions for requirements.
+ virtual Status DoEnable(bool enable) = 0;
+ virtual Result<State> DoGetState() = 0;
+ virtual Status DoSetState(State level) = 0;
+ virtual Status DoSetInterruptHandler(InterruptTrigger trigger,
+ InterruptHandler&& handler) = 0;
+ virtual Status DoEnableInterruptHandler(bool enable) = 0;
+
+ // The configuration of this line.
+ const internal::Provides config_;
+};
+
+// A digital I/O line that supports only interrupts.
+//
+// The input and output methods are hidden and must not be called.
+//
+// Use this class in APIs when only interrupt functionality is required.
+// Extend this class to implement a line that only supports interrupts.
+//
+class DigitalInterrupt
+ : public DigitalIoOptional,
+ public internal::Conversions<DigitalInterrupt, DigitalIoOptional> {
+ public:
+ // Available functionality
+ using DigitalIoOptional::ClearInterruptHandler;
+ using DigitalIoOptional::DisableInterruptHandler;
+ using DigitalIoOptional::EnableInterruptHandler;
+ using DigitalIoOptional::SetInterruptHandler;
+
+ protected:
+ constexpr DigitalInterrupt()
+ : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalInterrupt>()) {}
+
+ private:
+ // Unavailable functionality
+ using DigitalIoOptional::provides_input;
+ using DigitalIoOptional::provides_interrupt;
+ using DigitalIoOptional::provides_output;
+
+ using DigitalIoOptional::GetState;
+ using DigitalIoOptional::IsStateActive;
+ using DigitalIoOptional::SetState;
+ using DigitalIoOptional::SetStateActive;
+ using DigitalIoOptional::SetStateInactive;
+
+ // These overrides invoke PW_CRASH.
+ Status DoSetState(State) final;
+ Result<State> DoGetState() final;
+};
+
+// A digital I/O line that supports only input (getting state).
+//
+// The output and interrupt methods are hidden and must not be called.
+//
+// Use this class in APIs when only input functionality is required.
+// Extend this class to implement a line that only supports getting state.
+//
+class DigitalIn : public DigitalIoOptional,
+ public internal::Conversions<DigitalIn, DigitalIoOptional> {
+ public:
+ // Available functionality
+ using DigitalIoOptional::GetState;
+ using DigitalIoOptional::IsStateActive;
+
+ protected:
+ constexpr DigitalIn()
+ : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalIn>()) {}
+
+ private:
+ // Unavailable functionality
+ using DigitalIoOptional::provides_input;
+ using DigitalIoOptional::provides_interrupt;
+ using DigitalIoOptional::provides_output;
+
+ using DigitalIoOptional::ClearInterruptHandler;
+ using DigitalIoOptional::DisableInterruptHandler;
+ using DigitalIoOptional::EnableInterruptHandler;
+ using DigitalIoOptional::SetInterruptHandler;
+ using DigitalIoOptional::SetState;
+ using DigitalIoOptional::SetStateActive;
+ using DigitalIoOptional::SetStateInactive;
+
+ // These overrides invoke PW_CRASH.
+ Status DoSetState(State) final;
+ Status DoSetInterruptHandler(InterruptTrigger, InterruptHandler&&) final;
+ Status DoEnableInterruptHandler(bool) final;
+};
+
+// An input line that supports interrupts.
+//
+// The output methods are hidden and must not be called.
+//
+// Use in APIs when input and interrupt functionality is required.
+//
+// Extend this class to implement a line that supports input (getting state) and
+// listening for interrupts at the same time.
+//
+class DigitalInInterrupt
+ : public DigitalIoOptional,
+ public internal::Conversions<DigitalInInterrupt, DigitalIoOptional> {
+ public:
+ // Available functionality
+ using DigitalIoOptional::ClearInterruptHandler;
+ using DigitalIoOptional::DisableInterruptHandler;
+ using DigitalIoOptional::EnableInterruptHandler;
+ using DigitalIoOptional::GetState;
+ using DigitalIoOptional::IsStateActive;
+ using DigitalIoOptional::SetInterruptHandler;
+
+ protected:
+ constexpr DigitalInInterrupt()
+ : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalInInterrupt>()) {}
+
+ private:
+ // Unavailable functionality
+ using DigitalIoOptional::provides_input;
+ using DigitalIoOptional::provides_interrupt;
+ using DigitalIoOptional::provides_output;
+
+ using DigitalIoOptional::SetState;
+ using DigitalIoOptional::SetStateActive;
+ using DigitalIoOptional::SetStateInactive;
+
+ // These overrides invoke PW_CRASH.
+ Status DoSetState(State) final;
+};
+
+// A digital I/O line that supports only output (setting state).
+//
+// Input and interrupt functions are hidden and must not be called.
+//
+// Use in APIs when only output functionality is required.
+// Extend this class to implement a line that supports output only.
+//
+class DigitalOut : public DigitalIoOptional,
+ public internal::Conversions<DigitalOut, DigitalIoOptional> {
+ public:
+ // Available functionality
+ using DigitalIoOptional::SetState;
+ using DigitalIoOptional::SetStateActive;
+ using DigitalIoOptional::SetStateInactive;
+
+ protected:
+ constexpr DigitalOut()
+ : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalOut>()) {}
+
+ private:
+ // Unavailable functionality
+ using DigitalIoOptional::provides_input;
+ using DigitalIoOptional::provides_interrupt;
+ using DigitalIoOptional::provides_output;
+
+ using DigitalIoOptional::ClearInterruptHandler;
+ using DigitalIoOptional::DisableInterruptHandler;
+ using DigitalIoOptional::EnableInterruptHandler;
+ using DigitalIoOptional::GetState;
+ using DigitalIoOptional::IsStateActive;
+ using DigitalIoOptional::SetInterruptHandler;
+
+ // These overrides invoke PW_CRASH.
+ Result<State> DoGetState() final;
+ Status DoSetInterruptHandler(InterruptTrigger, InterruptHandler&&) final;
+ Status DoEnableInterruptHandler(bool) final;
+};
+
+// A digital I/O line that supports output and interrupts.
+//
+// Input methods are hidden and must not be called.
+//
+// Use in APIs when output and interrupt functionality is required. For
+// example, to represent a two-way signalling line.
+//
+// Extend this class to implement a line that supports both output and
+// listening for interrupts at the same time.
+//
+class DigitalOutInterrupt
+ : public DigitalIoOptional,
+ public internal::Conversions<DigitalOutInterrupt, DigitalIoOptional> {
+ public:
+ // Available functionality
+ using DigitalIoOptional::ClearInterruptHandler;
+ using DigitalIoOptional::DisableInterruptHandler;
+ using DigitalIoOptional::EnableInterruptHandler;
+ using DigitalIoOptional::SetInterruptHandler;
+ using DigitalIoOptional::SetState;
+ using DigitalIoOptional::SetStateActive;
+ using DigitalIoOptional::SetStateInactive;
+
+ protected:
+ constexpr DigitalOutInterrupt()
+ : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalOutInterrupt>()) {}
+
+ private:
+ // Unavailable functionality
+ using DigitalIoOptional::provides_input;
+ using DigitalIoOptional::provides_interrupt;
+ using DigitalIoOptional::provides_output;
+
+ using DigitalIoOptional::GetState;
+ using DigitalIoOptional::IsStateActive;
+
+ // These overrides invoke PW_CRASH.
+ Result<State> DoGetState() final;
+};
+
+// A digital I/O line that supports both input and output.
+//
+// Use in APIs when both input and output functionality is required. For
+// example, to represent a line which is shared by multiple controllers.
+//
+// Extend this class to implement a line that supports both input and output at
+// the same time.
+//
+class DigitalInOut
+ : public DigitalIoOptional,
+ public internal::Conversions<DigitalInOut, DigitalIoOptional> {
+ public:
+ // Available functionality
+ using DigitalIoOptional::GetState;
+ using DigitalIoOptional::IsStateActive;
+ using DigitalIoOptional::SetState;
+ using DigitalIoOptional::SetStateActive;
+ using DigitalIoOptional::SetStateInactive;
+
+ protected:
+ constexpr DigitalInOut()
+ : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalInOut>()) {}
+
+ private:
+ // Unavailable functionality
+ using DigitalIoOptional::provides_input;
+ using DigitalIoOptional::provides_interrupt;
+ using DigitalIoOptional::provides_output;
+
+ using DigitalIoOptional::ClearInterruptHandler;
+ using DigitalIoOptional::DisableInterruptHandler;
+ using DigitalIoOptional::EnableInterruptHandler;
+ using DigitalIoOptional::SetInterruptHandler;
+
+ // These overrides invoke PW_CRASH.
+ Status DoSetInterruptHandler(InterruptTrigger, InterruptHandler&&) final;
+ Status DoEnableInterruptHandler(bool) final;
+};
+
+// A line that supports input, output, and interrupts.
+//
+// Use in APIs when input, output, and interrupts are required. For example to
+// represent a two-way shared line with state transition notifications.
+//
+// Extend this class to implement a line that supports all the functionality at
+// the same time.
+//
+class DigitalInOutInterrupt
+ : public DigitalIoOptional,
+ public internal::Conversions<DigitalInOutInterrupt, DigitalIoOptional> {
+ public:
+ // Available functionality
+ using DigitalIoOptional::ClearInterruptHandler;
+ using DigitalIoOptional::DisableInterruptHandler;
+ using DigitalIoOptional::EnableInterruptHandler;
+ using DigitalIoOptional::GetState;
+ using DigitalIoOptional::IsStateActive;
+ using DigitalIoOptional::SetInterruptHandler;
+ using DigitalIoOptional::SetState;
+ using DigitalIoOptional::SetStateActive;
+ using DigitalIoOptional::SetStateInactive;
+
+ protected:
+ constexpr DigitalInOutInterrupt()
+ : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalInOutInterrupt>()) {
+ }
+
+ private:
+ // Unavailable functionality
+ using DigitalIoOptional::provides_input;
+ using DigitalIoOptional::provides_interrupt;
+ using DigitalIoOptional::provides_output;
+};
+
+} // namespace pw::digital_io