mirror of
https://github.com/esphome/esphome.git
synced 2026-09-16 01:28:39 +00:00
[pulse_counter] Migrate from legacy PCNT API to new ESP-IDF 5.x API (#13904)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
298efb5340
commit
13a124c86d
@@ -135,6 +135,7 @@ DEFAULT_EXCLUDED_IDF_COMPONENTS = (
|
||||
"esp_driver_dac", # DAC driver - only needed by esp32_dac component
|
||||
"esp_driver_i2s", # I2S driver - only needed by i2s_audio component
|
||||
"esp_driver_mcpwm", # MCPWM driver - ESPHome doesn't use motor control PWM
|
||||
"esp_driver_pcnt", # PCNT driver - only needed by pulse_counter, hlw8012 components
|
||||
"esp_driver_rmt", # RMT driver - only needed by remote_transmitter/receiver, neopixelbus
|
||||
"esp_driver_touch_sens", # Touch sensor driver - only needed by esp32_touch
|
||||
"esp_driver_twai", # TWAI/CAN driver - only needed by esp32_can component
|
||||
|
||||
@@ -94,10 +94,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
async def to_code(config):
|
||||
if CORE.is_esp32:
|
||||
# Re-enable ESP-IDF's legacy driver component (excluded by default to save compile time)
|
||||
# HLW8012 uses pulse_counter's PCNT storage which requires driver/pcnt.h
|
||||
# TODO: Remove this once pulse_counter migrates to new PCNT API (driver/pulse_cnt.h)
|
||||
include_builtin_idf_component("driver")
|
||||
include_builtin_idf_component("esp_driver_pcnt")
|
||||
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
#include "pulse_counter_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef HAS_PCNT
|
||||
#include <esp_private/esp_clk.h>
|
||||
#include <hal/pcnt_ll.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace pulse_counter {
|
||||
|
||||
@@ -56,103 +61,107 @@ pulse_counter_t BasicPulseCounterStorage::read_raw_value() {
|
||||
|
||||
#ifdef HAS_PCNT
|
||||
bool HwPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) {
|
||||
static pcnt_unit_t next_pcnt_unit = PCNT_UNIT_0;
|
||||
static pcnt_channel_t next_pcnt_channel = PCNT_CHANNEL_0;
|
||||
this->pin = pin;
|
||||
this->pin->setup();
|
||||
this->pcnt_unit = next_pcnt_unit;
|
||||
this->pcnt_channel = next_pcnt_channel;
|
||||
next_pcnt_unit = pcnt_unit_t(int(next_pcnt_unit) + 1);
|
||||
if (int(next_pcnt_unit) >= PCNT_UNIT_0 + PCNT_UNIT_MAX) {
|
||||
next_pcnt_unit = PCNT_UNIT_0;
|
||||
next_pcnt_channel = pcnt_channel_t(int(next_pcnt_channel) + 1);
|
||||
|
||||
pcnt_unit_config_t unit_config = {
|
||||
.low_limit = INT16_MIN,
|
||||
.high_limit = INT16_MAX,
|
||||
.flags = {.accum_count = true},
|
||||
};
|
||||
esp_err_t error = pcnt_new_unit(&unit_config, &this->pcnt_unit);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Creating PCNT unit failed: %s", esp_err_to_name(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" PCNT Unit Number: %u\n"
|
||||
" PCNT Channel Number: %u",
|
||||
this->pcnt_unit, this->pcnt_channel);
|
||||
pcnt_chan_config_t chan_config = {
|
||||
.edge_gpio_num = this->pin->get_pin(),
|
||||
.level_gpio_num = -1,
|
||||
};
|
||||
error = pcnt_new_channel(this->pcnt_unit, &chan_config, &this->pcnt_channel);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Creating PCNT channel failed: %s", esp_err_to_name(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
pcnt_count_mode_t rising = PCNT_COUNT_DIS, falling = PCNT_COUNT_DIS;
|
||||
pcnt_channel_edge_action_t rising = PCNT_CHANNEL_EDGE_ACTION_HOLD;
|
||||
pcnt_channel_edge_action_t falling = PCNT_CHANNEL_EDGE_ACTION_HOLD;
|
||||
switch (this->rising_edge_mode) {
|
||||
case PULSE_COUNTER_DISABLE:
|
||||
rising = PCNT_COUNT_DIS;
|
||||
rising = PCNT_CHANNEL_EDGE_ACTION_HOLD;
|
||||
break;
|
||||
case PULSE_COUNTER_INCREMENT:
|
||||
rising = PCNT_COUNT_INC;
|
||||
rising = PCNT_CHANNEL_EDGE_ACTION_INCREASE;
|
||||
break;
|
||||
case PULSE_COUNTER_DECREMENT:
|
||||
rising = PCNT_COUNT_DEC;
|
||||
rising = PCNT_CHANNEL_EDGE_ACTION_DECREASE;
|
||||
break;
|
||||
}
|
||||
switch (this->falling_edge_mode) {
|
||||
case PULSE_COUNTER_DISABLE:
|
||||
falling = PCNT_COUNT_DIS;
|
||||
falling = PCNT_CHANNEL_EDGE_ACTION_HOLD;
|
||||
break;
|
||||
case PULSE_COUNTER_INCREMENT:
|
||||
falling = PCNT_COUNT_INC;
|
||||
falling = PCNT_CHANNEL_EDGE_ACTION_INCREASE;
|
||||
break;
|
||||
case PULSE_COUNTER_DECREMENT:
|
||||
falling = PCNT_COUNT_DEC;
|
||||
falling = PCNT_CHANNEL_EDGE_ACTION_DECREASE;
|
||||
break;
|
||||
}
|
||||
|
||||
pcnt_config_t pcnt_config = {
|
||||
.pulse_gpio_num = this->pin->get_pin(),
|
||||
.ctrl_gpio_num = PCNT_PIN_NOT_USED,
|
||||
.lctrl_mode = PCNT_MODE_KEEP,
|
||||
.hctrl_mode = PCNT_MODE_KEEP,
|
||||
.pos_mode = rising,
|
||||
.neg_mode = falling,
|
||||
.counter_h_lim = 0,
|
||||
.counter_l_lim = 0,
|
||||
.unit = this->pcnt_unit,
|
||||
.channel = this->pcnt_channel,
|
||||
};
|
||||
esp_err_t error = pcnt_unit_config(&pcnt_config);
|
||||
error = pcnt_channel_set_edge_action(this->pcnt_channel, rising, falling);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Configuring Pulse Counter failed: %s", esp_err_to_name(error));
|
||||
ESP_LOGE(TAG, "Setting PCNT edge action failed: %s", esp_err_to_name(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->filter_us != 0) {
|
||||
uint16_t filter_val = std::min(static_cast<unsigned int>(this->filter_us * 80u), 1023u);
|
||||
ESP_LOGCONFIG(TAG, " Filter Value: %" PRIu32 "us (val=%u)", this->filter_us, filter_val);
|
||||
error = pcnt_set_filter_value(this->pcnt_unit, filter_val);
|
||||
uint32_t max_glitch_ns = PCNT_LL_MAX_GLITCH_WIDTH * 1000000u / (uint32_t) esp_clk_apb_freq();
|
||||
pcnt_glitch_filter_config_t filter_config = {
|
||||
.max_glitch_ns = std::min(this->filter_us * 1000u, max_glitch_ns),
|
||||
};
|
||||
error = pcnt_unit_set_glitch_filter(this->pcnt_unit, &filter_config);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Setting filter value failed: %s", esp_err_to_name(error));
|
||||
return false;
|
||||
}
|
||||
error = pcnt_filter_enable(this->pcnt_unit);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Enabling filter failed: %s", esp_err_to_name(error));
|
||||
ESP_LOGE(TAG, "Setting PCNT glitch filter failed: %s", esp_err_to_name(error));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
error = pcnt_counter_pause(this->pcnt_unit);
|
||||
error = pcnt_unit_add_watch_point(this->pcnt_unit, INT16_MIN);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Pausing pulse counter failed: %s", esp_err_to_name(error));
|
||||
ESP_LOGE(TAG, "Adding PCNT low limit watch point failed: %s", esp_err_to_name(error));
|
||||
return false;
|
||||
}
|
||||
error = pcnt_counter_clear(this->pcnt_unit);
|
||||
error = pcnt_unit_add_watch_point(this->pcnt_unit, INT16_MAX);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Clearing pulse counter failed: %s", esp_err_to_name(error));
|
||||
ESP_LOGE(TAG, "Adding PCNT high limit watch point failed: %s", esp_err_to_name(error));
|
||||
return false;
|
||||
}
|
||||
error = pcnt_counter_resume(this->pcnt_unit);
|
||||
|
||||
error = pcnt_unit_enable(this->pcnt_unit);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Resuming pulse counter failed: %s", esp_err_to_name(error));
|
||||
ESP_LOGE(TAG, "Enabling PCNT unit failed: %s", esp_err_to_name(error));
|
||||
return false;
|
||||
}
|
||||
error = pcnt_unit_clear_count(this->pcnt_unit);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Clearing PCNT unit failed: %s", esp_err_to_name(error));
|
||||
return false;
|
||||
}
|
||||
error = pcnt_unit_start(this->pcnt_unit);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Starting PCNT unit failed: %s", esp_err_to_name(error));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
pulse_counter_t HwPulseCounterStorage::read_raw_value() {
|
||||
pulse_counter_t counter;
|
||||
pcnt_get_counter_value(this->pcnt_unit, &counter);
|
||||
pulse_counter_t ret = counter - this->last_value;
|
||||
this->last_value = counter;
|
||||
int count;
|
||||
pcnt_unit_get_count(this->pcnt_unit, &count);
|
||||
pulse_counter_t ret = count - this->last_value;
|
||||
this->last_value = count;
|
||||
return ret;
|
||||
}
|
||||
#endif // HAS_PCNT
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
// TODO: Migrate from legacy PCNT API (driver/pcnt.h) to new PCNT API (driver/pulse_cnt.h)
|
||||
// The legacy PCNT API is deprecated in ESP-IDF 5.x. Migration would allow removing the
|
||||
// "driver" IDF component dependency. See:
|
||||
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/release-5.x/5.0/peripherals.html#id6
|
||||
#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3)
|
||||
#include <driver/pcnt.h>
|
||||
#if defined(USE_ESP32)
|
||||
#include <soc/soc_caps.h>
|
||||
#ifdef SOC_PCNT_SUPPORTED
|
||||
#include <driver/pulse_cnt.h>
|
||||
#define HAS_PCNT
|
||||
#endif // defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3)
|
||||
#endif // SOC_PCNT_SUPPORTED
|
||||
#endif // USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace pulse_counter {
|
||||
@@ -24,11 +23,7 @@ enum PulseCounterCountMode {
|
||||
PULSE_COUNTER_DECREMENT,
|
||||
};
|
||||
|
||||
#ifdef HAS_PCNT
|
||||
using pulse_counter_t = int16_t;
|
||||
#else // HAS_PCNT
|
||||
using pulse_counter_t = int32_t;
|
||||
#endif // HAS_PCNT
|
||||
|
||||
struct PulseCounterStorageBase {
|
||||
virtual bool pulse_counter_setup(InternalGPIOPin *pin) = 0;
|
||||
@@ -58,8 +53,8 @@ struct HwPulseCounterStorage : public PulseCounterStorageBase {
|
||||
bool pulse_counter_setup(InternalGPIOPin *pin) override;
|
||||
pulse_counter_t read_raw_value() override;
|
||||
|
||||
pcnt_unit_t pcnt_unit;
|
||||
pcnt_channel_t pcnt_channel;
|
||||
pcnt_unit_handle_t pcnt_unit{nullptr};
|
||||
pcnt_channel_handle_t pcnt_channel{nullptr};
|
||||
};
|
||||
#endif // HAS_PCNT
|
||||
|
||||
|
||||
@@ -129,10 +129,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
async def to_code(config):
|
||||
use_pcnt = config.get(CONF_USE_PCNT)
|
||||
if CORE.is_esp32 and use_pcnt:
|
||||
# Re-enable ESP-IDF's legacy driver component (excluded by default to save compile time)
|
||||
# Provides driver/pcnt.h header for hardware pulse counter API
|
||||
# TODO: Remove this once pulse_counter migrates to new PCNT API (driver/pulse_cnt.h)
|
||||
include_builtin_idf_component("driver")
|
||||
include_builtin_idf_component("esp_driver_pcnt")
|
||||
|
||||
var = await sensor.new_sensor(config, use_pcnt)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
Reference in New Issue
Block a user