Files
2026-01-30 12:57:51 +01:00

120 lines
3.6 KiB
C++

#pragma once
#include <peripherals/i2c_bus.h>
#include <algorithm>
class PCA9685Driver {
public:
static constexpr uint8_t DEFAULT_ADDR = 0x40;
PCA9685Driver(uint8_t addr = DEFAULT_ADDR) : _addr(addr) {}
bool begin() {
if (!I2CBus::instance().probe(_addr)) return false;
reset();
setOscillatorFrequency(25000000);
setPWMFreq(50);
_initialized = true;
return true;
}
void reset() {
writeReg(REG_MODE1, MODE1_RESTART);
vTaskDelay(pdMS_TO_TICKS(10));
}
void sleep() {
uint8_t mode = readReg(REG_MODE1);
writeReg(REG_MODE1, (mode & ~MODE1_RESTART) | MODE1_SLEEP);
vTaskDelay(pdMS_TO_TICKS(5));
}
void wakeup() {
uint8_t mode = readReg(REG_MODE1);
uint8_t wakeMode = mode & ~MODE1_SLEEP;
writeReg(REG_MODE1, wakeMode);
vTaskDelay(pdMS_TO_TICKS(5));
writeReg(REG_MODE1, wakeMode | MODE1_RESTART);
}
void setPWMFreq(float freq) {
freq = std::clamp(freq, 1.0f, 3500.0f);
float prescaleval = ((_oscFreq / (freq * 4096.0f)) + 0.5f) - 1;
if (prescaleval < 3) prescaleval = 3;
if (prescaleval > 255) prescaleval = 255;
uint8_t prescale = static_cast<uint8_t>(prescaleval);
uint8_t oldMode = readReg(REG_MODE1);
uint8_t newMode = (oldMode & ~MODE1_RESTART) | MODE1_SLEEP;
writeReg(REG_MODE1, newMode);
writeReg(REG_PRESCALE, prescale);
writeReg(REG_MODE1, oldMode);
vTaskDelay(pdMS_TO_TICKS(5));
writeReg(REG_MODE1, oldMode | MODE1_RESTART | MODE1_AI);
}
void setOscillatorFrequency(uint32_t freq) { _oscFreq = freq; }
uint8_t setPWM(uint8_t channel, uint16_t on, uint16_t off) {
if (channel > 15) return 1;
uint8_t buf[4] = {static_cast<uint8_t>(on & 0xFF), static_cast<uint8_t>(on >> 8),
static_cast<uint8_t>(off & 0xFF), static_cast<uint8_t>(off >> 8)};
return I2CBus::instance().writeReg(_addr, REG_LED0_ON_L + 4 * channel, buf, 4) == ESP_OK ? 0 : 1;
}
uint8_t setMultiplePWM(const uint16_t* values, uint8_t length) {
if (length > 16) length = 16;
uint8_t buf[64];
for (uint8_t i = 0; i < length; i++) {
uint16_t val = values[i] > 4095 ? 4095 : values[i];
uint8_t* b = &buf[i * 4];
b[0] = 0;
if (val == 0) {
b[1] = 0;
b[2] = 0;
b[3] = FULL_OFF_BIT;
} else if (val == 4095) {
b[1] = FULL_ON_BIT;
b[2] = 0;
b[3] = 0;
} else {
b[1] = 0;
b[2] = val & 0xFF;
b[3] = val >> 8;
}
}
return I2CBus::instance().writeReg(_addr, REG_LED0_ON_L, buf, length * 4) == ESP_OK ? 0 : 1;
}
bool isInitialized() const { return _initialized; }
private:
static constexpr uint8_t REG_MODE1 = 0x00;
static constexpr uint8_t REG_MODE2 = 0x01;
static constexpr uint8_t REG_PRESCALE = 0xFE;
static constexpr uint8_t REG_LED0_ON_L = 0x06;
static constexpr uint8_t MODE1_RESTART = 0x80;
static constexpr uint8_t MODE1_SLEEP = 0x10;
static constexpr uint8_t MODE1_AI = 0x20;
static constexpr uint8_t FULL_ON_BIT = 0x10;
static constexpr uint8_t FULL_OFF_BIT = 0x10;
void writeReg(uint8_t reg, uint8_t val) { I2CBus::instance().writeReg(_addr, reg, &val, 1); }
uint8_t readReg(uint8_t reg) {
uint8_t val = 0;
I2CBus::instance().readReg(_addr, reg, &val, 1);
return val;
}
uint8_t _addr;
bool _initialized = false;
uint32_t _oscFreq = 25000000;
};