From 0745d0b10d0c30e1e07b8c6b5c32c31abbd1c617 Mon Sep 17 00:00:00 2001 From: vihashah Date: Sat, 24 Jan 2026 21:41:42 -0800 Subject: [PATCH 1/3] updated pca code to work with new pwm --- include/libhal-expander/pca9685.hpp | 62 ++++++++++++++++++++++++++++- src/pca9685.cpp | 26 +++++++++++- 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/include/libhal-expander/pca9685.hpp b/include/libhal-expander/pca9685.hpp index b3a5143..604dc94 100644 --- a/include/libhal-expander/pca9685.hpp +++ b/include/libhal-expander/pca9685.hpp @@ -93,6 +93,34 @@ class pca9685 friend class pca9685; }; + /** + * @brief Representation & implementation of a pca9685 pwm16_channel + * + * This allows the user to only retrieve the frequency, and update duty cycle for any channel. + */ + class pwm16_channel : public hal::pwm16_channel + { + private: + pwm16_channel(pca9685* p_pca9685, hal::byte p_channel); + + /** + * @brief Retrieves the current frequency of the entire device. + */ + u32 driver_frequency() override; + /** + * @brief Set the duty cycle of an individual channel + * + * @param p_duty_cycle - the desired pwm duty cycle from 0 - 65536 + */ + void driver_duty_cycle(u16 p_duty_cycle) override; + + pca9685* m_pca9685; + hal::byte m_channel; + + friend class pca9685; + }; + + /** * @brief Enumeration describing the pin state choices if the OE pin is active * @@ -112,7 +140,7 @@ class pca9685 /** * @brief Collection of settings that can be configured * - */ + */ struct settings { /// Invert the voltage for all pins. @@ -153,6 +181,7 @@ class pca9685 * * NOTE: If two pwm channel objects are created with the same channel number, * both objects will be able to control the individual pin. + * NOTE: Prefer PWM16_channel version instead of this one as it will soon be deprecated. * * @tparam channel - Which channel pin to get. Can be from 0 to 15. * @return pwm_channel - implementation of hal::pwm for an individual pin on @@ -167,6 +196,35 @@ class pca9685 return pwm_channel(this, channel); } + /** + * @brief Get a pwm16 channel object + * + * NOTE: If two pwm channel objects are created with the same channel number, + * both objects will be able to control the individual pin. + * + * @tparam channel - Which channel pin to get. Can be from 0 to 15. + * @return pwm_channel - implementation of hal::pwm for an individual pin on + * the pca9685. + */ + template + pwm16_channel get_pwm16_channel() + { + static_assert(channel <= max_channel_count, + "The PCA9685 only has 16 channels!"); + + return pwm16_channel(this, channel); + } + + /** + * @brief Update's the entire device's frequency. + * + * @param p_frequency - frequency to set the whole pca9685 device to. + * @throws hal::argument_out_of_domain - if the frequency is outside of the + * available frequency ranges. + */ + void set_pwm_group_frequency(hal::hertz p_frequency) { + set_channel_frequency(p_frequency); + } /** * @brief Configure the device * @@ -180,9 +238,11 @@ class pca9685 private: void set_channel_frequency(hal::hertz p_frequency); void set_channel_duty_cycle(float p_duty_cycle, hal::byte p_channel); + hertz get_frequency(); hal::i2c* m_i2c; hal::byte m_address; settings m_settings{}; + hertz m_current_frequency; }; } // namespace hal::expander diff --git a/src/pca9685.cpp b/src/pca9685.cpp index ae3e572..8be1222 100644 --- a/src/pca9685.cpp +++ b/src/pca9685.cpp @@ -39,6 +39,24 @@ constexpr hal::byte pwm_channel_address(hal::byte p_channel) (p_channel * byte_per_pwm_channel)); } +pca9685::pwm16_channel::pwm16_channel(pca9685* p_pca9685, hal::byte p_channel) + : m_pca9685(p_pca9685) + , m_channel(p_channel) +{ +} + +void pca9685::pwm16_channel::driver_duty_cycle(u16 p_duty_cycle) +{ + u16 make_12_bit = (p_duty_cycle >> 4) && 0xFFF; + float duty_cycle = static_cast(make_12_bit) / 0xFFF; + m_pca9685->set_channel_duty_cycle(duty_cycle, m_channel); +} + +u32 pca9685::pwm16_channel::driver_frequency() +{ + return m_pca9685->get_frequency(); +} + pca9685::pca9685(hal::i2c& p_i2c, hal::byte p_address, std::optional p_settings) @@ -117,11 +135,15 @@ void pca9685::set_channel_frequency(hal::hertz p_frequency) m_address, std::array{ prescaler_address, prescale_value_byte }, hal::never_timeout()); - + // Configure device back to what it was before which may or may not be asleep configure(original_settings); + m_current_frequency = p_frequency; +} +hertz pca9685::get_frequency() +{ + return m_current_frequency; } - // NOLINTNEXTLINE void pca9685::set_channel_duty_cycle(float p_duty_cycle, hal::byte p_channel) { From 45c8ffda27088ce18f9b4d0e530cf171dd5aafe1 Mon Sep 17 00:00:00 2001 From: vihashah Date: Sat, 24 Jan 2026 21:41:42 -0800 Subject: [PATCH 2/3] updated pca code to work with new pwm --- include/libhal-expander/pca9685.hpp | 62 +++++++++++++++++++++++++++++ src/pca9685.cpp | 24 ++++++++++- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/include/libhal-expander/pca9685.hpp b/include/libhal-expander/pca9685.hpp index b3a5143..2c861f6 100644 --- a/include/libhal-expander/pca9685.hpp +++ b/include/libhal-expander/pca9685.hpp @@ -93,6 +93,34 @@ class pca9685 friend class pca9685; }; + /** + * @brief Representation & implementation of a pca9685 pwm16_channel + * + * This allows the user to only retrieve the frequency, and update duty cycle + * for any channel. + */ + class pwm16_channel : public hal::pwm16_channel + { + private: + pwm16_channel(pca9685* p_pca9685, hal::byte p_channel); + + /** + * @brief Retrieves the current frequency of the entire device. + */ + u32 driver_frequency() override; + /** + * @brief Set the duty cycle of an individual channel + * + * @param p_duty_cycle - the desired pwm duty cycle from 0 - 65536 + */ + void driver_duty_cycle(u16 p_duty_cycle) override; + + pca9685* m_pca9685; + hal::byte m_channel; + + friend class pca9685; + }; + /** * @brief Enumeration describing the pin state choices if the OE pin is active * @@ -153,6 +181,8 @@ class pca9685 * * NOTE: If two pwm channel objects are created with the same channel number, * both objects will be able to control the individual pin. + * NOTE: Prefer PWM16_channel version instead of this one as it will soon be + * deprecated. * * @tparam channel - Which channel pin to get. Can be from 0 to 15. * @return pwm_channel - implementation of hal::pwm for an individual pin on @@ -167,6 +197,36 @@ class pca9685 return pwm_channel(this, channel); } + /** + * @brief Get a pwm16 channel object + * + * NOTE: If two pwm channel objects are created with the same channel number, + * both objects will be able to control the individual pin. + * + * @tparam channel - Which channel pin to get. Can be from 0 to 15. + * @return pwm_channel - implementation of hal::pwm for an individual pin on + * the pca9685. + */ + template + pwm16_channel get_pwm16_channel() + { + static_assert(channel <= max_channel_count, + "The PCA9685 only has 16 channels!"); + + return pwm16_channel(this, channel); + } + + /** + * @brief Update's the entire device's frequency. + * + * @param p_frequency - frequency to set the whole pca9685 device to. + * @throws hal::argument_out_of_domain - if the frequency is outside of the + * available frequency ranges. + */ + void set_pwm_group_frequency(hal::hertz p_frequency) + { + set_channel_frequency(p_frequency); + } /** * @brief Configure the device * @@ -180,9 +240,11 @@ class pca9685 private: void set_channel_frequency(hal::hertz p_frequency); void set_channel_duty_cycle(float p_duty_cycle, hal::byte p_channel); + hertz get_frequency(); hal::i2c* m_i2c; hal::byte m_address; settings m_settings{}; + hertz m_current_frequency; }; } // namespace hal::expander diff --git a/src/pca9685.cpp b/src/pca9685.cpp index ae3e572..d1b78f8 100644 --- a/src/pca9685.cpp +++ b/src/pca9685.cpp @@ -39,6 +39,24 @@ constexpr hal::byte pwm_channel_address(hal::byte p_channel) (p_channel * byte_per_pwm_channel)); } +pca9685::pwm16_channel::pwm16_channel(pca9685* p_pca9685, hal::byte p_channel) + : m_pca9685(p_pca9685) + , m_channel(p_channel) +{ +} + +void pca9685::pwm16_channel::driver_duty_cycle(u16 p_duty_cycle) +{ + u16 make_12_bit = (p_duty_cycle >> 4) && 0xFFF; + float duty_cycle = static_cast(make_12_bit) / 0xFFF; + m_pca9685->set_channel_duty_cycle(duty_cycle, m_channel); +} + +u32 pca9685::pwm16_channel::driver_frequency() +{ + return m_pca9685->get_frequency(); +} + pca9685::pca9685(hal::i2c& p_i2c, hal::byte p_address, std::optional p_settings) @@ -120,8 +138,12 @@ void pca9685::set_channel_frequency(hal::hertz p_frequency) // Configure device back to what it was before which may or may not be asleep configure(original_settings); + m_current_frequency = p_frequency; +} +hertz pca9685::get_frequency() +{ + return m_current_frequency; } - // NOLINTNEXTLINE void pca9685::set_channel_duty_cycle(float p_duty_cycle, hal::byte p_channel) { From f6fafc4c70ea12025b88757c7a4262fe8ba69608 Mon Sep 17 00:00:00 2001 From: vihashah Date: Sun, 25 Jan 2026 13:27:45 -0800 Subject: [PATCH 3/3] pr changes --- include/libhal-expander/pca9685.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/libhal-expander/pca9685.hpp b/include/libhal-expander/pca9685.hpp index 2c861f6..a5f8f1d 100644 --- a/include/libhal-expander/pca9685.hpp +++ b/include/libhal-expander/pca9685.hpp @@ -181,8 +181,8 @@ class pca9685 * * NOTE: If two pwm channel objects are created with the same channel number, * both objects will be able to control the individual pin. - * NOTE: Prefer PWM16_channel version instead of this one as it will soon be - * deprecated. + * @deprecated: Prefer PWM16_channel version instead of this one as it will + * soon be deprecated. * * @tparam channel - Which channel pin to get. Can be from 0 to 15. * @return pwm_channel - implementation of hal::pwm for an individual pin on @@ -194,7 +194,7 @@ class pca9685 static_assert(channel <= max_channel_count, "The PCA9685 only has 16 channels!"); - return pwm_channel(this, channel); + return { this, channel }; } /** @@ -204,8 +204,8 @@ class pca9685 * both objects will be able to control the individual pin. * * @tparam channel - Which channel pin to get. Can be from 0 to 15. - * @return pwm_channel - implementation of hal::pwm for an individual pin on - * the pca9685. + * @return pwm16_channel - implementation of hal::pwm16_channel for an + * individual pin on the pca9685. */ template pwm16_channel get_pwm16_channel() @@ -213,7 +213,7 @@ class pca9685 static_assert(channel <= max_channel_count, "The PCA9685 only has 16 channels!"); - return pwm16_channel(this, channel); + return { this, channel }; } /**