diff --git a/Documentation/platforms/risc-v/esp32c6/index.rst b/Documentation/platforms/risc-v/esp32c6/index.rst index 197be4219d..bd98d966e6 100644 --- a/Documentation/platforms/risc-v/esp32c6/index.rst +++ b/Documentation/platforms/risc-v/esp32c6/index.rst @@ -287,7 +287,7 @@ Int. Temp. No LED No LED_PWM Yes MCPWM Yes -Pulse Counter No +Pulse Counter Yes RMT No RNG No RSA No diff --git a/Documentation/platforms/risc-v/esp32h2/index.rst b/Documentation/platforms/risc-v/esp32h2/index.rst index bf261e5538..95a2d0aef8 100644 --- a/Documentation/platforms/risc-v/esp32h2/index.rst +++ b/Documentation/platforms/risc-v/esp32h2/index.rst @@ -287,7 +287,7 @@ Int. Temp. No LED No LED_PWM Yes MCPWM No -Pulse Counter No +Pulse Counter Yes RMT No RNG No RSA No diff --git a/arch/risc-v/src/common/espressif/Kconfig b/arch/risc-v/src/common/espressif/Kconfig index 4ced0c7982..454112abd0 100644 --- a/arch/risc-v/src/common/espressif/Kconfig +++ b/arch/risc-v/src/common/espressif/Kconfig @@ -554,14 +554,20 @@ config ESP_MCPWM config ESP_PCNT bool "Pulse Counter (PCNT / QE) Module" default n + select CAPTURE depends on ESPRESSIF_ESP32H2 || ESPRESSIF_ESP32C6 - ---help--- - Pulse Counter is currently used to implement Quadrature - Encoder. menu "Pulse Counter (PCNT) Configuration" depends on ESP_PCNT +config ESP_PCNT_TEST_MODE + bool "Pulse Counter character driver loopback test mode (for testing only)" + default n + ---help--- + This enables a loopback test mode that attaches the transmitter + to the receiver internally, being able to test the PCNT + peripheral without any external connection. + config ESP_PCNT_AS_QE bool default n @@ -574,7 +580,7 @@ if ESP_PCNT_U0 config ESP_PCNT_U0_QE bool "Use this PCNT Unit as Quadrature Encoder" - default y + default n select ESP_PCNT_AS_QE config ESP_PCNT_U0_CH0_EDGE_PIN @@ -584,15 +590,15 @@ config ESP_PCNT_U0_CH0_EDGE_PIN config ESP_PCNT_U0_CH0_LEVEL_PIN int "PCNT_U0 CH0 Level/Control Pin Number" - default 4 + default 4 if !ESP_PCNT_U0_QE + default -1 if ESP_PCNT_U0_QE range -1 39 - depends on !ESP_PCNT_U0_QE config ESP_PCNT_U0_CH1_EDGE_PIN int "PCNT_U0 CH1 Edge/Pulse Pin Number" - default 0 + default 0 if !ESP_PCNT_U0_QE + default -1 if ESP_PCNT_U0_QE range -1 39 - depends on !ESP_PCNT_U0_QE config ESP_PCNT_U0_CH1_LEVEL_PIN int "PCNT_U0 CH1 Level/Control Pin Number" @@ -618,13 +624,14 @@ endif # ESP_PCNT_U0 config ESP_PCNT_U1 bool "Enable PCNT Unit 1" + depends on ESP_PCNT_U0 default n if ESP_PCNT_U1 config ESP_PCNT_U1_QE bool "Use this PCNT Unit as Quadrature Encoder" - default y + default n select ESP_PCNT_AS_QE config ESP_PCNT_U1_CH0_EDGE_PIN @@ -668,13 +675,14 @@ endif # ESP_PCNT_U1 config ESP_PCNT_U2 bool "Enable PCNT Unit 2" + depends on ESP_PCNT_U1 default n if ESP_PCNT_U2 config ESP_PCNT_U2_QE bool "Use this PCNT Unit as Quadrature Encoder" - default y + default n select ESP_PCNT_AS_QE config ESP_PCNT_U2_CH0_EDGE_PIN @@ -718,13 +726,14 @@ endif # ESP_PCNT_U2 config ESP_PCNT_U3 bool "Enable PCNT Unit 3" + depends on ESP_PCNT_U2 default n if ESP_PCNT_U3 config ESP_PCNT_U3_QE bool "Use this PCNT Unit as Quadrature Encoder" - default y + default n select ESP_PCNT_AS_QE config ESP_PCNT_U3_CH0_EDGE_PIN diff --git a/arch/risc-v/src/common/espressif/Make.defs b/arch/risc-v/src/common/espressif/Make.defs index 93517ae283..132bcf0c7a 100644 --- a/arch/risc-v/src/common/espressif/Make.defs +++ b/arch/risc-v/src/common/espressif/Make.defs @@ -85,8 +85,11 @@ ifeq ($(CONFIG_ESPRESSIF_LEDC),y) CHIP_CSRCS += esp_ledc.c endif -ifeq ($(CONFIG_ESP_PCNT_AS_QE),y) - CHIP_CSRCS += esp_qencoder.c +ifeq ($(CONFIG_ESP_PCNT),y) + CHIP_CSRCS += esp_pcnt.c + ifeq ($(CONFIG_ESP_PCNT_AS_QE),y) + CHIP_CSRCS += esp_qencoder.c + endif endif ifeq ($(CONFIG_ESPRESSIF_USBSERIAL),y) diff --git a/arch/risc-v/src/common/espressif/esp_pcnt.c b/arch/risc-v/src/common/espressif/esp_pcnt.c new file mode 100644 index 0000000000..fc5f8d5cdb --- /dev/null +++ b/arch/risc-v/src/common/espressif/esp_pcnt.c @@ -0,0 +1,1380 @@ +/**************************************************************************** + * arch/risc-v/src/common/espressif/esp_pcnt.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "riscv_internal.h" +#include "esp_pcnt.h" +#include "esp_gpio.h" + +#include "hal/pcnt_hal.h" +#include "hal/pcnt_ll.h" +#include "soc/gpio_sig_map.h" +#include "periph_ctrl.h" +#include "soc/soc_caps.h" +#include "soc/pcnt_periph.h" +#include "soc/pcnt_reg.h" +#include "soc/pcnt_struct.h" +#include "soc/gpio_pins.h" +#include "esp_clk.h" +#include "esp_irq.h" +#include "esp_attr.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PCNT_UNIT_COUNT SOC_PCNT_GROUPS * SOC_PCNT_UNITS_PER_GROUP +#define GET_UNIT_ID_FROM_RET_CHAN(chan_id) (int)(chan_id/SOC_PCNT_CHANNELS_PER_UNIT) +#define GET_CHAN_ID_FROM_RET_CHAN(unit_id, chan_id) (chan_id - (SOC_PCNT_CHANNELS_PER_UNIT * unit_id)) +#define CREATE_RET_CHAN_ID(unit_id, chan_id) ((SOC_PCNT_CHANNELS_PER_UNIT * unit_id) + chan_id) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* PCNT unit states */ + +enum esp_pcntstate_e +{ + PCNT_UNIT_INIT, + PCNT_UNIT_ENABLE, +}; + +struct esp_pcnt_glitch_filter_config_s +{ + uint32_t max_glitch_ns; /* Pulse width threshold, in ns */ +}; + +/* PCNT Unit Watch Point Private Data */ + +struct esp_pcnt_watch_point_priv_s +{ + pcnt_ll_watch_event_id_t event_id; /* Event type */ + int watch_point_value; /* Value to be watched */ +}; + +/* PCNT Unit Private Data */ + +struct esp_pcnt_priv_s +{ + const struct cap_ops_s *ops; + int unit_id; /* PCNT unit id */ + volatile enum esp_pcntstate_e state; /* PCNT unit work state (see enum esp_pcntstate_e) */ + struct esp_pcnt_unit_config_s config; /* Configuration struct */ + bool unit_used; /* PCNT unit usage flag */ + bool intr; /* PCNT unit interrupt enable flag */ + spinlock_t lock; /* Device specific lock. */ + int (*cb)(int, void *, void *); /* User defined callback */ + uint32_t accum_value; /* Accumulator value of overflowed PCNT unit */ + bool channels[SOC_PCNT_CHANNELS_PER_UNIT]; /* Channel information of PCNT unit */ + struct esp_pcnt_watch_point_priv_s watchers[PCNT_LL_WATCH_EVENT_MAX]; /* array of PCNT watchers */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int esp_pcnt_open(struct cap_lowerhalf_s *dev); +static int esp_pcnt_close(struct cap_lowerhalf_s *dev); +static int IRAM_ATTR esp_pcnt_isr_default(int irq, void *context, + void *arg); +static int esp_pcnt_isr_register(int (*fn)(int, void *, void *), + int intr_alloc_flags); +static int esp_pcnt_ioctl(struct cap_lowerhalf_s *dev, int cmd, + unsigned long arg); +static int esp_pcnt_unit_enable(struct cap_lowerhalf_s *dev); +static int esp_pcnt_unit_disable(struct cap_lowerhalf_s *dev); +static int esp_pcnt_unit_start(struct cap_lowerhalf_s *dev); +static int esp_pcnt_unit_stop(struct cap_lowerhalf_s *dev); +static int esp_pcnt_unit_get_count(struct cap_lowerhalf_s *dev, int *ret); +static int esp_pcnt_unit_clear_count(struct cap_lowerhalf_s *dev); +static int esp_pcnt_unit_set_glitch_filter(struct cap_lowerhalf_s *dev, + const struct esp_pcnt_glitch_filter_config_s *config); +static int esp_pcnt_unit_register_event_callback(struct cap_lowerhalf_s *dev, + int (*fn)(int, void *, void *)); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Standard file operations */ + +static const struct cap_ops_s g_esp_pcnt_fops = +{ + .start = esp_pcnt_open, + .stop = esp_pcnt_close, + .getduty = NULL, + .getfreq = NULL, + .ioctl = esp_pcnt_ioctl, +}; + +static struct esp_pcnt_priv_s pcnt_units[PCNT_UNIT_COUNT] = +{ + 0 +}; + +static pcnt_hal_context_t ctx; /* Struct of the common layer */ +static mutex_t g_pcnt_lock = NXMUTEX_INITIALIZER; /* Mutual exclusion mutex */ +static int g_pcnt_refs = 0; /* Reference count */ +static bool g_pcnt_intr = false; /* ISR register flag for peripheral */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp_pcnt_close + * + * Description: + * Stop PCNT unit from counting. + * + * Input Parameters: + * filep - The pointer of file, represents each user using the sensor + * + * Returned Value: + * Returns OK on success; a negated errno value on failure + * + ****************************************************************************/ + +static int esp_pcnt_close(struct cap_lowerhalf_s *dev) +{ + struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev; + int ret = OK; + + ret = esp_pcnt_unit_stop(dev); + if (ret != OK) + { + cperr("Failed to stop pcnt-%d", priv->unit_id); + return ret; + } + + if (priv->state != PCNT_UNIT_INIT) + { + ret = esp_pcnt_unit_disable(dev); + if (ret != OK) + { + cperr("Failed to disable pcnt-%d", priv->unit_id); + } + } + + return ret; +} + +/**************************************************************************** + * Name: esp_pcnt_open + * + * Description: + * Start PCNT unit for counting. + * + * Input Parameters: + * filep - The pointer of file, represents each user using the sensor + * + * Returned Value: + * Returns OK on success; a negated errno value on failure + * + ****************************************************************************/ + +static int esp_pcnt_open(struct cap_lowerhalf_s *dev) +{ + struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev; + int ret = OK; + + if (priv->state == PCNT_UNIT_INIT) + { + ret = esp_pcnt_unit_enable(dev); + if (ret != OK) + { + cperr("Failed to enable pcnt-%d", priv->unit_id); + return ret; + } + } + + ret = esp_pcnt_unit_start(dev); + if (ret != OK) + { + cperr("Failed to start pcnt-%d", priv->unit_id); + } + + return ret; +} + +/**************************************************************************** + * Name: esp_pcnt_ioctl + * + * Description: + * Lower-half logic may support platform-specific ioctl commands + * + * Input Parameters: + * filep - The pointer of file, represents each user using the sensor + * cmd - The ioctl command + * arg - The argument accompanying the ioctl command + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int esp_pcnt_ioctl(struct cap_lowerhalf_s *dev, int cmd, + unsigned long arg) +{ + struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev; + + struct esp_pcnt_glitch_filter_config_s glitch_config; + int ret = 0; + xcpt_t handler; + + /* Decode and dispatch the driver-specific IOCTL command */ + + switch (cmd) + { + case CAPIOC_PULSES: + + /* Get PCNT unit count value */ + + ret = esp_pcnt_unit_get_count(dev, (int *)arg); + if (ret != OK) + { + cperr("Could not clear pcnt-%d!\n", priv->unit_id); + } + + break; + + case CAPIOC_CLR_CNT: + + /* Clear PCNT unit count value */ + + ret = esp_pcnt_unit_clear_count(dev); + if (ret != OK) + { + cperr("Could not clear pcnt-%d!\n", priv->unit_id); + } + + break; + + case CAPIOC_FILTER: + + /* Configure glitch filter */ + + if (arg != 0) + { + glitch_config.max_glitch_ns = arg; + ret = esp_pcnt_unit_set_glitch_filter(dev, &glitch_config); + } + else + { + ret = esp_pcnt_unit_set_glitch_filter(dev, NULL); + } + + if (ret != OK) + { + cperr("Could not to set glitch filter to pcnt-%d!\n", + priv->unit_id); + } + + break; + + case CAPIOC_HANDLER: + + /* Set event callback */ + + handler = (xcpt_t)arg; + ret = esp_pcnt_unit_register_event_callback(dev, handler); + if (ret != OK) + { + cperr("Could not register callback-%lx to pcnt-%d!\n", + (uint32_t)handler, priv->unit_id); + } + + break; + + case CAPIOC_ADD_WP: + + /* Add watch point */ + + ret = esp_pcnt_unit_add_watch_point(dev, (int)arg); + if (ret != OK) + { + cperr("Could not to add watch point-%d to pcnt-%d!\n", + (int)arg, priv->unit_id); + } + + break; + + default: + cperr("Unrecognized IOCTL command: %d\n", cmd); + ret = -ENOTTY; + break; + } + + return ret; +} + +/**************************************************************************** + * Name: esp_pcnt_isr_default + * + * Description: + * Handler for the PCNT controller interrupt. + * + * Input Parameters: + * irq - Number of the IRQ that generated the interrupt + * context - Interrupt register state save info + * arg - PCNT controller private data + * + * Returned Value: + * Standard interrupt return value. + * + ****************************************************************************/ + +static int IRAM_ATTR esp_pcnt_isr_default(int irq, void *context, + void *arg) +{ + struct esp_pcnt_priv_s *unit; + int unit_id = 0; + uint32_t event_status = 0; + uint32_t intr_status = pcnt_ll_get_intr_status(ctx.dev); + struct esp_pcnt_watch_event_data_s data; + irqstate_t flags; + + for (unit_id = 0; unit_id < SOC_PCNT_UNITS_PER_GROUP; unit_id++) + { + if (intr_status & PCNT_LL_UNIT_WATCH_EVENT(unit_id)) + break; + } + + if (unit_id < SOC_PCNT_UNITS_PER_GROUP) + { + unit = &pcnt_units[unit_id]; + pcnt_ll_clear_intr_status(ctx.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id)); + event_status = pcnt_ll_get_event_status(ctx.dev, unit_id); + while (event_status) + { + int event_id = __builtin_ffs(event_status) - 1; + event_status &= (event_status - 1); + + flags = spin_lock_irqsave(unit->lock); + if (unit->config.accum_count) + { + if (event_id == PCNT_LL_WATCH_EVENT_LOW_LIMIT) + { + unit->accum_value += unit->config.low_limit; + } + else if (event_id == PCNT_LL_WATCH_EVENT_HIGH_LIMIT) + { + unit->accum_value += unit->config.high_limit; + } + } + + spin_unlock_irqrestore(&pcnt_units[unit_id].lock, flags); + if (unit->cb) + { + data.unit_id = unit_id; + data.watch_point_value = + unit->watchers[event_id].watch_point_value; + data.zero_cross_mode = + pcnt_ll_get_zero_cross_mode(ctx.dev, unit_id); + unit->cb(irq, context, &data); + } + } + } + + return 0; +} + +/**************************************************************************** + * Name: esp_pcnt_isr_register + * + * Description: + * This function registers an interrupt service routine (ISR) for the PCNT + * peripheral. It allocates a CPU interrupt, attaches the ISR to the + * interrupt, and returns the status of the operation. + * + * Input Parameters: + * fn - Pointer to the ISR function. + * intr_alloc_flags - Flags for the interrupt allocation. + * + * Returned Value: + * Returns OK on successful registration of the ISR; a negated errno value + * is returned on any failure. + * + ****************************************************************************/ + +static int esp_pcnt_isr_register(int (*fn)(int, void *, void *), + int intr_alloc_flags) +{ + int cpuint; + int ret; + int cpu = this_cpu(); + + DEBUGASSERT(fn); + + cpuint = esp_setup_irq(pcnt_periph_signals.groups[0].irq, + ESP_IRQ_PRIORITY_DEFAULT, + ESP_IRQ_TRIGGER_LEVEL); + if (cpuint < 0) + { + cperr("Failed to allocate a CPU interrupt.\n"); + return ERROR; + } + + ret = irq_attach(ESP_SOURCE2IRQ(pcnt_periph_signals.groups[0].irq), + fn, + 0); + if (ret < 0) + { + cperr("Couldn't attach IRQ to handler.\n"); + esp_teardown_irq(pcnt_periph_signals.groups[0].irq, cpuint); + return ERROR; + } + + up_enable_irq(ESP_SOURCE2IRQ(pcnt_periph_signals.groups[0].irq)); + return OK; +} + +/**************************************************************************** + * Name: esp_pcnt_unit_enable + * + * Description: + * Enable PCNT unit. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +static int esp_pcnt_unit_enable(struct cap_lowerhalf_s *dev) +{ + struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev; + + if (!priv->unit_used) + { + cperr("Invalid unit ID!\n"); + return ERROR; + } + + if (priv->state != PCNT_UNIT_INIT) + { + cperr("Unit is already enabled!\n"); + return ERROR; + } + + if (priv->intr) + { + pcnt_ll_enable_intr(ctx.dev, PCNT_LL_UNIT_WATCH_EVENT(priv->unit_id), + true); + } + + priv->state = PCNT_UNIT_ENABLE; + return OK; +} + +/**************************************************************************** + * Name: esp_pcnt_unit_disable + * + * Description: + * Disable PCNT unit. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +static int esp_pcnt_unit_disable(struct cap_lowerhalf_s *dev) +{ + struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev; + + if (!priv->unit_used) + { + cperr("Invalid unit ID!\n"); + return ERROR; + } + + if (priv->state != PCNT_UNIT_ENABLE) + { + cperr("Unit is already disabled!\n"); + return ERROR; + } + + if (priv->intr) + { + pcnt_ll_enable_intr(ctx.dev, PCNT_LL_UNIT_WATCH_EVENT(priv->unit_id), + false); + } + + priv->state = PCNT_UNIT_INIT; + return OK; +} + +/**************************************************************************** + * Name: esp_pcnt_unit_start + * + * Description: + * Start PCNT unit for counting. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +static int esp_pcnt_unit_start(struct cap_lowerhalf_s *dev) +{ + struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev; + irqstate_t flags; + + if (priv->state != PCNT_UNIT_ENABLE) + { + cperr("Unit not enabled yet!\n"); + return ERROR; + } + + flags = spin_lock_irqsave(&priv->lock); + pcnt_ll_start_count(ctx.dev, priv->unit_id); + spin_unlock_irqrestore(&priv->lock, flags); + + return OK; +} + +/**************************************************************************** + * Name: esp_pcnt_unit_stop + * + * Description: + * Stop PCNT unit from counting. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +static int esp_pcnt_unit_stop(struct cap_lowerhalf_s *dev) +{ + struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev; + irqstate_t flags; + + if (priv->state != PCNT_UNIT_ENABLE) + { + cperr("Unit not enabled yet!\n"); + return ERROR; + } + + flags = spin_lock_irqsave(&priv->lock); + pcnt_ll_stop_count(ctx.dev, priv->unit_id); + spin_unlock_irqrestore(&priv->lock, flags); + return OK; +} + +/**************************************************************************** + * Name: esp_pcnt_unit_get_count + * + * Description: + * Get count value given of PCNT unit. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * ret - Returned count value + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +static int esp_pcnt_unit_get_count(struct cap_lowerhalf_s *dev, int *ret) +{ + struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev; + irqstate_t flags; + + if (!priv->unit_used) + { + cperr("Invalid unit ID!\n"); + return ERROR; + } + + flags = spin_lock_irqsave(&priv->lock); + *ret = pcnt_ll_get_count(ctx.dev, priv->unit_id) + + priv->accum_value; + spin_unlock_irqrestore(&priv->lock, flags); + + return OK; +} + +/**************************************************************************** + * Name: esp_pcnt_unit_clear_count + * + * Description: + * Clear count value given of PCNT unit. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +static int esp_pcnt_unit_clear_count(struct cap_lowerhalf_s *dev) +{ + struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev; + irqstate_t flags; + + if (!priv->unit_used) + { + cperr("Invalid unit ID!\n"); + return ERROR; + } + + flags = spin_lock_irqsave(&priv->lock); + pcnt_ll_clear_count(ctx.dev, priv->unit_id); + priv->accum_value = 0; + spin_unlock_irqrestore(&priv->lock, flags); + + return OK; +} + +/**************************************************************************** + * Name: esp_pcnt_unit_set_glitch_filter + * + * Description: + * Configure glitch filter of given unit. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * config - Glitch filter configuration + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +static int esp_pcnt_unit_set_glitch_filter(struct cap_lowerhalf_s *dev, + const struct esp_pcnt_glitch_filter_config_s *config) +{ + struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev; + uint32_t glitch_filter_thres = 0; + irqstate_t flags; + + if (config != NULL) + { + glitch_filter_thres = ((esp_clk_apb_freq() / 1000000) * + (config->max_glitch_ns / 1000)); + if (glitch_filter_thres > PCNT_LL_MAX_GLITCH_WIDTH) + { + cperr("Glitch width %" PRId32 " out of range!\n", + config->max_glitch_ns); + return ERROR; + } + } + + flags = spin_lock_irqsave(&priv->lock); + if (config != NULL) + { + pcnt_ll_set_glitch_filter_thres(ctx.dev, priv->unit_id, + glitch_filter_thres); + pcnt_ll_enable_glitch_filter(ctx.dev, priv->unit_id, true); + } + else + { + pcnt_ll_enable_glitch_filter(ctx.dev, priv->unit_id, false); + } + + spin_unlock_irqrestore(&priv->lock, flags); + return OK; +} + +/**************************************************************************** + * Name: esp_pcnt_unit_register_event_callback + * + * Description: + * Set event callbacks for PCNT unit + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * fn - Pointer to the ISR function. + * + * Returned Value: + * Returns OK on successful registration of the ISR; a negated errno value + * is returned on any failure. + * + ****************************************************************************/ + +static int esp_pcnt_unit_register_event_callback(struct cap_lowerhalf_s *dev, + int (*fn)(int, void *, void *)) +{ + struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev; + int ret = OK; + int intr_flag = (fn != NULL) ? 1 : 0; + irqstate_t flags; + + priv->intr = intr_flag; + flags = spin_lock_irqsave(&priv->lock); + pcnt_ll_enable_intr(ctx.dev, PCNT_LL_UNIT_WATCH_EVENT(priv->unit_id), + intr_flag); + spin_unlock_irqrestore(&priv->lock, flags); + priv->cb = fn; + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp_pcnt_new_unit + * + * Description: + * Request PCNT unit and config it with given parameters. + * + * Input Parameters: + * config - PCNT unit configuration + * + * Returned Value: + * PCNT unit number (>=0) if success or -1 if fail. + * + ****************************************************************************/ + +struct cap_lowerhalf_s *esp_pcnt_new_unit( + struct esp_pcnt_unit_config_s *config) +{ + int unit_id; + int cpuint; + int cpu = this_cpu(); + int ret; + int i; + irqstate_t flags; + + if (config == NULL) + { + cperr("Configuration struct is NULL!\n"); + return NULL; + } + + if (config->low_limit >= 0 && config->low_limit < PCNT_LL_MIN_LIN) + { + cperr("Configuration low limit is out of range!\n"); + return NULL; + } + + if (config->high_limit < 0 && config->high_limit >= PCNT_LL_MAX_LIM) + { + cperr("Configuration high limit is out of range!\n"); + return NULL; + } + + flags = spin_lock_irqsave(&pcnt_units[unit_id].lock); + + if (g_pcnt_refs++ == 0) + { + periph_module_enable(pcnt_periph_signals.groups[0].module); + periph_module_reset(pcnt_periph_signals.groups[0].module); + pcnt_hal_init(&ctx, 0); + } + + for (unit_id = 0; unit_id < PCNT_UNIT_COUNT; unit_id++) + { + if (!pcnt_units[unit_id].unit_used) + { + pcnt_units[unit_id].unit_used = true; + break; + } + } + + if (unit_id == PCNT_UNIT_COUNT) + { + cperr("No available PCNT unit for allocation\n"); + + spin_unlock_irqrestore(&pcnt_units[unit_id].lock, flags); + return NULL; + } + + spin_unlock_irqrestore(&pcnt_units[unit_id].lock, flags); + cpinfo("Allocated pcnt unit: %" PRId16 "\n", unit_id); + + if (!g_pcnt_intr) + { + nxmutex_lock(&g_pcnt_lock); + ret = esp_pcnt_isr_register(esp_pcnt_isr_default, 0); + if (ret < 0) + { + pcnt_units[unit_id].unit_used = false; + nxmutex_unlock(&g_pcnt_lock); + return NULL; + } + + g_pcnt_intr = true; + nxmutex_unlock(&g_pcnt_lock); + } + + pcnt_ll_disable_all_events(ctx.dev, unit_id); + pcnt_ll_enable_glitch_filter(ctx.dev, unit_id, false); + pcnt_ll_set_high_limit_value(ctx.dev, unit_id, config->high_limit); + pcnt_ll_set_low_limit_value(ctx.dev, unit_id, config->low_limit); + + pcnt_units[unit_id].config.high_limit = config->high_limit; + pcnt_units[unit_id].config.low_limit = config->low_limit; + pcnt_units[unit_id].config.accum_count = config->accum_count; + pcnt_units[unit_id].intr = config->accum_count; + pcnt_units[unit_id].accum_value = 0; + pcnt_units[unit_id].unit_id = unit_id; + pcnt_units[unit_id].ops = &g_esp_pcnt_fops; + + flags = spin_lock_irqsave(&pcnt_units[unit_id].lock); + + pcnt_ll_stop_count(ctx.dev, unit_id); + pcnt_ll_clear_count(ctx.dev, unit_id); + + pcnt_ll_enable_intr(ctx.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id), + config->accum_count); + pcnt_ll_clear_intr_status(ctx.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id)); + spin_unlock_irqrestore(&pcnt_units[unit_id].lock, flags); + + pcnt_units[unit_id].state = PCNT_UNIT_INIT; + for (i = 0; i < PCNT_LL_WATCH_EVENT_MAX; i++) + { + pcnt_units[unit_id].watchers[i].event_id = PCNT_LL_WATCH_EVENT_INVALID; + } + + cpinfo("Allocated pcnt unit: %" PRId16 " count range:[%d %d]\n", unit_id, + pcnt_units[unit_id].config.low_limit, + pcnt_units[unit_id].config.high_limit); + + return (struct cap_lowerhalf_s *)&pcnt_units[unit_id]; +} + +/**************************************************************************** + * Name: esp_pcnt_del_unit + * + * Description: + * Delete PCNT unit. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +int esp_pcnt_del_unit(struct cap_lowerhalf_s *dev) +{ + struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev; + int i; + irqstate_t flags; + + if (priv->state != PCNT_UNIT_INIT) + { + cperr("Unit is running!\n"); + return ERROR; + } + + for (i = 0; i < SOC_PCNT_CHANNELS_PER_UNIT; i++) + { + if (!priv->channels[i]) + { + cperr("Channel %" PRId8 + " still in working!\n", i); + return ERROR; + } + } + + cpinfo("Delete pcnt unit: %" PRId8 "\n", priv->unit_id); + + if (priv->intr) + { + pcnt_ll_enable_intr(ctx.dev, PCNT_LL_UNIT_WATCH_EVENT(priv->unit_id), + false); + priv->intr = false; + } + + flags = spin_lock_irqsave(&priv->lock); + priv->unit_used = false; + g_pcnt_refs--; + if (g_pcnt_refs == 0) + { + periph_module_disable(pcnt_periph_signals.groups[0].module); + esp_teardown_irq(pcnt_periph_signals.groups[0].irq, -ENOMEM); + } + + spin_unlock_irqrestore(&priv->lock, flags); + + return OK; +} + +/**************************************************************************** + * Name: esp_pcnt_unit_add_watch_point + * + * Description: + * Add watch point to given PCNT unit. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * ret - Value to watch + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +int esp_pcnt_unit_add_watch_point(struct cap_lowerhalf_s *dev, + int watch_point) +{ + struct esp_pcnt_priv_s *unit = (struct esp_pcnt_priv_s *)dev; + int ret = OK; + struct esp_pcnt_watch_point_priv_s *wp; + irqstate_t flags; + + if (watch_point > unit->config.high_limit || + watch_point < unit->config.low_limit) + { + cperr("Watch point is out of limit!\n"); + return ERROR; + } + + flags = spin_lock_irqsave(&unit->lock); + + /* zero cross watch point */ + + if (watch_point == 0) + { + if (unit->watchers[PCNT_LL_WATCH_EVENT_ZERO_CROSS].event_id != + PCNT_LL_WATCH_EVENT_INVALID) + { + cperr("Zero cross event watcher has been installed already!\n"); + ret = ERROR; + } + else + { + unit->watchers[PCNT_LL_WATCH_EVENT_ZERO_CROSS].event_id = + PCNT_LL_WATCH_EVENT_ZERO_CROSS; + unit->watchers[PCNT_LL_WATCH_EVENT_ZERO_CROSS].watch_point_value = + 0; + pcnt_ll_enable_zero_cross_event(ctx.dev, + unit->unit_id, + true); + } + } + + /* high limit watch point */ + + else if (watch_point == unit->config.high_limit) + { + if (unit->watchers[PCNT_LL_WATCH_EVENT_HIGH_LIMIT].event_id != + PCNT_LL_WATCH_EVENT_INVALID) + { + cperr("High limit event watcher has been installed already!\n"); + ret = ERROR; + } + else + { + unit->watchers[PCNT_LL_WATCH_EVENT_HIGH_LIMIT].event_id = + PCNT_LL_WATCH_EVENT_HIGH_LIMIT; + unit->watchers[PCNT_LL_WATCH_EVENT_HIGH_LIMIT].watch_point_value = + unit->config.high_limit; + pcnt_ll_enable_high_limit_event(ctx.dev, + unit->unit_id, + true); + } + } + + /* low limit watch point */ + + else if (watch_point == unit->config.low_limit) + { + if (unit->watchers[PCNT_LL_WATCH_EVENT_LOW_LIMIT].event_id != + PCNT_LL_WATCH_EVENT_INVALID) + { + cperr("Low limit event watcher has been installed already!\n"); + ret = ERROR; + } + else + { + unit->watchers[PCNT_LL_WATCH_EVENT_LOW_LIMIT].event_id = + PCNT_LL_WATCH_EVENT_LOW_LIMIT; + unit->watchers[PCNT_LL_WATCH_EVENT_LOW_LIMIT].watch_point_value = + unit->config.low_limit; + pcnt_ll_enable_low_limit_event(ctx.dev, + unit->unit_id, + true); + } + } + + /* other threshold watch point */ + + else + { + int thres_num = SOC_PCNT_THRES_POINT_PER_UNIT - 1; + switch (thres_num) + { + case 1: + wp = &unit->watchers[PCNT_LL_WATCH_EVENT_THRES1]; + if (wp->event_id == PCNT_LL_WATCH_EVENT_INVALID) + { + wp->event_id = PCNT_LL_WATCH_EVENT_THRES1; + wp->watch_point_value = watch_point; + pcnt_ll_set_thres_value(ctx.dev, + unit->unit_id, + 1, + watch_point); + pcnt_ll_enable_thres_event(ctx.dev, + unit->unit_id, + 1, + true); + break; + } + else if (wp->watch_point_value == watch_point) + { + cperr("Watcher has been installed already!\n"); + ret = ERROR; + break; + } + + case 0: + wp = &unit->watchers[PCNT_LL_WATCH_EVENT_THRES0]; + if (wp->event_id == PCNT_LL_WATCH_EVENT_INVALID) + { + wp->event_id = PCNT_LL_WATCH_EVENT_THRES0; + wp->watch_point_value = watch_point; + pcnt_ll_set_thres_value(ctx.dev, + unit->unit_id, + 0, + watch_point); + pcnt_ll_enable_thres_event(ctx.dev, + unit->unit_id, + 0, + true); + break; + } + else if (wp->watch_point_value == watch_point) + { + cperr("Watcher has been installed already!\n"); + ret = ERROR; + break; + } + + default: + cperr("No free threshold watch point available!\n"); + ret = ERROR; + break; + } + } + + spin_unlock_irqrestore(&unit->lock, flags); + + if (ret != ERROR) + { + cpinfo("Watchpoint %d for pcnt unit-%d added successfully\n", + watch_point, + unit->unit_id); + } + + return ret; +} + +/**************************************************************************** + * Name: esp_pcnt_unit_remove_watch_point + * + * Description: + * Remove watch point from given PCNT unit. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * ret - Watch point value to remove + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +int esp_pcnt_unit_remove_watch_point(struct cap_lowerhalf_s *dev, + int watch_point) +{ + struct esp_pcnt_priv_s *unit = (struct esp_pcnt_priv_s *)dev; + int ret = OK; + int i; + pcnt_ll_watch_event_id_t event_id = PCNT_LL_WATCH_EVENT_INVALID; + irqstate_t flags; + + flags = spin_lock_irqsave(&unit->lock); + + for (i = 0; i < PCNT_LL_WATCH_EVENT_MAX; i++) + { + if (unit->watchers[i].event_id != PCNT_LL_WATCH_EVENT_INVALID && + unit->watchers[i].watch_point_value == watch_point) + { + event_id = unit->watchers[i].event_id; + unit->watchers[i].event_id = PCNT_LL_WATCH_EVENT_INVALID; + break; + } + } + + switch (event_id) + { + case PCNT_LL_WATCH_EVENT_ZERO_CROSS: + pcnt_ll_enable_zero_cross_event(ctx.dev, unit->unit_id, false); + break; + case PCNT_LL_WATCH_EVENT_LOW_LIMIT: + pcnt_ll_enable_low_limit_event(ctx.dev, unit->unit_id, false); + break; + case PCNT_LL_WATCH_EVENT_HIGH_LIMIT: + pcnt_ll_enable_high_limit_event(ctx.dev, unit->unit_id, false); + break; + case PCNT_LL_WATCH_EVENT_THRES0: + pcnt_ll_enable_thres_event(ctx.dev, unit->unit_id, 0, false); + break; + case PCNT_LL_WATCH_EVENT_THRES1: + pcnt_ll_enable_thres_event(ctx.dev, unit->unit_id, 1, false); + break; + default: + break; + } + + spin_unlock_irqrestore(&unit->lock, flags); + + return ret; +} + +/**************************************************************************** + * Name: esp_pcnt_new_channel + * + * Description: + * Request channel on given PCNT unit and config it with given parameters. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * config - PCNT unit channel configuration + * + * Returned Value: + * PCNT unit channel number (>=0) if success or -1 if fail. + * + ****************************************************************************/ + +int esp_pcnt_new_channel(struct cap_lowerhalf_s *dev, + struct esp_pcnt_chan_config_s *config) +{ + struct esp_pcnt_priv_s *priv = (struct esp_pcnt_priv_s *)dev; + int channel_id = -1; + int unit_id = priv->unit_id; + int gpio_mode; + int virt_gpio; + int ret_id = 0; + const pcnt_signal_conn_t *chan; + + if (!config) + { + cperr("Channel config is NULL!\n"); + return ERROR; + } + + if (!priv->unit_used) + { + cperr("Unit did not initialize yet!\n"); + return ERROR; + } + + if (priv->state != PCNT_UNIT_INIT) + { + cperr("Unit already running!\n"); + return ERROR; + } + + for (int i = 0; i < SOC_PCNT_CHANNELS_PER_UNIT; i++) + { + if (!priv->channels[i]) + { + channel_id = i; + break; + } + } + + if (channel_id == -1) + { + cperr("no free channel in unit %" PRId8 "!\n", priv->unit_id); + return ERROR; + } + + gpio_mode = INPUT_FUNCTION | PULLUP | + (config->flags && ESP_PCNT_CHAN_IO_LOOPBACK ? OUTPUT_FUNCTION : 0); + virt_gpio = (config->flags && ESP_PCNT_CHAN_VIRT_LVL_IO_LVL) ? + GPIO_MATRIX_CONST_ONE_INPUT : GPIO_MATRIX_CONST_ZERO_INPUT; + chan = &pcnt_periph_signals; + + if (config->edge_gpio_num >= 0) + { + esp_configgpio(config->edge_gpio_num, gpio_mode); + esp_gpio_matrix_in(config->edge_gpio_num, + chan->groups[0].units[unit_id].channels[channel_id].pulse_sig, + (config->flags && ESP_PCNT_CHAN_INVERT_EDGE_IN)); + } + else + { + /* using virtual IO */ + + esp_gpio_matrix_in(virt_gpio, + chan->groups[0].units[unit_id].channels[channel_id].pulse_sig, + (config->flags && ESP_PCNT_CHAN_INVERT_EDGE_IN)); + } + + if (config->level_gpio_num >= 0) + { + esp_configgpio(config->level_gpio_num, gpio_mode); + esp_gpio_matrix_in(config->level_gpio_num, + chan->groups[0].units[unit_id].channels[channel_id].control_sig, + (config->flags && ESP_PCNT_CHAN_INVERT_LVL_IN)); + } + else + { + /* using virtual IO */ + + esp_gpio_matrix_in(virt_gpio, + chan->groups[0].units[unit_id].channels[channel_id].control_sig, + (config->flags && ESP_PCNT_CHAN_INVERT_LVL_IN)); + } + + priv->channels[channel_id] = true; + + cpinfo("Added pcnt unit: %" PRId8 " channel: %" PRId8 "\n", + unit_id, channel_id); + + ret_id = CREATE_RET_CHAN_ID(unit_id, channel_id); + return ret_id; +} + +/**************************************************************************** + * Name: esp_pcnt_del_channel + * + * Description: + * Delete PCNT unit channel. + * + * Input Parameters: + * channel - Channel number to delete + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +int esp_pcnt_del_channel(int channel) +{ + int unit_id = GET_UNIT_ID_FROM_RET_CHAN(channel); + int chan_id = GET_CHAN_ID_FROM_RET_CHAN(unit_id, channel); + irqstate_t flags; + + flags = spin_lock_irqsave(&pcnt_units[unit_id].lock); + pcnt_units[unit_id].channels[chan_id] = false; + spin_unlock_irqrestore(&pcnt_units[unit_id].lock, flags); + + cpinfo("Deleted pcnt unit: %" PRId8 " channel: %" PRId8 "\n", + unit_id, chan_id); + return OK; +} + +/**************************************************************************** + * Name: esp_pcnt_channel_set_edge_action + * + * Description: + * Set channel actions when edge signal changes. + * + * Input Parameters: + * channel - Channel number to set actions + * post act - Action on posedge signal + * neg_act - Action on negedge signal + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +void esp_pcnt_channel_set_edge_action(int channel, + enum esp_pcnt_chan_edge_action_e pos_act, + enum esp_pcnt_chan_edge_action_e neg_act) +{ + int unit_id = GET_UNIT_ID_FROM_RET_CHAN(channel); + int chan_id = GET_CHAN_ID_FROM_RET_CHAN(unit_id, channel); + irqstate_t flags; + + flags = spin_lock_irqsave(&pcnt_units[unit_id].lock); + pcnt_ll_set_edge_action(ctx.dev, unit_id, chan_id, + pos_act, neg_act); + spin_unlock_irqrestore(&pcnt_units[unit_id].lock, flags); +} + +/**************************************************************************** + * Name: esp_pcnt_channel_set_level_action + * + * Description: + * Set channel actions when level signal changes. + * + * Input Parameters: + * channel - Channel number to set actions + * post act - Action on posedge signal + * neg_act - Action on negedge signal + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +void esp_pcnt_channel_set_level_action(int channel, + enum esp_pcnt_chan_level_action_e pos_act, + enum esp_pcnt_chan_level_action_e neg_act) +{ + int unit_id = GET_UNIT_ID_FROM_RET_CHAN(channel); + int chan_id = GET_CHAN_ID_FROM_RET_CHAN(unit_id, channel); + irqstate_t flags; + + flags = spin_lock_irqsave(&pcnt_units[unit_id].lock); + pcnt_ll_set_level_action(ctx.dev, unit_id, chan_id, + pos_act, neg_act); + spin_unlock_irqrestore(&pcnt_units[unit_id].lock, flags); +} diff --git a/arch/risc-v/src/common/espressif/esp_pcnt.h b/arch/risc-v/src/common/espressif/esp_pcnt.h new file mode 100644 index 0000000000..4b5ee0ea62 --- /dev/null +++ b/arch/risc-v/src/common/espressif/esp_pcnt.h @@ -0,0 +1,238 @@ +/**************************************************************************** + * arch/risc-v/src/common/espressif/esp_pcnt.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISC_V_SRC_COMMON_ESPRESSIF_ESP_PCNT_H +#define __ARCH_RISC_V_SRC_COMMON_ESPRESSIF_ESP_PCNT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ESP_PCNT_CHAN_INVERT_EDGE_IN (1 << 0) +#define ESP_PCNT_CHAN_INVERT_LVL_IN (1 << 1) +#define ESP_PCNT_CHAN_VIRT_EDGE_IO_LVL (1 << 2) +#define ESP_PCNT_CHAN_VIRT_LVL_IO_LVL (1 << 3) +#define ESP_PCNT_CHAN_IO_LOOPBACK (1 << 4) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +enum esp_pcnt_chan_level_action_e +{ + ESP_PCNT_CHAN_LEVEL_ACTION_KEEP, /* Keep current count mode */ + ESP_PCNT_CHAN_LEVEL_ACTION_INVERSE, /* Invert current count mode */ + ESP_PCNT_CHAN_LEVEL_ACTION_HOLD, /* Hold current count value */ +}; + +enum esp_pcnt_chan_edge_action_e +{ + ESP_PCNT_CHAN_EDGE_ACTION_HOLD, /* Hold current count value */ + ESP_PCNT_CHAN_EDGE_ACTION_INCREASE, /* Increase count value */ + ESP_PCNT_CHAN_EDGE_ACTION_DECREASE, /* Decrease count value */ +}; + +enum esp_pcnt_unit_zero_cross_mode_e +{ + ESP_PCNT_UNIT_ZERO_CROSS_POS_ZERO, /* From positive value to zero */ + ESP_PCNT_UNIT_ZERO_CROSS_NEG_ZERO, /* From negative value, to zero */ + ESP_PCNT_UNIT_ZERO_CROSS_NEG_POS, /* From negative value, to positive value */ + ESP_PCNT_UNIT_ZERO_CROSS_POS_NEG, /* From positive value, to negative value */ +}; + +struct esp_pcnt_unit_config_s +{ + int low_limit; /* Low limitation of the count unit */ + int high_limit; /* High limitation of the count unit */ + bool accum_count; /* Accumulate the count value when overflow flag */ +}; + +struct esp_pcnt_chan_config_s +{ + int edge_gpio_num; /* Edge signal GPIO number,-1 if unused */ + int level_gpio_num; /* Level signal GPIO number ,-1 if unused */ + int flags; /* Channel config flags */ +}; + +struct esp_pcnt_watch_event_data_s +{ + int unit_id; /* PCNT Unit id */ + int watch_point_value; /* Watch point value that triggered the event */ + enum esp_pcnt_unit_zero_cross_mode_e zero_cross_mode; /* Zero cross mode */ +}; + +/**************************************************************************** + * Public functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp_pcnt_new_unit + * + * Description: + * Request PCNT unit and config it with given parameters. + * + * Input Parameters: + * config - PCNT unit configuration + * + * Returned Value: + * PCNT unit number (>=0) if success or -1 if fail. + * + ****************************************************************************/ + +struct cap_lowerhalf_s *esp_pcnt_new_unit( + struct esp_pcnt_unit_config_s *config); + +/**************************************************************************** + * Name: esp_pcnt_del_unit + * + * Description: + * Delete PCNT unit. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +int esp_pcnt_del_unit(struct cap_lowerhalf_s *dev); + +/**************************************************************************** + * Name: esp_pcnt_unit_add_watch_point + * + * Description: + * Add watch point to given PCNT unit. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * ret - Value to watch + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +int esp_pcnt_unit_add_watch_point(struct cap_lowerhalf_s *dev, + int watch_point); + +/**************************************************************************** + * Name: esp_pcnt_unit_remove_watch_point + * + * Description: + * Remove watch point from given PCNT unit. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * ret - Watch point value to remove + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +int esp_pcnt_unit_remove_watch_point(struct cap_lowerhalf_s *dev, + int watch_point); + +/**************************************************************************** + * Name: esp_pcnt_new_channel + * + * Description: + * Request channel on given PCNT unit and config it with given parameters. + * + * Input Parameters: + * dev - Pointer to the pcnt driver struct + * config - PCNT unit channel configuration + * + * Returned Value: + * PCNT unit channel number (>=0) if success or -1 if fail. + * + ****************************************************************************/ + +int esp_pcnt_new_channel(struct cap_lowerhalf_s *dev, + struct esp_pcnt_chan_config_s *config); + +/**************************************************************************** + * Name: esp_pcnt_del_channel + * + * Description: + * Delete PCNT unit channel. + * + * Input Parameters: + * channel - Channel number to delete + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +int esp_pcnt_del_channel(int channel); + +/**************************************************************************** + * Name: esp_pcnt_channel_set_edge_action + * + * Description: + * Set channel actions when edge signal changes. + * + * Input Parameters: + * channel - Channel number to set actions + * post act - Action on posedge signal + * neg_act - Action on negedge signal + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +void esp_pcnt_channel_set_edge_action(int channel, + enum esp_pcnt_chan_edge_action_e pos_act, + enum esp_pcnt_chan_edge_action_e neg_act); + +/**************************************************************************** + * Name: esp_pcnt_channel_set_level_action + * + * Description: + * Set channel actions when level signal changes. + * + * Input Parameters: + * channel - Channel number to set actions + * post act - Action on posedge signal + * neg_act - Action on negedge signal + * + * Returned Value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +void esp_pcnt_channel_set_level_action(int channel, + enum esp_pcnt_chan_level_action_e pos_act, + enum esp_pcnt_chan_level_action_e neg_act); + +#endif /* __ARCH_RISC_V_SRC_COMMON_ESPRESSIF_ESP_PCNT_H */ diff --git a/arch/risc-v/src/common/espressif/esp_qencoder.c b/arch/risc-v/src/common/espressif/esp_qencoder.c index 8e0f53b881..d2ab542290 100644 --- a/arch/risc-v/src/common/espressif/esp_qencoder.c +++ b/arch/risc-v/src/common/espressif/esp_qencoder.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include @@ -80,24 +82,10 @@ * Private Types ****************************************************************************/ -/* Constant configuration structure that is retained in FLASH */ - -struct esp_qeconfig_s -{ - uint8_t pcntid; /* PCNT ID {0,1,2,3} */ - uint8_t ch0_gpio; /* Channel 0 gpio pin (Edge/Pulse) */ - uint8_t ch1_gpio; /* Channel 1 gpio pin (Level/Ctrl) */ - uint32_t ch0_pulse_sig; /* ch0 pulse signal index */ - uint32_t ch0_ctrl_sig; /* ch0 ctrl signal index */ - uint32_t ch1_pulse_sig; /* ch1 pulse signal index */ - uint32_t ch1_ctrl_sig; /* ch1 ctrl signal index */ - uint16_t filter_thres; /* Filter threshold for this PCNT Unit */ -}; - /* NOTE: we are using Quadrature Encoder in X4 mode on ESP PCNT, then * instead of using 'pulse_gpio' and 'ctrl_gpio' names, we only use ch0_gpio - * and ch1_gpio names. It avoid confusion, since the same signal that is used - * on pin 'pulse' of CH0 is also connected to 'ctrl' pin of the CH1 and + * and ch1_gpio names. It avoids confusion, since the same signal that is + * used on pin 'pulse' of CH0 is also connected to 'ctrl' pin of the CH1 and * 'ctrl' pin of CH0 is also connected on 'pulse' pin of CH1. */ @@ -113,10 +101,9 @@ struct qe_dev_lowerhalf_s /* ESP driver-specific fields: */ - const struct esp_qeconfig_s *config; /* static configuration */ - bool inuse; /* True: The lower-half driver is - * in-use */ - struct pcnt_dev_t *dev; /* Device handle */ + struct cap_lowerhalf_s *pcnt; /* Device handle */ + int pcnt_num; /* Pulse Counter (PCNT) unit number */ + bool inuse; /* True: The lower-half driver is in-use */ volatile int32_t position; /* The current position offset */ spinlock_t lock; /* Device specific lock. */ }; @@ -125,12 +112,6 @@ struct qe_dev_lowerhalf_s * Private Function Prototypes ****************************************************************************/ -/* Interrupt handling */ - -#if 0 /* FIXME: To be implemented */ -static int esp_interrupt(int irq, void *context, void *arg); -#endif - /* Lower-half Quadrature Encoder Driver Methods */ static int esp_setup(struct qe_lowerhalf_s *lower); @@ -163,90 +144,34 @@ static const struct qe_ops_s g_qecallbacks = /* Per-pcnt state structures */ #ifdef CONFIG_ESP_PCNT_U0_QE -static const struct esp_qeconfig_s g_pcnt0config = -{ - .pcntid = 0, - .ch0_gpio = CONFIG_ESP_PCNT_U0_CH0_EDGE_PIN, - .ch1_gpio = CONFIG_ESP_PCNT_U0_CH1_LEVEL_PIN, - .ch0_pulse_sig = PCNT_SIG_CH0_IN0_IDX, - .ch1_pulse_sig = PCNT_SIG_CH1_IN0_IDX, - .ch0_ctrl_sig = PCNT_CTRL_CH0_IN0_IDX, - .ch1_ctrl_sig = PCNT_CTRL_CH1_IN0_IDX, - .filter_thres = CONFIG_ESP_PCNT_U0_FILTER_THRES, -}; - static struct qe_dev_lowerhalf_s g_pcnt0lower = { .ops = &g_qecallbacks, - .config = &g_pcnt0config, .inuse = false, - .dev = PCNT_LL_GET_HW(0), }; #endif #ifdef CONFIG_ESP_PCNT_U1_QE -static const struct esp_qeconfig_s g_pcnt1config = -{ - .pcntid = 1, - .ch0_gpio = CONFIG_ESP_PCNT_U1_CH0_EDGE_PIN, - .ch1_gpio = CONFIG_ESP_PCNT_U1_CH1_LEVEL_PIN, - .ch0_pulse_sig = PCNT_SIG_CH0_IN1_IDX, - .ch1_pulse_sig = PCNT_SIG_CH1_IN1_IDX, - .ch0_ctrl_sig = PCNT_CTRL_CH0_IN1_IDX, - .ch1_ctrl_sig = PCNT_CTRL_CH1_IN1_IDX, - .filter_thres = CONFIG_ESP_PCNT_U1_FILTER_THRES, -}; - static struct qe_dev_lowerhalf_s g_pcnt1lower = { .ops = &g_qecallbacks, - .config = &g_pcnt1config, .inuse = false, - .dev = PCNT_LL_GET_HW(0), }; #endif #ifdef CONFIG_ESP_PCNT_U2_QE -static const struct esp_qeconfig_s g_pcnt2config = -{ - .pcntid = 2, - .ch0_gpio = CONFIG_ESP_PCNT_U2_CH0_EDGE_PIN, - .ch1_gpio = CONFIG_ESP_PCNT_U2_CH1_LEVEL_PIN, - .ch0_pulse_sig = PCNT_SIG_CH0_IN2_IDX, - .ch1_pulse_sig = PCNT_SIG_CH1_IN2_IDX, - .ch0_ctrl_sig = PCNT_CTRL_CH0_IN2_IDX, - .ch1_ctrl_sig = PCNT_CTRL_CH1_IN2_IDX, - .filter_thres = CONFIG_ESP_PCNT_U2_FILTER_THRES, -}; - static struct qe_dev_lowerhalf_s g_pcnt2lower = { .ops = &g_qecallbacks, - .config = &g_pcnt2config, .inuse = false, - .dev = PCNT_LL_GET_HW(0), }; #endif #ifdef CONFIG_ESP_PCNT_U3_QE -static const struct esp_qeconfig_s g_pcnt3config = -{ - .pcntid = 3, - .ch0_gpio = CONFIG_ESP_PCNT_U3_CH0_EDGE_PIN, - .ch1_gpio = CONFIG_ESP_PCNT_U3_CH1_LEVEL_PIN, - .ch0_pulse_sig = PCNT_SIG_CH0_IN3_IDX, - .ch1_pulse_sig = PCNT_SIG_CH1_IN3_IDX, - .ch0_ctrl_sig = PCNT_CTRL_CH0_IN3_IDX, - .ch1_ctrl_sig = PCNT_CTRL_CH1_IN3_IDX, - .filter_thres = CONFIG_ESP_PCNT_U3_FILTER_THRES, -}; - static struct qe_dev_lowerhalf_s g_pcnt3lower = { .ops = &g_qecallbacks, - .config = &g_pcnt3config, .inuse = false, - .dev = PCNT_LL_GET_HW(0), }; #endif @@ -254,27 +179,6 @@ static struct qe_dev_lowerhalf_s g_pcnt3lower = * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: esp_interrupt - * - * Description: - * Common timer interrupt handling. NOTE: Only 16-bit timers require timer - * interrupts. - * - ****************************************************************************/ - -#if 0 /* FIXME: To be implemented */ -static int esp_interrupt(int irq, void *context, void *arg) -{ - struct qe_dev_lowerhalf_s *priv = (struct qe_dev_lowerhalf_s *)arg; - uint16_t regval; - - DEBUGASSERT(priv != NULL); - - return OK; -} -#endif - /**************************************************************************** * Name: esp_setup * @@ -306,59 +210,15 @@ static int esp_setup(struct qe_lowerhalf_s *lower) if (priv->inuse) { - snerr("ERROR: PCNT%d is in-use\n", priv->config->pcntid); + snerr("ERROR: PCNT%d is in-use\n", priv->pcnt_num); spin_unlock_irqrestore(&priv->lock, flags); return -EBUSY; } - /* Disable interrupts */ - - pcnt_ll_enable_intr(priv->dev, priv->config->pcntid, false); - - /* Disable all events */ - - pcnt_ll_disable_all_events(priv->dev, priv->config->pcntid); - - /* Configure the limits range PCNT_CNT_L_LIM | PCNT_CNT_H_LIM */ - - pcnt_ll_set_high_limit_value(priv->dev, priv->config->pcntid, INT16_MAX); - pcnt_ll_set_low_limit_value(priv->dev, priv->config->pcntid, INT16_MIN); - - /* Setup POS/NEG/LCTRL/HCTRL/FILTER modes */ - - pcnt_ll_set_glitch_filter_thres(priv->dev, - priv->config->pcntid, - priv->config->filter_thres); - pcnt_ll_enable_glitch_filter(priv->dev, - priv->config->pcntid, - true); - - pcnt_ll_set_edge_action(priv->dev, - priv->config->pcntid, - 0, - PCNT_CHANNEL_EDGE_ACTION_DECREASE, - PCNT_CHANNEL_EDGE_ACTION_INCREASE); - pcnt_ll_set_level_action(priv->dev, - priv->config->pcntid, - 0, - PCNT_CHANNEL_LEVEL_ACTION_KEEP, - PCNT_CHANNEL_LEVEL_ACTION_INVERSE); - - pcnt_ll_set_edge_action(priv->dev, - priv->config->pcntid, - 1, - PCNT_CHANNEL_EDGE_ACTION_INCREASE, - PCNT_CHANNEL_EDGE_ACTION_DECREASE); - pcnt_ll_set_level_action(priv->dev, - priv->config->pcntid, - 1, - PCNT_CHANNEL_LEVEL_ACTION_KEEP, - PCNT_CHANNEL_LEVEL_ACTION_INVERSE); - /* Clear the Reset bit to enable the Pulse Counter */ - pcnt_ll_clear_count(priv->dev, priv->config->pcntid); - pcnt_ll_start_count(priv->dev, priv->config->pcntid); + priv->pcnt->ops->ioctl(priv->pcnt, CAPIOC_CLR_CNT, 0); + priv->pcnt->ops->start(priv->pcnt); /* Mark as being used to prevent double opening of the same unit which * would reset position @@ -400,11 +260,7 @@ static int esp_shutdown(struct qe_lowerhalf_s *lower) /* Disable interrupts */ - pcnt_ll_enable_intr(priv->dev, priv->config->pcntid, false); - - /* Disable all events */ - - pcnt_ll_disable_all_events(priv->dev, priv->config->pcntid); + priv->pcnt->ops->ioctl(priv->pcnt, SNIOC_DISABLE, 0); /* Make sure initial position is 0 */ @@ -439,13 +295,20 @@ static int esp_position(struct qe_lowerhalf_s *lower, int32_t *pos) struct qe_dev_lowerhalf_s *priv = (struct qe_dev_lowerhalf_s *)lower; irqstate_t flags; int32_t position; - int16_t count; + int count = 0; + int ret = OK; flags = spin_lock_irqsave(&priv->lock); position = priv->position; - count = (int16_t)(pcnt_ll_get_count(priv->dev, - priv->config->pcntid) & 0xffff); + ret = (priv->pcnt->ops->ioctl(priv->pcnt, + CAPIOC_PULSES, + (unsigned long)&count) & 0xffff); + + if (ret < 0) + { + snerr("ERROR: Failed to get PCNT%d count value \n", priv->pcnt_num); + } /* Update the position measurement */ @@ -496,7 +359,6 @@ static int esp_reset(struct qe_lowerhalf_s *lower) struct qe_dev_lowerhalf_s *priv = (struct qe_dev_lowerhalf_s *)lower; irqstate_t flags; - uint32_t regval; sninfo("Resetting position to zero\n"); @@ -506,7 +368,7 @@ static int esp_reset(struct qe_lowerhalf_s *lower) /* Reset RST bit and clear RST bit to enable counting again */ - pcnt_ll_clear_count(priv->dev, priv->config->pcntid); + priv->pcnt->ops->ioctl(priv->pcnt, CAPIOC_CLR_CNT, 0); priv->position = 0; @@ -572,16 +434,18 @@ static int esp_ioctl(struct qe_lowerhalf_s *lower, int cmd, * called from board-specific logic. * * Input Parameters: - * devpath - The full path to the driver to register. E.g., "/dev/qe0" - * pcnt - The PCNT number to used. 'pcnt' must be an element of - * {0,1,2,3} + * devpath - The full path to the driver to register. E.g., "/dev/qe0" + * pcnt - Pointer to the pcnt driver struct + * pcnt_num - The PCNT number to used. 'pcnt' must be an element of + * {0,1,2,3} * * Returned Value: * Zero on success; A negated errno value is returned on failure. * ****************************************************************************/ -int esp_qeinitialize(const char *devpath, int pcnt) +int esp_qeinitialize(const char *devpath, struct cap_lowerhalf_s *pcnt, + int pcnt_num) { struct qe_dev_lowerhalf_s *priv; int ret; @@ -590,7 +454,7 @@ int esp_qeinitialize(const char *devpath, int pcnt) * timer */ - switch (pcnt) + switch (pcnt_num) { #ifdef CONFIG_ESP_PCNT_U0_QE case 0: @@ -618,12 +482,13 @@ int esp_qeinitialize(const char *devpath, int pcnt) if (priv == NULL) { - snerr("ERROR: PCNT%d support not configured\n", pcnt); + snerr("ERROR: PCNT%d support not configured\n", pcnt_num); return -ENXIO; } /* Register the upper-half driver */ + priv->pcnt = pcnt; ret = qe_register(devpath, (struct qe_lowerhalf_s *)priv); if (ret < 0) { @@ -631,27 +496,7 @@ int esp_qeinitialize(const char *devpath, int pcnt) return ret; } - /* Configure GPIO pins as Input with Pull-Up enabled */ - - esp_configgpio(priv->config->ch0_gpio, INPUT_FUNCTION | PULLUP); - esp_configgpio(priv->config->ch1_gpio, INPUT_FUNCTION | PULLUP); - - /* Connect Channel A (ch0_gpio) and Channel B (ch1_gpio) crossed for X4 */ - - esp_gpio_matrix_in(priv->config->ch0_gpio, - priv->config->ch0_pulse_sig, 0); - esp_gpio_matrix_in(priv->config->ch1_gpio, - priv->config->ch0_ctrl_sig, 0); - - esp_gpio_matrix_in(priv->config->ch1_gpio, - priv->config->ch1_pulse_sig, 0); - esp_gpio_matrix_in(priv->config->ch0_gpio, - priv->config->ch1_ctrl_sig, 0); - - /* Enable the PCNT Clock and Reset the peripheral */ - - periph_module_enable(PERIPH_PCNT_MODULE); - periph_module_reset(PERIPH_PCNT_MODULE); + priv->pcnt_num = pcnt_num; return OK; } diff --git a/arch/risc-v/src/common/espressif/esp_qencoder.h b/arch/risc-v/src/common/espressif/esp_qencoder.h index 16cd5cd2d9..f964a1ec22 100644 --- a/arch/risc-v/src/common/espressif/esp_qencoder.h +++ b/arch/risc-v/src/common/espressif/esp_qencoder.h @@ -64,21 +64,22 @@ * Name: esp_qeinitialize * * Description: - * Initialize a quadrature encoder interface. This function must be called - * from board-specific logic.. + * Initialize a quadrature encoder interface. This function must be + * called from board-specific logic. * * Input Parameters: - * devpath - The full path to the driver to register. E.g., "/dev/qe0" - * pcnt - The PCNT number to used. 'tim' must be an element of - * {0,1,2,3} + * devpath - The full path to the driver to register. E.g., "/dev/qe0" + * pcnt - Pointer to the pcnt driver struct + * pcnt_num - The PCNT number to used. 'pcnt' must be an element of + * {0,1,2,3} * * Returned Value: * Zero on success; A negated errno value is returned on failure. * ****************************************************************************/ -int esp_qeinitialize(const char *devpath, int pcnt); +int esp_qeinitialize(const char *devpath, struct cap_lowerhalf_s *pcnt, + int pcnt_num); #endif /* CONFIG_SENSORS_QENCODER */ #endif /* __ARCH_RISC_V_SRC_COMMON_ESPRESSIF_ESP_QENCODER_H */ - diff --git a/arch/risc-v/src/esp32c6/hal_esp32c6.mk b/arch/risc-v/src/esp32c6/hal_esp32c6.mk index 373ec4c60f..a0e6c8476c 100644 --- a/arch/risc-v/src/esp32c6/hal_esp32c6.mk +++ b/arch/risc-v/src/esp32c6/hal_esp32c6.mk @@ -131,6 +131,7 @@ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)ledc_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)ledc_hal_iram.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)lp_timer_hal.c +CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)pcnt_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)rmt_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)i2c_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)spi_hal.c @@ -156,6 +157,7 @@ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)risc CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)lldesc.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)gpio_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)ledc_periph.c +CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)pcnt_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)rmt_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)i2c_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)mcpwm_periph.c diff --git a/arch/risc-v/src/esp32h2/hal_esp32h2.mk b/arch/risc-v/src/esp32h2/hal_esp32h2.mk index 5d3d19ef26..852d08dd90 100644 --- a/arch/risc-v/src/esp32h2/hal_esp32h2.mk +++ b/arch/risc-v/src/esp32h2/hal_esp32h2.mk @@ -117,6 +117,7 @@ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)ledc_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)ledc_hal_iram.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)lp_timer_hal.c +CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)pcnt_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)rmt_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)i2c_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)spi_hal.c @@ -139,6 +140,7 @@ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)risc CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)lldesc.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)gpio_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)ledc_periph.c +CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)pcnt_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)rmt_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)i2c_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)mcpwm_periph.c diff --git a/boards/risc-v/esp32c6/common/src/esp_board_qencoder.c b/boards/risc-v/esp32c6/common/include/esp_board_pcnt.h similarity index 50% rename from boards/risc-v/esp32c6/common/src/esp_board_qencoder.c rename to boards/risc-v/esp32c6/common/include/esp_board_pcnt.h index 6f9e0afaad..b917b75095 100644 --- a/boards/risc-v/esp32c6/common/src/esp_board_qencoder.c +++ b/boards/risc-v/esp32c6/common/include/esp_board_pcnt.h @@ -1,7 +1,5 @@ /**************************************************************************** - * boards/risc-v/esp32c6/common/src/esp_board_qencoder.c - * - * SPDX-License-Identifier: Apache-2.0 + * boards/risc-v/esp32c6/common/include/esp_board_pcnt.h * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -20,80 +18,62 @@ * ****************************************************************************/ +#ifndef __BOARDS_RISC_V_ESP32C6_COMMON_INCLUDE_ESP_BOARD_PCNT_H +#define __BOARDS_RISC_V_ESP32C6_COMMON_INCLUDE_ESP_BOARD_PCNT_H + /**************************************************************************** * Included Files ****************************************************************************/ #include -#include -#include -#include - -#include -#include - -#include "espressif/esp_qencoder.h" - /**************************************************************************** - * Public Functions + * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** - * Name: board_qencoder_initialize + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: board_pcnt_initialize * * Description: - * Initialize the quadrature encoder driver + * Initialize the pulse counter/quadrature encoder driver + * + * Input Parameters: + * None + * + * Returned Value: + * OK on success; errno on failure. * ****************************************************************************/ -int board_qencoder_initialize(void) -{ - int ret; - char devpath[12]; - int devno = 0; +int board_pcnt_initialize(void); - /* Initialize a quadrature encoder interface. */ -#ifdef CONFIG_ESP_PCNT_U0_QE - snprintf(devpath, sizeof(devpath), "/dev/qe%d", devno++); - ret = esp_qeinitialize(devpath, 0); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: esp_qeinitialize failed: %d\n", ret); - return ret; - } -#endif - -#ifdef CONFIG_ESP_PCNT_U1_QE - snprintf(devpath, sizeof(devpath), "/dev/qe%d", devno++); - ret = esp_qeinitialize(devpath, 1); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: esp_qeinitialize failed: %d\n", ret); - return ret; - } -#endif - -#ifdef CONFIG_ESP_PCNT_U2_QE - snprintf(devpath, sizeof(devpath), "/dev/qe%d", devno++); - ret = esp_qeinitialize(devpath, 2); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: esp_qeinitialize failed: %d\n", ret); - return ret; - } -#endif - -#ifdef CONFIG_ESP_PCNT_U3_QE - snprintf(devpath, sizeof(devpath), "/dev/qe%d", devno++); - ret = esp_qeinitialize(devpath, 3); - if (ret < 0) - { - syslog(LOG_ERR, "ERROR: esp_qeinitialize failed: %d\n", ret); - return ret; - } -#endif - - return ret; +#undef EXTERN +#ifdef __cplusplus } +#endif +#endif /* __BOARDS_RISC_V_ESP32C6_COMMON_INCLUDE_ESP_BOARD_PCNT_H */ diff --git a/boards/risc-v/esp32c6/common/src/Make.defs b/boards/risc-v/esp32c6/common/src/Make.defs index 7b1378b890..a74f146ff2 100644 --- a/boards/risc-v/esp32c6/common/src/Make.defs +++ b/boards/risc-v/esp32c6/common/src/Make.defs @@ -71,7 +71,7 @@ ifeq ($(CONFIG_CL_MFRC522),y) endif ifeq ($(CONFIG_ESP_PCNT),y) - CSRCS += esp_board_qencoder.c + CSRCS += esp_board_pcnt.c endif DEPPATH += --dep-path src diff --git a/boards/risc-v/esp32c6/common/src/esp_board_pcnt.c b/boards/risc-v/esp32c6/common/src/esp_board_pcnt.c new file mode 100644 index 0000000000..12beaec211 --- /dev/null +++ b/boards/risc-v/esp32c6/common/src/esp_board_pcnt.c @@ -0,0 +1,333 @@ +/**************************************************************************** + * boards/risc-v/esp32c6/common/src/esp_board_pcnt.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include +#include "espressif/esp_pcnt.h" +#include "espressif/esp_gpio.h" +#ifdef CONFIG_ESP_PCNT_AS_QE +#include "espressif/esp_qencoder.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PCNT_HIGH_LIMIT 1000 +#define PCNT_LOW_LIMIT -1000 + +#define PCNT_GLITCH_FILTER(pcnt, thres) pcnt->ops->ioctl(pcnt, \ + CAPIOC_FILTER, \ + thres) \ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_pcnt_init + * + * Description: + * Initialize and register the pulse counter driver + * + * Input Parameters: + * devpath - The full path to the driver to register. + * unit_cfg - PCNT unit configuration + * chan0_cfg - PCNT unit channel 0 configuration + * chan1_cfg - PCNT unit channel 1 configuration + * glitch_threshold - Threshold value for glitch filter in ns + * + * Returned Value: + * Valid PCNT device structure reference on success; NULL, otherwise. + * + ****************************************************************************/ + +static struct cap_lowerhalf_s *board_pcnt_init( + const char *devpath, + struct esp_pcnt_unit_config_s *unit_cfg, + struct esp_pcnt_chan_config_s *chan0_cfg, + struct esp_pcnt_chan_config_s *chan1_cfg, + uint32_t glitch_threshold) +{ + struct cap_lowerhalf_s *pcnt; + int chan0; + int chan1; + int ret; + + pcnt = esp_pcnt_new_unit(unit_cfg); + if (!pcnt) + { + syslog(LOG_ERR, "Failed to create unit!\n"); + return NULL; + } + + chan0 = esp_pcnt_new_channel(pcnt, chan0_cfg); + if (chan0 == ERROR) + { + syslog(LOG_ERR, "Failed to create channel!\n"); + esp_pcnt_del_unit(pcnt); + return NULL; + } + +#ifdef CONFIG_ESP_PCNT_TEST_MODE + esp_pcnt_channel_set_edge_action(chan0, ESP_PCNT_CHAN_EDGE_ACTION_HOLD, + ESP_PCNT_CHAN_EDGE_ACTION_INCREASE); + esp_pcnt_channel_set_level_action(chan0, ESP_PCNT_CHAN_LEVEL_ACTION_KEEP, + ESP_PCNT_CHAN_LEVEL_ACTION_KEEP); +#else + esp_pcnt_channel_set_edge_action(chan0, ESP_PCNT_CHAN_EDGE_ACTION_DECREASE, + ESP_PCNT_CHAN_EDGE_ACTION_INCREASE); + esp_pcnt_channel_set_level_action(chan0, ESP_PCNT_CHAN_LEVEL_ACTION_KEEP, + ESP_PCNT_CHAN_LEVEL_ACTION_INVERSE); +#endif + + if (chan1_cfg) + { + chan1 = esp_pcnt_new_channel(pcnt, chan1_cfg); + if (chan1 == ERROR) + { + syslog(LOG_ERR, "Failed to create channel!\n"); + esp_pcnt_del_channel(chan0); + esp_pcnt_del_unit(pcnt); + return NULL; + } + + esp_pcnt_channel_set_edge_action(chan1, + ESP_PCNT_CHAN_EDGE_ACTION_INCREASE, + ESP_PCNT_CHAN_EDGE_ACTION_DECREASE); + esp_pcnt_channel_set_level_action(chan1, + ESP_PCNT_CHAN_LEVEL_ACTION_KEEP, + ESP_PCNT_CHAN_LEVEL_ACTION_INVERSE); + } + + PCNT_GLITCH_FILTER(pcnt, glitch_threshold); + + ret = cap_register(devpath, pcnt); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Error registering PCNT!\n"); + esp_pcnt_del_channel(chan0); + if (chan1_cfg) + { + esp_pcnt_del_channel(chan1); + } + + esp_pcnt_del_unit(pcnt); + return NULL; + } + + return pcnt; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_pcnt_initialize + * + * Description: + * Initialize the pulse counter/quadrature encoder driver + * + * Input Parameters: + * None + * + * Returned Value: + * OK on success; errno on failure. + * + ****************************************************************************/ + +int board_pcnt_initialize(void) +{ + struct cap_lowerhalf_s *pcnt; + int ret = OK; + int glitch_threshold = 0; + +#ifdef CONFIG_ESP_PCNT_AS_QE + char devpath[12]; + int devno = 0; +#endif + + struct esp_pcnt_unit_config_s unit_cfg = + { + .high_limit = PCNT_HIGH_LIMIT, + .low_limit = PCNT_LOW_LIMIT, + .accum_count = false, + }; + + struct esp_pcnt_chan_config_s chan0_cfg = + { + 0 + }; + + struct esp_pcnt_chan_config_s chan1_cfg = + { + 0 + }; + +#ifdef CONFIG_ESP_PCNT_U0 + chan0_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U0_CH0_EDGE_PIN; + chan0_cfg.level_gpio_num = CONFIG_ESP_PCNT_U0_CH0_LEVEL_PIN; +#ifdef CONFIG_ESP_PCNT_TEST_MODE + chan0_cfg.flags = ESP_PCNT_CHAN_IO_LOOPBACK; +#endif + + chan1_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U0_CH1_LEVEL_PIN; + chan1_cfg.level_gpio_num = CONFIG_ESP_PCNT_U0_CH1_EDGE_PIN; +#ifndef CONFIG_ESP_PCNT_U0_FILTER_EN + glitch_threshold = 0; +#else + glitch_threshold = CONFIG_ESP_PCNT_U0_FILTER_THRES; +#endif + + pcnt = board_pcnt_init("/dev/pcnt0", &unit_cfg, &chan0_cfg, + &chan1_cfg, glitch_threshold); + if (!pcnt) + { + syslog(LOG_ERR, "ERROR: pcnt initialize failed: %d\n", ret); + return ERROR; + } + +#ifdef CONFIG_ESP_PCNT_U0_QE + snprintf(devpath, sizeof(devpath), "/dev/qe%d", devno++); + ret = esp_qeinitialize(devpath, pcnt, 0); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: esp_qeinitialize failed: %d\n", ret); + return ret; + } + + pcnt = NULL; +#endif +#endif + +#ifdef CONFIG_ESP_PCNT_U1 + chan0_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U1_CH0_EDGE_PIN; + chan0_cfg.level_gpio_num = CONFIG_ESP_PCNT_U1_CH0_LEVEL_PIN; + + chan1_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U1_CH1_EDGE_PIN; + chan1_cfg.level_gpio_num = CONFIG_ESP_PCNT_U1_CH1_LEVEL_PIN; +#ifndef CONFIG_ESP_PCNT_U1_FILTER_EN + glitch_threshold = 0; +#else + glitch_threshold = CONFIG_ESP_PCNT_U1_FILTER_THRES; +#endif + + pcnt = board_pcnt_init("/dev/pcnt1", &unit_cfg, &chan0_cfg, + &chan1_cfg, glitch_threshold); + if (!pcnt) + { + syslog(LOG_ERR, "ERROR: pcnt initialize failed: %d\n", ret); + return ERROR; + } + +#ifdef CONFIG_ESP_PCNT_U1_QE + snprintf(devpath, sizeof(devpath), "/dev/qe%d", devno++); + ret = esp_qeinitialize(devpath, pcnt, 1); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: esp_qeinitialize failed: %d\n", ret); + return ret; + } + + pcnt = NULL; +#endif +#endif + +#ifdef CONFIG_ESP_PCNT_U2 + chan0_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U2_CH0_EDGE_PIN; + chan0_cfg.level_gpio_num = CONFIG_ESP_PCNT_U2_CH0_LEVEL_PIN; + + chan1_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U2_CH1_EDGE_PIN; + chan1_cfg.level_gpio_num = CONFIG_ESP_PCNT_U2_CH1_LEVEL_PIN; +#ifndef CONFIG_ESP_PCNT_U2_FILTER_EN + glitch_threshold = 0; +#else + glitch_threshold = CONFIG_ESP_PCNT_U2_FILTER_THRES; +#endif + + pcnt = board_pcnt_init("/dev/pcnt2", &unit_cfg, &chan0_cfg, + &chan1_cfg, glitch_threshold); + + if (!pcnt) + { + syslog(LOG_ERR, "ERROR: pcnt initialize failed: %d\n", ret); + return ERROR; + } + +#ifdef CONFIG_ESP_PCNT_U2_QE + snprintf(devpath, sizeof(devpath), "/dev/qe%d", devno++); + ret = esp_qeinitialize(devpath, pcnt, 2); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: esp_qeinitialize failed: %d\n", ret); + return ret; + } + + pcnt = NULL; +#endif +#endif + +#ifdef CONFIG_ESP_PCNT_U3 + chan0_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U3_CH0_EDGE_PIN; + chan0_cfg.level_gpio_num = CONFIG_ESP_PCNT_U3_CH0_LEVEL_PIN; + + chan1_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U3_CH1_EDGE_PIN; + chan1_cfg.level_gpio_num = CONFIG_ESP_PCNT_U3_CH1_LEVEL_PIN; +#ifndef CONFIG_ESP_PCNT_U3_FILTER_EN + glitch_threshold = 0; +#else + glitch_threshold = CONFIG_ESP_PCNT_U3_FILTER_THRES; +#endif + + pcnt = board_pcnt_init("/dev/pcnt3", &unit_cfg, &chan0_cfg, + &chan1_cfg, glitch_threshold); + if (!pcnt) + { + syslog(LOG_ERR, "ERROR: pcnt initialize failed: %d\n", ret); + return ERROR; + } + +#ifdef CONFIG_ESP_PCNT_U3_QE + snprintf(devpath, sizeof(devpath), "/dev/qe%d", devno++); + ret = esp_qeinitialize(devpath, pcnt, 3); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: esp_qeinitialize failed: %d\n", ret); + return ret; + } + + pcnt = NULL; +#endif +#endif + + return ret; +} diff --git a/boards/risc-v/esp32c6/esp32c6-devkitc/configs/qencoder/defconfig b/boards/risc-v/esp32c6/esp32c6-devkitc/configs/qencoder/defconfig index e93c8625c4..77e98c6684 100644 --- a/boards/risc-v/esp32c6/esp32c6-devkitc/configs/qencoder/defconfig +++ b/boards/risc-v/esp32c6/esp32c6-devkitc/configs/qencoder/defconfig @@ -27,6 +27,7 @@ CONFIG_ESP_PCNT_U0=y CONFIG_ESP_PCNT_U0_CH0_EDGE_PIN=10 CONFIG_ESP_PCNT_U0_CH1_LEVEL_PIN=11 CONFIG_ESP_PCNT_U0_FILTER_EN=y +CONFIG_ESP_PCNT_U0_QE=y CONFIG_EXAMPLES_QENCODER=y CONFIG_EXAMPLES_QENCODER_DELAY=1000 CONFIG_EXAMPLES_QENCODER_NSAMPLES=20 diff --git a/boards/risc-v/esp32c6/esp32c6-devkitc/src/esp32c6_bringup.c b/boards/risc-v/esp32c6/esp32c6-devkitc/src/esp32c6_bringup.c index dd70866ed8..c9ba0f6310 100644 --- a/boards/risc-v/esp32c6/esp32c6-devkitc/src/esp32c6_bringup.c +++ b/boards/risc-v/esp32c6/esp32c6-devkitc/src/esp32c6_bringup.c @@ -102,8 +102,8 @@ # include "esp_board_mcpwm.h" #endif -#ifdef CONFIG_ESP_PCNT_AS_QE -# include "esp_board_qencoder.h" +#ifdef CONFIG_ESP_PCNT +# include "esp_board_pcnt.h" #endif #ifdef CONFIG_SYSTEM_NXDIAG_ESPRESSIF_CHIP_WO_TOOL @@ -391,13 +391,13 @@ int esp_bringup(void) } #endif -#ifdef CONFIG_SENSORS_QENCODER - /* Initialize and register the qencoder driver */ +#ifdef CONFIG_ESP_PCNT + /* Initialize and register the pcnt/qencoder driver */ - ret = board_qencoder_initialize(); + ret = board_pcnt_initialize(); if (ret < 0) { - syslog(LOG_ERR, "ERROR: board_qencoder_initialize failed: %d\n", ret); + syslog(LOG_ERR, "ERROR: board_pcnt_initialize failed: %d\n", ret); } #endif diff --git a/boards/risc-v/esp32c6/common/include/esp_board_qencoder.h b/boards/risc-v/esp32h2/common/include/esp_board_pcnt.h similarity index 83% rename from boards/risc-v/esp32c6/common/include/esp_board_qencoder.h rename to boards/risc-v/esp32h2/common/include/esp_board_pcnt.h index 7c5ac9329c..25326328cf 100644 --- a/boards/risc-v/esp32c6/common/include/esp_board_qencoder.h +++ b/boards/risc-v/esp32h2/common/include/esp_board_pcnt.h @@ -1,5 +1,5 @@ /**************************************************************************** - * boards/risc-v/esp32c6/common/include/esp_board_qencoder.h + * boards/risc-v/esp32h2/common/include/esp_board_pcnt.h * * SPDX-License-Identifier: Apache-2.0 * @@ -20,8 +20,8 @@ * ****************************************************************************/ -#ifndef __BOARDS_RISC_V_ESP32C6_COMMON_INCLUDE_ESP_BOARD_QENCODER_H -#define __BOARDS_RISC_V_ESP32C6_COMMON_INCLUDE_ESP_BOARD_QENCODER_H +#ifndef __BOARDS_RISC_V_ESP32H2_COMMON_INCLUDE_ESP_BOARD_PCNT_H +#define __BOARDS_RISC_V_ESP32H2_COMMON_INCLUDE_ESP_BOARD_PCNT_H /**************************************************************************** * Included Files @@ -58,19 +58,25 @@ extern "C" ****************************************************************************/ /**************************************************************************** - * Name: board_qencoder_initialize + * Name: board_pcnt_initialize * * Description: - * Initialize the quadrature encoder driver for the given timer + * Initialize the pulse counter/quadrature encoder driver + * + * Input Parameters: + * None + * + * Returned Value: + * OK on success; errno on failure. * ****************************************************************************/ -int board_qencoder_initialize(void); +int board_pcnt_initialize(void); #undef EXTERN #ifdef __cplusplus } #endif -#endif /* __BOARDS_RISC_V_ESP32C6_COMMON_INCLUDE_ESP_BOARD_QENCODER_H */ +#endif /* __BOARDS_RISC_V_ESP32H2_COMMON_INCLUDE_ESP_BOARD_PCNT_H */ diff --git a/boards/risc-v/esp32h2/common/src/Make.defs b/boards/risc-v/esp32h2/common/src/Make.defs index 1756e73b8a..380d83ab26 100644 --- a/boards/risc-v/esp32h2/common/src/Make.defs +++ b/boards/risc-v/esp32h2/common/src/Make.defs @@ -62,6 +62,10 @@ ifeq ($(CONFIG_ESP_MCPWM),y) CSRCS += esp_board_mcpwm.c endif +ifeq ($(CONFIG_ESP_PCNT),y) + CSRCS += esp_board_pcnt.c +endif + DEPPATH += --dep-path src VPATH += :src CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)board$(DELIM)src diff --git a/boards/risc-v/esp32h2/common/src/esp_board_pcnt.c b/boards/risc-v/esp32h2/common/src/esp_board_pcnt.c new file mode 100644 index 0000000000..1df1dcddfe --- /dev/null +++ b/boards/risc-v/esp32h2/common/src/esp_board_pcnt.c @@ -0,0 +1,333 @@ +/**************************************************************************** + * boards/risc-v/esp32h2/common/src/esp_board_pcnt.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include +#include "espressif/esp_pcnt.h" +#include "espressif/esp_gpio.h" +#ifdef CONFIG_ESP_PCNT_AS_QE +#include "espressif/esp_qencoder.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PCNT_HIGH_LIMIT 1000 +#define PCNT_LOW_LIMIT -1000 + +#define PCNT_GLITCH_FILTER(pcnt, thres) pcnt->ops->ioctl(pcnt, \ + CAPIOC_FILTER, \ + thres) \ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_pcnt_init + * + * Description: + * Initialize and register the pulse counter driver + * + * Input Parameters: + * devpath - The full path to the driver to register. + * unit_cfg - PCNT unit configuration + * chan0_cfg - PCNT unit channel 0 configuration + * chan1_cfg - PCNT unit channel 1 configuration + * glitch_threshold - Threshold value for glitch filter in ns + * + * Returned Value: + * Valid PCNT device structure reference on success; NULL, otherwise. + * + ****************************************************************************/ + +static struct cap_lowerhalf_s *board_pcnt_init( + const char *devpath, + struct esp_pcnt_unit_config_s *unit_cfg, + struct esp_pcnt_chan_config_s *chan0_cfg, + struct esp_pcnt_chan_config_s *chan1_cfg, + uint32_t glitch_threshold) +{ + struct cap_lowerhalf_s *pcnt; + int chan0; + int chan1; + int ret; + + pcnt = esp_pcnt_new_unit(unit_cfg); + if (!pcnt) + { + syslog(LOG_ERR, "Failed to create unit!\n"); + return NULL; + } + + chan0 = esp_pcnt_new_channel(pcnt, chan0_cfg); + if (chan0 == ERROR) + { + syslog(LOG_ERR, "Failed to create channel!\n"); + esp_pcnt_del_unit(pcnt); + return NULL; + } + +#ifdef CONFIG_ESP_PCNT_TEST_MODE + esp_pcnt_channel_set_edge_action(chan0, ESP_PCNT_CHAN_EDGE_ACTION_HOLD, + ESP_PCNT_CHAN_EDGE_ACTION_INCREASE); + esp_pcnt_channel_set_level_action(chan0, ESP_PCNT_CHAN_LEVEL_ACTION_KEEP, + ESP_PCNT_CHAN_LEVEL_ACTION_KEEP); +#else + esp_pcnt_channel_set_edge_action(chan0, ESP_PCNT_CHAN_EDGE_ACTION_DECREASE, + ESP_PCNT_CHAN_EDGE_ACTION_INCREASE); + esp_pcnt_channel_set_level_action(chan0, ESP_PCNT_CHAN_LEVEL_ACTION_KEEP, + ESP_PCNT_CHAN_LEVEL_ACTION_INVERSE); +#endif + + if (chan1_cfg) + { + chan1 = esp_pcnt_new_channel(pcnt, chan1_cfg); + if (chan1 == ERROR) + { + syslog(LOG_ERR, "Failed to create channel!\n"); + esp_pcnt_del_channel(chan0); + esp_pcnt_del_unit(pcnt); + return NULL; + } + + esp_pcnt_channel_set_edge_action(chan1, + ESP_PCNT_CHAN_EDGE_ACTION_INCREASE, + ESP_PCNT_CHAN_EDGE_ACTION_DECREASE); + esp_pcnt_channel_set_level_action(chan1, + ESP_PCNT_CHAN_LEVEL_ACTION_KEEP, + ESP_PCNT_CHAN_LEVEL_ACTION_INVERSE); + } + + PCNT_GLITCH_FILTER(pcnt, glitch_threshold); + + ret = cap_register(devpath, pcnt); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Error registering PCNT!\n"); + esp_pcnt_del_channel(chan0); + if (chan1_cfg) + { + esp_pcnt_del_channel(chan1); + } + + esp_pcnt_del_unit(pcnt); + return NULL; + } + + return pcnt; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_pcnt_initialize + * + * Description: + * Initialize the pulse counter/quadrature encoder driver + * + * Input Parameters: + * None + * + * Returned Value: + * OK on success; errno on failure. + * + ****************************************************************************/ + +int board_pcnt_initialize(void) +{ + struct cap_lowerhalf_s *pcnt; + int ret = OK; + int glitch_threshold = 0; + +#ifdef CONFIG_ESP_PCNT_AS_QE + char devpath[12]; + int devno = 0; +#endif + + struct esp_pcnt_unit_config_s unit_cfg = + { + .high_limit = PCNT_HIGH_LIMIT, + .low_limit = PCNT_LOW_LIMIT, + .accum_count = false, + }; + + struct esp_pcnt_chan_config_s chan0_cfg = + { + 0 + }; + + struct esp_pcnt_chan_config_s chan1_cfg = + { + 0 + }; + +#ifdef CONFIG_ESP_PCNT_U0 + chan0_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U0_CH0_EDGE_PIN; + chan0_cfg.level_gpio_num = CONFIG_ESP_PCNT_U0_CH0_LEVEL_PIN; +#ifdef CONFIG_ESP_PCNT_TEST_MODE + chan0_cfg.flags = ESP_PCNT_CHAN_IO_LOOPBACK; +#endif + + chan1_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U0_CH1_LEVEL_PIN; + chan1_cfg.level_gpio_num = CONFIG_ESP_PCNT_U0_CH1_EDGE_PIN; +#ifndef CONFIG_ESP_PCNT_U0_FILTER_EN + glitch_threshold = 0; +#else + glitch_threshold = CONFIG_ESP_PCNT_U0_FILTER_THRES; +#endif + + pcnt = board_pcnt_init("/dev/pcnt0", &unit_cfg, &chan0_cfg, + &chan1_cfg, glitch_threshold); + if (!pcnt) + { + syslog(LOG_ERR, "ERROR: pcnt initialize failed: %d\n", ret); + return ERROR; + } + +#ifdef CONFIG_ESP_PCNT_U0_QE + snprintf(devpath, sizeof(devpath), "/dev/qe%d", devno++); + ret = esp_qeinitialize(devpath, pcnt, 0); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: esp_qeinitialize failed: %d\n", ret); + return ret; + } + + pcnt = NULL; +#endif +#endif + +#ifdef CONFIG_ESP_PCNT_U1 + chan0_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U1_CH0_EDGE_PIN; + chan0_cfg.level_gpio_num = CONFIG_ESP_PCNT_U1_CH0_LEVEL_PIN; + + chan1_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U1_CH1_EDGE_PIN; + chan1_cfg.level_gpio_num = CONFIG_ESP_PCNT_U1_CH1_LEVEL_PIN; +#ifndef CONFIG_ESP_PCNT_U1_FILTER_EN + glitch_threshold = 0; +#else + glitch_threshold = CONFIG_ESP_PCNT_U1_FILTER_THRES; +#endif + + pcnt = board_pcnt_init("/dev/pcnt1", &unit_cfg, &chan0_cfg, + &chan1_cfg, glitch_threshold); + if (!pcnt) + { + syslog(LOG_ERR, "ERROR: pcnt initialize failed: %d\n", ret); + return ERROR; + } + +#ifdef CONFIG_ESP_PCNT_U1_QE + snprintf(devpath, sizeof(devpath), "/dev/qe%d", devno++); + ret = esp_qeinitialize(devpath, pcnt, 1); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: esp_qeinitialize failed: %d\n", ret); + return ret; + } + + pcnt = NULL; +#endif +#endif + +#ifdef CONFIG_ESP_PCNT_U2 + chan0_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U2_CH0_EDGE_PIN; + chan0_cfg.level_gpio_num = CONFIG_ESP_PCNT_U2_CH0_LEVEL_PIN; + + chan1_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U2_CH1_EDGE_PIN; + chan1_cfg.level_gpio_num = CONFIG_ESP_PCNT_U2_CH1_LEVEL_PIN; +#ifndef CONFIG_ESP_PCNT_U2_FILTER_EN + glitch_threshold = 0; +#else + glitch_threshold = CONFIG_ESP_PCNT_U2_FILTER_THRES; +#endif + + pcnt = board_pcnt_init("/dev/pcnt2", &unit_cfg, &chan0_cfg, + &chan1_cfg, glitch_threshold); + + if (!pcnt) + { + syslog(LOG_ERR, "ERROR: pcnt initialize failed: %d\n", ret); + return ERROR; + } + +#ifdef CONFIG_ESP_PCNT_U2_QE + snprintf(devpath, sizeof(devpath), "/dev/qe%d", devno++); + ret = esp_qeinitialize(devpath, pcnt, 2); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: esp_qeinitialize failed: %d\n", ret); + return ret; + } + + pcnt = NULL; +#endif +#endif + +#ifdef CONFIG_ESP_PCNT_U3 + chan0_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U3_CH0_EDGE_PIN; + chan0_cfg.level_gpio_num = CONFIG_ESP_PCNT_U3_CH0_LEVEL_PIN; + + chan1_cfg.edge_gpio_num = CONFIG_ESP_PCNT_U3_CH1_EDGE_PIN; + chan1_cfg.level_gpio_num = CONFIG_ESP_PCNT_U3_CH1_LEVEL_PIN; +#ifndef CONFIG_ESP_PCNT_U3_FILTER_EN + glitch_threshold = 0; +#else + glitch_threshold = CONFIG_ESP_PCNT_U3_FILTER_THRES; +#endif + + pcnt = board_pcnt_init("/dev/pcnt3", &unit_cfg, &chan0_cfg, + &chan1_cfg, glitch_threshold); + if (!pcnt) + { + syslog(LOG_ERR, "ERROR: pcnt initialize failed: %d\n", ret); + return ERROR; + } + +#ifdef CONFIG_ESP_PCNT_U3_QE + snprintf(devpath, sizeof(devpath), "/dev/qe%d", devno++); + ret = esp_qeinitialize(devpath, pcnt, 3); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: esp_qeinitialize failed: %d\n", ret); + return ret; + } + + pcnt = NULL; +#endif +#endif + + return ret; +} diff --git a/boards/risc-v/esp32h2/esp32h2-devkit/src/esp32h2_bringup.c b/boards/risc-v/esp32h2/esp32h2-devkit/src/esp32h2_bringup.c index 41b3b93cb4..cdbcf5102c 100644 --- a/boards/risc-v/esp32h2/esp32h2-devkit/src/esp32h2_bringup.c +++ b/boards/risc-v/esp32h2/esp32h2-devkit/src/esp32h2_bringup.c @@ -94,6 +94,11 @@ # include "esp_board_mcpwm.h" #endif +#ifdef CONFIG_ESP_PCNT +# include "espressif/esp_pcnt.h" +# include "esp_board_pcnt.h" +#endif + #ifdef CONFIG_SYSTEM_NXDIAG_ESPRESSIF_CHIP_WO_TOOL # include "espressif/esp_nxdiag.h" #endif @@ -354,6 +359,14 @@ int esp_bringup(void) } #endif +#ifdef CONFIG_ESP_PCNT + ret = board_pcnt_initialize(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: board_pcnt_initialize failed: %d\n", ret); + } +#endif + #ifdef CONFIG_SYSTEM_NXDIAG_ESPRESSIF_CHIP_WO_TOOL ret = esp_nxdiag_initialize(); if (ret < 0)