Skip to content

Commit

Permalink
anemometer: redesigned driver to use PCNT on ESP32 and interrupts on …
Browse files Browse the repository at this point in the history
…ESP8266
  • Loading branch information
QB4-dev committed Feb 18, 2024
1 parent dc7954f commit 1db2400
Showing 1 changed file with 113 additions and 28 deletions.
141 changes: 113 additions & 28 deletions components/anemometer/anemometer.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@

#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_timer.h>
#include <driver/gpio.h>
#include <esp_timer.h>
#include <esp_log.h>
#include <esp_idf_lib_helpers.h>

#if HELPER_TARGET_IS_ESP32
#include <driver/pulse_cnt.h>
static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
#define PORT_ENTER_CRITICAL() portENTER_CRITICAL(&mux)
#define PORT_EXIT_CRITICAL() portEXIT_CRITICAL(&mux)
Expand All @@ -58,34 +60,120 @@ 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)
{
CHECK_ARG(conf);
CHECK_ARG(anemometer);

gpio_config_t io_conf;
anemometer_priv_t *priv;
esp_err_t rc;

Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down

0 comments on commit 1db2400

Please sign in to comment.