diff --git a/include/libhal-expander/pca9685.hpp b/include/libhal-expander/pca9685.hpp index b3a5143..a5f8f1d 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. + * @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 @@ -164,9 +194,39 @@ class pca9685 static_assert(channel <= max_channel_count, "The PCA9685 only has 16 channels!"); - return pwm_channel(this, channel); + return { 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 pwm16_channel - implementation of hal::pwm16_channel 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 { 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..b844189 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,13 @@ 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) {