pw_ft6236: Initial implementation

Change-Id: I25f5d0c5b12c04b1af0bf7565255b93108911c42
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/experimental/+/171603
Reviewed-by: Armando Montanez <amontanez@google.com>
Commit-Queue: Anthony DiGirolamo <tonymd@google.com>
diff --git a/applications/app_common_impl/BUILD.gn b/applications/app_common_impl/BUILD.gn
index df20efb..9e05df3 100644
--- a/applications/app_common_impl/BUILD.gn
+++ b/applications/app_common_impl/BUILD.gn
@@ -136,6 +136,7 @@
   "$dir_pw_digital_io_pico",
   "$dir_pw_display",
   "$dir_pw_framebuffer_pool",
+  "$dir_pw_ft6236",
   "$dir_pw_i2c_rp2040",
   "$dir_pw_log",
   "$dir_pw_spi_pico",
diff --git a/applications/app_common_impl/common_pico.cc b/applications/app_common_impl/common_pico.cc
index 8abc503..bc35519 100644
--- a/applications/app_common_impl/common_pico.cc
+++ b/applications/app_common_impl/common_pico.cc
@@ -23,6 +23,7 @@
 #include "hardware/vreg.h"
 #include "pico/stdlib.h"
 #include "pw_digital_io_pico/digital_io.h"
+#include "pw_ft6236/device.h"
 #include "pw_i2c_rp2040/initiator.h"
 #include "pw_log/log.h"
 #include "pw_pixel_pusher_rp2040_pio/pixel_pusher.h"
@@ -219,6 +220,10 @@
 
   i2c_bus.Enable();
 
+  pw::ft6236::Device touch_screen(i2c_bus);
+  touch_screen.Enable();
+  touch_screen.LogControllerInfo();
+
 #if BACKLIGHT_GPIO != -1
   SetBacklight(0xffff);  // Full brightness.
 #endif
diff --git a/build_overrides/pigweed.gni b/build_overrides/pigweed.gni
index 210f6f9..bdf8f80 100644
--- a/build_overrides/pigweed.gni
+++ b/build_overrides/pigweed.gni
@@ -152,4 +152,6 @@
   dir_pw_pixel_pusher_rp2040_pio =
       get_path_info("$dir_pigweed_experimental/pw_pixel_pusher_rp2040_pio",
                     "abspath")
+  dir_pw_ft6236 =
+      get_path_info("$dir_pigweed_experimental/pw_ft6236", "abspath")
 }
diff --git a/pw_ft6236/BUILD.gn b/pw_ft6236/BUILD.gn
new file mode 100644
index 0000000..1e78995
--- /dev/null
+++ b/pw_ft6236/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2023 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/pi_pico.gni")
+import("//build_overrides/pigweed.gni")
+import("$dir_pw_build/target_types.gni")
+
+config("default_config") {
+  include_dirs = [ "public" ]
+}
+
+pw_source_set("pw_ft6236") {
+  public_configs = [ ":default_config" ]
+  public_deps = [
+    "$dir_pw_i2c:initiator",
+    "$dir_pw_status",
+  ]
+  deps = [
+    "$dir_pw_digital_io",
+    "$dir_pw_i2c:register_device",
+    "$dir_pw_log",
+  ]
+  sources = [ "device.cc" ]
+  remove_configs = [ "$dir_pw_build:strict_warnings" ]
+}
diff --git a/pw_ft6236/device.cc b/pw_ft6236/device.cc
new file mode 100644
index 0000000..4d19079
--- /dev/null
+++ b/pw_ft6236/device.cc
@@ -0,0 +1,162 @@
+// Copyright 2023 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_ft6236/device.h"
+
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+
+#include "pw_bytes/bit.h"
+#include "pw_i2c/address.h"
+#include "pw_i2c/register_device.h"
+#include "pw_log/log.h"
+#include "pw_status/status.h"
+
+using ::pw::Status;
+using namespace std::chrono_literals;
+
+namespace pw::ft6236 {
+
+namespace {
+
+#define FT62XX_REG_THRESHHOLD 0x80
+#define FT62XX_REG_POINTRATE 0x88
+#define FT62XX_REG_CHIPID 0xA3
+#define FT62XX_REG_FIRMVERS 0xA6
+#define FT62XX_REG_VENDID 0xA8
+
+constexpr pw::i2c::Address kAddress = pw::i2c::Address::SevenBit<0x38>();
+
+}  // namespace
+
+Device::Device(pw::i2c::Initiator& initiator)
+    : initiator_(initiator),
+      device_(initiator,
+              kAddress,
+              endian::little,
+              pw::i2c::RegisterAddressSize::k1Byte) {
+  delta_micros_ = 0;
+  this_update_micros_ = 0;
+  last_update_micros_ = 0;
+}
+Device::~Device() = default;
+
+Status Device::Enable() {
+  Result<std::byte> vendor_id_result = device_.ReadRegister(
+      FT62XX_REG_VENDID, pw::chrono::SystemClock::for_at_least(10ms));
+  if (vendor_id_result.value_or(std::byte{0}) != std::byte{0x11}) {
+    return Status::NotFound();
+  }
+
+  SetThreshhold(128);
+
+  return OkStatus();
+}
+
+Status Device::SetThreshhold(uint8_t threshhold) {
+  PW_TRY(device_.WriteRegister(FT62XX_REG_THRESHHOLD,
+                               std::byte{threshhold},
+                               pw::chrono::SystemClock::for_at_least(10ms)));
+  return OkStatus();
+}
+
+Status Device::Probe() {
+  pw::Status probe_result(initiator_.ProbeDeviceFor(
+      kAddress, pw::chrono::SystemClock::for_at_least(10ms)));
+
+  if (probe_result != pw::OkStatus()) {
+    PW_LOG_DEBUG("FT6236 Probe Failed");
+  }
+  PW_LOG_DEBUG("FT6236 Probe Ok");
+  return probe_result;
+}
+
+void Device::LogControllerInfo() {
+  Result<std::byte> result = device_.ReadRegister(
+      FT62XX_REG_VENDID, pw::chrono::SystemClock::for_at_least(10ms));
+  PW_LOG_DEBUG("Vend ID: 0x%x", (uint8_t)result.value_or(std::byte{0}));
+
+  result = device_.ReadRegister(FT62XX_REG_CHIPID,
+                                pw::chrono::SystemClock::for_at_least(10ms));
+  PW_LOG_DEBUG("Chip ID: 0x%x (0x36==FT6236)",
+               (uint8_t)result.value_or(std::byte{0}));
+
+  result = device_.ReadRegister(FT62XX_REG_FIRMVERS,
+                                pw::chrono::SystemClock::for_at_least(10ms));
+  PW_LOG_DEBUG("Firmware Version: %u", (uint8_t)result.value_or(std::byte{0}));
+
+  result = device_.ReadRegister(FT62XX_REG_POINTRATE,
+                                pw::chrono::SystemClock::for_at_least(10ms));
+  PW_LOG_DEBUG("Point Rate Hz: %u", (uint8_t)result.value_or(std::byte{0}));
+
+  result = device_.ReadRegister(FT62XX_REG_THRESHHOLD,
+                                pw::chrono::SystemClock::for_at_least(10ms));
+  PW_LOG_DEBUG("Threshhold: %u", (uint8_t)result.value_or(std::byte{0}));
+}
+
+void Device::LogTouchInfo() {
+  if (touch_count_ == 0) {
+    return;
+  }
+
+  PW_LOG_DEBUG("Touches: %d", touch_count_);
+
+  for (int i = 0; i < touch_count_; i++) {
+    PW_LOG_DEBUG("(x,y)=(%d, %d) weight=%d area=%d",
+                 touches_[i].x,
+                 touches_[i].y,
+                 touches_[i].weight,
+                 touches_[i].area);
+  }
+}
+
+bool Device::ReadData() {
+  // Read 16 registers starting from 0x00.
+  std::array<std::byte, 16> rx_buffer;
+  device_.ReadRegisters(
+      0, rx_buffer, pw::chrono::SystemClock::for_at_least(10ms));
+
+  // Number of touches (0, 1 or 2) is at 0x02.
+  touch_count_ = (uint8_t)rx_buffer[0x02];
+
+  // Return false if no new touches are present.
+  if (touch_count_ == 0) {
+    return false;
+  }
+
+  // Read Touch #1 X coordinate high and low registers.
+  touches_[0].x =
+      (((uint8_t)rx_buffer[0x03] & 0x0F) << 8) | (uint8_t)rx_buffer[0x04];
+  // Read Touch #1 Y coordinate high and low registers.
+  touches_[0].y =
+      (((uint8_t)rx_buffer[0x05] & 0x0F) << 8) | (uint8_t)rx_buffer[0x06];
+  // Read Touch #1 Misc data
+  touches_[0].weight = (uint8_t)rx_buffer[0x07];
+  touches_[0].area = (uint8_t)rx_buffer[0x08] & 0x0F;
+
+  // Read Touch #2 X coordinate high and low registers.
+  touches_[1].x =
+      (((uint8_t)rx_buffer[0x09] & 0x0F) << 8) | (uint8_t)rx_buffer[0x0A];
+  // Read Touch #2 Y coordinate high and low registers.
+  touches_[1].y =
+      (((uint8_t)rx_buffer[0x0B] & 0x0F) << 8) | (uint8_t)rx_buffer[0x0C];
+  // Read Touch #2 Misc data
+  touches_[0].weight = (uint8_t)rx_buffer[0x0D];
+  touches_[0].area = (uint8_t)rx_buffer[0x0E] & 0x0F;
+
+  return true;
+}
+
+}  // namespace pw::ft6236
diff --git a/pw_ft6236/public/pw_ft6236/device.h b/pw_ft6236/public/pw_ft6236/device.h
new file mode 100644
index 0000000..649aa85
--- /dev/null
+++ b/pw_ft6236/public/pw_ft6236/device.h
@@ -0,0 +1,57 @@
+// Copyright 2023 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 <array>
+#include <cstdint>
+
+#include "pw_i2c/address.h"
+#include "pw_i2c/initiator.h"
+#include "pw_i2c/register_device.h"
+#include "pw_status/status.h"
+
+namespace pw::ft6236 {
+
+struct Touch {
+  uint16_t x;
+  uint16_t y;
+  uint8_t weight;
+  uint8_t area;
+};
+
+class Device {
+ public:
+  Device(pw::i2c::Initiator& initiator);
+  ~Device();
+
+  Status Enable();
+  Status Probe();
+  void LogControllerInfo();
+  void LogTouchInfo();
+  Status SetThreshhold(uint8_t threshhold);
+  bool ReadData();
+
+ private:
+  pw::i2c::Initiator& initiator_;
+  pw::i2c::RegisterDevice device_;
+
+  std::array<Touch, 2> touches_;
+  uint8_t touch_count_;
+
+  uint32_t delta_micros_ = 0;
+  uint32_t this_update_micros_ = 0;
+  uint32_t last_update_micros_ = 0;
+};
+
+}  // namespace pw::ft6236