16#ifdef PCA9685_HEADER_INCLUDED
17#include "../inc/pca9685.hpp"
19#include "../inc/pca9685.hpp"
22template <
typename I2cType>
27template <
typename I2cType>
35template <
typename I2cType>
38 if (!i2c_ || !i2c_->EnsureInitialized()) {
39 setError(Error::I2cWrite);
46 if (!writeMode1(0x00)) {
50 waitOscillatorStartup();
52 last_error_ = Error::None;
56template <
typename I2cType>
58 if (!EnsureInitialized()) {
59 setError(Error::NotInitialized);
62 if (freq_hz < 24.0F || freq_hz > 1526.0F) {
63 setError(Error::OutOfRange);
66 uint8_t prescale = calcPrescale(freq_hz);
68 if (!readReg(
static_cast<uint8_t
>(Register::MODE1), old_mode)) {
72 if (!writeMode1(
static_cast<uint8_t
>(old_mode | MODE1_SLEEP_))) {
75 if (!writeReg(
static_cast<uint8_t
>(Register::PRE_SCALE), prescale)) {
78 if ((old_mode & MODE1_SLEEP_) == 0) {
82 if (!restartAfterSleep(old_mode,
true)) {
86 last_error_ = Error::None;
90template <
typename I2cType>
92 uint16_t off_time)
noexcept {
93 if (!EnsureInitialized()) {
94 setError(Error::NotInitialized);
97 if (channel >= MAX_CHANNELS_ || on_time > MAX_PWM_ || off_time > MAX_PWM_) {
98 setError(Error::OutOfRange);
101 uint8_t reg =
static_cast<uint8_t
>(Register::LED0_ON_L) + (4 * channel);
102 const auto data = pwmFrame(on_time, off_time);
103 if (!writeRegBlock(reg, data.data(), 4)) {
106 last_error_ = Error::None;
110template <
typename I2cType>
114 duty = ::std::max(duty, 0.0F);
115 duty = ::std::min(duty, 1.0F);
119 return SetChannelFullOn(channel);
122 auto off_time =
static_cast<uint16_t
>(lroundf((duty * MAX_PWM_)));
123 return SetPwm(channel, 0, off_time);
126template <
typename I2cType>
128 if (!EnsureInitialized()) {
129 setError(Error::NotInitialized);
132 if (on_time > MAX_PWM_ || off_time > MAX_PWM_) {
133 setError(Error::OutOfRange);
136 const auto data = pwmFrame(on_time, off_time);
137 if (!writeRegBlock(
static_cast<uint8_t
>(Register::ALL_LED_ON_L), data.data(), 4)) {
140 last_error_ = Error::None;
144template <
typename I2cType>
146 if (!EnsureInitialized()) {
147 setError(Error::NotInitialized);
150 if (!readReg(
static_cast<uint8_t
>(Register::PRE_SCALE), prescale)) {
153 last_error_ = Error::None;
159template <
typename I2cType>
161 if (!EnsureInitialized()) {
162 setError(Error::NotInitialized);
166 if (!readReg(
static_cast<uint8_t
>(Register::MODE1), mode1)) {
169 if (!writeMode1(
static_cast<uint8_t
>(mode1 | MODE1_SLEEP_))) {
172 last_error_ = Error::None;
176template <
typename I2cType>
178 if (!EnsureInitialized()) {
179 setError(Error::NotInitialized);
183 if (!readReg(
static_cast<uint8_t
>(Register::MODE1), mode1)) {
186 const bool asleep = (mode1 & MODE1_SLEEP_) != 0;
187 const bool restart = (mode1 & MODE1_RESTART_) != 0;
188 if (asleep || restart) {
191 if (!restartAfterSleep(mode1, restart)) {
195 last_error_ = Error::None;
201template <
typename I2cType>
203 if (!EnsureInitialized()) {
204 setError(Error::NotInitialized);
207 return modifyReg(
static_cast<uint8_t
>(Register::MODE2), 0x10,
208 invert ?
static_cast<uint8_t
>(0x10U) :
static_cast<uint8_t
>(0x00U));
211template <
typename I2cType>
213 if (!EnsureInitialized()) {
214 setError(Error::NotInitialized);
217 return modifyReg(
static_cast<uint8_t
>(Register::MODE2), 0x04,
218 totem_pole ?
static_cast<uint8_t
>(0x04U) :
static_cast<uint8_t
>(0x00U));
221template <
typename I2cType>
224 setError(Error::InvalidParam);
234template <
typename I2cType>
236 if (!EnsureInitialized()) {
237 setError(Error::NotInitialized);
240 if (channel >= MAX_CHANNELS_) {
241 setError(Error::OutOfRange);
244 uint8_t reg =
static_cast<uint8_t
>(Register::LED0_ON_L) + (4 * channel);
246 ::std::array<uint8_t, 4> data = {0x00, 0x10, 0x00, 0x00};
247 if (!writeRegBlock(reg, data.data(), 4)) {
250 last_error_ = Error::None;
254template <
typename I2cType>
256 if (!EnsureInitialized()) {
257 setError(Error::NotInitialized);
260 if (channel >= MAX_CHANNELS_) {
261 setError(Error::OutOfRange);
264 uint8_t reg =
static_cast<uint8_t
>(Register::LED0_ON_L) + (4 * channel);
266 ::std::array<uint8_t, 4> data = {0x00, 0x00, 0x00, 0x10};
267 if (!writeRegBlock(reg, data.data(), 4)) {
270 last_error_ = Error::None;
276template <
typename I2cType>
281 for (
int attempt = 0; attempt <= retries_; ++attempt) {
282 if (i2c_->Write(addr_, reg, &value, 1)) {
285 if (attempt < retries_ && retry_delay_) {
289 setError(Error::I2cWrite);
293template <
typename I2cType>
298 for (
int attempt = 0; attempt <= retries_; ++attempt) {
299 if (i2c_->Read(addr_, reg, &value, 1)) {
302 if (attempt < retries_ && retry_delay_) {
306 setError(Error::I2cRead);
310template <
typename I2cType>
312 size_t len)
noexcept {
316 for (
int attempt = 0; attempt <= retries_; ++attempt) {
317 if (i2c_->Write(addr_, reg, data, len)) {
320 if (attempt < retries_ && retry_delay_) {
324 setError(Error::I2cWrite);
328template <
typename I2cType>
333 for (
int attempt = 0; attempt <= retries_; ++attempt) {
334 if (i2c_->Read(addr_, reg, data, len)) {
337 if (attempt < retries_ && retry_delay_) {
341 setError(Error::I2cRead);
345template <
typename I2cType>
349 const auto mode1 =
static_cast<uint8_t
>((value & ~MODE1_RESTART_) | MODE1_AI_);
350 return writeReg(
static_cast<uint8_t
>(Register::MODE1), mode1);
353template <
typename I2cType>
355 const auto awake =
static_cast<uint8_t
>(mode1 & ~MODE1_SLEEP_);
356 if (!writeMode1(awake)) {
359 waitOscillatorStartup();
363 return writeReg(
static_cast<uint8_t
>(Register::MODE1),
364 static_cast<uint8_t
>((awake & ~MODE1_RESTART_) | MODE1_AI_ | MODE1_RESTART_));
367template <
typename I2cType>
369 if constexpr (
requires(I2cType& bus, uint32_t us) { bus.DelayUs(us); }) {
370 i2c_->DelayUs(OSC_STARTUP_US_);
377 for (uint32_t i = 0; i < OSC_STARTUP_FALLBACK_READS_; ++i) {
378 (void)i2c_->Read(addr_,
static_cast<uint8_t
>(Register::MODE1), &scratch, 1);
383template <
typename I2cType>
385 uint16_t off_time)
noexcept {
386 if (on_time == off_time) {
389 return {0x00, 0x00, 0x00, 0x10};
391 return {
static_cast<uint8_t
>(on_time & 0xFF),
static_cast<uint8_t
>((on_time >> 8) & 0x0F),
392 static_cast<uint8_t
>(off_time & 0xFF),
static_cast<uint8_t
>((off_time >> 8) & 0x0F)};
395template <
typename I2cType>
398 if (!readReg(reg, current)) {
401 uint8_t new_val = (current &
static_cast<uint8_t
>(~mask)) | (value & mask);
402 if (!writeReg(reg, new_val)) {
405 last_error_ = Error::None;
409template <
typename I2cType>
411 float prescale_val = (
static_cast<float>(OSC_FREQ_) / (4096.0F * freq_hz)) - 1.0F;
412 prescale_val = ::std::max(prescale_val, 3.0F);
413 prescale_val = ::std::min(prescale_val, 255.0F);
414 return static_cast<uint8_t
>(lroundf(prescale_val));
bool SetPwmFreq(float freq_hz) noexcept
Set the PWM frequency for all channels.
Definition pca9685.ipp:57
bool SetPwm(uint8_t channel, uint16_t on_time, uint16_t off_time) noexcept
Set the PWM on/off time for a channel.
Definition pca9685.ipp:91
bool SetAllPwm(uint16_t on_time, uint16_t off_time) noexcept
Set all channels to the same PWM value.
Definition pca9685.ipp:127
uint8_t calcPrescale(float freq_hz) const noexcept
Compute prescale value for given frequency.
Definition pca9685.ipp:410
bool Wake() noexcept
Wake the PCA9685 from sleep mode.
Definition pca9685.ipp:177
bool SetOutputInvert(bool invert) noexcept
Set output polarity inversion.
Definition pca9685.ipp:202
static ::std::array< uint8_t, 4 > pwmFrame(uint16_t on_time, uint16_t off_time) noexcept
Build the four LEDn / ALL_LED bytes for ON and OFF ticks; equal ticks give full OFF.
Definition pca9685.ipp:384
bool EnsureInitialized() noexcept
Ensure the driver and I2C bus are initialized (lazy initialization).
Definition pca9685.ipp:28
bool Reset() noexcept
Reset the device to its power-on default state.
Definition pca9685.ipp:36
PCA9685(I2cType *bus, uint8_t address)
Construct a new PCA9685 driver instance.
Definition pca9685.ipp:23
void waitOscillatorStartup() noexcept
Wait OSC_STARTUP_US_ through I2cType::DelayUs(), or with MODE1 reads if the port has none.
Definition pca9685.ipp:368
bool readReg(uint8_t reg, uint8_t &value) noexcept
Read one byte from a register.
Definition pca9685.ipp:294
bool SetDuty(uint8_t channel, float duty) noexcept
Set the duty cycle for a channel (0.0-1.0).
Definition pca9685.ipp:113
bool SetOutputEnable(bool enable) noexcept
Enable or blank all 16 outputs with the OE pin.
Definition pca9685.ipp:222
bool readRegBlock(uint8_t reg, uint8_t *data, size_t len) noexcept
Read a block of bytes from a register.
Definition pca9685.ipp:329
bool SetChannelFullOn(uint8_t channel) noexcept
Set a channel to fully ON (100% duty, no PWM).
Definition pca9685.ipp:235
bool modifyReg(uint8_t reg, uint8_t mask, uint8_t value) noexcept
Read-modify-write a single register.
Definition pca9685.ipp:396
bool Sleep() noexcept
Put the PCA9685 into low-power sleep mode.
Definition pca9685.ipp:160
bool SetChannelFullOff(uint8_t channel) noexcept
Set a channel to fully OFF (0% duty, no PWM).
Definition pca9685.ipp:255
bool writeMode1(uint8_t value) noexcept
Write MODE1 with AI forced to 1 and RESTART forced to 0.
Definition pca9685.ipp:346
bool restartAfterSleep(uint8_t mode1, bool restart) noexcept
Clear SLEEP, wait for the oscillator, then optionally write RESTART (datasheet 7.3....
Definition pca9685.ipp:354
bool GetPrescale(uint8_t &prescale) noexcept
Get the current prescale value (frequency divider).
Definition pca9685.ipp:145
bool writeReg(uint8_t reg, uint8_t value) noexcept
Write one byte to a register.
Definition pca9685.ipp:277
bool writeRegBlock(uint8_t reg, const uint8_t *data, size_t len) noexcept
Write a block of bytes to a register.
Definition pca9685.ipp:311
bool SetOutputDriverMode(bool totem_pole) noexcept
Set the output driver mode.
Definition pca9685.ipp:212
@ ACTIVE
Pin function is asserted.
@ INACTIVE
Pin function is deasserted.
@ OE
Output Enable (active-low on the physical pin)