From 1db2400dd98f628c3a49a371cc67ca8f80d78add Mon Sep 17 00:00:00 2001 From: kuba Date: Sun, 18 Feb 2024 19:17:39 +0100 Subject: [PATCH] anemometer: redesigned driver to use PCNT on ESP32 and interrupts on ESP8266 --- components/anemometer/anemometer.c | 141 +++++++++++++++++++++++------ 1 file changed, 113 insertions(+), 28 deletions(-) diff --git a/components/anemometer/anemometer.c b/components/anemometer/anemometer.c index 0f8845f0..78da5aa2 100644 --- a/components/anemometer/anemometer.c +++ b/components/anemometer/anemometer.c @@ -38,11 +38,13 @@ #include #include -#include #include +#include +#include #include #if HELPER_TARGET_IS_ESP32 +#include static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; #define PORT_ENTER_CRITICAL() portENTER_CRITICAL(&mux) #define PORT_EXIT_CRITICAL() portEXIT_CRITICAL(&mux) @@ -58,26 +60,113 @@ static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; #define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) typedef struct { - gpio_num_t input_pin; //!< GPIO input pin - float sf; //!< scale factor - uint32_t pps; //!< measured pulses count per second - TickType_t init_tick; //!< measurement init tick + gpio_num_t input_pin; //!< GPIO input pin + float sf; //!< scale factor + int pps; //!< measured pulses count per second + esp_timer_handle_t timer; //!< periodic timer to reset pps count +#if HELPER_TARGET_IS_ESP8266 + int pulse_count; //!< pulses counter +#elif HELPER_TARGET_IS_ESP32 + pcnt_unit_handle_t pcnt_unit; //!< hardware pulse counter + pcnt_channel_handle_t pcnt_ch; //!< hardware pulse counter channel +#endif } anemometer_priv_t; -static inline void pps_iterate(anemometer_priv_t *priv, TickType_t ticks) +#if HELPER_TARGET_IS_ESP8266 +static void IRAM_ATTR gpio_isr_handler(void* arg) +{ + anemometer_priv_t *priv = (anemometer_priv_t *)arg; + priv->pulse_count++; +} + +static esp_err_t anemometer_pulse_counter_init(anemometer_priv_t *priv) { - if(ticks - priv->init_tick > pdMS_TO_TICKS(1000)){ - priv->init_tick = ticks; - priv->pps = 0; + /* enable interrupts */ + esp_err_t rc = gpio_install_isr_service(0); + if (rc != ESP_OK && rc != ESP_ERR_INVALID_STATE){ + return rc; } + /* setup GPIO */ + gpio_config_t io_conf = { + .intr_type = GPIO_INTR_POSEDGE, + .pin_bit_mask = (1 << priv->input_pin), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE + }; + ESP_ERROR_CHECK(gpio_config(&io_conf)); + ESP_ERROR_CHECK(gpio_isr_handler_add(priv->input_pin, gpio_isr_handler,priv)); + return ESP_OK; } -static void IRAM_ATTR gpio_isr_handler(void* arg) +static esp_err_t anemometer_pulse_counter_deinit(anemometer_priv_t *priv) +{ + ESP_ERROR_CHECK(gpio_isr_handler_remove(priv->input_pin)); + return ESP_OK; +} + +static void timer_event_callback(void *arg) { anemometer_priv_t *priv = (anemometer_priv_t *)arg; - TickType_t ticks = xTaskGetTickCountFromISR(); - pps_iterate(arg,ticks); - priv->pps++; + + PORT_ENTER_CRITICAL(); + priv->pps = priv->pulse_count; + priv->pulse_count = 0; + PORT_EXIT_CRITICAL(); +} + +#elif HELPER_TARGET_IS_ESP32 +static esp_err_t anemometer_pulse_counter_init(anemometer_priv_t *priv) +{ + pcnt_unit_config_t unit_config = { + .high_limit = 2048, + .low_limit = -1 + }; + + pcnt_chan_config_t ch_config = { + .edge_gpio_num = priv->input_pin, + .level_gpio_num = -1 + }; + + ESP_ERROR_CHECK(pcnt_new_unit(&unit_config,&priv->pcnt_unit)); + ESP_ERROR_CHECK(pcnt_new_channel(priv->pcnt_unit,&ch_config,&priv->pcnt_ch)); + ESP_ERROR_CHECK(pcnt_channel_set_edge_action(priv->pcnt_ch, + PCNT_CHANNEL_EDGE_ACTION_INCREASE,PCNT_CHANNEL_EDGE_ACTION_HOLD)); + ESP_ERROR_CHECK(pcnt_unit_enable(priv->pcnt_unit)); + ESP_ERROR_CHECK(pcnt_unit_clear_count(priv->pcnt_unit)); + ESP_ERROR_CHECK(pcnt_unit_start(priv->pcnt_unit)); + return ESP_OK; +} + +static esp_err_t anemometer_pulse_counter_deinit(anemometer_priv_t *priv) +{ + ESP_ERROR_CHECK(pcnt_unit_stop(priv->pcnt_unit)); + ESP_ERROR_CHECK(pcnt_unit_disable(priv->pcnt_unit)); + ESP_ERROR_CHECK(pcnt_del_channel(priv->pcnt_ch)); + ESP_ERROR_CHECK(pcnt_del_unit(priv->pcnt_unit)); + return ESP_OK; +} + +static void timer_event_callback(void *arg) +{ + anemometer_priv_t *priv = (anemometer_priv_t *)arg; + + pcnt_unit_get_count(priv->pcnt_unit,&priv->pps); + pcnt_unit_clear_count(priv->pcnt_unit); +} +#endif + +static esp_err_t anemometer_timer_setup(anemometer_priv_t *priv) +{ + esp_timer_create_args_t timer_args = { + .name = "anemometer", + .dispatch_method = ESP_TIMER_TASK, + .callback = timer_event_callback, + .arg = priv, + }; + + ESP_ERROR_CHECK(esp_timer_create(&timer_args,&priv->timer)); + ESP_ERROR_CHECK(esp_timer_start_periodic(priv->timer,1000*1000)); + return ESP_OK; } esp_err_t anemometer_init(const anemometer_config_t *conf, anemometer_t *anemometer) @@ -85,7 +174,6 @@ esp_err_t anemometer_init(const anemometer_config_t *conf, anemometer_t *anemome CHECK_ARG(conf); CHECK_ARG(anemometer); - gpio_config_t io_conf; anemometer_priv_t *priv; esp_err_t rc; @@ -97,21 +185,18 @@ esp_err_t anemometer_init(const anemometer_config_t *conf, anemometer_t *anemome priv->input_pin = conf->input_pin; priv->sf = conf->scale_factor ? conf->scale_factor : ANEMOMETER_DEFAULT_SF; - /* enable interrupts */ - rc = gpio_install_isr_service(0); - if (rc != ESP_OK && rc != ESP_ERR_INVALID_STATE){ + rc = anemometer_pulse_counter_init(priv); + if (rc != ESP_OK){ free(priv); return rc; } - /* setup GPIO */ - io_conf.intr_type = GPIO_INTR_POSEDGE; - io_conf.pin_bit_mask = (1 << priv->input_pin); - io_conf.mode = GPIO_MODE_INPUT; - io_conf.pull_up_en = GPIO_PULLUP_ENABLE; - gpio_config(&io_conf); - gpio_isr_handler_add(priv->input_pin, gpio_isr_handler, (void *) priv); - + rc = anemometer_timer_setup(priv); + if (rc != ESP_OK){ + anemometer_pulse_counter_deinit(priv); + free(priv); + return rc; + } *anemometer = priv; return ESP_OK; } @@ -121,7 +206,8 @@ esp_err_t anemometer_deinit(anemometer_t *anemometer) CHECK_ARG(anemometer); anemometer_priv_t *priv = (anemometer_priv_t *)anemometer; - gpio_isr_handler_remove(priv->input_pin); + ESP_ERROR_CHECK(esp_timer_stop(priv->timer)); + anemometer_pulse_counter_deinit(priv); free(priv); return ESP_OK; } @@ -130,11 +216,10 @@ esp_err_t anemometer_get_wind_speed(anemometer_t *anemometer, float *speed) { CHECK_ARG(anemometer); CHECK_ARG(speed); + anemometer_priv_t *priv = (anemometer_priv_t *)anemometer; - TickType_t ticks = xTaskGetTickCount(); PORT_ENTER_CRITICAL(); - pps_iterate(priv,ticks); *speed = priv->sf * priv->pps; PORT_EXIT_CRITICAL(); return ESP_OK;