mirror of
https://github.com/esphome/esphome.git
synced 2024-11-09 16:57:47 +01:00
Add LEDC set_frequency action (#754)
* Add LEDC set_frequency Fixes https://github.com/esphome/feature-requests/issues/380 * Fix log * Fixes * Format * Update test1.yaml * Update test1.yaml * Fix
This commit is contained in:
parent
72d6471ab8
commit
68d0d045c0
5 changed files with 98 additions and 59 deletions
|
@ -11,10 +11,10 @@ namespace ledc {
|
|||
static const char *TAG = "ledc.output";
|
||||
|
||||
void LEDCOutput::write_state(float state) {
|
||||
if (this->pin_->is_inverted()) {
|
||||
if (this->pin_->is_inverted())
|
||||
state = 1.0f - state;
|
||||
}
|
||||
|
||||
this->duty_ = state;
|
||||
const uint32_t max_duty = (uint32_t(1) << this->bit_depth_) - 1;
|
||||
const float duty_rounded = roundf(state * max_duty);
|
||||
auto duty = static_cast<uint32_t>(duty_rounded);
|
||||
|
@ -22,18 +22,45 @@ void LEDCOutput::write_state(float state) {
|
|||
}
|
||||
|
||||
void LEDCOutput::setup() {
|
||||
ledcSetup(this->channel_, this->frequency_, this->bit_depth_);
|
||||
ledcAttachPin(this->pin_->get_pin(), this->channel_);
|
||||
|
||||
this->apply_frequency(this->frequency_);
|
||||
this->turn_off();
|
||||
// Attach pin after setting default value
|
||||
ledcAttachPin(this->pin_->get_pin(), this->channel_);
|
||||
}
|
||||
|
||||
void LEDCOutput::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "LEDC Output:");
|
||||
LOG_PIN(" Pin", this->pin_);
|
||||
LOG_PIN(" Pin ", this->pin_);
|
||||
ESP_LOGCONFIG(TAG, " LEDC Channel: %u", this->channel_);
|
||||
ESP_LOGCONFIG(TAG, " Frequency: %.1f Hz", this->frequency_);
|
||||
ESP_LOGCONFIG(TAG, " Bit Depth: %u", this->bit_depth_);
|
||||
}
|
||||
|
||||
float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) { return 80e6f / float(1 << bit_depth); }
|
||||
float ledc_min_frequency_for_bit_depth(uint8_t bit_depth) {
|
||||
const float max_div_num = ((1 << 20) - 1) / 256.0f;
|
||||
return 80e6f / (max_div_num * float(1 << bit_depth));
|
||||
}
|
||||
optional<uint8_t> ledc_bit_depth_for_frequency(float frequency) {
|
||||
for (int i = 20; i >= 1; i--) {
|
||||
const float min_frequency = ledc_min_frequency_for_bit_depth(frequency);
|
||||
const float max_frequency = ledc_max_frequency_for_bit_depth(frequency);
|
||||
if (min_frequency <= frequency && frequency <= max_frequency)
|
||||
return i;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void LEDCOutput::apply_frequency(float frequency) {
|
||||
auto bit_depth_opt = ledc_bit_depth_for_frequency(frequency);
|
||||
if (!bit_depth_opt.has_value()) {
|
||||
ESP_LOGW(TAG, "Frequency %f can't be achieved with any bit depth", frequency);
|
||||
this->status_set_warning();
|
||||
}
|
||||
this->bit_depth_ = *bit_depth_opt;
|
||||
this->frequency_ = frequency;
|
||||
ledcSetup(this->channel_, frequency, this->bit_depth_);
|
||||
// re-apply duty
|
||||
this->write_state(this->duty_);
|
||||
}
|
||||
|
||||
uint8_t next_ledc_channel = 0;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/esphal.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
@ -16,11 +17,10 @@ class LEDCOutput : public output::FloatOutput, public Component {
|
|||
explicit LEDCOutput(GPIOPin *pin) : pin_(pin) { this->channel_ = next_ledc_channel++; }
|
||||
|
||||
void set_channel(uint8_t channel) { this->channel_ = channel; }
|
||||
void set_bit_depth(uint8_t bit_depth) { this->bit_depth_ = bit_depth; }
|
||||
void set_frequency(float frequency) { this->frequency_ = frequency; }
|
||||
/// Dynamically change frequency at runtime
|
||||
void apply_frequency(float frequency);
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
/// Setup LEDC.
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
@ -28,13 +28,28 @@ class LEDCOutput : public output::FloatOutput, public Component {
|
|||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
/// Override FloatOutput's write_state.
|
||||
void write_state(float adjusted_value) override;
|
||||
void write_state(float state) override;
|
||||
|
||||
protected:
|
||||
GPIOPin *pin_;
|
||||
uint8_t channel_{};
|
||||
uint8_t bit_depth_{};
|
||||
float frequency_{};
|
||||
float duty_{0.0f};
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetFrequencyAction : public Action<Ts...> {
|
||||
public:
|
||||
SetFrequencyAction(LEDCOutput *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(float, frequency);
|
||||
|
||||
void play(Ts... x) {
|
||||
float freq = this->frequency_.value(x...);
|
||||
this->parent_->apply_frequency(freq);
|
||||
}
|
||||
|
||||
protected:
|
||||
LEDCOutput *parent_;
|
||||
};
|
||||
|
||||
} // namespace ledc
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import math
|
||||
|
||||
from esphome import pins
|
||||
from esphome import pins, automation
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
|
@ -15,53 +13,36 @@ def calc_max_frequency(bit_depth):
|
|||
|
||||
|
||||
def calc_min_frequency(bit_depth):
|
||||
# LEDC_DIV_NUM_HSTIMER is 15-bit unsigned integer
|
||||
# lower 8 bits represent fractional part
|
||||
max_div_num = ((1 << 16) - 1) / 256.0
|
||||
max_div_num = ((2**20) - 1) / 256.0
|
||||
return 80e6 / (max_div_num * (2**bit_depth))
|
||||
|
||||
|
||||
def validate_frequency_bit_depth(obj):
|
||||
frequency = obj[CONF_FREQUENCY]
|
||||
if CONF_BIT_DEPTH not in obj:
|
||||
obj = obj.copy()
|
||||
for bit_depth in range(15, 0, -1):
|
||||
if calc_min_frequency(bit_depth) <= frequency <= calc_max_frequency(bit_depth):
|
||||
obj[CONF_BIT_DEPTH] = bit_depth
|
||||
break
|
||||
else:
|
||||
min_freq = min(calc_min_frequency(x) for x in range(1, 16))
|
||||
max_freq = max(calc_max_frequency(x) for x in range(1, 16))
|
||||
if frequency < min_freq:
|
||||
raise cv.Invalid("This frequency setting is not possible, please choose a higher "
|
||||
"frequency (at least {}Hz)".format(int(min_freq)))
|
||||
if frequency > max_freq:
|
||||
raise cv.Invalid("This frequency setting is not possible, please choose a lower "
|
||||
"frequency (at most {}Hz)".format(int(max_freq)))
|
||||
raise cv.Invalid("Invalid frequency!")
|
||||
|
||||
bit_depth = obj[CONF_BIT_DEPTH]
|
||||
min_freq = calc_min_frequency(bit_depth)
|
||||
max_freq = calc_max_frequency(bit_depth)
|
||||
if frequency > max_freq:
|
||||
raise cv.Invalid('Maximum frequency for bit depth {} is {}Hz. Please decrease the '
|
||||
'bit_depth.'.format(bit_depth, int(math.floor(max_freq))))
|
||||
if frequency < calc_min_frequency(bit_depth):
|
||||
raise cv.Invalid('Minimum frequency for bit depth {} is {}Hz. Please increase the '
|
||||
'bit_depth.'.format(bit_depth, int(math.ceil(min_freq))))
|
||||
return obj
|
||||
def validate_frequency(value):
|
||||
value = cv.frequency(value)
|
||||
min_freq = calc_min_frequency(20)
|
||||
max_freq = calc_max_frequency(1)
|
||||
if value < min_freq:
|
||||
raise cv.Invalid("This frequency setting is not possible, please choose a higher "
|
||||
"frequency (at least {}Hz)".format(int(min_freq)))
|
||||
if value > max_freq:
|
||||
raise cv.Invalid("This frequency setting is not possible, please choose a lower "
|
||||
"frequency (at most {}Hz)".format(int(max_freq)))
|
||||
return value
|
||||
|
||||
|
||||
ledc_ns = cg.esphome_ns.namespace('ledc')
|
||||
LEDCOutput = ledc_ns.class_('LEDCOutput', output.FloatOutput, cg.Component)
|
||||
SetFrequencyAction = ledc_ns.class_('SetFrequencyAction', automation.Action)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||
cv.Required(CONF_ID): cv.declare_id(LEDCOutput),
|
||||
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(CONF_FREQUENCY, default='1kHz'): cv.frequency,
|
||||
cv.Optional(CONF_BIT_DEPTH): cv.int_range(min=1, max=15),
|
||||
cv.Optional(CONF_CHANNEL): cv.int_range(min=0, max=15),
|
||||
}).extend(cv.COMPONENT_SCHEMA), validate_frequency_bit_depth)
|
||||
|
||||
cv.Optional(CONF_BIT_DEPTH): cv.invalid("The bit_depth option has been removed in v1.14, the "
|
||||
"best bit depth is now automatically calculated."),
|
||||
}).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
|
@ -72,4 +53,15 @@ def to_code(config):
|
|||
if CONF_CHANNEL in config:
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
cg.add(var.set_frequency(config[CONF_FREQUENCY]))
|
||||
cg.add(var.set_bit_depth(config[CONF_BIT_DEPTH]))
|
||||
|
||||
|
||||
@automation.register_action('output.ledc.set_frequency', SetFrequencyAction, cv.Schema({
|
||||
cv.Required(CONF_ID): cv.use_id(LEDCOutput),
|
||||
cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency),
|
||||
}))
|
||||
def ledc_set_frequency_to_code(config, action_id, template_arg, args):
|
||||
paren = yield cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = yield cg.templatable(config[CONF_FREQUENCY], args, float)
|
||||
cg.add(var.set_frequency(template_))
|
||||
yield var
|
||||
|
|
|
@ -29,14 +29,6 @@ build_flags =
|
|||
; log messages
|
||||
src_filter = +<esphome>
|
||||
|
||||
[env:livingroom32]
|
||||
platform = espressif32@1.6.0
|
||||
board = nodemcu-32s
|
||||
framework = arduino
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags = ${common.build_flags} -DUSE_ETHERNET
|
||||
src_filter = ${common.src_filter} +<tests/livingroom32.cpp>
|
||||
|
||||
[env:livingroom8266]
|
||||
platform = espressif8266@1.8.0
|
||||
board = nodemcuv2
|
||||
|
@ -47,3 +39,11 @@ lib_deps =
|
|||
Hash
|
||||
build_flags = ${common.build_flags}
|
||||
src_filter = ${common.src_filter} +<tests/livingroom8266.cpp>
|
||||
|
||||
[env:livingroom32]
|
||||
platform = espressif32@1.6.0
|
||||
board = nodemcu-32s
|
||||
framework = arduino
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags = ${common.build_flags} -DUSE_ETHERNET
|
||||
src_filter = ${common.src_filter} +<tests/livingroom32.cpp>
|
||||
|
|
|
@ -722,6 +722,12 @@ binary_sensor:
|
|||
- binary_sensor.template.publish:
|
||||
id: garage_door
|
||||
state: OFF
|
||||
- output.ledc.set_frequency:
|
||||
id: gpio_19
|
||||
frequency: 500.0Hz
|
||||
- output.ledc.set_frequency:
|
||||
id: gpio_19
|
||||
frequency: !lambda 'return 500.0;'
|
||||
- platform: pn532
|
||||
uid: 74-10-37-94
|
||||
name: "PN532 NFC Tag"
|
||||
|
@ -789,7 +795,6 @@ output:
|
|||
pin: 19
|
||||
id: gpio_19
|
||||
frequency: 1500Hz
|
||||
bit_depth: 8
|
||||
channel: 14
|
||||
max_power: 0.5
|
||||
- platform: pca9685
|
||||
|
|
Loading…
Reference in a new issue