unfinished pca9634 component

This commit is contained in:
Samuel Sieb
2022-05-16 01:04:04 -07:00
parent 81d44929f6
commit 63db603e78
5 changed files with 288 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
# PCA9634 output component
This component is not finished. I'm submitting it only for reference.

View File

@@ -0,0 +1,39 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c
from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_INVERTED
DEPENDENCIES = ['i2c']
MULTI_CONF = True
pca9634_ns = cg.esphome_ns.namespace('pca9634')
PCA9634GroupMode = mcp23008_ns.enum('PCA9634GroupMode')
PCA9634_GROUP_MODES = {
'DIM': PCA9634GroupMode.PCA9634_MODE2_DIM,
'BLINK': PCA9634GroupMode.PCA9634_MODE2_BLINK,
}
PCA9634OutputMode = mcp23008_ns.enum('PCA9634OutputMode')
PCA9634_OUTPUT_MODES = {
'TOTEM_POLE': PCA9634OutputMode.PCA9634_MODE2_TOTEM_POLE,
'OPEN_DRAIN': PCA9634OutputMode.PCA9634_MODE2_OPEN_DRAIN,
}
PCA9634Output = pca9634_ns.class_('PCA9345Output', cg.Component, i2c.I2CDevice)
CONF_GROUP_MODE = 'group_mode'
CONF_OUTPUT_MODE = 'output_mode'
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(PCA9634Output),
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
cv.Optional(CONF_GROUP_MODE, default="DIM"): cv.enum(PCA9634_GROUP_MODES, upper=True),
cv.Optional(CONF_OUTPUT_MODE, default="TOTEM_POLE"): cv.enum(PCA9634_OUTPUT_MODES, upper=True),
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(None))
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)

View File

@@ -0,0 +1,25 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import output
from esphome.const import CONF_CHANNEL, CONF_ID
from . import PCA9634Output, pca9634_ns
DEPENDENCIES = ['pca9634']
PCA9634Channel = pca9634_ns.class_('PCA9634Channel', output.FloatOutput)
CONF_PCA9634_ID = 'pca9634_id'
CONF_ISOLATE = 'isolate'
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({
cv.Required(CONF_ID): cv.declare_id(PCA9634Channel),
cv.GenerateID(CONF_PCA9634_ID): cv.use_id(PCA9634Output),
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=9),
cv.Optional(CONF_ISOLATE, default=False): cv.boolean,
})
async def to_code(config):
paren = await cg.get_variable(config[CONF_PCA9634_ID])
rhs = paren.create_channel(config[CONF_CHANNEL])
var = cg.Pvariable(config[CONF_ID], rhs)
await output.register_output(var, config)

View File

@@ -0,0 +1,140 @@
#include "pca9634_output.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace pca9634 {
static const char *TAG = "pca9634";
static const uint8_t PCA9634_ADDR_SOFTWARE_RESET = 0x06;
static const uint8_t PCA9634_MODE2_BLINK = 0x20;
static const uint8_t PCA9634_MODE2_INVERTED = 0x10;
static const uint8_t PCA9634_MODE2_OUTPUT_ONACK = 0x08;
static const uint8_t PCA9634_MODE2_OUTPUT_TOTEM_POLE = 0x04;
static const uint8_t PCA9634_MODE2_OUTNE_DEFAULT = 0x01;
static const uint8_t PCA9634_REGISTER_MODE1 = 0x00;
static const uint8_t PCA9634_REGISTER_MODE2 = 0x01;
static const uint8_t PCA9634_REGISTER_LED0 = 0x02;
static const uint8_t PCA9634_REGISTER_PRE_SCALE = 0xFE;
void PCA9634Output::setup() {
ESP_LOGCONFIG(TAG, "Setting up PCA9634OutputComponent...");
uint8_t data[14];
ESP_LOGV(TAG, " Resetting device...");
data[0] = 0xa5; data[1] = 0x5a;
if (!this->write_bytes_raw(PCA9634_ADDR_SOFTWARE_RESET, data, 2)) {
this->mark_failed();
return;
}
data[0] = 0; // MODE1
data[1] = this->group_mode_ | this->output_mode_ | PCA9634_MODE2_OUTNE_DEFAULT; // MODE2
if (this->inverted_)
data[1] |= PCA9634_MODE2_INVERTED;
if (!this->write_byte(PCA9685_REGISTER_MODE1, PCA9685_MODE1_RESTART | PCA9685_MODE1_AUTOINC)) {
this->mark_failed();
return;
}
if (!this->write_byte(PCA9685_REGISTER_MODE2, this->mode_)) {
this->mark_failed();
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();
return;
}
mode1 = (mode1 & ~PCA9685_MODE1_RESTART) | PCA9685_MODE1_SLEEP;
if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) {
this->mark_failed();
return;
}
if (!this->write_byte(PCA9685_REGISTER_PRE_SCALE, pre_scaler)) {
this->mark_failed();
return;
}
mode1 = (mode1 & ~PCA9685_MODE1_SLEEP) | PCA9685_MODE1_RESTART;
if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) {
this->mark_failed();
return;
}
delayMicroseconds(500);
this->loop();
}
void PCA9634Output::dump_config() {
ESP_LOGCONFIG(TAG, "PCA9634:");
ESP_LOGCONFIG(TAG, " Mode: 0x%02X", this->mode_);
if (this->is_failed()) {
ESP_LOGE(TAG, "Setting up PCA9634 failed!");
}
}
void PCA9634Output::loop() {
if (!this->update_)
return;
const uint16_t num_channels = this->max_channel_ - this->min_channel_ + 1;
for (uint8_t channel = this->min_channel_; channel <= this->max_channel_; channel++) {
uint16_t phase_begin = uint16_t(channel - this->min_channel_) / num_channels * 4096;
uint16_t phase_end;
uint16_t amount = this->pwm_amounts_[channel];
if (amount == 0) {
phase_end = 4096;
} else if (amount >= 4096) {
phase_begin = 4096;
phase_end = 0;
} else {
phase_end = phase_begin + amount;
if (phase_end >= 4096)
phase_end -= 4096;
}
ESP_LOGVV(TAG, "Channel %02u: amount=%04u phase_begin=%04u phase_end=%04u", channel, amount, phase_begin,
phase_end);
uint8_t data[4];
data[0] = phase_begin & 0xFF;
data[1] = (phase_begin >> 8) & 0xFF;
data[2] = phase_end & 0xFF;
data[3] = (phase_end >> 8) & 0xFF;
uint8_t reg = PCA9634_REGISTER_LED0 + 4 * channel;
if (!this->write_bytes(reg, data, 4)) {
this->status_set_warning();
return;
}
}
this->status_clear_warning();
this->update_ = false;
}
PCA9634Channel *PCA9634Output::create_channel(uint8_t channel) {
return new PCA9634Channel(this, channel);
}
void PCA9634Channel::write_state(float state) {
uint8_t value = static_cast<uint8_t>(roundf(state * 255));
this->parent_->set_channel_value_(this->channel_, value);
}
} // namespace pca9634
} // namespace esphome

View File

@@ -0,0 +1,80 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/output/float_output.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace pca9634 {
/// Inverts polarity of channel output signal
extern const uint8_t PCA9685_MODE_INVERTED;
/// Channel update happens upon ACK (post-set) rather than on STOP (endTransmission)
extern const uint8_t PCA9685_MODE_OUTPUT_ONACK;
/// Use a totem-pole (push-pull) style output rather than an open-drain structure.
extern const uint8_t PCA9685_MODE_OUTPUT_TOTEM_POLE;
/// For active low output enable, sets channel output to high-impedance state
extern const uint8_t PCA9685_MODE_OUTNE_HIGHZ;
/// Similarly, sets channel output to high if in totem-pole mode, otherwise
extern const uint8_t PCA9685_MODE_OUTNE_LOW;
enum PCA9634GroupMode {
PCA9634_MODE2_DIM = 0x00,
PCA9634_MODE2_BLINK = 0x20,
};
enum PCA9634OutputMode {
PCA9634_MODE2_OPEN_DRAIN = 0x00,
PCA9634_MODE2_TOTEM_POLE = 0x04,
};
class PCA9634Output;
class PCA9634Channel : public output::FloatOutput {
public:
PCA9634Channel(PCA9634Output *parent, uint8_t channel) : parent_(parent), channel_(channel) {}
protected:
void write_state(float state) override;
PCA9634Output *parent_;
uint8_t channel_;
};
/// PCA9634 float output component
class PCA9634Output : public Component, public i2c::I2CDevice {
public:
PCA9685Output();
PCA9685Channel *create_channel(uint8_t channel);
void setup() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::HARDWARE; }
void loop() override;
void set_group_mode(PCA9634GroupMode mode) { this->group_mode_ = mode; }
void set_output_mode(PCA9634OutputMode mode) { this->output_mode_ = mode; }
void set_inverted(boolean inverted) { this->inverted_ = inverted; }
protected:
friend PCA9634Channel;
void set_channel_value_(uint8_t channel, uint8_t value) {
if (this->pwm_values_[channel] != value) {
this->update_ = true;
this->pwm_values_[channel] = value;
}
}
PCA9634GroupMode group_mode_{PCA9634_MODE2_DIM};
PCA9634OutputMode output_mode_{PCA9634_MODE2_TOTEM_POLE};
boolean inverted_{false};
uint8_t pwm_values_[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint8_t grppwm_{0};
uint8_t grpfreq_{0};
bool update_{false};
};
} // namespace pca9634
} // namespace esphome