pw_analog: Add analog::AnalogInput interface

Adds the pw_analog module with the pw::analog::AnalogInput interface for
sampling the ADC to get a voltage reading.

Testing:
Host test -- OK

Change-Id: Ief497da89e34eec6aa1be4c3f29c484a85a39cfb
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/38601
Commit-Queue: Kevin Zeng <zengk@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 6040df6..2011dea 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -205,6 +205,7 @@
     deps = [
       "$dir_pigweed/docs",
       "$dir_pw_allocator",
+      "$dir_pw_analog",
       "$dir_pw_base64",
       "$dir_pw_blob_store",
       "$dir_pw_bytes",
@@ -247,6 +248,7 @@
   pw_test_group("pw_module_tests") {
     group_deps = [
       "$dir_pw_allocator:tests",
+      "$dir_pw_analog:tests",
       "$dir_pw_assert:tests",
       "$dir_pw_base64:tests",
       "$dir_pw_blob_store:tests",
diff --git a/modules.gni b/modules.gni
index fa5c03c..d0c9043 100644
--- a/modules.gni
+++ b/modules.gni
@@ -17,6 +17,7 @@
   # allows modules to be moved or swapped out without breaking existing builds.
   # All module variables are prefixed with dir_.
   dir_docker = get_path_info("docker", "abspath")
+  dir_pw_analog = get_path_info("pw_analog", "abspath")
   dir_pw_allocator = get_path_info("pw_allocator", "abspath")
   dir_pw_arduino_build = get_path_info("pw_arduino_build", "abspath")
   dir_pw_assert = get_path_info("pw_assert", "abspath")
diff --git a/pw_analog/BUILD b/pw_analog/BUILD
new file mode 100644
index 0000000..3d524d9
--- /dev/null
+++ b/pw_analog/BUILD
@@ -0,0 +1,46 @@
+# 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.
+
+load(
+    "//pw_build:pigweed.bzl",
+    "pw_cc_library",
+    "pw_cc_test",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # Apache License 2.0
+
+pw_cc_library(
+    name = "analog_input",
+    hdrs = [
+        "public/pw_analog/analog_input.h",
+    ],
+    includes = ["public"],
+    deps = [
+        "//pw_chrono:system_clock",
+        "//pw_result",
+    ],
+)
+
+pw_cc_test(
+    name = "analog_input_test",
+    srcs = [
+        "analog_input_test.cc",
+    ],
+    deps = [
+        ":analog_input",
+        "//pw_unit_test",
+    ],
+)
diff --git a/pw_analog/BUILD.gn b/pw_analog/BUILD.gn
new file mode 100644
index 0000000..8b1eab7
--- /dev/null
+++ b/pw_analog/BUILD.gn
@@ -0,0 +1,47 @@
+# 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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_chrono/backend.gni")
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_unit_test/test.gni")
+
+config("public_include_path") {
+  include_dirs = [ "public" ]
+}
+
+pw_source_set("pw_analog") {
+  public_configs = [ ":public_include_path" ]
+  public_deps = [
+    "$dir_pw_chrono:system_clock",
+    "$dir_pw_result",
+  ]
+  public = [ "public/pw_analog/analog_input.h" ]
+}
+
+pw_test_group("tests") {
+  tests = [ ":analog_input_test" ]
+}
+
+pw_test("analog_input_test") {
+  enable_if = pw_chrono_SYSTEM_CLOCK_BACKEND != ""
+  sources = [ "analog_input_test.cc" ]
+  deps = [ ":pw_analog" ]
+}
+
+pw_doc_group("docs") {
+  sources = [ "docs.rst" ]
+}
diff --git a/pw_analog/analog_input_test.cc b/pw_analog/analog_input_test.cc
new file mode 100644
index 0000000..a704fcb
--- /dev/null
+++ b/pw_analog/analog_input_test.cc
@@ -0,0 +1,57 @@
+// 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.
+#include "pw_analog/analog_input.h"
+
+#include "gtest/gtest.h"
+
+namespace pw {
+namespace analog {
+namespace {
+
+constexpr int32_t kLimitsMax = 4096;
+constexpr int32_t kLimitsMin = 0;
+
+// Dummy test analog input that's used for testing.
+class TestAnalogInput : public AnalogInput {
+ public:
+  TestAnalogInput()
+      : limits_({
+            .min = kLimitsMin,
+            .max = kLimitsMax,
+        }) {}
+
+  Result<int32_t> TryReadUntil(chrono::SystemClock::time_point) override {
+    return Status::Unimplemented();
+  }
+
+  Limits GetLimits() const override { return limits_; }
+
+ private:
+  const Limits limits_;
+};
+
+TEST(AnalogInputTest, Construction) {
+  TestAnalogInput analog_input = TestAnalogInput();
+}
+
+TEST(AnalogInputTest, GetLimits) {
+  TestAnalogInput analog_input = TestAnalogInput();
+  AnalogInput::Limits limits = analog_input.GetLimits();
+  EXPECT_EQ(limits.min, kLimitsMin);
+  EXPECT_EQ(limits.max, kLimitsMax);
+}
+
+}  // namespace
+}  // namespace analog
+}  // namespace pw
diff --git a/pw_analog/docs.rst b/pw_analog/docs.rst
new file mode 100644
index 0000000..1c5d485
--- /dev/null
+++ b/pw_analog/docs.rst
@@ -0,0 +1,21 @@
+.. _module-pw_analog:
+
+---------
+pw_analog
+---------
+
+.. warning::
+  This module is under construction and may not be ready for use.
+
+pw_analog contains interfaces and utility functions for using the ADC.
+
+Features
+========
+
+pw::analog::AnalogInput
+-----------------------
+The common interface for obtaining ADC samples. This interface represents
+a single analog input or channel. Users will need to supply their own ADC
+driver implementation in order to configure and enable the ADC peripheral.
+Users are responsible for managing multithreaded access to the ADC driver if the
+ADC services multiple channels.
diff --git a/pw_analog/public/pw_analog/analog_input.h b/pw_analog/public/pw_analog/analog_input.h
new file mode 100644
index 0000000..39024a0
--- /dev/null
+++ b/pw_analog/public/pw_analog/analog_input.h
@@ -0,0 +1,74 @@
+// 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_chrono/system_clock.h"
+#include "pw_result/result.h"
+
+namespace pw::analog {
+
+// Base interface for getting ADC samples from one ADC channel in a thread
+// safe manner.
+//
+// The ADC backend interface is up to the user to define and implement for now.
+// This gives the flexibility for the ADC driver implementation.
+//
+// AnalogInput controls a specific input/channel where the ADC peripheral may be
+// shared across multiple channels that may be controlled by multiple threads.
+// The implementer of this pure virtual interface is responsible for ensuring
+// thread safety and access at the driver level.
+class AnalogInput {
+ public:
+  // Limits struct that specifies the min and max of the sample range.
+  // These values do not change at run time.
+  struct Limits {
+    int32_t min;
+    int32_t max;
+  };
+
+  virtual ~AnalogInput() = default;
+
+  // Blocks until the specified timeout duration has elapsed or the ADC sample
+  // has been returned, whichever comes first.
+  //
+  // This method is thread safe.
+  //
+  // Returns:
+  //   Sample.
+  //   ResourceExhuasted: ADC peripheral in use.
+  //   DeadlineExceedded: Timed out waiting for a sample.
+  //   Other statuses left up to the implementer.
+  Result<int32_t> TryReadFor(chrono::SystemClock::duration timeout) {
+    return TryReadUntil(chrono::SystemClock::TimePointAfterAtLeast(timeout));
+  }
+
+  // Blocks until the deadline time has been reached or the ADC sample
+  // has been returned, whichever comes first.
+  //
+  // This method is thread safe.
+  //
+  // Returns:
+  //   Sample.
+  //   ResourceExhuasted: ADC peripheral in use.
+  //   DeadlineExceedded: Timed out waiting for a sample.
+  //   Other statuses left up to the implementer.
+  virtual Result<int32_t> TryReadUntil(
+      chrono::SystemClock::time_point deadline) = 0;
+
+  // Returns the range of the ADC sample.
+  // These values do not change at run time.
+  virtual Limits GetLimits() const = 0;
+};
+
+}  // namespace pw::analog