summaryrefslogtreecommitdiff
path: root/src/drivers/include/gpio_expander.hpp
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2022-11-16 11:24:33 +1100
committerjacqueline <me@jacqueline.id.au>2022-11-16 11:24:33 +1100
commit0ef333164bb47956a5ac686f376583e865cedcc4 (patch)
treec3c2f1be88fce8e3a5f5077b3ba166071aaa58a6 /src/drivers/include/gpio_expander.hpp
parent8c51280bc68e51b76243e83b84762e33c52510ca (diff)
downloadtangara-fw-0ef333164bb47956a5ac686f376583e865cedcc4.tar.gz
Use underscores instead of dashes in filenames consistently
Diffstat (limited to 'src/drivers/include/gpio_expander.hpp')
-rw-r--r--src/drivers/include/gpio_expander.hpp208
1 files changed, 208 insertions, 0 deletions
diff --git a/src/drivers/include/gpio_expander.hpp b/src/drivers/include/gpio_expander.hpp
new file mode 100644
index 00000000..7ae0f107
--- /dev/null
+++ b/src/drivers/include/gpio_expander.hpp
@@ -0,0 +1,208 @@
+#pragma once
+
+#include <stdint.h>
+#include <atomic>
+#include <functional>
+#include <mutex>
+#include <tuple>
+#include <utility>
+
+#include "driver/i2c.h"
+#include "esp_check.h"
+#include "esp_err.h"
+#include "esp_log.h"
+#include "freertos/FreeRTOS.h"
+
+namespace drivers {
+
+/**
+ * Wrapper for interfacing with the PCA8575 GPIO expander. Includes basic
+ * low-level pin setting methods, as well as higher level convenience functions
+ * for reading, writing, and atomically interacting with the SPI chip select
+ * pins.
+ *
+ * Each method of this class can be called safely from any thread, and all
+ * updates are guaranteed to be atomic. Any access to chip select related pins
+ * should be done whilst holding `cs_lock` (preferably via the helper methods).
+ */
+class GpioExpander {
+ public:
+ GpioExpander();
+ ~GpioExpander();
+
+ static const uint8_t kPca8575Address = 0x20;
+ static const uint8_t kPca8575Timeout = 100 / portTICK_RATE_MS;
+
+ // Port A:
+ // 0 - audio power enable
+ // 1 - usb interface power enable
+ // 2 - display power enable
+ // 3 - sd card power enable
+ // 4 - charge power ok (active low)
+ // 5 - sd mux switch
+ // 6 - sd chip select
+ // 7 - display chip select
+ // All power switches low, chip selects high, active-low charge power high
+ static const uint8_t kPortADefault = 0b11010001;
+
+ // Port B:
+ // 0 - 3.5mm jack detect (active low)
+ // 1 - dac soft mute switch
+ // 2 - GPIO
+ // 3 - GPIO
+ // 4 - GPIO
+ // 5 - GPIO
+ // 6 - GPIO
+ // 7 - GPIO
+ // DAC mute output low, everything else is active-low inputs.
+ static const uint8_t kPortBDefault = 0b11111111;
+
+ /*
+ * Convenience mehod for packing the port a and b bytes into a single 16 bit
+ * value.
+ */
+ static uint16_t pack(uint8_t a, uint8_t b) { return ((uint16_t)b) << 8 | a; }
+
+ /*
+ * Convenience mehod for unpacking the result of `pack` back into two single
+ * byte port datas.
+ */
+ static std::pair<uint8_t, uint8_t> unpack(uint16_t ba) {
+ return std::pair((uint8_t)ba, (uint8_t)(ba >> 8));
+ }
+
+ /*
+ * Convenience function for running some arbitrary pin writing code, then
+ * flushing a `Write()` to the expander. Example usage:
+ *
+ * ```
+ * gpio_.with([&](auto& gpio) {
+ * gpio.set_pin(AUDIO_POWER_ENABLE, true);
+ * });
+ * ```
+ */
+ void with(std::function<void(GpioExpander&)> f);
+
+ /**
+ * Sets the ports on the GPIO expander to the values currently represented
+ * in `ports`.
+ */
+ esp_err_t Write(void);
+
+ /**
+ * Reads from the GPIO expander, populating `inputs` with the most recent
+ * values.
+ */
+ esp_err_t Read(void);
+
+ /* Maps each pin of the expander to its number in a `pack`ed uint16. */
+ enum Pin {
+ // Port A
+ AUDIO_POWER_ENABLE = 0,
+ USB_INTERFACE_POWER_ENABLE = 1,
+ DISPLAY_POWER_ENABLE = 2,
+ SD_CARD_POWER_ENABLE = 3,
+ CHARGE_POWER_OK = 4, // Active-low input
+ SD_MUX_SWITCH = 5,
+ SD_CHIP_SELECT = 6,
+ DISPLAY_CHIP_SELECT = 7,
+
+ // Port B
+ PHONE_DETECT = 8, // Active-high input
+ DAC_MUTE = 9,
+ GPIO_1 = 10,
+ GPIO_2 = 11,
+ GPIO_3 = 12,
+ GPIO_4 = 13,
+ GPIO_5 = 14,
+ GPIO_6 = 15,
+ };
+
+ /* Pins whose access should be guarded by `cs_lock`. */
+ enum ChipSelect {
+ SD_CARD = SD_CHIP_SELECT,
+ DISPLAY = DISPLAY_CHIP_SELECT,
+ };
+
+ /* Nicer value names for use with the SD_MUX_SWITCH pin. */
+ enum SdController {
+ SD_MUX_ESP = 0,
+ SD_MUX_USB = 1,
+ };
+
+ /**
+ * Returns the current driven status of each of the ports. The first byte is
+ * port a, and the second byte is port b.
+ */
+ std::atomic<uint16_t>& ports() { return ports_; }
+
+ /*
+ * Sets a single specific pin to the given value. `true` corresponds to
+ * HIGH, and `false` corresponds to LOW.
+ *
+ * Calls to this method will be buffered in memory until a call to `Write()`
+ * is made.
+ */
+ void set_pin(Pin pin, bool value);
+ void set_pin(ChipSelect cs, bool value);
+
+ /**
+ * Returns the input status of each of the ports. The first byte is port a,
+ * and the second byte is port b.
+ */
+ const std::atomic<uint16_t>& inputs() const { return inputs_; }
+
+ /* Returns the most recently cached value of the given pin. Only valid for
+ * pins used as inputs; to check what value we're driving a pin, use
+ * `ports()`.
+ */
+ bool get_input(Pin pin) const;
+
+ /* Returns the mutex that must be held whilst pulling a CS pin low. */
+ std::mutex& cs_mutex() { return cs_mutex_; }
+
+ /*
+ * Helper class containing an active `cs_mutex` lock. When an instance of
+ * this class is destroyed (usually by falling out of scope), the associated
+ * CS pin will be driven high before the lock is released.
+ */
+ class SpiLock {
+ public:
+ SpiLock(GpioExpander& gpio, ChipSelect cs);
+ ~SpiLock();
+
+ SpiLock(const SpiLock&) = delete;
+
+ private:
+ std::scoped_lock<std::mutex> lock_;
+ GpioExpander& gpio_;
+ ChipSelect cs_;
+ };
+
+ /*
+ * Pulls the given CS pin low to signal that we are about to communicate
+ * with a particular device, after acquiring a lock on `cs_mutex`. The
+ * recommended way to safely interact with devices on the SPI bus is to have
+ * a self-contained block like so:
+ *
+ * ```
+ * {
+ * auto lock = AcquireSpiBus(WHATEVER);
+ * // Do some cool things here.
+ * }
+ * ```
+ */
+ SpiLock AcquireSpiBus(ChipSelect cs);
+
+ // Not copyable or movable. There should usually only ever be once instance
+ // of this class, and that instance will likely have a static lifetime.
+ GpioExpander(const GpioExpander&) = delete;
+ GpioExpander& operator=(const GpioExpander&) = delete;
+
+ private:
+ std::mutex cs_mutex_;
+ std::atomic<uint16_t> ports_;
+ std::atomic<uint16_t> inputs_;
+};
+
+} // namespace drivers