mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 01:07:45 +01:00
Add support for TI TLC59208F (#718)
* Add support for TI TLC59208F The chip is a 8-BIT FM+ I2C BUS LED DRIVER with 8 open-drain output channels. Its features include: - 256 linear levels - group dimming - group blinking - 64 slave addresses - customizable sub addresses and all call address - output update on stop or on ACK - 3.3V or 5V supply with 5V tolerant IO - no glitch startup - 50mA / output continuous current up to 17V * Convert macro to uint8_t Variables had to be renamed, clang-format would protest against mixed case in global variable name. * Change gen-call reset to use the correct i2c bus
This commit is contained in:
parent
5f2808ec2f
commit
9d7f76773d
5 changed files with 310 additions and 0 deletions
20
esphome/components/tlc59208f/__init__.py
Normal file
20
esphome/components/tlc59208f/__init__.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
MULTI_CONF = True
|
||||
|
||||
tlc59208f_ns = cg.esphome_ns.namespace('tlc59208f')
|
||||
TLC59208FOutput = tlc59208f_ns.class_('TLC59208FOutput', cg.Component, i2c.I2CDevice)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(TLC59208FOutput),
|
||||
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x20))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
24
esphome/components/tlc59208f/output.py
Normal file
24
esphome/components/tlc59208f/output.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
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 TLC59208FOutput, tlc59208f_ns
|
||||
|
||||
DEPENDENCIES = ['tlc59208f']
|
||||
|
||||
TLC59208FChannel = tlc59208f_ns.class_('TLC59208FChannel', output.FloatOutput)
|
||||
CONF_TLC59208F_ID = 'tlc59208f_id'
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||
cv.Required(CONF_ID): cv.declare_id(TLC59208FChannel),
|
||||
cv.GenerateID(CONF_TLC59208F_ID): cv.use_id(TLC59208FOutput),
|
||||
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=7),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
paren = yield cg.get_variable(config[CONF_TLC59208F_ID])
|
||||
rhs = paren.create_channel(config[CONF_CHANNEL])
|
||||
var = cg.Pvariable(config[CONF_ID], rhs)
|
||||
yield output.register_output(var, config)
|
155
esphome/components/tlc59208f/tlc59208f_output.cpp
Normal file
155
esphome/components/tlc59208f/tlc59208f_output.cpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
#include "tlc59208f_output.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace tlc59208f {
|
||||
|
||||
static const char *TAG = "tlc59208f";
|
||||
|
||||
// * marks register defaults
|
||||
// 0*: Register auto increment disabled, 1: Register auto increment enabled
|
||||
const uint8_t TLC59208F_MODE1_AI2 = (1 << 7);
|
||||
// 0*: don't auto increment bit 1, 1: auto increment bit 1
|
||||
const uint8_t TLC59208F_MODE1_AI1 = (1 << 6);
|
||||
// 0*: don't auto increment bit 0, 1: auto increment bit 0
|
||||
const uint8_t TLC59208F_MODE1_AI0 = (1 << 5);
|
||||
// 0: normal mode, 1*: low power mode, osc off
|
||||
const uint8_t TLC59208F_MODE1_SLEEP = (1 << 4);
|
||||
// 0*: device doesn't respond to i2c bus sub-address 1, 1: responds
|
||||
const uint8_t TLC59208F_MODE1_SUB1 = (1 << 3);
|
||||
// 0*: device doesn't respond to i2c bus sub-address 2, 1: responds
|
||||
const uint8_t TLC59208F_MODE1_SUB2 = (1 << 2);
|
||||
// 0*: device doesn't respond to i2c bus sub-address 3, 1: responds
|
||||
const uint8_t TLC59208F_MODE1_SUB3 = (1 << 1);
|
||||
// 0: device doesn't respond to i2c all-call 3, 1*: responds to all-call
|
||||
const uint8_t TLC59208F_MODE1_ALLCALL = (1 << 0);
|
||||
|
||||
// 0*: Group dimming, 1: Group blinking
|
||||
const uint8_t TLC59208F_MODE2_DMBLNK = (1 << 5);
|
||||
// 0*: Output change on Stop command, 1: Output change on ACK
|
||||
const uint8_t TLC59208F_MODE2_OCH = (1 << 3);
|
||||
// 0*: WDT disabled, 1: WDT enabled
|
||||
const uint8_t TLC59208F_MODE2_WDTEN = (1 << 2);
|
||||
// WDT timeouts
|
||||
const uint8_t TLC59208F_MODE2_WDT_5MS = (0 << 0);
|
||||
const uint8_t TLC59208F_MODE2_WDT_15MS = (1 << 0);
|
||||
const uint8_t TLC59208F_MODE2_WDT_25MS = (2 << 0);
|
||||
const uint8_t TLC59208F_MODE2_WDT_35MS = (3 << 0);
|
||||
|
||||
// --- Special function ---
|
||||
// Call address to perform software reset, no devices will ACK
|
||||
const uint8_t TLC59208F_SWRST_ADDR = 0x96; //(0x4b 7-bit addr + ~W)
|
||||
const uint8_t TLC59208F_SWRST_SEQ[2] = {0xa5, 0x5a};
|
||||
|
||||
// --- Registers ---2
|
||||
// Mode register 1
|
||||
const uint8_t TLC59208F_REG_MODE1 = 0x00;
|
||||
// Mode register 2
|
||||
const uint8_t TLC59208F_REG_MODE2 = 0x01;
|
||||
// PWM0
|
||||
const uint8_t TLC59208F_REG_PWM0 = 0x02;
|
||||
// Group PWM
|
||||
const uint8_t TLC59208F_REG_GROUPPWM = 0x0a;
|
||||
// Group Freq
|
||||
const uint8_t TLC59208F_REG_GROUPFREQ = 0x0b;
|
||||
// LEDOUTx registers
|
||||
const uint8_t TLC59208F_REG_LEDOUT0 = 0x0c;
|
||||
const uint8_t TLC59208F_REG_LEDOUT1 = 0x0d;
|
||||
// Sub-address registers
|
||||
const uint8_t TLC59208F_REG_SUBADR1 = 0x0e; // default: 0x92 (8-bit addr)
|
||||
const uint8_t TLC59208F_REG_SUBADR2 = 0x0f; // default: 0x94 (8-bit addr)
|
||||
const uint8_t TLC59208F_REG_SUBADR3 = 0x10; // default: 0x98 (8-bit addr)
|
||||
// All call address register
|
||||
const uint8_t TLC59208F_REG_ALLCALLADR = 0x11; // default: 0xd0 (8-bit addr)
|
||||
|
||||
// --- Output modes ---
|
||||
static const uint8_t LDR_OFF = 0x00;
|
||||
static const uint8_t LDR_ON = 0x01;
|
||||
static const uint8_t LDR_PWM = 0x02;
|
||||
static const uint8_t LDR_GRPPWM = 0x03;
|
||||
|
||||
void TLC59208FOutput::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up TLC59208FOutputComponent...");
|
||||
|
||||
ESP_LOGV(TAG, " Resetting all devices on the bus...");
|
||||
|
||||
// Reset all devices on the bus
|
||||
if (!this->parent_->write_byte(TLC59208F_SWRST_ADDR >> 1, TLC59208F_SWRST_SEQ[0], TLC59208F_SWRST_SEQ[1])) {
|
||||
ESP_LOGE(TAG, "RESET failed");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Auto increment registers, and respond to all-call address
|
||||
if (!this->write_byte(TLC59208F_REG_MODE1, TLC59208F_MODE1_AI2 | TLC59208F_MODE1_ALLCALL)) {
|
||||
ESP_LOGE(TAG, "MODE1 failed");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if (!this->write_byte(TLC59208F_REG_MODE2, this->mode_)) {
|
||||
ESP_LOGE(TAG, "MODE2 failed");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
// Set all 3 outputs to be individually controlled
|
||||
// TODO: think of a way to support group dimming
|
||||
if (!this->write_byte(TLC59208F_REG_LEDOUT0, (LDR_PWM << 6) | (LDR_PWM << 4) | (LDR_PWM << 2) | (LDR_PWM << 0))) {
|
||||
ESP_LOGE(TAG, "LEDOUT0 failed");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if (!this->write_byte(TLC59208F_REG_LEDOUT1, (LDR_PWM << 6) | (LDR_PWM << 4) | (LDR_PWM << 2) | (LDR_PWM << 0))) {
|
||||
ESP_LOGE(TAG, "LEDOUT1 failed");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
delayMicroseconds(500);
|
||||
|
||||
this->loop();
|
||||
}
|
||||
|
||||
void TLC59208FOutput::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "TLC59208F:");
|
||||
ESP_LOGCONFIG(TAG, " Mode: 0x%02X", this->mode_);
|
||||
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Setting up TLC59208F failed!");
|
||||
}
|
||||
}
|
||||
|
||||
void TLC59208FOutput::loop() {
|
||||
if (this->min_channel_ == 0xFF || !this->update_)
|
||||
return;
|
||||
|
||||
for (uint8_t channel = this->min_channel_; channel <= this->max_channel_; channel++) {
|
||||
uint8_t pwm = this->pwm_amounts_[channel];
|
||||
ESP_LOGVV(TAG, "Channel %02u: pwm=%04u ", channel, pwm);
|
||||
|
||||
uint8_t reg = TLC59208F_REG_PWM0 + channel;
|
||||
if (!this->write_byte(reg, pwm)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->status_clear_warning();
|
||||
this->update_ = false;
|
||||
}
|
||||
|
||||
TLC59208FChannel *TLC59208FOutput::create_channel(uint8_t channel) {
|
||||
this->min_channel_ = std::min(this->min_channel_, channel);
|
||||
this->max_channel_ = std::max(this->max_channel_, channel);
|
||||
auto *c = new TLC59208FChannel(this, channel);
|
||||
return c;
|
||||
}
|
||||
|
||||
void TLC59208FChannel::write_state(float state) {
|
||||
const uint8_t max_duty = 255;
|
||||
const float duty_rounded = roundf(state * max_duty);
|
||||
auto duty = static_cast<uint8_t>(duty_rounded);
|
||||
this->parent_->set_channel_value_(this->channel_, duty);
|
||||
}
|
||||
|
||||
} // namespace tlc59208f
|
||||
} // namespace esphome
|
67
esphome/components/tlc59208f/tlc59208f_output.h
Normal file
67
esphome/components/tlc59208f/tlc59208f_output.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace tlc59208f {
|
||||
|
||||
// 0*: Group dimming, 1: Group blinking
|
||||
extern const uint8_t TLC59208F_MODE2_DMBLNK;
|
||||
// 0*: Output change on Stop command, 1: Output change on ACK
|
||||
extern const uint8_t TLC59208F_MODE2_OCH;
|
||||
// 0*: WDT disabled, 1: WDT enabled
|
||||
extern const uint8_t TLC59208F_MODE2_WDTEN;
|
||||
// WDT timeouts
|
||||
extern const uint8_t TLC59208F_MODE2_WDT_5MS;
|
||||
extern const uint8_t TLC59208F_MODE2_WDT_15MS;
|
||||
extern const uint8_t TLC59208F_MODE2_WDT_25MS;
|
||||
extern const uint8_t TLC59208F_MODE2_WDT_35MS;
|
||||
|
||||
class TLC59208FOutput;
|
||||
|
||||
class TLC59208FChannel : public output::FloatOutput {
|
||||
public:
|
||||
TLC59208FChannel(TLC59208FOutput *parent, uint8_t channel) : parent_(parent), channel_(channel) {}
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
|
||||
TLC59208FOutput *parent_;
|
||||
uint8_t channel_;
|
||||
};
|
||||
|
||||
/// TLC59208F float output component.
|
||||
class TLC59208FOutput : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
TLC59208FOutput(uint8_t mode = TLC59208F_MODE2_OCH) : mode_(mode) {}
|
||||
|
||||
TLC59208FChannel *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;
|
||||
|
||||
protected:
|
||||
friend TLC59208FChannel;
|
||||
|
||||
void set_channel_value_(uint8_t channel, uint8_t value) {
|
||||
if (this->pwm_amounts_[channel] != value)
|
||||
this->update_ = true;
|
||||
this->pwm_amounts_[channel] = value;
|
||||
}
|
||||
|
||||
uint8_t mode_;
|
||||
|
||||
uint8_t min_channel_{0xFF};
|
||||
uint8_t max_channel_{0x00};
|
||||
uint8_t pwm_amounts_[256] = {
|
||||
0,
|
||||
};
|
||||
bool update_{true};
|
||||
};
|
||||
|
||||
} // namespace tlc59208f
|
||||
} // namespace esphome
|
|
@ -745,6 +745,14 @@ pca9685:
|
|||
frequency: 500
|
||||
address: 0x0
|
||||
|
||||
tlc59208f:
|
||||
- address: 0x20
|
||||
id: tlc59208f_1
|
||||
- address: 0x22
|
||||
id: tlc59208f_2
|
||||
- address: 0x24
|
||||
id: tlc59208f_3
|
||||
|
||||
my9231:
|
||||
data_pin: GPIO12
|
||||
clock_pin: GPIO14
|
||||
|
@ -789,6 +797,42 @@ output:
|
|||
- platform: pca9685
|
||||
id: pca_7
|
||||
channel: 7
|
||||
- platform: tlc59208f
|
||||
id: tlc_0
|
||||
channel: 0
|
||||
tlc59208f_id: 'tlc59208f_1'
|
||||
- platform: tlc59208f
|
||||
id: tlc_1
|
||||
channel: 1
|
||||
tlc59208f_id: 'tlc59208f_1'
|
||||
- platform: tlc59208f
|
||||
id: tlc_2
|
||||
channel: 2
|
||||
tlc59208f_id: 'tlc59208f_1'
|
||||
- platform: tlc59208f
|
||||
id: tlc_3
|
||||
channel: 0
|
||||
tlc59208f_id: 'tlc59208f_2'
|
||||
- platform: tlc59208f
|
||||
id: tlc_4
|
||||
channel: 1
|
||||
tlc59208f_id: 'tlc59208f_2'
|
||||
- platform: tlc59208f
|
||||
id: tlc_5
|
||||
channel: 2
|
||||
tlc59208f_id: 'tlc59208f_2'
|
||||
- platform: tlc59208f
|
||||
id: tlc_6
|
||||
channel: 0
|
||||
tlc59208f_id: 'tlc59208f_3'
|
||||
- platform: tlc59208f
|
||||
id: tlc_7
|
||||
channel: 1
|
||||
tlc59208f_id: 'tlc59208f_3'
|
||||
- platform: tlc59208f
|
||||
id: tlc_8
|
||||
channel: 2
|
||||
tlc59208f_id: 'tlc59208f_3'
|
||||
- platform: gpio
|
||||
id: id2
|
||||
pin:
|
||||
|
|
Loading…
Reference in a new issue