mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 01:07:45 +01:00
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:
parent
bbb0105c2f
commit
582d90ad72
4 changed files with 52 additions and 16 deletions
|
@ -1,7 +1,7 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
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"]
|
||||
MULTI_CONF = True
|
||||
|
@ -9,21 +9,39 @@ MULTI_CONF = True
|
|||
pca9685_ns = cg.esphome_ns.namespace("pca9685")
|
||||
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.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.Optional(CONF_EXTERNAL_CLOCK_INPUT, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(i2c.i2c_device_schema(0x40))
|
||||
.extend(i2c.i2c_device_schema(0x40)),
|
||||
validate_frequency,
|
||||
)
|
||||
|
||||
|
||||
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 i2c.register_i2c_device(var, config)
|
||||
|
|
|
@ -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_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_SLEEP = 0b00010000;
|
||||
|
||||
|
@ -28,10 +29,13 @@ void PCA9685Output::setup() {
|
|||
ESP_LOGCONFIG(TAG, "Setting up PCA9685OutputComponent...");
|
||||
|
||||
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)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->set_i2c_address(address_tmp);
|
||||
|
||||
if (!this->write_byte(PCA9685_REGISTER_MODE1, PCA9685_MODE1_RESTART | PCA9685_MODE1_AUTOINC)) {
|
||||
this->mark_failed();
|
||||
|
@ -42,14 +46,6 @@ void PCA9685Output::setup() {
|
|||
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;
|
||||
if (!this->read_byte(PCA9685_REGISTER_MODE1, &mode1)) {
|
||||
this->mark_failed();
|
||||
|
@ -60,6 +56,20 @@ void PCA9685Output::setup() {
|
|||
this->mark_failed();
|
||||
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)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
|
@ -78,7 +88,12 @@ void PCA9685Output::setup() {
|
|||
void PCA9685Output::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "PCA9685:");
|
||||
ESP_LOGCONFIG(TAG, " Mode: 0x%02X", this->mode_);
|
||||
ESP_LOGCONFIG(TAG, " Frequency: %.0f Hz", this->frequency_);
|
||||
if (this->extclk_) {
|
||||
ESP_LOGCONFIG(TAG, " EXTCLK: enabled");
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, " EXTCLK: disabled");
|
||||
ESP_LOGCONFIG(TAG, " Frequency: %.0f Hz", this->frequency_);
|
||||
}
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Setting up PCA9685 failed!");
|
||||
}
|
||||
|
|
|
@ -37,8 +37,7 @@ class PCA9685Channel : public output::FloatOutput {
|
|||
/// PCA9685 float output component.
|
||||
class PCA9685Output : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
PCA9685Output(float frequency, uint8_t mode = PCA9685_MODE_OUTPUT_ONACK | PCA9685_MODE_OUTPUT_TOTEM_POLE)
|
||||
: frequency_(frequency), mode_(mode) {}
|
||||
PCA9685Output(uint8_t mode = PCA9685_MODE_OUTPUT_ONACK | PCA9685_MODE_OUTPUT_TOTEM_POLE) : mode_(mode) {}
|
||||
|
||||
void register_channel(PCA9685Channel *channel);
|
||||
|
||||
|
@ -46,6 +45,8 @@ class PCA9685Output : public Component, public i2c::I2CDevice {
|
|||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
void loop() override;
|
||||
void set_extclk(bool extclk) { this->extclk_ = extclk; }
|
||||
void set_frequency(float frequency) { this->frequency_ = frequency; }
|
||||
|
||||
protected:
|
||||
friend PCA9685Channel;
|
||||
|
@ -58,6 +59,7 @@ class PCA9685Output : public Component, public i2c::I2CDevice {
|
|||
|
||||
float frequency_;
|
||||
uint8_t mode_;
|
||||
bool extclk_ = false;
|
||||
|
||||
uint8_t min_channel_{0xFF};
|
||||
uint8_t max_channel_{0x00};
|
||||
|
|
|
@ -219,6 +219,7 @@ CONF_EVENT = "event"
|
|||
CONF_EXPIRE_AFTER = "expire_after"
|
||||
CONF_EXPORT_ACTIVE_ENERGY = "export_active_energy"
|
||||
CONF_EXPORT_REACTIVE_ENERGY = "export_reactive_energy"
|
||||
CONF_EXTERNAL_CLOCK_INPUT = "external_clock_input"
|
||||
CONF_EXTERNAL_COMPONENTS = "external_components"
|
||||
CONF_EXTERNAL_VCC = "external_vcc"
|
||||
CONF_FALLING_EDGE = "falling_edge"
|
||||
|
|
Loading…
Reference in a new issue