Esp32 pulsecounter optional pcnt (#3691)

Co-authored-by: RoboMagus <->
This commit is contained in:
RoboMagus 2022-09-02 03:22:34 +02:00 committed by GitHub
parent 2650441013
commit 1b4156646e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 36 deletions

View file

@ -16,8 +16,17 @@ enum HLW8012SensorModels {
HLW8012_SENSOR_MODEL_BL0937 HLW8012_SENSOR_MODEL_BL0937
}; };
#ifdef HAS_PCNT
#define USE_PCNT true
#else
#define USE_PCNT false
#endif
class HLW8012Component : public PollingComponent { class HLW8012Component : public PollingComponent {
public: public:
HLW8012Component()
: cf_store_(*pulse_counter::get_storage(USE_PCNT)), cf1_store_(*pulse_counter::get_storage(USE_PCNT)) {}
void setup() override; void setup() override;
void dump_config() override; void dump_config() override;
float get_setup_priority() const override; float get_setup_priority() const override;
@ -49,9 +58,9 @@ class HLW8012Component : public PollingComponent {
uint64_t cf_total_pulses_{0}; uint64_t cf_total_pulses_{0};
GPIOPin *sel_pin_; GPIOPin *sel_pin_;
InternalGPIOPin *cf_pin_; InternalGPIOPin *cf_pin_;
pulse_counter::PulseCounterStorage cf_store_; pulse_counter::PulseCounterStorageBase &cf_store_;
InternalGPIOPin *cf1_pin_; InternalGPIOPin *cf1_pin_;
pulse_counter::PulseCounterStorage cf1_store_; pulse_counter::PulseCounterStorageBase &cf1_store_;
sensor::Sensor *voltage_sensor_{nullptr}; sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr}; sensor::Sensor *current_sensor_{nullptr};
sensor::Sensor *power_sensor_{nullptr}; sensor::Sensor *power_sensor_{nullptr};

View file

@ -8,8 +8,16 @@ static const char *const TAG = "pulse_counter";
const char *const EDGE_MODE_TO_STRING[] = {"DISABLE", "INCREMENT", "DECREMENT"}; const char *const EDGE_MODE_TO_STRING[] = {"DISABLE", "INCREMENT", "DECREMENT"};
#ifndef HAS_PCNT #ifdef HAS_PCNT
void IRAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) { PulseCounterStorageBase *get_storage(bool hw_pcnt) {
return (hw_pcnt ? (PulseCounterStorageBase *) (new HwPulseCounterStorage)
: (PulseCounterStorageBase *) (new BasicPulseCounterStorage));
}
#else
PulseCounterStorageBase *get_storage(bool) { return new BasicPulseCounterStorage; }
#endif
void IRAM_ATTR BasicPulseCounterStorage::gpio_intr(BasicPulseCounterStorage *arg) {
const uint32_t now = micros(); const uint32_t now = micros();
const bool discard = now - arg->last_pulse < arg->filter_us; const bool discard = now - arg->last_pulse < arg->filter_us;
arg->last_pulse = now; arg->last_pulse = now;
@ -28,23 +36,22 @@ void IRAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) {
break; break;
} }
} }
bool PulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { bool BasicPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) {
this->pin = pin; this->pin = pin;
this->pin->setup(); this->pin->setup();
this->isr_pin = this->pin->to_isr(); this->isr_pin = this->pin->to_isr();
this->pin->attach_interrupt(PulseCounterStorage::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE); this->pin->attach_interrupt(BasicPulseCounterStorage::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE);
return true; return true;
} }
pulse_counter_t PulseCounterStorage::read_raw_value() { pulse_counter_t BasicPulseCounterStorage::read_raw_value() {
pulse_counter_t counter = this->counter; pulse_counter_t counter = this->counter;
pulse_counter_t ret = counter - this->last_value; pulse_counter_t ret = counter - this->last_value;
this->last_value = counter; this->last_value = counter;
return ret; return ret;
} }
#endif
#ifdef HAS_PCNT #ifdef HAS_PCNT
bool PulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { bool HwPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) {
static pcnt_unit_t next_pcnt_unit = PCNT_UNIT_0; static pcnt_unit_t next_pcnt_unit = PCNT_UNIT_0;
this->pin = pin; this->pin = pin;
this->pin->setup(); this->pin->setup();
@ -127,7 +134,7 @@ bool PulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) {
} }
return true; return true;
} }
pulse_counter_t PulseCounterStorage::read_raw_value() { pulse_counter_t HwPulseCounterStorage::read_raw_value() {
pulse_counter_t counter; pulse_counter_t counter;
pcnt_get_counter_value(this->pcnt_unit, &counter); pcnt_get_counter_value(this->pcnt_unit, &counter);
pulse_counter_t ret = counter - this->last_value; pulse_counter_t ret = counter - this->last_value;

View file

@ -24,31 +24,44 @@ using pulse_counter_t = int16_t;
using pulse_counter_t = int32_t; using pulse_counter_t = int32_t;
#endif #endif
struct PulseCounterStorage { struct PulseCounterStorageBase {
bool pulse_counter_setup(InternalGPIOPin *pin); virtual bool pulse_counter_setup(InternalGPIOPin *pin) = 0;
pulse_counter_t read_raw_value(); virtual pulse_counter_t read_raw_value() = 0;
static void gpio_intr(PulseCounterStorage *arg);
#ifndef HAS_PCNT
volatile pulse_counter_t counter{0};
volatile uint32_t last_pulse{0};
#endif
InternalGPIOPin *pin; InternalGPIOPin *pin;
#ifdef HAS_PCNT
pcnt_unit_t pcnt_unit;
#else
ISRInternalGPIOPin isr_pin;
#endif
PulseCounterCountMode rising_edge_mode{PULSE_COUNTER_INCREMENT}; PulseCounterCountMode rising_edge_mode{PULSE_COUNTER_INCREMENT};
PulseCounterCountMode falling_edge_mode{PULSE_COUNTER_DISABLE}; PulseCounterCountMode falling_edge_mode{PULSE_COUNTER_DISABLE};
uint32_t filter_us{0}; uint32_t filter_us{0};
pulse_counter_t last_value{0}; pulse_counter_t last_value{0};
}; };
struct BasicPulseCounterStorage : public PulseCounterStorageBase {
static void gpio_intr(BasicPulseCounterStorage *arg);
bool pulse_counter_setup(InternalGPIOPin *pin) override;
pulse_counter_t read_raw_value() override;
volatile pulse_counter_t counter{0};
volatile uint32_t last_pulse{0};
ISRInternalGPIOPin isr_pin;
};
#ifdef HAS_PCNT
struct HwPulseCounterStorage : public PulseCounterStorageBase {
bool pulse_counter_setup(InternalGPIOPin *pin) override;
pulse_counter_t read_raw_value() override;
pcnt_unit_t pcnt_unit;
};
#endif
PulseCounterStorageBase *get_storage(bool hw_pcnt = false);
class PulseCounterSensor : public sensor::Sensor, public PollingComponent { class PulseCounterSensor : public sensor::Sensor, public PollingComponent {
public: public:
explicit PulseCounterSensor(bool hw_pcnt = false) : storage_(*get_storage(hw_pcnt)) {}
void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void set_pin(InternalGPIOPin *pin) { pin_ = pin; }
void set_rising_edge_mode(PulseCounterCountMode mode) { storage_.rising_edge_mode = mode; } void set_rising_edge_mode(PulseCounterCountMode mode) { storage_.rising_edge_mode = mode; }
void set_falling_edge_mode(PulseCounterCountMode mode) { storage_.falling_edge_mode = mode; } void set_falling_edge_mode(PulseCounterCountMode mode) { storage_.falling_edge_mode = mode; }
@ -65,7 +78,7 @@ class PulseCounterSensor : public sensor::Sensor, public PollingComponent {
protected: protected:
InternalGPIOPin *pin_; InternalGPIOPin *pin_;
PulseCounterStorage storage_; PulseCounterStorageBase &storage_;
uint32_t last_time_{0}; uint32_t last_time_{0};
uint32_t current_total_{0}; uint32_t current_total_{0};
sensor::Sensor *total_sensor_; sensor::Sensor *total_sensor_;

View file

@ -20,6 +20,8 @@ from esphome.const import (
) )
from esphome.core import CORE from esphome.core import CORE
CONF_USE_PCNT = "use_pcnt"
pulse_counter_ns = cg.esphome_ns.namespace("pulse_counter") pulse_counter_ns = cg.esphome_ns.namespace("pulse_counter")
PulseCounterCountMode = pulse_counter_ns.enum("PulseCounterCountMode") PulseCounterCountMode = pulse_counter_ns.enum("PulseCounterCountMode")
COUNT_MODES = { COUNT_MODES = {
@ -40,11 +42,19 @@ SetTotalPulsesAction = pulse_counter_ns.class_(
def validate_internal_filter(value): def validate_internal_filter(value):
value = cv.positive_time_period_microseconds(value) use_pcnt = value.get(CONF_USE_PCNT)
if CORE.is_esp32: if CORE.is_esp8266 and use_pcnt:
if value.total_microseconds > 13: raise cv.Invalid(
raise cv.Invalid("Maximum internal filter value for ESP32 is 13us") "Using hardware PCNT is only available on ESP32",
return value [CONF_USE_PCNT],
)
if CORE.is_esp32 and use_pcnt:
if value.get(CONF_INTERNAL_FILTER).total_microseconds > 13:
raise cv.Invalid(
"Maximum internal filter value when using ESP32 hardware PCNT is 13us",
[CONF_INTERNAL_FILTER],
)
return value return value
@ -69,7 +79,7 @@ def validate_count_mode(value):
return value return value
CONFIG_SCHEMA = ( CONFIG_SCHEMA = cv.All(
sensor.sensor_schema( sensor.sensor_schema(
PulseCounterSensor, PulseCounterSensor,
unit_of_measurement=UNIT_PULSES_PER_MINUTE, unit_of_measurement=UNIT_PULSES_PER_MINUTE,
@ -95,21 +105,25 @@ CONFIG_SCHEMA = (
), ),
validate_count_mode, validate_count_mode,
), ),
cv.Optional(CONF_INTERNAL_FILTER, default="13us"): validate_internal_filter, cv.SplitDefault(CONF_USE_PCNT, esp32=True): cv.boolean,
cv.Optional(
CONF_INTERNAL_FILTER, default="13us"
): cv.positive_time_period_microseconds,
cv.Optional(CONF_TOTAL): sensor.sensor_schema( cv.Optional(CONF_TOTAL): sensor.sensor_schema(
unit_of_measurement=UNIT_PULSES, unit_of_measurement=UNIT_PULSES,
icon=ICON_PULSE, icon=ICON_PULSE,
accuracy_decimals=0, accuracy_decimals=0,
state_class=STATE_CLASS_TOTAL_INCREASING, state_class=STATE_CLASS_TOTAL_INCREASING,
), ),
} },
) )
.extend(cv.polling_component_schema("60s")) .extend(cv.polling_component_schema("60s")),
validate_internal_filter,
) )
async def to_code(config): async def to_code(config):
var = await sensor.new_sensor(config) var = await sensor.new_sensor(config, config.get(CONF_USE_PCNT))
await cg.register_component(var, config) await cg.register_component(var, config)
pin = await cg.gpio_pin_expression(config[CONF_PIN]) pin = await cg.gpio_pin_expression(config[CONF_PIN])