Initial RGB Status, IMU, fuel guage, and IO expander tests
Change-Id: I583f019d942eac36420763d866c5a68dd9cdb337
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/kudzu/+/175352
Reviewed-by: Armando Montanez <amontanez@google.com>
diff --git a/applications/app_common/public/app_common/common.h b/applications/app_common/public/app_common/common.h
index f55d970..0ef81e4 100644
--- a/applications/app_common/public/app_common/common.h
+++ b/applications/app_common/public/app_common/common.h
@@ -28,6 +28,8 @@
// any other methods in this class.
static pw::Status Init();
+ static pw::Status FrameCallback();
+
// Return an initialized display.
static pw::display::Display& GetDisplay();
diff --git a/applications/app_common_impl/BUILD.gn b/applications/app_common_impl/BUILD.gn
index 062f9a4..59fb25c 100644
--- a/applications/app_common_impl/BUILD.gn
+++ b/applications/app_common_impl/BUILD.gn
@@ -146,10 +146,13 @@
"$dir_pw_framebuffer_pool",
"$dir_pw_ft6236",
"$dir_pw_i2c_rp2040",
+ "$dir_pw_icm42670p",
"$dir_pw_log",
+ "$dir_pw_max17948",
"$dir_pw_spi_rp2040",
"$dir_pw_sync:borrow",
"$dir_pw_sync:mutex",
+ "$dir_pw_tca9535",
"$dir_pw_thread:thread",
"$dir_pw_thread_freertos:thread",
"//applications/app_common:app_common.facade",
diff --git a/applications/app_common_impl/app_common_vars.gni b/applications/app_common_impl/app_common_vars.gni
index a5f6a44..8e39b2e 100644
--- a/applications/app_common_impl/app_common_vars.gni
+++ b/applications/app_common_impl/app_common_vars.gni
@@ -63,8 +63,8 @@
pw_app_common_SPI_CLOCK_GPIO = ""
# RP2040 I2C pins.
- pw_app_common_I2C_BUS0_SCL = "8"
- pw_app_common_I2C_BUS0_SDA = "9"
- pw_app_common_I2C_BUS1_SCL = "2"
- pw_app_common_I2C_BUS1_SDA = "3"
+ pw_app_common_I2C_BUS0_SCL = "9"
+ pw_app_common_I2C_BUS0_SDA = "8"
+ pw_app_common_I2C_BUS1_SCL = "3"
+ pw_app_common_I2C_BUS1_SDA = "2"
}
diff --git a/applications/app_common_impl/common_host_imgui.cc b/applications/app_common_impl/common_host_imgui.cc
index 49a561f..1015ed9 100644
--- a/applications/app_common_impl/common_host_imgui.cc
+++ b/applications/app_common_impl/common_host_imgui.cc
@@ -57,6 +57,8 @@
} // namespace
// static
+Status Common::FrameCallback() { return pw::OkStatus(); }
+
Status Common::Init() { return s_display_driver.Init(); }
// static
diff --git a/applications/app_common_impl/common_pico.cc b/applications/app_common_impl/common_pico.cc
index 0ef1785..7aada42 100644
--- a/applications/app_common_impl/common_pico.cc
+++ b/applications/app_common_impl/common_pico.cc
@@ -26,13 +26,16 @@
#include "pw_digital_io_rp2040/digital_io.h"
#include "pw_ft6236/device.h"
#include "pw_i2c_rp2040/initiator.h"
+#include "pw_icm42670p/device.h"
#include "pw_log/log.h"
+#include "pw_max17948/device.h"
#include "pw_pixel_pusher_rp2040_pio/pixel_pusher.h"
#include "pw_spi_rp2040/chip_selector.h"
#include "pw_spi_rp2040/initiator.h"
#include "pw_status/status.h"
#include "pw_sync/borrow.h"
#include "pw_sync/mutex.h"
+#include "pw_tca9535/device.h"
#include "pw_thread/detached_thread.h"
#include "pw_thread/thread.h"
#include "pw_thread_freertos/context.h"
@@ -183,6 +186,30 @@
}
#endif
+const uint8_t kStatusPinRed = 23;
+const uint8_t kStatusPinGreen = 24;
+const uint8_t kStatusPinBlue = 25;
+
+void ConfigStatusRgb() {
+ std::array<uint8_t, 3> pwm_pins = {
+ kStatusPinRed, kStatusPinGreen, kStatusPinBlue};
+ for (auto& pin : pwm_pins) {
+ gpio_set_function(pin, GPIO_FUNC_PWM);
+ auto slice_num = pwm_gpio_to_slice_num(pin);
+ pwm_config cfg = pwm_get_default_config();
+ pwm_set_wrap(slice_num, 65535);
+ pwm_init(slice_num, &cfg, true);
+
+ pwm_set_gpio_level(pin, 65535);
+ }
+}
+
+void SetStatusRgb(uint8_t r, uint8_t g, uint8_t b) {
+ pwm_set_gpio_level(kStatusPinRed, 65535 - r * r);
+ pwm_set_gpio_level(kStatusPinGreen, 65535 - g * g);
+ pwm_set_gpio_level(kStatusPinBlue, 65535 - b * b);
+}
+
SpiValues::SpiValues(pw::spi::Config config,
pw::spi::ChipSelector& selector,
pw::sync::VirtualMutex& initiator_mutex)
@@ -205,6 +232,9 @@
pw::i2c::PicoInitiator i2c0_bus(ki2c0Config);
pw::i2c::PicoInitiator i2c1_bus(ki2c1Config);
+pw::tca9535::Device io_expander(i2c1_bus);
+pw::icm42670p::Device imu(i2c0_bus);
+pw::max17948::Device fuel_guage(i2c0_bus);
pw::ft6236::Device touch_screen_controller(i2c0_bus);
Touchscreen s_touchscreen = Touchscreen(&touch_screen_controller);
@@ -213,8 +243,29 @@
kDisplayDrawThreadStackWords>
display_draw_thread_context;
+Rp2040DigitalInOut s_io_reset_n(10);
+Rp2040DigitalInOut s_imu_fsync(13);
+
} // namespace
+Status Common::FrameCallback() {
+ // touch_screen_controller.LogControllerInfo();
+
+ if (io_expander.Probe() == pw::OkStatus()) {
+ io_expander.LogControllerInfo();
+ }
+
+ if (fuel_guage.Probe() == pw::OkStatus()) {
+ fuel_guage.LogControllerInfo();
+ }
+
+ if (imu.Probe() == pw::OkStatus()) {
+ imu.LogControllerInfo();
+ }
+
+ return pw::OkStatus();
+}
+
// static
Status Common::Init() {
#if OVERCLOCK_250
@@ -224,6 +275,10 @@
set_sys_clock_khz(250000, false);
#endif
+ ConfigStatusRgb();
+ // Set to a dim pink.
+ SetStatusRgb(32, 12, 32);
+
s_display_cs_pin.Enable();
s_display_dc_pin.Enable();
#if DISPLAY_RESET_GPIO != -1
@@ -237,8 +292,31 @@
i2c0_bus.Enable();
i2c1_bus.Enable();
+ s_io_reset_n.Enable();
+ s_io_reset_n.SetStateActive();
+
+ s_imu_fsync.Enable();
+ s_imu_fsync.SetStateInactive();
+
touch_screen_controller.Enable();
- touch_screen_controller.LogControllerInfo();
+ if (touch_screen_controller.Probe() == pw::OkStatus()) {
+ touch_screen_controller.LogControllerInfo();
+ }
+
+ io_expander.Enable();
+ if (io_expander.Probe() == pw::OkStatus()) {
+ io_expander.LogControllerInfo();
+ }
+
+ fuel_guage.Enable();
+ if (fuel_guage.Probe() == pw::OkStatus()) {
+ fuel_guage.LogControllerInfo();
+ }
+
+ imu.Enable();
+ if (imu.Probe() == pw::OkStatus()) {
+ imu.LogControllerInfo();
+ }
#if BACKLIGHT_GPIO != -1
SetBacklight(0xffff); // Full brightness.
diff --git a/applications/badge/main.cc b/applications/badge/main.cc
index f4118a0..eab46c1 100644
--- a/applications/badge/main.cc
+++ b/applications/badge/main.cc
@@ -142,6 +142,7 @@
Touchscreen& touchscreen = Common::GetTouchscreen();
+ uint32_t frame_start_millis = pw::spin_delay::Millis();
// The display loop.
while (1) {
frame_counter.StartFrame();
@@ -212,6 +213,11 @@
// Every second make a log message.
frame_counter.EndFrame();
+
+ if (pw::spin_delay::Millis() > frame_start_millis + 10000) {
+ Common::FrameCallback();
+ frame_start_millis = pw::spin_delay::Millis();
+ }
}
}
diff --git a/build_overrides/pigweed.gni b/build_overrides/pigweed.gni
index 4bea6b9..60067b8 100644
--- a/build_overrides/pigweed.gni
+++ b/build_overrides/pigweed.gni
@@ -31,4 +31,7 @@
get_path_info("../lib/pw_touchscreen_null", "abspath")
dir_pw_touchscreen_ft6236 =
get_path_info("../lib/pw_touchscreen_ft6236", "abspath")
+ dir_pw_tca9535 = get_path_info("../lib/pw_tca9535", "abspath")
+ dir_pw_max17948 = get_path_info("../lib/pw_max17948", "abspath")
+ dir_pw_icm42670p = get_path_info("../lib/pw_icm42670p", "abspath")
}
diff --git a/lib/pw_icm42670p/BUILD.gn b/lib/pw_icm42670p/BUILD.gn
new file mode 100644
index 0000000..a24ca1a
--- /dev/null
+++ b/lib/pw_icm42670p/BUILD.gn
@@ -0,0 +1,36 @@
+# 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_icm42670p") {
+ public_configs = [ ":default_config" ]
+ public_deps = [
+ "$dir_pw_i2c:initiator",
+ "$dir_pw_status",
+ ]
+ public = [ "public/pw_icm42670p/device.h" ]
+ 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/lib/pw_icm42670p/device.cc b/lib/pw_icm42670p/device.cc
new file mode 100644
index 0000000..31faab8
--- /dev/null
+++ b/lib/pw_icm42670p/device.cc
@@ -0,0 +1,103 @@
+// 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_icm42670p/device.h"
+
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+
+#define PW_LOG_MODULE_NAME "pw_icm42670p"
+#define PW_LOG_LEVEL PW_LOG_LEVEL_DEBUG
+
+#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::icm42670p {
+
+namespace {
+
+constexpr pw::i2c::Address kAddress = pw::i2c::Address::SevenBit<0x68>();
+
+void ImuReadReg(pw::i2c::RegisterDevice& device,
+ uint8_t addr,
+ const char* name) {
+ auto data =
+ device.ReadRegister8(addr, pw::chrono::SystemClock::for_at_least(10ms));
+
+ if (data.ok()) {
+ PW_LOG_INFO("%s: %02x", name, *data);
+ } else {
+ PW_LOG_INFO("failed to read %s", name);
+ }
+}
+
+void ImuReadReg16(pw::i2c::RegisterDevice& device,
+ uint8_t addr,
+ const char* name) {
+ auto data =
+ device.ReadRegister16(addr, pw::chrono::SystemClock::for_at_least(10ms));
+
+ if (data.ok()) {
+ PW_LOG_INFO("%s: %04x", name, *data);
+ } else {
+ PW_LOG_INFO("failed to read %s", name);
+ }
+}
+
+} // namespace
+
+Device::Device(pw::i2c::Initiator& initiator)
+ : initiator_(initiator),
+ device_(initiator,
+ kAddress,
+ endian::little,
+ pw::i2c::RegisterAddressSize::k1Byte) {}
+
+Status Device::Enable() {
+ device_.WriteRegister8(
+ 0x1f, 0x0f, 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(50ms)));
+
+ if (probe_result != pw::OkStatus()) {
+ PW_LOG_DEBUG("ICM-42670-P Probe Failed");
+ } else {
+ PW_LOG_DEBUG("ICM-42670-P Probe Ok");
+ }
+ return probe_result;
+}
+
+void Device::LogControllerInfo() {
+ device_.WriteRegister8(
+ 0x1f, 0x0f, pw::chrono::SystemClock::for_at_least(10ms));
+
+ ImuReadReg(device_, 0x75, "WHO_AM_I");
+ ImuReadReg(device_, 0x1f, "PWR_MGMT0");
+ ImuReadReg(device_, 0x0c, "X0");
+ ImuReadReg(device_, 0x0b, "X1");
+}
+
+} // namespace pw::icm42670p
diff --git a/lib/pw_icm42670p/public/pw_icm42670p/device.h b/lib/pw_icm42670p/public/pw_icm42670p/device.h
new file mode 100644
index 0000000..288e7a1
--- /dev/null
+++ b/lib/pw_icm42670p/public/pw_icm42670p/device.h
@@ -0,0 +1,40 @@
+// 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::icm42670p {
+
+class Device {
+ public:
+ Device(pw::i2c::Initiator& initiator);
+ ~Device() = default;
+
+ Status Enable();
+ Status Probe();
+ void LogControllerInfo();
+
+ private:
+ pw::i2c::Initiator& initiator_;
+ pw::i2c::RegisterDevice device_;
+};
+
+} // namespace pw::icm42670p
diff --git a/lib/pw_max17948/BUILD.gn b/lib/pw_max17948/BUILD.gn
new file mode 100644
index 0000000..b3a6d66
--- /dev/null
+++ b/lib/pw_max17948/BUILD.gn
@@ -0,0 +1,36 @@
+# 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_max17948") {
+ public_configs = [ ":default_config" ]
+ public_deps = [
+ "$dir_pw_i2c:initiator",
+ "$dir_pw_status",
+ ]
+ public = [ "public/pw_max17948/device.h" ]
+ 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/lib/pw_max17948/device.cc b/lib/pw_max17948/device.cc
new file mode 100644
index 0000000..6b92b14
--- /dev/null
+++ b/lib/pw_max17948/device.cc
@@ -0,0 +1,99 @@
+// 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_max17948/device.h"
+
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+
+#define PW_LOG_MODULE_NAME "pw_max17948"
+#define PW_LOG_LEVEL PW_LOG_LEVEL_DEBUG
+
+#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::max17948 {
+
+namespace {
+
+constexpr pw::i2c::Address kAddress = pw::i2c::Address::SevenBit<0x36>();
+
+void FuelReadReg(pw::i2c::RegisterDevice& device,
+ uint8_t addr,
+ const char* name) {
+ auto data =
+ device.ReadRegister16(addr, pw::chrono::SystemClock::for_at_least(10ms));
+
+ if (data.ok()) {
+ PW_LOG_INFO("%s: %04x", name, *data);
+ } else {
+ PW_LOG_INFO("failed to read %s", name);
+ }
+}
+
+} // namespace
+
+Device::Device(pw::i2c::Initiator& initiator)
+ : initiator_(initiator),
+ device_(initiator,
+ kAddress,
+ endian::little,
+ pw::i2c::RegisterAddressSize::k1Byte) {}
+
+Status Device::Enable() {
+ device_.WriteRegister8(
+ 0x1f, 0x0f, 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("MAX17948 Probe Failed");
+ } else {
+ PW_LOG_DEBUG("MAX17948 Probe Ok");
+ }
+ return probe_result;
+}
+
+void Device::LogControllerInfo() {
+ auto data =
+ device_.ReadRegister16(0x2, pw::chrono::SystemClock::for_at_least(10ms));
+ if (data.ok()) {
+ PW_LOG_INFO("VCELL: %d mV", static_cast<uint32_t>(*data) * 78125 / 1000000);
+ } else {
+ PW_LOG_INFO("failed to read VCELL");
+ }
+ FuelReadReg(device_, 0x4, "SOC");
+ FuelReadReg(device_, 0x6, "MODE");
+ FuelReadReg(device_, 0x8, "VERSION");
+ FuelReadReg(device_, 0xa, "HIBRT");
+ FuelReadReg(device_, 0xc, "CONFIG");
+ FuelReadReg(device_, 0x14, "VALRT");
+ FuelReadReg(device_, 0x16, "CRATE");
+ FuelReadReg(device_, 0x18, "VRESET/ID");
+ FuelReadReg(device_, 0x1a, "STATUS");
+}
+
+} // namespace pw::max17948
diff --git a/lib/pw_max17948/public/pw_max17948/device.h b/lib/pw_max17948/public/pw_max17948/device.h
new file mode 100644
index 0000000..6f21234
--- /dev/null
+++ b/lib/pw_max17948/public/pw_max17948/device.h
@@ -0,0 +1,40 @@
+// 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::max17948 {
+
+class Device {
+ public:
+ Device(pw::i2c::Initiator& initiator);
+ ~Device() = default;
+
+ Status Enable();
+ Status Probe();
+ void LogControllerInfo();
+
+ private:
+ pw::i2c::Initiator& initiator_;
+ pw::i2c::RegisterDevice device_;
+};
+
+} // namespace pw::max17948
diff --git a/lib/pw_tca9535/BUILD.gn b/lib/pw_tca9535/BUILD.gn
new file mode 100644
index 0000000..14db013
--- /dev/null
+++ b/lib/pw_tca9535/BUILD.gn
@@ -0,0 +1,36 @@
+# 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_tca9535") {
+ public_configs = [ ":default_config" ]
+ public_deps = [
+ "$dir_pw_i2c:initiator",
+ "$dir_pw_status",
+ ]
+ public = [ "public/pw_tca9535/device.h" ]
+ 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/lib/pw_tca9535/device.cc b/lib/pw_tca9535/device.cc
new file mode 100644
index 0000000..e32c456
--- /dev/null
+++ b/lib/pw_tca9535/device.cc
@@ -0,0 +1,91 @@
+// 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_tca9535/device.h"
+
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+
+#define PW_LOG_MODULE_NAME "pw_tca9535"
+#define PW_LOG_LEVEL PW_LOG_LEVEL_DEBUG
+
+#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::tca9535 {
+
+namespace {
+
+constexpr pw::i2c::Address kAddress = pw::i2c::Address::SevenBit<0x20>();
+
+} // namespace
+
+Device::Device(pw::i2c::Initiator& initiator)
+ : initiator_(initiator),
+ device_(initiator,
+ kAddress,
+ endian::little,
+ pw::i2c::RegisterAddressSize::k1Byte) {}
+
+Status Device::Enable() {
+ // Port 0 is all button input.
+ device_.WriteRegister8(
+ 0x6, 0xff, pw::chrono::SystemClock::for_at_least(10ms));
+ // Port 1 pins 6 and 7 are DISP_RESET and TOUCH_RESET which should be high.
+ device_.WriteRegister8(
+ 0x3, 0xff, pw::chrono::SystemClock::for_at_least(10ms));
+ device_.WriteRegister8(
+ 0x7, 0x3f, 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("TCA9535 Probe Failed");
+ } else {
+ PW_LOG_DEBUG("TCA9535 Probe Ok");
+ }
+ return probe_result;
+}
+
+void Device::LogControllerInfo() {
+ auto port0 =
+ device_.ReadRegister8(0x0, pw::chrono::SystemClock::for_at_least(10ms));
+ if (port0.ok()) {
+ PW_LOG_INFO("port 0: %02x", *port0);
+ } else {
+ PW_LOG_INFO("port 0 read failed");
+ }
+
+ auto port1 =
+ device_.ReadRegister8(0x1, pw::chrono::SystemClock::for_at_least(10ms));
+ if (port1.ok()) {
+ PW_LOG_INFO("port 1: %02x", *port1);
+ } else {
+ PW_LOG_INFO("port 1 read failed");
+ }
+}
+
+} // namespace pw::tca9535
diff --git a/lib/pw_tca9535/public/pw_tca9535/device.h b/lib/pw_tca9535/public/pw_tca9535/device.h
new file mode 100644
index 0000000..c571beb
--- /dev/null
+++ b/lib/pw_tca9535/public/pw_tca9535/device.h
@@ -0,0 +1,40 @@
+// 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::tca9535 {
+
+class Device {
+ public:
+ Device(pw::i2c::Initiator& initiator);
+ ~Device() = default;
+
+ Status Enable();
+ Status Probe();
+ void LogControllerInfo();
+
+ private:
+ pw::i2c::Initiator& initiator_;
+ pw::i2c::RegisterDevice device_;
+};
+
+} // namespace pw::tca9535
diff --git a/targets/rp2040/BUILD.gn b/targets/rp2040/BUILD.gn
index 4b82865..71be1b6 100644
--- a/targets/rp2040/BUILD.gn
+++ b/targets/rp2040/BUILD.gn
@@ -88,7 +88,7 @@
# Kudzu pin assignments.
pw_app_common_BACKLIGHT_GPIO = "15"
- pw_app_common_DISPLAY_TE_GPIO = "21"
+ pw_app_common_DISPLAY_TE_GPIO = "16"
pw_app_common_DISPLAY_CS_GPIO = "17"
pw_app_common_DISPLAY_DC_GPIO = "20"
pw_app_common_SPI_MOSI_GPIO = "19"