diff --git a/cmake/targets/detector.cmake b/cmake/targets/detector.cmake index fec9942..144c29c 100644 --- a/cmake/targets/detector.cmake +++ b/cmake/targets/detector.cmake @@ -5,6 +5,8 @@ set(DETECTOR_SOURCE_FILES "${PROJECT_SRC_DIR}/serial/i2cdevice.cpp" "${PROJECT_SRC_DIR}/serial/i2cbus.cpp" "${PROJECT_SRC_DIR}/serial/i2cdevices/generalcall.cpp" + "${PROJECT_SRC_DIR}/serial/i2cdevices/pca9554.cpp" + "${PROJECT_SRC_DIR}/serial/i2cdevices/pca9536.cpp" ) set(DETECTOR_HEADER_FILES @@ -16,6 +18,9 @@ set(DETECTOR_HEADER_FILES "${PROJECT_HEADER_DIR}/muonpi/serial/i2cbus.h" "${PROJECT_HEADER_DIR}/muonpi/serial/i2ceeprom.h" "${PROJECT_HEADER_DIR}/muonpi/serial/i2cdevices/generalcall.h" + "${PROJECT_HEADER_DIR}/muonpi/serial/i2cdevices/pca95xx.h" + "${PROJECT_HEADER_DIR}/muonpi/serial/i2cdevices/pca9554.h" + "${PROJECT_HEADER_DIR}/muonpi/serial/i2cdevices/pca9536.h" ) if (NOT LIBMUONPI_FORMAT_ONLY) if (LIBMUONPI_BUILD_DETECTOR) # libraries specific to the Detector library diff --git a/include/muonpi/serial/i2cdevices/pca9536.h b/include/muonpi/serial/i2cdevices/pca9536.h new file mode 100644 index 0000000..5a5134d --- /dev/null +++ b/include/muonpi/serial/i2cdevices/pca9536.h @@ -0,0 +1,138 @@ +#ifndef MUONPI_SERIAL_I2CDEVICES_PCA9536_H +#define MUONPI_SERIAL_I2CDEVICES_PCA9536_H + +#include "muonpi/serial/i2cbus.h" +#include "muonpi/serial/i2cdevices/pca95xx.h" + +#include + +namespace muonpi::serial::devices { + +/** + * @brief The pca9536 class. interact with the PCA9554 8 bit IO extender. + * This implementation follows the datasheet closely: + * https://www.ti.com/lit/ds/symlink/pca9536.pdf + */ +class pca9536 : public pca95xx<4> { +public: + /** + * @brief pca9536 + * @param bus_traffic The bus traffic object from the i2c_bus. + * @param path The path of the i2c bus file descriptor + * @param address The address to use + */ + pca9536(traffic_t& bus_traffic, const std::string& path, i2c_device::address_type address); + + /** + * @brief pca9536 Attempts to auto setup the connection + * @param bus_traffic The bus traffic object from the i2c_bus. + * @param path The path of the i2c bus file descriptor + */ + pca9536(traffic_t& bus_traffic, const std::string& path); + + /** + * @brief identify Attempts to positively identify the device. + * @return true if identification was successful. + */ + [[nodiscard]] auto identify() -> bool override; + + constexpr static std::array addresses {0b1000001}; + + template + using register_t = simple_register; + + struct input_r : public register_t<0x00> { + constexpr static i2c_tag_type register_tag { i2c_register_tag::read }; + + const value_type I4_7 : 4 {}; + value_type I3 : 1 {}; + value_type I2 : 1 {}; + value_type I1 : 1 {}; + value_type I0 : 1 {}; + + [[nodiscard]] constexpr auto get() const noexcept -> value_type override { + return static_cast((I3 << 3U) | (I2 << 2U) | (I1 << 1U) | I0); + } + + constexpr explicit input_r(value_type v) noexcept + : I3 {static_cast((v & 0x08U) >> 3U)} + , I2 {static_cast((v & 0x04U) >> 2U)} + , I1 {static_cast((v & 0x02U) >> 1U)} + , I0 {static_cast(v & 0x01U)} {} + + constexpr explicit input_r() noexcept = default; + }; + + struct output_r : public register_t<0x01> { + constexpr static i2c_tag_type register_tag { i2c_register_tag::read_write }; + + const value_type O4_7 : 4 {0xF}; + value_type O3 : 1 {1}; + value_type O2 : 1 {1}; + value_type O1 : 1 {1}; + value_type O0 : 1 {1}; + + [[nodiscard]] constexpr auto get() const noexcept -> value_type override { + return static_cast((O3 << 3U) | (O2 << 2U) | (O1 << 1U) | O0); + } + + constexpr explicit output_r(value_type v) noexcept + : O3 {static_cast((v & 0x08U) >> 3U)} + , O2 {static_cast((v & 0x04U) >> 2U)} + , O1 {static_cast((v & 0x02U) >> 1U)} + , O0 {static_cast(v & 0x01U)} {} + + constexpr explicit output_r() noexcept = default; + }; + + struct polarity_r : public register_t<0x02> { + constexpr static i2c_tag_type register_tag { i2c_register_tag::read_write }; + + constexpr static value_type invert {1}; + constexpr static value_type retain {0}; + + const value_type N4_7 : 4 {0x0}; + value_type N3 : 1 {retain}; + value_type N2 : 1 {retain}; + value_type N1 : 1 {retain}; + value_type N0 : 1 {retain}; + + [[nodiscard]] constexpr auto get() const noexcept -> value_type override { + return static_cast((N3 << 3U) | (N2 << 2U) | (N1 << 1U) | N0); + } + + constexpr explicit polarity_r(value_type v) noexcept + : N3 {static_cast((v & 0x08U) >> 3U)} + , N2 {static_cast((v & 0x04U) >> 2U)} + , N1 {static_cast((v & 0x02U) >> 1U)} + , N0 {static_cast(v & 0x01U)} {} + constexpr explicit polarity_r() noexcept = default; + }; + + struct configuration_r : public register_t<0x03> { + constexpr static i2c_tag_type register_tag { i2c_register_tag::read_write }; + + constexpr static value_type input {1}; + constexpr static value_type output {0}; + + const value_type C4_7 : 4 {0xF}; + value_type C3 : 1 {input}; + value_type C2 : 1 {input}; + value_type C1 : 1 {input}; + value_type C0 : 1 {input}; + + [[nodiscard]] constexpr auto get() const noexcept -> value_type override { + return static_cast((C3 << 3U) | (C2 << 2U) | (C1 << 1U) | C0); + } + + constexpr explicit configuration_r(value_type v) noexcept + : C3 {static_cast((v & 0x08U) >> 3U)} + , C2 {static_cast((v & 0x04U) >> 2U)} + , C1 {static_cast((v & 0x02U) >> 1U)} + , C0 {static_cast(v & 0x01U)} {} + constexpr explicit configuration_r() noexcept = default; + }; +}; + +} // namespace muonpi::serial::devices +#endif // MUONPI_SERIAL_I2CDEVICES_PCA9536_H diff --git a/include/muonpi/serial/i2cdevices/pca9554.h b/include/muonpi/serial/i2cdevices/pca9554.h new file mode 100644 index 0000000..934d6d8 --- /dev/null +++ b/include/muonpi/serial/i2cdevices/pca9554.h @@ -0,0 +1,169 @@ +#ifndef MUONPI_SERIAL_I2CDEVICES_PCA9554_H +#define MUONPI_SERIAL_I2CDEVICES_PCA9554_H + +#include "muonpi/multiaddressrange.h" +#include "muonpi/serial/i2cbus.h" +#include "muonpi/serial/i2cdevices/pca95xx.h" + +#include + +namespace muonpi::serial::devices { +/** + * @brief The pca9554 class. interact with the PCA9554 8 bit IO extender. + * This implementation follows the datasheet closely: + * https://www.ti.com/lit/ds/symlink/pca9554.pdf + */ +class pca9554 : public pca95xx<8> { +public: + /** + * @brief pca9554 + * @param bus_traffic The bus traffic object from the i2c_bus. + * @param path The path of the i2c bus file descriptor + * @param address The address to use + */ + pca9554(traffic_t& bus_traffic, const std::string& path, i2c_device::address_type address); + + /** + * @brief pca9554 Attempts to auto setup the connection + * @param bus_traffic The bus traffic object from the i2c_bus. + * @param path The path of the i2c bus file descriptor + */ + pca9554(traffic_t& bus_traffic, const std::string& path); + + /** + * @brief identify Attempts to positively identify the device. + * @return true if identification was successful. + */ + [[nodiscard]] auto identify() -> bool override; + + constexpr static multi_address_range addresses { + {address_range {0b0100000, {0b00000111}}, address_range {0b0111000, {0b00000111}}}}; + + template + using register_t = simple_register; + + struct input_r : public register_t<0x00> { + constexpr static i2c_tag_type register_tag { i2c_register_tag::read }; + + value_type I7 : 1 {}; + value_type I6 : 1 {}; + value_type I5 : 1 {}; + value_type I4 : 1 {}; + value_type I3 : 1 {}; + value_type I2 : 1 {}; + value_type I1 : 1 {}; + value_type I0 : 1 {}; + + [[nodiscard]] constexpr auto get() const noexcept -> value_type override { + return static_cast((I7 << 7U) | (I6 << 6U) | (I5 << 5U) | (I4 << 4U) + | (I3 << 3U) | (I2 << 2U) | (I1 << 1U) | I0); + } + + constexpr explicit input_r(value_type v) noexcept + : I7 {static_cast((v & 0x80U) >> 7U)} + , I6 {static_cast((v & 0x40U) >> 6U)} + , I5 {static_cast((v & 0x20U) >> 5U)} + , I4 {static_cast((v & 0x10U) >> 4U)} + , I3 {static_cast((v & 0x08U) >> 3U)} + , I2 {static_cast((v & 0x04U) >> 2U)} + , I1 {static_cast((v & 0x02U) >> 1U)} + , I0 {static_cast(v & 0x01U)} {} + constexpr explicit input_r() noexcept = default; + }; + + struct output_r : public register_t<0x01> { + constexpr static i2c_tag_type register_tag { i2c_register_tag::read_write }; + + value_type O7 : 1 {1}; + value_type O6 : 1 {1}; + value_type O5 : 1 {1}; + value_type O4 : 1 {1}; + value_type O3 : 1 {1}; + value_type O2 : 1 {1}; + value_type O1 : 1 {1}; + value_type O0 : 1 {1}; + + [[nodiscard]] constexpr auto get() const noexcept -> value_type override { + return static_cast((O7 << 7U) | (O6 << 6U) | (O5 << 5U) | (O4 << 4U) + | (O3 << 3U) | (O2 << 2U) | (O1 << 1U) | O0); + } + + constexpr explicit output_r(value_type v) noexcept + : O7 {static_cast((v & 0x80U) >> 7U)} + , O6 {static_cast((v & 0x40U) >> 6U)} + , O5 {static_cast((v & 0x20U) >> 5U)} + , O4 {static_cast((v & 0x10U) >> 4U)} + , O3 {static_cast((v & 0x08U) >> 3U)} + , O2 {static_cast((v & 0x04U) >> 2U)} + , O1 {static_cast((v & 0x02U) >> 1U)} + , O0 {static_cast(v & 0x01U)} {} + constexpr explicit output_r() noexcept = default; + }; + + struct polarity_r : public register_t<0x02> { + constexpr static i2c_tag_type register_tag { i2c_register_tag::read_write }; + + constexpr static value_type invert {1}; + constexpr static value_type retain {0}; + + value_type N7 : 1 {retain}; + value_type N6 : 1 {retain}; + value_type N5 : 1 {retain}; + value_type N4 : 1 {retain}; + value_type N3 : 1 {retain}; + value_type N2 : 1 {retain}; + value_type N1 : 1 {retain}; + value_type N0 : 1 {retain}; + + [[nodiscard]] constexpr auto get() const noexcept -> value_type override { + return static_cast((N7 << 7U) | (N6 << 6U) | (N5 << 5U) | (N4 << 4U) + | (N3 << 3U) | (N2 << 2U) | (N1 << 1U) | N0); + } + + constexpr explicit polarity_r(value_type v) noexcept + : N7 {static_cast((v & 0x80U) >> 7U)} + , N6 {static_cast((v & 0x40U) >> 6U)} + , N5 {static_cast((v & 0x20U) >> 5U)} + , N4 {static_cast((v & 0x10U) >> 4U)} + , N3 {static_cast((v & 0x08U) >> 3U)} + , N2 {static_cast((v & 0x04U) >> 2U)} + , N1 {static_cast((v & 0x02U) >> 1U)} + , N0 {static_cast(v & 0x01U)} {} + constexpr explicit polarity_r() noexcept = default; + }; + + struct configuration_r : public register_t<0x03> { + constexpr static i2c_tag_type register_tag { i2c_register_tag::read_write }; + + constexpr static value_type input {1}; + constexpr static value_type output {0}; + + value_type C7 : 1 {input}; + value_type C6 : 1 {input}; + value_type C5 : 1 {input}; + value_type C4 : 1 {input}; + value_type C3 : 1 {input}; + value_type C2 : 1 {input}; + value_type C1 : 1 {input}; + value_type C0 : 1 {input}; + + [[nodiscard]] constexpr auto get() const noexcept -> value_type override { + return static_cast((C7 << 7U) | (C6 << 6U) | (C5 << 5U) | (C4 << 4U) + | (C3 << 3U) | (C2 << 2U) | (C1 << 1U) | C0); + } + + constexpr explicit configuration_r(value_type v) noexcept + : C7 {static_cast((v & 0x80U) >> 7U)} + , C6 {static_cast((v & 0x40U) >> 6U)} + , C5 {static_cast((v & 0x20U) >> 5U)} + , C4 {static_cast((v & 0x10U) >> 4U)} + , C3 {static_cast((v & 0x08U) >> 3U)} + , C2 {static_cast((v & 0x04U) >> 2U)} + , C1 {static_cast((v & 0x02U) >> 1U)} + , C0 {static_cast(v & 0x01U)} {} + constexpr explicit configuration_r() noexcept = default; + }; +}; + +} // namespace muonpi::serial::devices +#endif // MUONPI_SERIAL_I2CDEVICES_PCA9554_H diff --git a/include/muonpi/serial/i2cdevices/pca95xx.h b/include/muonpi/serial/i2cdevices/pca95xx.h new file mode 100644 index 0000000..dd52645 --- /dev/null +++ b/include/muonpi/serial/i2cdevices/pca95xx.h @@ -0,0 +1,87 @@ +#ifndef MUONPI_SERIAL_I2CDEVICES_PCA95XX_H +#define MUONPI_SERIAL_I2CDEVICES_PCA95XX_H +#include "muonpi/serial/i2cbus.h" +#include "muonpi/serial/i2cdevice.h" + +#include + +namespace muonpi::serial::devices { + +template +concept bits_c = ((B == 4) || (B == 8)); + +template +requires bits_c + /** + * @brief I2C io extender device class. + * This class provides access to i2c 4-bit and 8-bit bidirectional digital i/o + * extenders. + * @note valid template specializations are available for values of the template + * parameter BITS of 4 and 8. + */ + class pca95xx : public i2c_device { +public: + static constexpr std::size_t width {BITS}; + +protected: + pca95xx(traffic_t& bus_traffic, const std::string& path, i2c_device::address_type address); + pca95xx(traffic_t& bus_traffic, + const std::string& path, + const address_iterable auto& addresses); + + template > T> + [[nodiscard]] auto identify_impl() -> bool { + if (flag_set(Flags::Error)) { + return false; + } + if (!present()) { + return false; + } + + const auto input {read()}; + if (!input.has_value()) { + return false; + } + const auto output {read()}; + if (!output.has_value()) { + return false; + } + if (output.value().get() != typename T::output_r {}.get()) { + return false; + } + const auto polarity {read()}; + if (!polarity.has_value()) { + return false; + } + if (polarity.value().get() != typename T::polarity_r {}.get()) { + return false; + } + const auto configuration {read()}; + if (!configuration.has_value()) { + return false; + } + if (configuration.value().get() != typename T::configuration_r {}.get()) { + return false; + } + return true; + } +}; + +/*************************** + * Implementation part + ***************************/ + +template +requires bits_c pca95xx::pca95xx(traffic_t& bus_traffic, + const std::string& path, + i2c_device::address_type address) + : i2c_device {bus_traffic, path, address} {} + +template +requires bits_c pca95xx::pca95xx(traffic_t& bus_traffic, + const std::string& path, + const address_iterable auto& addresses) + : i2c_device {bus_traffic, path, addresses} {} + +} // namespace muonpi::serial::devices +#endif // MUONPI_SERIAL_I2CDEVICES_PCA95XX_H diff --git a/src/serial/i2cdevices/pca9536.cpp b/src/serial/i2cdevices/pca9536.cpp new file mode 100644 index 0000000..6c735ef --- /dev/null +++ b/src/serial/i2cdevices/pca9536.cpp @@ -0,0 +1,18 @@ +#include "muonpi/serial/i2cdevices/pca9536.h" + +namespace muonpi::serial::devices { +pca9536::pca9536(traffic_t& bus_traffic, const std::string& path, i2c_device::address_type address) + : pca95xx<4> {bus_traffic, path, address} { + set_name("PCA9536"); +} + +pca9536::pca9536(traffic_t& bus_traffic, const std::string& path) + : pca95xx<4> {bus_traffic, path, addresses} { + set_name("PCA9536"); +} + +auto pca9536::identify() -> bool { + return identify_impl(); +} + +} // namespace muonpi::serial::devices diff --git a/src/serial/i2cdevices/pca9554.cpp b/src/serial/i2cdevices/pca9554.cpp new file mode 100644 index 0000000..2961375 --- /dev/null +++ b/src/serial/i2cdevices/pca9554.cpp @@ -0,0 +1,18 @@ +#include "muonpi/serial/i2cdevices/pca9554.h" + +namespace muonpi::serial::devices { +pca9554::pca9554(traffic_t& bus_traffic, const std::string& path, i2c_device::address_type address) + : pca95xx<8> {bus_traffic, path, address} { + set_name("PCA9554"); +} + +pca9554::pca9554(traffic_t& bus_traffic, const std::string& path) + : pca95xx<8> {bus_traffic, path, addresses} { + set_name("PCA9554"); +} + +auto pca9554::identify() -> bool { + return identify_impl(); +} + +} // namespace muonpi::serial::devices diff --git a/src/serial/i2cdevices/pca95xx.cpp b/src/serial/i2cdevices/pca95xx.cpp new file mode 100644 index 0000000..b066a17 --- /dev/null +++ b/src/serial/i2cdevices/pca95xx.cpp @@ -0,0 +1 @@ +#include "muonpi/serial/i2cdevices/pca95xx.h"