PCA9685, fix reset device and add option EXTCLK (#3845)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Stanislav Habich 2023-01-18 01:00:35 +01:00 committed by GitHub
parent bbb0105c2f
commit 582d90ad72
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 52 additions and 16 deletions

View file

@ -1,7 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c from esphome.components import i2c
from esphome.const import CONF_FREQUENCY, CONF_ID from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_EXTERNAL_CLOCK_INPUT
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]
MULTI_CONF = True MULTI_CONF = True
@ -9,21 +9,39 @@ MULTI_CONF = True
pca9685_ns = cg.esphome_ns.namespace("pca9685") pca9685_ns = cg.esphome_ns.namespace("pca9685")
PCA9685Output = pca9685_ns.class_("PCA9685Output", cg.Component, i2c.I2CDevice) PCA9685Output = pca9685_ns.class_("PCA9685Output", cg.Component, i2c.I2CDevice)
CONFIG_SCHEMA = (
def validate_frequency(config):
if config[CONF_EXTERNAL_CLOCK_INPUT]:
if CONF_FREQUENCY in config:
raise cv.Invalid(
"Frequency cannot be set when using an external clock input"
)
return config
if CONF_FREQUENCY not in config:
raise cv.Invalid("Frequency is required")
return config
CONFIG_SCHEMA = cv.All(
cv.Schema( cv.Schema(
{ {
cv.GenerateID(): cv.declare_id(PCA9685Output), cv.GenerateID(): cv.declare_id(PCA9685Output),
cv.Required(CONF_FREQUENCY): cv.All( cv.Optional(CONF_FREQUENCY): cv.All(
cv.frequency, cv.Range(min=23.84, max=1525.88) cv.frequency, cv.Range(min=23.84, max=1525.88)
), ),
cv.Optional(CONF_EXTERNAL_CLOCK_INPUT, default=False): cv.boolean,
} }
) )
.extend(cv.COMPONENT_SCHEMA) .extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x40)) .extend(i2c.i2c_device_schema(0x40)),
validate_frequency,
) )
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID], config[CONF_FREQUENCY]) var = cg.new_Pvariable(config[CONF_ID])
if CONF_FREQUENCY in config:
cg.add(var.set_frequency(config[CONF_FREQUENCY]))
cg.add(var.set_extclk(config[CONF_EXTERNAL_CLOCK_INPUT]))
await cg.register_component(var, config) await cg.register_component(var, config)
await i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)

View file

@ -21,6 +21,7 @@ static const uint8_t PCA9685_REGISTER_LED0 = 0x06;
static const uint8_t PCA9685_REGISTER_PRE_SCALE = 0xFE; static const uint8_t PCA9685_REGISTER_PRE_SCALE = 0xFE;
static const uint8_t PCA9685_MODE1_RESTART = 0b10000000; static const uint8_t PCA9685_MODE1_RESTART = 0b10000000;
static const uint8_t PCA9685_MODE1_EXTCLK = 0b01000000;
static const uint8_t PCA9685_MODE1_AUTOINC = 0b00100000; static const uint8_t PCA9685_MODE1_AUTOINC = 0b00100000;
static const uint8_t PCA9685_MODE1_SLEEP = 0b00010000; static const uint8_t PCA9685_MODE1_SLEEP = 0b00010000;
@ -28,10 +29,13 @@ void PCA9685Output::setup() {
ESP_LOGCONFIG(TAG, "Setting up PCA9685OutputComponent..."); ESP_LOGCONFIG(TAG, "Setting up PCA9685OutputComponent...");
ESP_LOGV(TAG, " Resetting devices..."); ESP_LOGV(TAG, " Resetting devices...");
uint8_t address_tmp = this->address_;
this->set_i2c_address(0x00);
if (!this->write_bytes(PCA9685_REGISTER_SOFTWARE_RESET, nullptr, 0)) { if (!this->write_bytes(PCA9685_REGISTER_SOFTWARE_RESET, nullptr, 0)) {
this->mark_failed(); this->mark_failed();
return; return;
} }
this->set_i2c_address(address_tmp);
if (!this->write_byte(PCA9685_REGISTER_MODE1, PCA9685_MODE1_RESTART | PCA9685_MODE1_AUTOINC)) { if (!this->write_byte(PCA9685_REGISTER_MODE1, PCA9685_MODE1_RESTART | PCA9685_MODE1_AUTOINC)) {
this->mark_failed(); this->mark_failed();
@ -42,14 +46,6 @@ void PCA9685Output::setup() {
return; return;
} }
int pre_scaler = static_cast<int>((25000000 / (4096 * this->frequency_)) - 1);
if (pre_scaler > 255)
pre_scaler = 255;
if (pre_scaler < 3)
pre_scaler = 3;
ESP_LOGV(TAG, " -> Prescaler: %d", pre_scaler);
uint8_t mode1; uint8_t mode1;
if (!this->read_byte(PCA9685_REGISTER_MODE1, &mode1)) { if (!this->read_byte(PCA9685_REGISTER_MODE1, &mode1)) {
this->mark_failed(); this->mark_failed();
@ -60,6 +56,20 @@ void PCA9685Output::setup() {
this->mark_failed(); this->mark_failed();
return; return;
} }
int pre_scaler = 3;
if (this->extclk_) {
mode1 = mode1 | PCA9685_MODE1_EXTCLK;
if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) {
this->mark_failed();
return;
}
} else {
pre_scaler = static_cast<int>((25000000 / (4096 * this->frequency_)) - 1);
pre_scaler = clamp(pre_scaler, 3, 255);
ESP_LOGV(TAG, " -> Prescaler: %d", pre_scaler);
}
if (!this->write_byte(PCA9685_REGISTER_PRE_SCALE, pre_scaler)) { if (!this->write_byte(PCA9685_REGISTER_PRE_SCALE, pre_scaler)) {
this->mark_failed(); this->mark_failed();
return; return;
@ -78,7 +88,12 @@ void PCA9685Output::setup() {
void PCA9685Output::dump_config() { void PCA9685Output::dump_config() {
ESP_LOGCONFIG(TAG, "PCA9685:"); ESP_LOGCONFIG(TAG, "PCA9685:");
ESP_LOGCONFIG(TAG, " Mode: 0x%02X", this->mode_); ESP_LOGCONFIG(TAG, " Mode: 0x%02X", this->mode_);
if (this->extclk_) {
ESP_LOGCONFIG(TAG, " EXTCLK: enabled");
} else {
ESP_LOGCONFIG(TAG, " EXTCLK: disabled");
ESP_LOGCONFIG(TAG, " Frequency: %.0f Hz", this->frequency_); ESP_LOGCONFIG(TAG, " Frequency: %.0f Hz", this->frequency_);
}
if (this->is_failed()) { if (this->is_failed()) {
ESP_LOGE(TAG, "Setting up PCA9685 failed!"); ESP_LOGE(TAG, "Setting up PCA9685 failed!");
} }

View file

@ -37,8 +37,7 @@ class PCA9685Channel : public output::FloatOutput {
/// PCA9685 float output component. /// PCA9685 float output component.
class PCA9685Output : public Component, public i2c::I2CDevice { class PCA9685Output : public Component, public i2c::I2CDevice {
public: public:
PCA9685Output(float frequency, uint8_t mode = PCA9685_MODE_OUTPUT_ONACK | PCA9685_MODE_OUTPUT_TOTEM_POLE) PCA9685Output(uint8_t mode = PCA9685_MODE_OUTPUT_ONACK | PCA9685_MODE_OUTPUT_TOTEM_POLE) : mode_(mode) {}
: frequency_(frequency), mode_(mode) {}
void register_channel(PCA9685Channel *channel); void register_channel(PCA9685Channel *channel);
@ -46,6 +45,8 @@ class PCA9685Output : public Component, public i2c::I2CDevice {
void dump_config() override; void dump_config() override;
float get_setup_priority() const override { return setup_priority::HARDWARE; } float get_setup_priority() const override { return setup_priority::HARDWARE; }
void loop() override; void loop() override;
void set_extclk(bool extclk) { this->extclk_ = extclk; }
void set_frequency(float frequency) { this->frequency_ = frequency; }
protected: protected:
friend PCA9685Channel; friend PCA9685Channel;
@ -58,6 +59,7 @@ class PCA9685Output : public Component, public i2c::I2CDevice {
float frequency_; float frequency_;
uint8_t mode_; uint8_t mode_;
bool extclk_ = false;
uint8_t min_channel_{0xFF}; uint8_t min_channel_{0xFF};
uint8_t max_channel_{0x00}; uint8_t max_channel_{0x00};

View file

@ -219,6 +219,7 @@ CONF_EVENT = "event"
CONF_EXPIRE_AFTER = "expire_after" CONF_EXPIRE_AFTER = "expire_after"
CONF_EXPORT_ACTIVE_ENERGY = "export_active_energy" CONF_EXPORT_ACTIVE_ENERGY = "export_active_energy"
CONF_EXPORT_REACTIVE_ENERGY = "export_reactive_energy" CONF_EXPORT_REACTIVE_ENERGY = "export_reactive_energy"
CONF_EXTERNAL_CLOCK_INPUT = "external_clock_input"
CONF_EXTERNAL_COMPONENTS = "external_components" CONF_EXTERNAL_COMPONENTS = "external_components"
CONF_EXTERNAL_VCC = "external_vcc" CONF_EXTERNAL_VCC = "external_vcc"
CONF_FALLING_EDGE = "falling_edge" CONF_FALLING_EDGE = "falling_edge"