pulse_cnt: new driver for PCNT peripheral

This commit is contained in:
morris 2022-01-17 14:42:09 +08:00
parent 6e405cc209
commit ec8defaa96
36 changed files with 1829 additions and 375 deletions

View File

@ -48,7 +48,7 @@ if(CONFIG_SOC_RMT_SUPPORTED)
endif()
if(CONFIG_SOC_PCNT_SUPPORTED)
list(APPEND srcs "pcnt.c")
list(APPEND srcs "pcnt_legacy.c" "pulse_cnt.c")
endif()
if(CONFIG_SOC_SDMMC_HOST_SUPPORTED)

View File

@ -22,7 +22,7 @@ menu "Driver configurations"
endmenu # ADC Configuration
menu "MCPWM configuration"
depends on SOC_MCPWM_SUPPORTED
config MCPWM_ISR_IN_IRAM
bool "Place MCPWM ISR function into IRAM"
default n
@ -32,7 +32,6 @@ menu "Driver configurations"
Note that if this option is selected, all user registered ISR callbacks should never
try to use cache as well. (with IRAM_ATTR)
endmenu # MCPWM Configuration
menu "SPI configuration"
@ -158,8 +157,6 @@ menu "Driver configurations"
endmenu # UART Configuration
menu "GPIO Configuration"
visible if IDF_TARGET_ESP32
config GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL
bool "Support light sleep GPIO pullup/pulldown configuration for ESP32"
depends on IDF_TARGET_ESP32
@ -168,10 +165,10 @@ menu "Driver configurations"
pullup/pulldown mode in sleep.
If this option is selected, chip will automatically emulate the behaviour of switching,
and about 450B of source codes would be placed into IRAM.
endmenu # GPIO Configuration
menu "GDMA Configuration"
depends on SOC_GDMA_SUPPORTED
config GDMA_CTRL_FUNC_IN_IRAM
bool "Place GDMA control functions into IRAM"
default n
@ -202,17 +199,56 @@ menu "Driver configurations"
bool "GPTimer ISR IRAM-Safe"
default n
help
This will ensure the GPTimer interrupt handle is IRAM-Safe, allow to avoid flash
cache misses, and also be able to run whilst the cache is disabled.
(e.g. SPI Flash write)
Ensure the GPTimer interrupt is IRAM-Safe by allowing the interrupt handler to be
executable when the cache is disabled (e.g. SPI Flash write).
config GPTIMER_SUPPRESS_DEPRECATE_WARN
bool "Suppress leagcy driver deprecated warning"
bool "Suppress legacy driver deprecated warning"
default n
help
Wether to suppress the deprecation warnings when using legacy timer group driver (driver/timer.h).
If you want to continue using the legacy driver, and don't want to see related deprecation warnings,
you can enable this option.
config GPTIMER_ENABLE_DEBUG_LOG
bool "Enable debug log"
default n
help
Wether to enable the debug log message for GPTimer driver.
Note that, this option only controls the GPTimer driver log, won't affect other drivers.
endmenu # GPTimer Configuration
menu "PCNT Configuration"
depends on SOC_PCNT_SUPPORTED
config PCNT_CTRL_FUNC_IN_IRAM
bool "Place PCNT control functions into IRAM"
default n
help
Place PCNT control functions (like start/stop) into IRAM,
so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context.
Enabling this option can improve driver performance as well.
config PCNT_ISR_IRAM_SAFE
bool "PCNT ISR IRAM-Safe"
default n
help
Ensure the PCNT interrupt is IRAM-Safe by allowing the interrupt handler to be
executable when the cache is disabled (e.g. SPI Flash write).
config PCNT_SUPPRESS_DEPRECATE_WARN
bool "Suppress legacy driver deprecated warning"
default n
help
Wether to suppress the deprecation warnings when using legacy PCNT driver (driver/pcnt.h).
If you want to continue using the legacy driver, and don't want to see related deprecation warnings,
you can enable this option.
config PCNT_ENABLE_DEBUG_LOG
bool "Enable debug log"
default n
help
Wether to enable the debug log message for PCNT driver.
Note that, this option only controls the PCNT driver log, won't affect other drivers.
endmenu # PCNT Configuration
endmenu # Driver configurations

View File

@ -4,10 +4,14 @@
* SPDX-License-Identifier: Apache-2.0
*/
// #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG // uncomment this line to enable debug logs
#include <stdlib.h>
#include <sys/lock.h>
#include "sdkconfig.h"
#if CONFIG_GPTIMER_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "freertos/FreeRTOS.h"
#include "esp_attr.h"
#include "esp_err.h"
@ -90,7 +94,7 @@ static gptimer_platform_t s_platform;
static gptimer_group_t *gptimer_acquire_group_handle(int group_id);
static void gptimer_release_group_handle(gptimer_group_t *group);
static esp_err_t gptimer_select_periph_clock(gptimer_t *timer, gptimer_clock_source_t src_clk, uint32_t resolution_hz);
IRAM_ATTR static void gptimer_default_isr(void *args);
static void gptimer_default_isr(void *args);
esp_err_t gptimer_new_timer(const gptimer_config_t *config, gptimer_handle_t *ret_timer)
{
@ -205,7 +209,7 @@ esp_err_t gptimer_del_timer(gptimer_handle_t timer)
esp_err_t gptimer_set_raw_count(gptimer_handle_t timer, unsigned long long value)
{
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE_ISR(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
portENTER_CRITICAL_SAFE(&timer->spinlock);
timer_hal_set_counter_value(&timer->hal, value);
@ -215,7 +219,7 @@ esp_err_t gptimer_set_raw_count(gptimer_handle_t timer, unsigned long long value
esp_err_t gptimer_get_raw_count(gptimer_handle_t timer, unsigned long long *value)
{
ESP_RETURN_ON_FALSE(timer && value, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE_ISR(timer && value, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
portENTER_CRITICAL_SAFE(&timer->spinlock);
*value = timer_ll_get_counter_value(timer->hal.dev, timer->timer_id);
@ -252,9 +256,9 @@ esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer
}
// enable/disable GPTimer interrupt events
portENTER_CRITICAL_SAFE(&group->spinlock);
portENTER_CRITICAL(&group->spinlock);
timer_ll_enable_intr(timer->hal.dev, TIMER_LL_EVENT_ALARM(timer->timer_id), cbs->on_alarm != NULL); // enable timer interrupt
portEXIT_CRITICAL_SAFE(&group->spinlock);
portEXIT_CRITICAL(&group->spinlock);
timer->on_alarm = cbs->on_alarm;
timer->user_ctx = user_data;
@ -263,11 +267,11 @@ esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer
esp_err_t gptimer_set_alarm_action(gptimer_handle_t timer, const gptimer_alarm_config_t *config)
{
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE_ISR(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
if (config) {
// When auto_reload is enabled, alarm_count should not be equal to reload_count
bool valid_auto_reload = !config->flags.auto_reload_on_alarm || config->alarm_count != config->reload_count;
ESP_RETURN_ON_FALSE(valid_auto_reload, ESP_ERR_INVALID_ARG, TAG, "reload count can't equal to alarm count");
ESP_RETURN_ON_FALSE_ISR(valid_auto_reload, ESP_ERR_INVALID_ARG, TAG, "reload count can't equal to alarm count");
timer->reload_count = config->reload_count;
timer->alarm_count = config->alarm_count;
@ -292,15 +296,15 @@ esp_err_t gptimer_set_alarm_action(gptimer_handle_t timer, const gptimer_alarm_c
esp_err_t gptimer_start(gptimer_handle_t timer)
{
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE_ISR(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
// acquire power manager lock
if (timer->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(timer->pm_lock), TAG, "acquire APB_FREQ_MAX lock failed");
ESP_RETURN_ON_ERROR_ISR(esp_pm_lock_acquire(timer->pm_lock), TAG, "acquire APB_FREQ_MAX lock failed");
}
// interrupt interupt service
if (timer->intr) {
ESP_RETURN_ON_ERROR(esp_intr_enable(timer->intr), TAG, "enable interrupt service failed");
ESP_RETURN_ON_ERROR_ISR(esp_intr_enable(timer->intr), TAG, "enable interrupt service failed");
}
portENTER_CRITICAL_SAFE(&timer->spinlock);
@ -314,7 +318,7 @@ esp_err_t gptimer_start(gptimer_handle_t timer)
esp_err_t gptimer_stop(gptimer_handle_t timer)
{
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE_ISR(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
// disable counter, alarm, autoreload
portENTER_CRITICAL_SAFE(&timer->spinlock);
@ -325,11 +329,11 @@ esp_err_t gptimer_stop(gptimer_handle_t timer)
// disable interrupt service
if (timer->intr) {
ESP_RETURN_ON_ERROR(esp_intr_disable(timer->intr), TAG, "disable interrupt service failed");
ESP_RETURN_ON_ERROR_ISR(esp_intr_disable(timer->intr), TAG, "disable interrupt service failed");
}
// release power manager lock
if (timer->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_release(timer->pm_lock), TAG, "release APB_FREQ_MAX lock failed");
ESP_RETURN_ON_ERROR_ISR(esp_pm_lock_release(timer->pm_lock), TAG, "release APB_FREQ_MAX lock failed");
}
return ESP_OK;
@ -337,7 +341,9 @@ esp_err_t gptimer_stop(gptimer_handle_t timer)
static gptimer_group_t *gptimer_acquire_group_handle(int group_id)
{
// esp_log_level_set(TAG, ESP_LOG_DEBUG);
#if CONFIG_GPTIMER_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
bool new_group = false;
gptimer_group_t *group = NULL;

View File

@ -0,0 +1,296 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "hal/pcnt_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Type of PCNT unit handle
*/
typedef struct pcnt_unit_t *pcnt_unit_handle_t;
/**
* @brief Type of PCNT channel handle
*/
typedef struct pcnt_chan_t *pcnt_channel_handle_t;
/**
* @brief PCNT watch event data
*/
typedef struct {
int watch_point_value; /*!< Watch point value that triggered the event */
pcnt_unit_zero_cross_mode_t zero_cross_mode; /*!< Zero cross mode */
} pcnt_watch_event_data_t;
/**
* @brief PCNT watch event callback prototype
*
* @note The callback function is invoked from an ISR context, so it should meet the restrictions of not calling any blocking APIs when implementing the callback.
* e.g. must use ISR version of FreeRTOS APIs.
*
* @param[in] unit PCNT unit handle
* @param[in] edata PCNT event data, fed by the driver
* @param[in] user_ctx User data, passed from `pcnt_unit_register_event_callbacks()`
* @return Whether a high priority task has been woken up by this function
*/
typedef bool (*pcnt_watch_cb_t)(pcnt_unit_handle_t unit, pcnt_watch_event_data_t *edata, void *user_ctx);
/**
* @brief Group of supported PCNT callbacks
* @note The callbacks are all running under ISR environment
* @note When CONFIG_PCNT_ISR_IRAM_SAFE is enabled, the callback itself and functions callbed by it should be placed in IRAM.
*/
typedef struct {
pcnt_watch_cb_t on_reach; /*!< Called when PCNT unit counter reaches any watch point */
} pcnt_event_callbacks_t;
/**
* @brief PCNT unit configuration
*/
typedef struct {
int low_limit; /*!< Low limitation of the count unit, should be lower than 0 */
int high_limit; /*!< High limitation of the count unit, should be higher than 0 */
} pcnt_unit_config_t;
/**
* @brief PCNT channel configuration
*/
typedef struct {
int edge_gpio_num; /*!< GPIO number used by the edge signal, input mode with pull up enabled. Set to -1 if unused */
int level_gpio_num; /*!< GPIO number used by the level signal, input mode with pull up enabled. Set to -1 if unused */
struct {
uint32_t invert_edge_input: 1; /*!< Invert the input edge signal */
uint32_t invert_level_input: 1; /*!< Invert the input level signal */
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
} flags;
} pcnt_chan_config_t;
/**
* @brief PCNT glitch filter configuration
*/
typedef struct {
uint32_t max_glitch_ns; /*!< Pulse width smaller than this threshold will be treated as glitch and ignored, in the unit of ns */
} pcnt_glitch_filter_config_t;
/**
* @brief Create a new PCNT unit, and return the handle
*
* @note The newly created PCNT unit is put into the stopped state.
*
* @param[in] config PCNT unit configuration
* @param[out] ret_unit Returned PCNT unit handle
* @return
* - ESP_OK: Create PCNT unit successfully
* - ESP_ERR_INVALID_ARG: Create PCNT unit failed because of invalid argument (e.g. high/low limit value out of the range)
* - ESP_ERR_NO_MEM: Create PCNT unit failed because out of memory
* - ESP_ERR_NOT_FOUND: Create PCNT unit failed because all PCNT units are used up and no more free one
* - ESP_FAIL: Create PCNT unit failed because of other error
*/
esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *ret_unit);
/**
* @brief Delete the PCNT unit handle
*
* @note Users must ensure that the PCNT unit is stopped before deleting the unit. Users can force a working unit into the stopped state via `pcnt_unit_stop()`.
*
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
* @return
* - ESP_OK: Delete the PCNT unit successfully
* - ESP_ERR_INVALID_ARG: Delete the PCNT unit failed because of invalid argument
* - ESP_ERR_INVALID_STATE: Delete the PCNT unit failed because corresponding PCNT channels and/or watch points are still in working
* - ESP_FAIL: Delete the PCNT unit failed because of other error
*/
esp_err_t pcnt_del_unit(pcnt_unit_handle_t unit);
/**
* @brief Set glitch filter for PCNT unit
*
* @note The glitch filter module is clocked from APB, and APB frequency can be changed during DFS, which in return make the filter out of action.
* So this function will lazy-install a PM lock internally when the power management is enabled. With this lock, the APB frequency won't be changed.
* The PM lock can only be uninstalled in `pcnt_del_unit()`.
*
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
* @param[in] config PCNT filter configuration, set config to NULL means disabling the filter function
* @return
* - ESP_OK: Set glitch filter successfully
* - ESP_ERR_INVALID_ARG: Set glitch filter failed because of invalid argument (e.g. glitch width is too big)
* - ESP_FAIL: Set glitch filter failed because of other error
*/
esp_err_t pcnt_unit_set_glitch_filter(pcnt_unit_handle_t unit, const pcnt_glitch_filter_config_t *config);
/**
* @brief Start the PCNT unit, the counter will start to count according to the edge and/or level input signals
*
* @note This function will acquire the PM lock when power management is enabled. Also see `pcnt_unit_set_glitch_filter()` for the condition of PM lock installation.
* @note The number of calls to this function should be equal to the number of calls to `pcnt_unit_stop()`.
* @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it's allowed to be executed when Cache is disabled
*
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
* @return
* - ESP_OK: Start PCNT unit successfully
* - ESP_ERR_INVALID_ARG: Start PCNT unit failed because of invalid argument
* - ESP_FAIL: Start PCNT unit failed because of other error
*/
esp_err_t pcnt_unit_start(pcnt_unit_handle_t unit);
/**
* @brief Stop PCNT from counting
*
* @note If power management is enabled, this function will release the PM lock acquired in `pcnt_unit_start()`.
* Also see `pcnt_unit_set_glitch_filter()` for the condition of PM lock installation.
* @note The stop operation won't clear the counter. Also see `pcnt_unit_clear_count()` for how to clear pulse count value.
* @note The number of calls to this function should be equal to the number of calls to `pcnt_unit_start()`.
* @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it is allowed to be executed when Cache is disabled
*
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
* @return
* - ESP_OK: Stop PCNT unit successfully
* - ESP_ERR_INVALID_ARG: Stop PCNT unit failed because of invalid argument
* - ESP_FAIL: Stop PCNT unit failed because of other error
*/
esp_err_t pcnt_unit_stop(pcnt_unit_handle_t unit);
/**
* @brief Clear PCNT pulse count value to zero
*
* @note It's recommended to call this function after adding a watch point by `pcnt_unit_add_watch_point()`, so that the newly added watch point is effective immediately.
* @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it's allowed to be executed when Cache is disabled
*
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
* @return
* - ESP_OK: Clear PCNT pulse count successfully
* - ESP_ERR_INVALID_ARG: Clear PCNT pulse count failed because of invalid argument
* - ESP_FAIL: Clear PCNT pulse count failed because of other error
*/
esp_err_t pcnt_unit_clear_count(pcnt_unit_handle_t unit);
/**
* @brief Get PCNT count value
*
* @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it's allowed to be executed when Cache is disabled
*
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
* @param[out] value Returned count value
* @return
* - ESP_OK: Get PCNT pulse count successfully
* - ESP_ERR_INVALID_ARG: Get PCNT pulse count failed because of invalid argument
* - ESP_FAIL: Get PCNT pulse count failed because of other error
*/
esp_err_t pcnt_unit_get_count(pcnt_unit_handle_t unit, int *value);
/**
* @brief Set event callbacks for PCNT unit
*
* @note User can deregister the previous callback by calling this function with an empty `cbs`.
*
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
* @param[in] cbs Group of callback functions
* @param[in] user_data User data, which will be passed to callback functions directly
* @return
* - ESP_OK: Set event callbacks successfully
* - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
* - ESP_FAIL: Set event callbacks failed because of other error
*/
esp_err_t pcnt_unit_register_event_callbacks(pcnt_unit_handle_t unit, const pcnt_event_callbacks_t *cbs, void *user_data);
/**
* @brief Add a watch point for PCNT unit, PCNT will generate an event when the counter value reaches the watch point value
*
* @note The number of calls to this function should be equal to the number of calls to `pcnt_unit_remove_watch_point()`, otherwise the unit can't be deleted
*
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
* @param[in] watch_point Value to be watched
* @return
* - ESP_OK: Add watch point successfully
* - ESP_ERR_INVALID_ARG: Add watch point failed because of invalid argument (e.g. the value to be watched is out of the limitation set in `pcnt_unit_config_t`)
* - ESP_ERR_INVALID_STATE: Add watch point failed because the same watch point has already been added
* - ESP_ERR_NOT_FOUND: Add watch point failed because no more hardware watch point can be configured
* - ESP_FAIL: Add watch point failed because of other error
*/
esp_err_t pcnt_unit_add_watch_point(pcnt_unit_handle_t unit, int watch_point);
/**
* @brief Remove a watch point for PCNT unit
*
* @note The number of calls to this function should be equal to the number of calls to `pcnt_unit_add_watch_point()`, otherwise the unit can't be deleted
*
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
* @param[in] watch_point Watch point value
* @return
* - ESP_OK: Remove watch point successfully
* - ESP_ERR_INVALID_ARG: Remove watch point failed because of invalid argument
* - ESP_ERR_INVALID_STATE: Remove watch point failed because the watch point was not added by `pcnt_unit_add_watch_point()` yet
* - ESP_FAIL: Remove watch point failed because of other error
*/
esp_err_t pcnt_unit_remove_watch_point(pcnt_unit_handle_t unit, int watch_point);
/**
* @brief Create PCNT channel for specific unit, each PCNT has several channels associated with it
*
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
* @param[in] config PCNT channel configuration
* @param[out] ret_chan Returned channel handle
* @return
* - ESP_OK: Create PCNT channel successfully
* - ESP_ERR_INVALID_ARG: Create PCNT channel failed because of invalid argument
* - ESP_ERR_NO_MEM: Create PCNT channel failed because of insufficient memory
* - ESP_ERR_NOT_FOUND: Create PCNT channel failed because all PCNT channels are used up and no more free one
* - ESP_FAIL: Create PCNT channel failed because of other error
*/
esp_err_t pcnt_new_channel(pcnt_unit_handle_t unit, const pcnt_chan_config_t *config, pcnt_channel_handle_t *ret_chan);
/**
* @brief Delete the PCNT channel
*
* @param[in] chan PCNT channel handle created by `pcnt_new_channel()`
* @return
* - ESP_OK: Delete the PCNT channel successfully
* - ESP_ERR_INVALID_ARG: Delete the PCNT channel failed because of invalid argument
* - ESP_FAIL: Delete the PCNT channel failed because of other error
*/
esp_err_t pcnt_del_channel(pcnt_channel_handle_t chan);
/**
* @brief Set channel actions when edge signal changes (e.g. falling or rising edge occurred).
* The edge signal is input from the `edge_gpio_num` configured in `pcnt_chan_config_t`.
* We use these actions to control when and how to change the counter value.
*
* @param[in] chan PCNT channel handle created by `pcnt_new_channel()`
* @param[in] pos_act Action on posedge signal
* @param[in] neg_act Action on negedge signal
* @return
* - ESP_OK: Set edge action for PCNT channel successfully
* - ESP_ERR_INVALID_ARG: Set edge action for PCNT channel failed because of invalid argument
* - ESP_FAIL: Set edge action for PCNT channel failed because of other error
*/
esp_err_t pcnt_channel_set_edge_action(pcnt_channel_handle_t chan, pcnt_channel_edge_action_t pos_act, pcnt_channel_edge_action_t neg_act);
/**
* @brief Set channel actions when level signal changes (e.g. signal level goes from high to low).
* The level signal is input from the `level_gpio_num` configured in `pcnt_chan_config_t`.
* We use these actions to control when and how to change the counting mode.
*
* @param[in] chan PCNT channel handle created by `pcnt_new_channel()`
* @param[in] high_act Action on high level signal
* @param[in] low_act Action on low level signal
* @return
* - ESP_OK: Set level action for PCNT channel successfully
* - ESP_ERR_INVALID_ARG: Set level action for PCNT channel failed because of invalid argument
* - ESP_FAIL: Set level action for PCNT channel failed because of other error
*/
esp_err_t pcnt_channel_set_level_action(pcnt_channel_handle_t chan, pcnt_channel_level_action_t high_act, pcnt_channel_level_action_t low_act);
#ifdef __cplusplus
}
#endif

View File

@ -9,3 +9,9 @@
* the legacy timer group driver (deprecated/driver/timer.h) and the new gptimer driver (driver/gptimer.h).
*/
int timer_group_driver_init_count = 0;
/**
* @brief This count is used to prevent the coexistence of
* the legacy pcnt driver (deprecated/driver/pcnt.h) and the new pulse_cnt driver (driver/pulse_cnt.h).
*/
int pcnt_driver_init_count = 0;

View File

@ -18,3 +18,8 @@ entries:
gdma: gdma_stop (noflash)
gdma: gdma_append (noflash)
gdma: gdma_reset (noflash)
if PCNT_CTRL_FUNC_IN_IRAM = y:
pulse_cnt: pcnt_unit_start (noflash)
pulse_cnt: pcnt_unit_stop (noflash)
pulse_cnt: pcnt_unit_clear_count (noflash)
pulse_cnt: pcnt_unit_get_count (noflash)

View File

@ -0,0 +1,714 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <sys/lock.h>
#include "sdkconfig.h"
#if CONFIG_PCNT_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "freertos/FreeRTOS.h"
#include "esp_heap_caps.h"
#include "esp_intr_alloc.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_pm.h"
#include "esp_rom_gpio.h"
#include "soc/soc_caps.h"
#include "soc/pcnt_periph.h"
#include "hal/pcnt_hal.h"
#include "hal/pcnt_ll.h"
#include "hal/gpio_hal.h"
#include "esp_private/esp_clk.h"
#include "esp_private/periph_ctrl.h"
#include "driver/gpio.h"
#include "driver/pulse_cnt.h"
// If ISR handler is allowed to run whilst cache is disabled,
// Make sure all the code and related variables used by the handler are in the SRAM
#if CONFIG_PCNT_ISR_IRAM_SAFE
#define PCNT_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
#define PCNT_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define PCNT_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
#define PCNT_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif //CONFIG_PCNT_ISR_IRAM_SAFE
#define PCNT_PM_LOCK_NAME_LEN_MAX 16
static const char *TAG = "pcnt";
typedef struct pcnt_platform_t pcnt_platform_t;
typedef struct pcnt_group_t pcnt_group_t;
typedef struct pcnt_unit_t pcnt_unit_t;
typedef struct pcnt_chan_t pcnt_chan_t;
struct pcnt_platform_t {
_lock_t mutex; // platform level mutex lock
pcnt_group_t *groups[SOC_PCNT_GROUPS]; // pcnt group pool
int group_ref_counts[SOC_PCNT_GROUPS]; // reference count used to protect group install/uninstall
};
struct pcnt_group_t {
int group_id; // Group ID, index from 0
portMUX_TYPE spinlock; // to protect per-group register level concurrent access
pcnt_hal_context_t hal;
pcnt_unit_t *units[SOC_PCNT_UNITS_PER_GROUP]; // array of PCNT units
};
typedef struct {
pcnt_ll_watch_event_id_t event_id; // event type
int watch_point_value; // value to be watched
} pcnt_watch_point_t;
typedef enum {
PCNT_FSM_STOP,
PCNT_FSM_START,
} pcnt_lifecycle_fsm_t;
struct pcnt_unit_t {
pcnt_group_t *group; // which group the pcnt unit belongs to
portMUX_TYPE spinlock; // Spinlock, stop one unit from accessing different parts of a same register concurrently
uint32_t unit_id; // allocated unit numerical ID
int low_limit; // low limit value
int high_limit; // high limit value
pcnt_chan_t *channels[SOC_PCNT_CHANNELS_PER_UNIT]; // array of PCNT channels
pcnt_watch_point_t watchers[PCNT_LL_WATCH_EVENT_MAX]; // array of PCNT watchers
intr_handle_t intr; // interrupt handle
esp_pm_lock_handle_t pm_lock; // PM lock, for glitch filter, as that module can only be functional under APB
#if CONFIG_PM_ENABLE
char pm_lock_name[PCNT_PM_LOCK_NAME_LEN_MAX]; // pm lock name
#endif
pcnt_lifecycle_fsm_t fsm; // access to fsm should be protect by spinlock, as fsm can also accessed from ISR handler
pcnt_watch_cb_t on_reach; // user registered callback function
void *user_data; // user data registered by user, which would be passed to the right callback function
};
struct pcnt_chan_t {
pcnt_unit_t *unit; // pointer to the PCNT unit where it derives from
uint32_t channel_id; // channel ID, index from 0
int edge_gpio_num;
int level_gpio_num;
};
// pcnt driver platform, it's always a singleton
static pcnt_platform_t s_platform;
static pcnt_group_t *pcnt_acquire_group_handle(int group_id);
static void pcnt_release_group_handle(pcnt_group_t *group);
static void pcnt_default_isr(void *args);
esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *ret_unit)
{
esp_err_t ret = ESP_OK;
pcnt_group_t *group = NULL;
pcnt_unit_t *unit = NULL;
int group_id = -1;
int unit_id = -1;
ESP_GOTO_ON_FALSE(config && ret_unit, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
ESP_GOTO_ON_FALSE(config->low_limit < 0 && config->high_limit > 0 && config->low_limit >= PCNT_LL_MIN_LIN && config->high_limit <= PCNT_LL_MAX_LIM,
ESP_ERR_INVALID_ARG, err, TAG, "invalid limit range:[%d,%d]", config->low_limit, config->high_limit);
unit = heap_caps_calloc(1, sizeof(pcnt_unit_t), PCNT_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(unit, ESP_ERR_NO_MEM, err, TAG, "no mem for unit");
for (int i = 0; (i < SOC_PCNT_GROUPS) && (unit_id < 0); i++) {
group = pcnt_acquire_group_handle(i);
ESP_GOTO_ON_FALSE(group, ESP_ERR_NO_MEM, err, TAG, "no mem for group (%d)", i);
// loop to search free unit in the group
portENTER_CRITICAL(&group->spinlock);
for (int j = 0; j < SOC_PCNT_UNITS_PER_GROUP; j++) {
if (!group->units[j]) {
group_id = i;
unit_id = j;
group->units[j] = unit;
break;
}
}
portEXIT_CRITICAL(&group->spinlock);
if (unit_id < 0) {
pcnt_release_group_handle(group);
group = NULL;
}
}
ESP_GOTO_ON_FALSE(unit_id != -1, ESP_ERR_NOT_FOUND, err, TAG, "no free unit");
unit->group = group;
unit->unit_id = unit_id;
// some events are enabled by default, disable them all
pcnt_ll_disable_all_events(group->hal.dev, unit_id);
// disable filter by default
pcnt_ll_enable_glitch_filter(group->hal.dev, unit_id, false);
// set default high/low limitation value
// note: limit value takes effect only after counter clear
pcnt_ll_set_high_limit_value(group->hal.dev, unit_id, config->high_limit);
pcnt_ll_set_low_limit_value(group->hal.dev, unit_id, config->low_limit);
unit->high_limit = config->high_limit;
unit->low_limit = config->low_limit;
// clear/pause register is shared by all units, so using group's spinlock
portENTER_CRITICAL(&group->spinlock);
pcnt_ll_stop_count(group->hal.dev, unit_id);
pcnt_ll_clear_count(group->hal.dev, unit_id);
pcnt_ll_enable_intr(group->hal.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id), false);
pcnt_ll_clear_intr_status(group->hal.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id));
portEXIT_CRITICAL(&group->spinlock);
unit->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
unit->fsm = PCNT_FSM_STOP;
for (int i = 0; i < PCNT_LL_WATCH_EVENT_MAX; i++) {
unit->watchers[i].event_id = PCNT_LL_WATCH_EVENT_INVALID; // invalid all watch point
}
ESP_LOGD(TAG, "new pcnt unit (%d,%d) at %p, count range:[%d,%d]", group_id, unit_id, unit, unit->low_limit, unit->high_limit);
*ret_unit = unit;
return ESP_OK;
err:
if (unit) {
free(unit);
}
if (group) {
pcnt_release_group_handle(group);
}
return ret;
}
esp_err_t pcnt_del_unit(pcnt_unit_handle_t unit)
{
pcnt_group_t *group = NULL;
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(unit->fsm == PCNT_FSM_STOP, ESP_ERR_INVALID_STATE, TAG, "can't delete unit as it's not in stop state");
for (int i = 0; i < SOC_PCNT_CHANNELS_PER_UNIT; i++) {
ESP_RETURN_ON_FALSE(!unit->channels[i], ESP_ERR_INVALID_STATE, TAG, "channel %d still in working", i);
}
for (int i = 0; i < PCNT_LL_WATCH_EVENT_MAX; i++) {
ESP_RETURN_ON_FALSE(unit->watchers[i].event_id == PCNT_LL_WATCH_EVENT_INVALID, ESP_ERR_INVALID_STATE, TAG,
"watch point %d still in working", unit->watchers[i].watch_point_value);
}
group = unit->group;
int group_id = group->group_id;
int unit_id = unit->unit_id;
portENTER_CRITICAL(&group->spinlock);
group->units[unit_id] = NULL;
portEXIT_CRITICAL(&group->spinlock);
if (unit->intr) {
esp_intr_free(unit->intr);
ESP_LOGD(TAG, "uninstall interrupt service for unit (%d,%d)", group_id, unit_id);
}
if (unit->pm_lock) {
esp_pm_lock_delete(unit->pm_lock);
ESP_LOGD(TAG, "uninstall APB_FREQ_MAX lock for unit (%d,%d)", group_id, unit_id);
}
free(unit);
ESP_LOGD(TAG, "del unit (%d,%d)", group_id, unit_id);
// unit has a reference on group, release it now
pcnt_release_group_handle(group);
return ESP_OK;
}
esp_err_t pcnt_unit_set_glitch_filter(pcnt_unit_handle_t unit, const pcnt_glitch_filter_config_t *config)
{
pcnt_group_t *group = NULL;
uint32_t glitch_filter_thres = 0;
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
group = unit->group;
if (config) {
glitch_filter_thres = esp_clk_apb_freq() / 1000000 * config->max_glitch_ns / 1000;
ESP_RETURN_ON_FALSE(glitch_filter_thres <= PCNT_LL_MAX_GLITCH_WIDTH, ESP_ERR_INVALID_ARG, TAG, "glitch width out of range");
// The filter module is working against APB clock, so lazy install PM lock
#if CONFIG_PM_ENABLE
if (!unit->pm_lock) {
sprintf(unit->pm_lock_name, "pcnt_%d_%d", group->group_id, unit->unit_id); // e.g. pcnt_0_0
ESP_RETURN_ON_ERROR(esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, unit->pm_lock_name, &unit->pm_lock), TAG, "install pm lock failed");
ESP_LOGD(TAG, "install APB_FREQ_MAX lock for unit (%d,%d)", group->group_id, unit->unit_id);
}
#endif
}
// filter control bit is mixed with other PCNT control bits in the same register
portENTER_CRITICAL(&unit->spinlock);
if (config) {
pcnt_ll_set_glitch_filter_thres(group->hal.dev, unit->unit_id, glitch_filter_thres);
pcnt_ll_enable_glitch_filter(group->hal.dev, unit->unit_id, true);
} else {
pcnt_ll_enable_glitch_filter(group->hal.dev, unit->unit_id, false);
}
portEXIT_CRITICAL(&unit->spinlock);
return ESP_OK;
}
esp_err_t pcnt_unit_start(pcnt_unit_handle_t unit)
{
pcnt_group_t *group = NULL;
ESP_RETURN_ON_FALSE_ISR(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
group = unit->group;
// acquire power manager lock
if (unit->pm_lock) {
ESP_RETURN_ON_ERROR_ISR(esp_pm_lock_acquire(unit->pm_lock), TAG, "acquire APB_FREQ_MAX lock failed");
}
// enable interupt service
if (unit->intr) {
ESP_RETURN_ON_ERROR_ISR(esp_intr_enable(unit->intr), TAG, "enable interrupt service failed");
}
// all PCNT units share the same register to control counter
portENTER_CRITICAL_SAFE(&group->spinlock);
pcnt_ll_start_count(group->hal.dev, unit->unit_id);
portEXIT_CRITICAL_SAFE(&group->spinlock);
portENTER_CRITICAL_SAFE(&unit->spinlock);
unit->fsm = PCNT_FSM_START;
portEXIT_CRITICAL_SAFE(&unit->spinlock);
return ESP_OK;
}
esp_err_t pcnt_unit_stop(pcnt_unit_handle_t unit)
{
pcnt_group_t *group = NULL;
ESP_RETURN_ON_FALSE_ISR(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
group = unit->group;
// all PCNT units share the same register to control counter
portENTER_CRITICAL_SAFE(&group->spinlock);
pcnt_ll_stop_count(group->hal.dev, unit->unit_id);
portEXIT_CRITICAL_SAFE(&group->spinlock);
portENTER_CRITICAL_SAFE(&unit->spinlock);
unit->fsm = PCNT_FSM_STOP;
portEXIT_CRITICAL_SAFE(&unit->spinlock);
// disable interrupt service
if (unit->intr) {
ESP_RETURN_ON_ERROR_ISR(esp_intr_disable(unit->intr), TAG, "disable interrupt service failed");
}
// release power manager lock
if (unit->pm_lock) {
ESP_RETURN_ON_ERROR_ISR(esp_pm_lock_release(unit->pm_lock), TAG, "release APB_FREQ_MAX lock failed");
}
return ESP_OK;
}
esp_err_t pcnt_unit_clear_count(pcnt_unit_handle_t unit)
{
pcnt_group_t *group = NULL;
ESP_RETURN_ON_FALSE_ISR(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
group = unit->group;
// all PCNT units share the same register to control counter
portENTER_CRITICAL_SAFE(&group->spinlock);
pcnt_ll_clear_count(group->hal.dev, unit->unit_id);
portEXIT_CRITICAL_SAFE(&group->spinlock);
return ESP_OK;
}
esp_err_t pcnt_unit_get_count(pcnt_unit_handle_t unit, int *value)
{
pcnt_group_t *group = NULL;
ESP_RETURN_ON_FALSE_ISR(unit && value, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
group = unit->group;
*value = pcnt_ll_get_count(group->hal.dev, unit->unit_id);
return ESP_OK;
}
esp_err_t pcnt_unit_register_event_callbacks(pcnt_unit_handle_t unit, const pcnt_event_callbacks_t *cbs, void *user_data)
{
pcnt_group_t *group = NULL;
ESP_RETURN_ON_FALSE(unit && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
group = unit->group;
int group_id = group->group_id;
int unit_id = unit->unit_id;
#if CONFIG_PCNT_ISR_IRAM_SAFE
if (cbs->on_reach) {
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_reach), ESP_ERR_INVALID_ARG, TAG, "on_reach callback not in IRAM");
}
if (user_data) {
ESP_RETURN_ON_FALSE(esp_ptr_in_dram(user_data) ||
esp_ptr_in_diram_dram(user_data) ||
esp_ptr_in_rtc_dram_fast(user_data),
ESP_ERR_INVALID_ARG, TAG, "user context not in DRAM");
}
#endif
// lazy install interrupt service
if (!unit->intr) {
int isr_flags = PCNT_INTR_ALLOC_FLAGS;
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(pcnt_periph_signals.groups[group_id].irq, isr_flags,
(uint32_t)pcnt_ll_get_intr_status_reg(group->hal.dev), PCNT_LL_UNIT_WATCH_EVENT(unit_id),
pcnt_default_isr, unit, &unit->intr),
TAG, "install interrupt service failed");
}
// enable/disable PCNT interrupt events
portENTER_CRITICAL(&group->spinlock);
pcnt_ll_enable_intr(group->hal.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id), cbs->on_reach != NULL);
portEXIT_CRITICAL(&group->spinlock);
unit->on_reach = cbs->on_reach;
unit->user_data = user_data;
return ESP_OK;
}
esp_err_t pcnt_unit_add_watch_point(pcnt_unit_handle_t unit, int watch_point)
{
esp_err_t ret = ESP_OK;
pcnt_group_t *group = NULL;
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(watch_point <= unit->high_limit && watch_point >= unit->low_limit,
ESP_ERR_INVALID_ARG, TAG, "watch_point out of limit");
group = unit->group;
// event enable/disable is mixed with other control function in the same register
portENTER_CRITICAL(&unit->spinlock);
// zero cross watch point
if (watch_point == 0) {
if (unit->watchers[PCNT_LL_WATCH_EVENT_ZERO_CROSS].event_id != PCNT_LL_WATCH_EVENT_INVALID) {
ret = ESP_ERR_INVALID_STATE; // zero cross event watcher has been installed already
} 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(group->hal.dev, unit->unit_id, true);
}
}
// high limit watch point
else if (watch_point == unit->high_limit) {
if (unit->watchers[PCNT_LL_WATCH_EVENT_HIGH_LIMIT].event_id != PCNT_LL_WATCH_EVENT_INVALID) {
ret = ESP_ERR_INVALID_STATE; // high limit event watcher has been installed already
} 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->high_limit;
pcnt_ll_enable_high_limit_event(group->hal.dev, unit->unit_id, true);
}
}
// low limit watch point
else if (watch_point == unit->low_limit) {
if (unit->watchers[PCNT_LL_WATCH_EVENT_LOW_LIMIT].event_id != PCNT_LL_WATCH_EVENT_INVALID) {
ret = ESP_ERR_INVALID_STATE; // low limit event watcher has been installed already
} 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->low_limit;
pcnt_ll_enable_low_limit_event(group->hal.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:
if (unit->watchers[PCNT_LL_WATCH_EVENT_THRES1].event_id == PCNT_LL_WATCH_EVENT_INVALID) {
unit->watchers[PCNT_LL_WATCH_EVENT_THRES1].event_id = PCNT_LL_WATCH_EVENT_THRES1;
unit->watchers[PCNT_LL_WATCH_EVENT_THRES1].watch_point_value = watch_point;
pcnt_ll_set_thres_value(group->hal.dev, unit->unit_id, 1, watch_point);
pcnt_ll_enable_thres_event(group->hal.dev, unit->unit_id, 1, true);
break;
} else if (unit->watchers[PCNT_LL_WATCH_EVENT_THRES1].watch_point_value == watch_point) {
ret = ESP_ERR_INVALID_STATE;
break;
}
/* fall-through */
case 0:
if (unit->watchers[PCNT_LL_WATCH_EVENT_THRES0].event_id == PCNT_LL_WATCH_EVENT_INVALID) {
unit->watchers[PCNT_LL_WATCH_EVENT_THRES0].event_id = PCNT_LL_WATCH_EVENT_THRES0;
unit->watchers[PCNT_LL_WATCH_EVENT_THRES0].watch_point_value = watch_point;
pcnt_ll_set_thres_value(group->hal.dev, unit->unit_id, 0, watch_point);
pcnt_ll_enable_thres_event(group->hal.dev, unit->unit_id, 0, true);
break;
} else if (unit->watchers[PCNT_LL_WATCH_EVENT_THRES0].watch_point_value == watch_point) {
ret = ESP_ERR_INVALID_STATE;
break;
}
/* fall-through */
default:
ret = ESP_ERR_NOT_FOUND; // no free threshold watch point available
break;
}
}
portEXIT_CRITICAL(&unit->spinlock);
ESP_RETURN_ON_ERROR(ret, TAG, "add watchpoint %d failed", watch_point);
return ESP_OK;
}
esp_err_t pcnt_unit_remove_watch_point(pcnt_unit_handle_t unit, int watch_point)
{
pcnt_group_t *group = NULL;
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
group = unit->group;
pcnt_ll_watch_event_id_t event_id = PCNT_LL_WATCH_EVENT_INVALID;
// event enable/disable is mixed with other control function in the same register
portENTER_CRITICAL(&unit->spinlock);
for (int 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(group->hal.dev, unit->unit_id, false);
break;
case PCNT_LL_WATCH_EVENT_LOW_LIMIT:
pcnt_ll_enable_low_limit_event(group->hal.dev, unit->unit_id, false);
break;
case PCNT_LL_WATCH_EVENT_HIGH_LIMIT:
pcnt_ll_enable_high_limit_event(group->hal.dev, unit->unit_id, false);
break;
case PCNT_LL_WATCH_EVENT_THRES0:
pcnt_ll_enable_thres_event(group->hal.dev, unit->unit_id, 0, false);
break;
case PCNT_LL_WATCH_EVENT_THRES1:
pcnt_ll_enable_thres_event(group->hal.dev, unit->unit_id, 1, false);
break;
default:
break;
}
portEXIT_CRITICAL(&unit->spinlock);
ESP_RETURN_ON_FALSE(event_id != PCNT_LL_WATCH_EVENT_INVALID, ESP_ERR_INVALID_STATE, TAG, "watch point %d not added yet", watch_point);
return ESP_OK;
}
esp_err_t pcnt_new_channel(pcnt_unit_handle_t unit, const pcnt_chan_config_t *config, pcnt_channel_handle_t *ret_chan)
{
esp_err_t ret = ESP_OK;
pcnt_chan_t *channel = NULL;
pcnt_group_t *group = NULL;
ESP_GOTO_ON_FALSE(unit && config && ret_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
group = unit->group;
int group_id = group->group_id;
int unit_id = unit->unit_id;
channel = heap_caps_calloc(1, sizeof(pcnt_chan_t), PCNT_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(channel, ESP_ERR_NO_MEM, err, TAG, "no mem for channel");
// search for a free channel
int channel_id = -1;
portENTER_CRITICAL(&unit->spinlock);
for (int i = 0; i < SOC_PCNT_CHANNELS_PER_UNIT; i++) {
if (!unit->channels[i]) {
channel_id = i;
unit->channels[channel_id] = channel;
break;
}
}
portEXIT_CRITICAL(&unit->spinlock);
ESP_GOTO_ON_FALSE(channel_id != -1, ESP_ERR_NOT_FOUND, err, TAG, "no free channel in unit (%d,%d)", group_id, unit_id);
// GPIO configuration
gpio_config_t gpio_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), // also enable the output path if `io_loop_back` is enabled
.pull_down_en = false,
.pull_up_en = true,
};
if (config->edge_gpio_num >= 0) {
gpio_conf.pin_bit_mask = 1ULL << config->edge_gpio_num;
ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config edge GPIO failed");
esp_rom_gpio_connect_in_signal(config->edge_gpio_num,
pcnt_periph_signals.groups[group_id].units[unit_id].channels[channel_id].pulse_sig,
config->flags.invert_edge_input);
}
if (config->level_gpio_num >= 0) {
gpio_conf.pin_bit_mask = 1ULL << config->level_gpio_num;
ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config level GPIO failed");
esp_rom_gpio_connect_in_signal(config->level_gpio_num,
pcnt_periph_signals.groups[group_id].units[unit_id].channels[channel_id].control_sig,
config->flags.invert_level_input);
}
channel->channel_id = channel_id;
channel->unit = unit;
channel->edge_gpio_num = config->edge_gpio_num;
channel->level_gpio_num = config->level_gpio_num;
ESP_LOGD(TAG, "new pcnt channel(%d,%d,%d) at %p", group_id, unit_id, channel_id, channel);
*ret_chan = channel;
return ESP_OK;
err:
if (channel) {
free(channel);
}
return ret;
}
esp_err_t pcnt_del_channel(pcnt_channel_handle_t chan)
{
ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
pcnt_unit_t *unit = chan->unit;
pcnt_group_t *group = unit->group;
int group_id = group->group_id;
int unit_id = unit->unit_id;
int channel_id = chan->channel_id;
portENTER_CRITICAL(&unit->spinlock);
unit->channels[channel_id] = NULL;
portEXIT_CRITICAL(&unit->spinlock);
if (chan->level_gpio_num >= 0) {
gpio_reset_pin(chan->level_gpio_num);
}
if (chan->edge_gpio_num >= 0) {
gpio_reset_pin(chan->edge_gpio_num);
}
free(chan);
ESP_LOGD(TAG, "del pcnt channel(%d,%d,%d)", group_id, unit_id, channel_id);
return ESP_OK;
}
esp_err_t pcnt_channel_set_edge_action(pcnt_channel_handle_t chan, pcnt_channel_edge_action_t pos_act, pcnt_channel_edge_action_t neg_act)
{
pcnt_group_t *group = NULL;
ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
pcnt_unit_t *unit = chan->unit;
group = unit->group;
// mode control bits are mixed with other PCNT control bits in a same register
portENTER_CRITICAL(&unit->spinlock);
pcnt_ll_set_edge_action(group->hal.dev, unit->unit_id, chan->channel_id, pos_act, neg_act);
portEXIT_CRITICAL(&unit->spinlock);
return ESP_OK;
}
esp_err_t pcnt_channel_set_level_action(pcnt_channel_handle_t chan, pcnt_channel_level_action_t high_act, pcnt_channel_level_action_t low_act)
{
pcnt_group_t *group = NULL;
ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
pcnt_unit_t *unit = chan->unit;
group = unit->group;
// mode control bits are mixed with other PCNT control bits in a same register
portENTER_CRITICAL(&unit->spinlock);
pcnt_ll_set_level_action(group->hal.dev, unit->unit_id, chan->channel_id, high_act, low_act);
portEXIT_CRITICAL(&unit->spinlock);
return ESP_OK;
}
static pcnt_group_t *pcnt_acquire_group_handle(int group_id)
{
#if CONFIG_PCNT_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
bool new_group = false;
pcnt_group_t *group = NULL;
// prevent install pcnt group concurrently
_lock_acquire(&s_platform.mutex);
if (!s_platform.groups[group_id]) {
group = heap_caps_calloc(1, sizeof(pcnt_group_t), PCNT_MEM_ALLOC_CAPS);
if (group) {
new_group = true;
s_platform.groups[group_id] = group; // register to platform
// initialize pcnt group members
group->group_id = group_id;
group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
// enable APB access pcnt registers
periph_module_enable(pcnt_periph_signals.groups[group_id].module);
periph_module_reset(pcnt_periph_signals.groups[group_id].module);
// initialize HAL context
pcnt_hal_init(&group->hal, group_id);
}
} else {
group = s_platform.groups[group_id];
}
if (group) {
// someone acquired the group handle means we have a new object that refer to this group
s_platform.group_ref_counts[group_id]++;
}
_lock_release(&s_platform.mutex);
if (new_group) {
ESP_LOGD(TAG, "new group (%d) at %p", group_id, group);
}
return group;
}
static void pcnt_release_group_handle(pcnt_group_t *group)
{
int group_id = group->group_id;
bool do_deinitialize = false;
_lock_acquire(&s_platform.mutex);
s_platform.group_ref_counts[group_id]--;
if (s_platform.group_ref_counts[group_id] == 0) {
assert(s_platform.groups[group_id]);
do_deinitialize = true;
s_platform.groups[group_id] = NULL; // deregister from platform
periph_module_disable(pcnt_periph_signals.groups[group_id].module);
}
_lock_release(&s_platform.mutex);
if (do_deinitialize) {
free(group);
ESP_LOGD(TAG, "del group (%d)", group_id);
}
}
IRAM_ATTR static void pcnt_default_isr(void *args)
{
bool need_yield = false;
pcnt_unit_t *unit = (pcnt_unit_t *)args;
int unit_id = unit->unit_id;
pcnt_group_t *group = unit->group;
pcnt_watch_cb_t on_reach = unit->on_reach;
uint32_t intr_status = pcnt_ll_get_intr_status(group->hal.dev);
if (intr_status & PCNT_LL_UNIT_WATCH_EVENT(unit_id)) {
pcnt_ll_clear_intr_status(group->hal.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id));
uint32_t event_status = pcnt_ll_get_event_status(group->hal.dev, unit_id);
// iter on each event_id
while (event_status) {
int event_id = __builtin_ffs(event_status) - 1;
event_status &= (event_status - 1); // clear the right most bit
// invoked user registered callback
if (on_reach) {
pcnt_watch_event_data_t edata = {
.watch_point_value = unit->watchers[event_id].watch_point_value,
.zero_cross_mode = pcnt_ll_get_zero_cross_mode(group->hal.dev, unit_id),
};
if (on_reach(unit, &edata, unit->user_data)) {
// check if we need to yield for high priority task
need_yield = true;
}
}
}
}
if (need_yield) {
portYIELD_FROM_ISR();
}
}
/**
* @brief This function will be called during start up, to check that pulse_cnt driver is not running along with the legacy pcnt driver
*/
__attribute__((constructor))
static void check_pulse_cnt_driver_conflict(void)
{
extern int pcnt_driver_init_count;
pcnt_driver_init_count++;
if (pcnt_driver_init_count > 1) {
ESP_EARLY_LOGE(TAG, "CONFLICT! The pulse_cnt driver can't work along with the legacy pcnt driver");
abort();
}
}

View File

@ -2,7 +2,6 @@ set(srcs "test_app_main.c"
"test_gptimer.c"
"test_gptimer_iram.c")
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES driver unity spi_flash)
idf_component_register(SRCS ${srcs})
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u test_app_include_gptimer" "-u test_app_include_gptimer_iram")

View File

@ -12,6 +12,6 @@ from pytest_embedded import Dut
'release',
], indirect=True)
def test_gptimer(dut: Dut) -> None:
dut.expect('Press ENTER to see the list of tests')
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('*')
dut.expect_unity_test_output()

View File

@ -0,0 +1,18 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(pcnt_test)
if(CONFIG_COMPILER_DUMP_RTL_FILES)
add_custom_target(check_test_app_sections ALL
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
--rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/driver/
--elf-file ${CMAKE_BINARY_DIR}/pcnt_test.elf
find-refs
--from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata
--exit-code
DEPENDS ${elf}
)
endif()

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- |

View File

@ -0,0 +1,6 @@
set(srcs "test_app_main.c"
"test_pulse_cnt.c")
idf_component_register(SRCS ${srcs})
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u test_app_include_pulse_cnt")

View File

@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
// Some resources are lazy allocated in pulse_cnt driver, the threshold is left for that case
#define TEST_MEMORY_LEAK_THRESHOLD (-300)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
// ____ ____ _ _ _____ _____ _
// | _ \ / ___| \ | |_ _| |_ _|__ ___| |_
// | |_) | | | \| | | | | |/ _ \/ __| __|
// | __/| |___| |\ | | | | | __/\__ \ |_
// |_| \____|_| \_| |_| |_|\___||___/\__|
printf(" ____ ____ _ _ _____ _____ _\r\n");
printf("| _ \\ / ___| \\ | |_ _| |_ _|__ ___| |_\r\n");
printf("| |_) | | | \\| | | | | |/ _ \\/ __| __|\r\n");
printf("| __/| |___| |\\ | | | | | __/\\__ \\ |_\r\n");
printf("|_| \\____|_| \\_| |_| |_|\\___||___/\\__|\r\n");
unity_run_menu();
}

View File

@ -0,0 +1,408 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "driver/pulse_cnt.h"
#include "driver/gpio.h"
#include "soc/soc_caps.h"
#include "esp_attr.h"
#define TEST_PCNT_GPIO_A 0
#define TEST_PCNT_GPIO_B 2
#if CONFIG_PCNT_ISR_IRAM_SAFE
#define TEST_PCNT_CALLBACK_ATTR IRAM_ATTR
#else
#define TEST_PCNT_CALLBACK_ATTR
#endif // CONFIG_PCNT_ISR_IRAM_SAFE
// helper function to simulate several rising edges on gpio
static void test_gpio_simulate_rising_edge(int gpio_sig, size_t times)
{
while (times--) {
TEST_ESP_OK(gpio_set_level(gpio_sig, 0));
TEST_ESP_OK(gpio_set_level(gpio_sig, 1));
}
}
// helper function to simulate several groups of quadrature signals
static void test_gpio_simulate_quadrature_signals(int gpio_sig_a, int gpio_sig_b, size_t times)
{
while (times--) {
TEST_ESP_OK(gpio_set_level(gpio_sig_a, 1));
TEST_ESP_OK(gpio_set_level(gpio_sig_b, 0));
vTaskDelay(1);
TEST_ESP_OK(gpio_set_level(gpio_sig_a, 0));
TEST_ESP_OK(gpio_set_level(gpio_sig_b, 0));
vTaskDelay(1);
TEST_ESP_OK(gpio_set_level(gpio_sig_a, 0));
TEST_ESP_OK(gpio_set_level(gpio_sig_b, 1));
vTaskDelay(1);
TEST_ESP_OK(gpio_set_level(gpio_sig_a, 1));
TEST_ESP_OK(gpio_set_level(gpio_sig_b, 1));
vTaskDelay(1);
}
}
TEST_CASE("pcnt_unit_install_uninstall", "[pcnt]")
{
pcnt_unit_config_t unit_config = {
.low_limit = -100,
.high_limit = 100,
};
pcnt_unit_handle_t units[SOC_PCNT_UNITS_PER_GROUP];
int count_value = 0;
printf("install pcnt units and check initial count\r\n");
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
TEST_ESP_OK(pcnt_new_unit(&unit_config, &units[i]));
TEST_ESP_OK(pcnt_unit_get_count(units[i], &count_value));
TEST_ASSERT_EQUAL(0, count_value);
}
// no more free pcnt units
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, pcnt_new_unit(&unit_config, &units[0]));
printf("set glitch filter\r\n");
pcnt_glitch_filter_config_t filter_config = {
.max_glitch_ns = 1000,
};
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
TEST_ESP_OK(pcnt_unit_set_glitch_filter(units[i], &filter_config));
}
// invalid glitch configuration
filter_config.max_glitch_ns = 500000;
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, pcnt_unit_set_glitch_filter(units[0], &filter_config));
printf("start pcnt units\r\n");
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
TEST_ESP_OK(pcnt_unit_start(units[i]));
}
// can't uninstall unit before stop it
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, pcnt_del_unit(units[0]));
printf("stop pcnt units\r\n");
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
TEST_ESP_OK(pcnt_unit_stop(units[i]));
}
printf("uninstall pcnt units\r\n");
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
TEST_ESP_OK(pcnt_del_unit(units[i]));
}
}
TEST_CASE("pcnt_channel_install_uninstall", "[pcnt]")
{
pcnt_unit_config_t unit_config = {
.low_limit = -100,
.high_limit = 100,
};
pcnt_chan_config_t chan_config = {
.edge_gpio_num = TEST_PCNT_GPIO_A, // only detect edge signal in this case
.level_gpio_num = -1,
.flags.io_loop_back = true,
};
pcnt_unit_handle_t units[SOC_PCNT_UNITS_PER_GROUP];
pcnt_channel_handle_t chans[SOC_PCNT_UNITS_PER_GROUP][SOC_PCNT_CHANNELS_PER_UNIT];
printf("install pcnt units\r\n");
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
TEST_ESP_OK(pcnt_new_unit(&unit_config, &units[i]));
}
printf("install pcnt channels\r\n");
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
for (int j = 0; j < SOC_PCNT_CHANNELS_PER_UNIT; j++) {
TEST_ESP_OK(pcnt_new_channel(units[i], &chan_config, &chans[i][j]));
TEST_ESP_OK(pcnt_channel_set_edge_action(chans[i][j], PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_level_action(chans[i][j], PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
}
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, pcnt_new_channel(units[i], &chan_config, &chans[i][0]));
}
printf("start units\r\n");
int count_value = 0;
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
// start unit
TEST_ESP_OK(pcnt_unit_start(units[i]));
// trigger 10 rising edge on GPIO0
test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 10);
TEST_ESP_OK(pcnt_unit_get_count(units[i], &count_value));
// each channel increases to the same unit counter
TEST_ASSERT_EQUAL(10 * SOC_PCNT_CHANNELS_PER_UNIT, count_value);
}
printf("clear counts\r\n");
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
TEST_ESP_OK(pcnt_unit_clear_count(units[i]));
TEST_ESP_OK(pcnt_unit_get_count(units[i], &count_value));
TEST_ASSERT_EQUAL(0, count_value);
}
printf("stop unit\r\n");
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
// stop unit
TEST_ESP_OK(pcnt_unit_stop(units[i]));
}
// trigger 10 rising edge on GPIO0 shouldn't increase the counter
test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 10);
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
TEST_ESP_OK(pcnt_unit_get_count(units[i], &count_value));
TEST_ASSERT_EQUAL(0, count_value);
}
printf("restart units\r\n");
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
// start unit
TEST_ESP_OK(pcnt_unit_start(units[i]));
// trigger 10 rising edge on GPIO
test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 10);
TEST_ESP_OK(pcnt_unit_get_count(units[i], &count_value));
// each channel increases to the same unit counter
TEST_ASSERT_EQUAL(10 * SOC_PCNT_CHANNELS_PER_UNIT, count_value);
}
printf("uninstall channels and units\r\n");
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
// stop unit
TEST_ESP_OK(pcnt_unit_stop(units[i]));
// can't uninstall unit when channel is still alive
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, pcnt_del_unit(units[i]));
for (int j = 0; j < SOC_PCNT_CHANNELS_PER_UNIT; j++) {
TEST_ESP_OK(pcnt_del_channel(chans[i][j]));
}
TEST_ESP_OK(pcnt_del_unit(units[i]));
}
}
/**
* @brief Using this context to save the triggered watchpoints in sequence
*/
typedef struct {
uint32_t index;
int triggered_watch_values[8];
} test_pcnt_quadrature_context_t;
TEST_PCNT_CALLBACK_ATTR
static bool test_pcnt_quadrature_reach_watch_point(pcnt_unit_handle_t handle, pcnt_watch_event_data_t *event_data, void *user_data)
{
test_pcnt_quadrature_context_t *user_ctx = (test_pcnt_quadrature_context_t *)user_data;
user_ctx->triggered_watch_values[user_ctx->index++] = event_data->watch_point_value;
return false;
}
TEST_CASE("pcnt_quadrature_decode_event", "[pcnt]")
{
pcnt_unit_config_t unit_config = {
.low_limit = -100,
.high_limit = 100
};
printf("install pcnt unit\r\n");
pcnt_unit_handle_t unit = NULL;
TEST_ESP_OK(pcnt_new_unit(&unit_config, &unit));
pcnt_glitch_filter_config_t filter_config = {
.max_glitch_ns = 1000,
};
TEST_ESP_OK(pcnt_unit_set_glitch_filter(unit, &filter_config));
printf("install two pcnt channels with different edge/level action\r\n");
pcnt_chan_config_t channel_config = {
.edge_gpio_num = TEST_PCNT_GPIO_A,
.level_gpio_num = TEST_PCNT_GPIO_B,
.flags.io_loop_back = true,
};
pcnt_channel_handle_t channelA = NULL;
TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelA));
TEST_ESP_OK(pcnt_channel_set_edge_action(channelA, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE));
TEST_ESP_OK(pcnt_channel_set_level_action(channelA, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
// switch edge gpio and level gpio, the assign to another channel in the same unit
pcnt_channel_handle_t channelB = NULL;
channel_config.edge_gpio_num = TEST_PCNT_GPIO_B;
channel_config.level_gpio_num = TEST_PCNT_GPIO_A;
TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelB));
TEST_ESP_OK(pcnt_channel_set_edge_action(channelB, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_DECREASE));
TEST_ESP_OK(pcnt_channel_set_level_action(channelB, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
// ensure the simulation signal in a stable state
TEST_ESP_OK(gpio_set_level(TEST_PCNT_GPIO_A, 1));
TEST_ESP_OK(gpio_set_level(TEST_PCNT_GPIO_B, 1));
pcnt_event_callbacks_t cbs = {
.on_reach = test_pcnt_quadrature_reach_watch_point,
};
test_pcnt_quadrature_context_t user_data = {
.index = 0,
.triggered_watch_values = {}
};
TEST_ESP_OK(pcnt_unit_register_event_callbacks(unit, &cbs, &user_data));
printf("add watchpoints\r\n");
TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 0));
TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 100));
TEST_ESP_OK(pcnt_unit_add_watch_point(unit, -100));
TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 50));
TEST_ESP_OK(pcnt_unit_add_watch_point(unit, -50));
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, pcnt_unit_add_watch_point(unit, 33));
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, pcnt_unit_add_watch_point(unit, 50));
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, pcnt_unit_add_watch_point(unit, 100));
// Clear internal counter, and make the watch points take effect
TEST_ESP_OK(pcnt_unit_clear_count(unit));
TEST_ESP_OK(pcnt_unit_start(unit));
printf("simulating quadrature signals\r\n");
test_gpio_simulate_quadrature_signals(TEST_PCNT_GPIO_A, TEST_PCNT_GPIO_B, 30);
// simply wait for done
vTaskDelay(pdMS_TO_TICKS(100));
int count_value;
printf("checking count value\r\n");
TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value));
printf("count_value=%d\r\n", count_value);
TEST_ASSERT_EQUAL(-20, count_value); // 0-30*4+100
TEST_ASSERT_EQUAL(3, user_data.index);
TEST_ASSERT_EQUAL(-50, user_data.triggered_watch_values[0]);
TEST_ASSERT_EQUAL(-100, user_data.triggered_watch_values[1]);
TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[2]);
printf("simulating quadrature signals in another direction\r\n");
user_data.index = 0;
test_gpio_simulate_quadrature_signals(TEST_PCNT_GPIO_B, TEST_PCNT_GPIO_A, 40);
// simply wait for done
vTaskDelay(pdMS_TO_TICKS(100));
printf("checking count value\r\n");
TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value));
printf("count_value=%d\r\n", count_value);
TEST_ASSERT_EQUAL(40, count_value); // -20+40*4-100
TEST_ASSERT_EQUAL(4, user_data.index);
TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[0]);
TEST_ASSERT_EQUAL(50, user_data.triggered_watch_values[1]);
TEST_ASSERT_EQUAL(100, user_data.triggered_watch_values[2]);
TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[3]);
printf("remove watchpoints and uninstall channels\r\n");
TEST_ESP_OK(pcnt_unit_remove_watch_point(unit, 0));
TEST_ESP_OK(pcnt_unit_remove_watch_point(unit, 100));
TEST_ESP_OK(pcnt_unit_remove_watch_point(unit, -100));
TEST_ESP_OK(pcnt_unit_remove_watch_point(unit, 50));
TEST_ESP_OK(pcnt_unit_remove_watch_point(unit, -50));
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, pcnt_unit_remove_watch_point(unit, 50));
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, pcnt_unit_remove_watch_point(unit, 33));
TEST_ESP_OK(pcnt_del_channel(channelA));
TEST_ESP_OK(pcnt_del_channel(channelB));
TEST_ESP_OK(pcnt_unit_stop(unit));
TEST_ESP_OK(pcnt_del_unit(unit));
}
typedef struct {
pcnt_unit_zero_cross_mode_t mode;
} test_pcnt_zero_cross_context_t;
TEST_PCNT_CALLBACK_ATTR
static bool test_pcnt_on_zero_cross(pcnt_unit_handle_t handle, pcnt_watch_event_data_t *event_data, void *user_data)
{
test_pcnt_zero_cross_context_t *user_ctx = (test_pcnt_zero_cross_context_t *)user_data;
user_ctx->mode = event_data->zero_cross_mode;
return false;
}
TEST_CASE("pcnt_zero_cross_mode", "[pcnt]")
{
pcnt_unit_config_t unit_config = {
.low_limit = -100,
.high_limit = 100
};
printf("install pcnt unit\r\n");
pcnt_unit_handle_t unit = NULL;
TEST_ESP_OK(pcnt_new_unit(&unit_config, &unit));
printf("add watchpoint to detect zero cross\r\n");
TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 0));
printf("register callback for zero cross event\r\n");
pcnt_event_callbacks_t cbs = {
.on_reach = test_pcnt_on_zero_cross,
};
test_pcnt_zero_cross_context_t user_data = {};
TEST_ESP_OK(pcnt_unit_register_event_callbacks(unit, &cbs, &user_data));
printf("install pcnt channels\r\n");
pcnt_chan_config_t channel_config = {
.edge_gpio_num = TEST_PCNT_GPIO_A,
.level_gpio_num = -1,
.flags.io_loop_back = true,
};
pcnt_channel_handle_t channelA = NULL;
pcnt_channel_handle_t channelB = NULL;
TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelA));
TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelB));
printf("Initialize pcnt actions for channels\r\n");
// only channel will increase the counter, 0->1
TEST_ESP_OK(pcnt_channel_set_edge_action(channelA, PCNT_CHANNEL_EDGE_ACTION_HOLD, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_level_action(channelA, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
TEST_ESP_OK(pcnt_channel_set_edge_action(channelB, PCNT_CHANNEL_EDGE_ACTION_HOLD, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_level_action(channelB, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
printf("start unit\r\n");
TEST_ESP_OK(pcnt_unit_start(unit));
int count_value = 0;
printf("counter goes 0->1\r\n");
TEST_ESP_OK(pcnt_channel_set_edge_action(channelA, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 1);
TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value));
TEST_ASSERT_EQUAL(1, count_value);
printf("counter goes 1->-1\r\n");
TEST_ESP_OK(pcnt_channel_set_edge_action(channelA, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_edge_action(channelB, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 1);
TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value));
TEST_ASSERT_EQUAL(-1, count_value);
TEST_ASSERT_EQUAL(PCNT_UNIT_ZERO_CROSS_POS_NEG, user_data.mode);
printf("counter goes -1->1\r\n");
TEST_ESP_OK(pcnt_channel_set_edge_action(channelA, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_edge_action(channelB, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 1);
TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value));
TEST_ASSERT_EQUAL(1, count_value);
TEST_ASSERT_EQUAL(PCNT_UNIT_ZERO_CROSS_NEG_POS, user_data.mode);
printf("counter goes 1->0->-1\r\n");
TEST_ESP_OK(pcnt_channel_set_edge_action(channelA, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_edge_action(channelB, PCNT_CHANNEL_EDGE_ACTION_HOLD, PCNT_CHANNEL_EDGE_ACTION_HOLD));
test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 2);
TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value));
TEST_ASSERT_EQUAL(-1, count_value);
TEST_ASSERT_EQUAL(PCNT_UNIT_ZERO_CROSS_POS_ZERO, user_data.mode);
printf("counter goes -1->0->1\r\n");
TEST_ESP_OK(pcnt_channel_set_edge_action(channelA, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_edge_action(channelB, PCNT_CHANNEL_EDGE_ACTION_HOLD, PCNT_CHANNEL_EDGE_ACTION_HOLD));
test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 2);
TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value));
TEST_ASSERT_EQUAL(1, count_value);
TEST_ASSERT_EQUAL(PCNT_UNIT_ZERO_CROSS_NEG_ZERO, user_data.mode);
TEST_ESP_OK(pcnt_unit_stop(unit));
TEST_ESP_OK(pcnt_unit_remove_watch_point(unit, 0));
TEST_ESP_OK(pcnt_del_channel(channelA));
TEST_ESP_OK(pcnt_del_channel(channelB));
TEST_ESP_OK(pcnt_del_unit(unit));
}
void test_app_include_pulse_cnt(void)
{
}

View File

@ -0,0 +1,23 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'iram_safe',
'release',
],
indirect=True,
)
def test_gptimer(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('*')
dut.expect_unity_test_output()

View File

@ -0,0 +1,6 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_PCNT_CTRL_FUNC_IN_IRAM=y
CONFIG_PCNT_ISR_IRAM_SAFE=y
# silent the error check, as the error string are stored in rodata, causing RTL check failure
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y

View File

@ -0,0 +1,5 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@ -0,0 +1,2 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n

View File

@ -1,16 +1,8 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
@ -22,6 +14,7 @@
#pragma once
#include <limits.h>
#include <stdlib.h>
#include <stdbool.h>
#include "soc/pcnt_struct.h"
@ -31,19 +24,23 @@
extern "C" {
#endif
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
#define PCNT_LL_MAX_GLITCH_WIDTH 1023
#define PCNT_LL_MAX_LIM SHRT_MAX
#define PCNT_LL_MIN_LIN SHRT_MIN
typedef enum {
PCNT_LL_EVENT_THRES1,
PCNT_LL_EVENT_THRES0,
PCNT_LL_EVENT_LOW_LIMIT,
PCNT_LL_EVENT_HIGH_LIMIT,
PCNT_LL_EVENT_ZERO_CROSS,
PCNT_LL_EVENT_MAX
} pcnt_ll_event_id_t;
PCNT_LL_WATCH_EVENT_INVALID = -1,
PCNT_LL_WATCH_EVENT_THRES1,
PCNT_LL_WATCH_EVENT_THRES0,
PCNT_LL_WATCH_EVENT_LOW_LIMIT,
PCNT_LL_WATCH_EVENT_HIGH_LIMIT,
PCNT_LL_WATCH_EVENT_ZERO_CROSS,
PCNT_LL_WATCH_EVENT_MAX
} pcnt_ll_watch_event_id_t;
#define PCNT_LL_EVENT_MASK ((1 << PCNT_LL_EVENT_MAX) - 1)
#define PCNT_LL_WATCH_EVENT_MASK ((1 << PCNT_LL_WATCH_EVENT_MAX) - 1)
#define PCNT_LL_UNIT_WATCH_EVENT(unit_id) (1 << (unit_id))
/**
* @brief Set PCNT channel edge action
@ -156,7 +153,8 @@ static inline void pcnt_ll_enable_intr(pcnt_dev_t *hw, uint32_t unit_mask, bool
* @param hw Peripheral PCNT hardware instance address.
* @return Interrupt status word
*/
__attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
{
return hw->int_st.val;
}
@ -167,7 +165,8 @@ __attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pc
* @param hw Peripheral PCNT hardware instance address.
* @param status value to clear interrupt status
*/
__attribute__((always_inline)) static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
__attribute__((always_inline))
static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
{
hw->int_clr.val = status;
}
@ -233,7 +232,7 @@ static inline void pcnt_ll_enable_thres_event(pcnt_dev_t *hw, uint32_t unit, uin
*/
static inline void pcnt_ll_disable_all_events(pcnt_dev_t *hw, uint32_t unit)
{
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_EVENT_MASK << 11);
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_WATCH_EVENT_MASK << 11);
}
/**
@ -344,13 +343,14 @@ static inline uint32_t pcnt_ll_get_unit_status(pcnt_dev_t *hw, uint32_t unit)
}
/**
* @brief Get PCNT count sign
* @brief Get PCNT zero cross mode
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @return Count sign
* @return Zero cross mode
*/
static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint32_t unit)
__attribute__((always_inline))
static inline pcnt_unit_zero_cross_mode_t pcnt_ll_get_zero_cross_mode(pcnt_dev_t *hw, uint32_t unit)
{
return hw->status_unit[unit].val & 0x03;
}
@ -362,6 +362,7 @@ static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint
* @param unit PCNT unit number
* @return Event status word
*/
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit)
{
return hw->status_unit[unit].val >> 2;
@ -404,6 +405,18 @@ static inline void pcnt_ll_enable_glitch_filter(pcnt_dev_t *hw, uint32_t unit, b
hw->conf_unit[unit].conf0.filter_en = enable;
}
/**
* @brief Get interrupt status register address.
*
* @param hw Beginning address of the peripheral registers.
*
* @return Interrupt status register address
*/
static inline volatile void *pcnt_ll_get_intr_status_reg(pcnt_dev_t *hw)
{
return &hw->int_st.val;
}
#ifdef __cplusplus
}
#endif

View File

@ -1,16 +1,8 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
@ -22,6 +14,7 @@
#pragma once
#include <limits.h>
#include <stdlib.h>
#include <stdbool.h>
#include "soc/pcnt_struct.h"
@ -31,19 +24,23 @@
extern "C" {
#endif
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
#define PCNT_LL_MAX_GLITCH_WIDTH 1023
#define PCNT_LL_MAX_LIM SHRT_MAX
#define PCNT_LL_MIN_LIN SHRT_MIN
typedef enum {
PCNT_LL_EVENT_THRES1,
PCNT_LL_EVENT_THRES0,
PCNT_LL_EVENT_LOW_LIMIT,
PCNT_LL_EVENT_HIGH_LIMIT,
PCNT_LL_EVENT_ZERO_CROSS,
PCNT_LL_EVENT_MAX
} pcnt_ll_event_id_t;
PCNT_LL_WATCH_EVENT_INVALID = -1,
PCNT_LL_WATCH_EVENT_THRES1,
PCNT_LL_WATCH_EVENT_THRES0,
PCNT_LL_WATCH_EVENT_LOW_LIMIT,
PCNT_LL_WATCH_EVENT_HIGH_LIMIT,
PCNT_LL_WATCH_EVENT_ZERO_CROSS,
PCNT_LL_WATCH_EVENT_MAX
} pcnt_ll_watch_event_id_t;
#define PCNT_LL_EVENT_MASK ((1 << PCNT_LL_EVENT_MAX) - 1)
#define PCNT_LL_WATCH_EVENT_MASK ((1 << PCNT_LL_WATCH_EVENT_MAX) - 1)
#define PCNT_LL_UNIT_WATCH_EVENT(unit_id) (1 << (unit_id))
/**
* @brief Set PCNT channel edge action
@ -156,7 +153,8 @@ static inline void pcnt_ll_enable_intr(pcnt_dev_t *hw, uint32_t unit_mask, bool
* @param hw Peripheral PCNT hardware instance address.
* @return Interrupt status word
*/
__attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
{
return hw->int_st.val;
}
@ -167,7 +165,8 @@ __attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pc
* @param hw Peripheral PCNT hardware instance address.
* @param status value to clear interrupt status
*/
__attribute__((always_inline)) static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
__attribute__((always_inline))
static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
{
hw->int_clr.val = status;
}
@ -233,7 +232,7 @@ static inline void pcnt_ll_enable_thres_event(pcnt_dev_t *hw, uint32_t unit, uin
*/
static inline void pcnt_ll_disable_all_events(pcnt_dev_t *hw, uint32_t unit)
{
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_EVENT_MASK << 11);
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_WATCH_EVENT_MASK << 11);
}
/**
@ -344,13 +343,14 @@ static inline uint32_t pcnt_ll_get_unit_status(pcnt_dev_t *hw, uint32_t unit)
}
/**
* @brief Get PCNT count sign
* @brief Get PCNT zero cross mode
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @return Count sign
* @return Zero cross mode
*/
static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint32_t unit)
__attribute__((always_inline))
static inline pcnt_unit_zero_cross_mode_t pcnt_ll_get_zero_cross_mode(pcnt_dev_t *hw, uint32_t unit)
{
return hw->status_unit[unit].val & 0x03;
}
@ -362,6 +362,7 @@ static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint
* @param unit PCNT unit number
* @return Event status word
*/
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit)
{
return hw->status_unit[unit].val >> 2;
@ -404,6 +405,18 @@ static inline void pcnt_ll_enable_glitch_filter(pcnt_dev_t *hw, uint32_t unit, b
hw->conf_unit[unit].conf0.filter_en_un = enable;
}
/**
* @brief Get interrupt status register address.
*
* @param hw Beginning address of the peripheral registers.
*
* @return Interrupt status register address
*/
static inline volatile void *pcnt_ll_get_intr_status_reg(pcnt_dev_t *hw)
{
return &hw->int_st.val;
}
#ifdef __cplusplus
}
#endif

View File

@ -1,16 +1,8 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
@ -22,6 +14,7 @@
#pragma once
#include <limits.h>
#include <stdlib.h>
#include <stdbool.h>
#include "soc/pcnt_struct.h"
@ -31,19 +24,23 @@
extern "C" {
#endif
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
#define PCNT_LL_MAX_GLITCH_WIDTH 1023
#define PCNT_LL_MAX_LIM SHRT_MAX
#define PCNT_LL_MIN_LIN SHRT_MIN
typedef enum {
PCNT_LL_EVENT_THRES1,
PCNT_LL_EVENT_THRES0,
PCNT_LL_EVENT_LOW_LIMIT,
PCNT_LL_EVENT_HIGH_LIMIT,
PCNT_LL_EVENT_ZERO_CROSS,
PCNT_LL_EVENT_MAX
} pcnt_ll_event_id_t;
PCNT_LL_WATCH_EVENT_INVALID = -1,
PCNT_LL_WATCH_EVENT_THRES1,
PCNT_LL_WATCH_EVENT_THRES0,
PCNT_LL_WATCH_EVENT_LOW_LIMIT,
PCNT_LL_WATCH_EVENT_HIGH_LIMIT,
PCNT_LL_WATCH_EVENT_ZERO_CROSS,
PCNT_LL_WATCH_EVENT_MAX
} pcnt_ll_watch_event_id_t;
#define PCNT_LL_EVENT_MASK ((1 << PCNT_LL_EVENT_MAX) - 1)
#define PCNT_LL_WATCH_EVENT_MASK ((1 << PCNT_LL_WATCH_EVENT_MAX) - 1)
#define PCNT_LL_UNIT_WATCH_EVENT(unit_id) (1 << (unit_id))
/**
* @brief Set PCNT channel edge action
@ -156,7 +153,8 @@ static inline void pcnt_ll_enable_intr(pcnt_dev_t *hw, uint32_t unit_mask, bool
* @param hw Peripheral PCNT hardware instance address.
* @return Interrupt status word
*/
__attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
{
return hw->int_st.val;
}
@ -167,7 +165,8 @@ __attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pc
* @param hw Peripheral PCNT hardware instance address.
* @param status value to clear interrupt status
*/
__attribute__((always_inline)) static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
__attribute__((always_inline))
static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
{
hw->int_clr.val = status;
}
@ -233,7 +232,7 @@ static inline void pcnt_ll_enable_thres_event(pcnt_dev_t *hw, uint32_t unit, uin
*/
static inline void pcnt_ll_disable_all_events(pcnt_dev_t *hw, uint32_t unit)
{
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_EVENT_MASK << 11);
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_WATCH_EVENT_MASK << 11);
}
/**
@ -344,13 +343,14 @@ static inline uint32_t pcnt_ll_get_unit_status(pcnt_dev_t *hw, uint32_t unit)
}
/**
* @brief Get PCNT count sign
* @brief Get PCNT zero cross mode
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @return Count sign
* @return Zero cross mode
*/
static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint32_t unit)
__attribute__((always_inline))
static inline pcnt_unit_zero_cross_mode_t pcnt_ll_get_zero_cross_mode(pcnt_dev_t *hw, uint32_t unit)
{
return hw->status_unit[unit].val & 0x03;
}
@ -362,6 +362,7 @@ static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint
* @param unit PCNT unit number
* @return Event status word
*/
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit)
{
return hw->status_unit[unit].val >> 2;
@ -404,6 +405,18 @@ static inline void pcnt_ll_enable_glitch_filter(pcnt_dev_t *hw, uint32_t unit, b
hw->conf_unit[unit].conf0.filter_en_un = enable;
}
/**
* @brief Get interrupt status register address.
*
* @param hw Beginning address of the peripheral registers.
*
* @return Interrupt status register address
*/
static inline volatile void *pcnt_ll_get_intr_status_reg(pcnt_dev_t *hw)
{
return &hw->int_st.val;
}
#ifdef __cplusplus
}
#endif

View File

@ -1,16 +1,8 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
@ -18,22 +10,19 @@
* See readme.md in hal/include/hal/readme.md
******************************************************************************/
// The HAL layer for PCNT.
// There is no parameter check in the hal layer, so the caller must ensure the correctness of the parameters.
#pragma once
#include "soc/pcnt_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct pcnt_dev_t *pcnt_soc_handle_t; // PCNT SOC layer handle
/**
* Context that should be maintained by both the driver and the HAL
*/
typedef struct {
pcnt_dev_t *dev; /*!< PCNT peripheral register base address */
pcnt_soc_handle_t dev; // PCNT SOC layer handle
} pcnt_hal_context_t;
/**

View File

@ -1,16 +1,8 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@ -20,7 +12,6 @@ extern "C" {
/**
* @brief PCNT channel action on control level
*
*/
typedef enum {
PCNT_CHANNEL_LEVEL_ACTION_KEEP, /*!< Keep current count mode */
@ -30,7 +21,6 @@ typedef enum {
/**
* @brief PCNT channel action on signal edge
*
*/
typedef enum {
PCNT_CHANNEL_EDGE_ACTION_HOLD, /*!< Hold current count value */
@ -39,15 +29,14 @@ typedef enum {
} pcnt_channel_edge_action_t;
/**
* @brief PCNT unit counter value's sign
*
* @brief PCNT unit zero cross mode
*/
typedef enum {
PCNT_UNIT_COUNT_SIGN_ZERO_POS, /*!< positive value to zero */
PCNT_UNIT_COUNT_SIGN_ZERO_NEG, /*!< negative value to zero */
PCNT_UNIT_COUNT_SIGN_NEG, /*!< counter value negative */
PCNT_UNIT_COUNT_SIGN_POS, /*!< counter value positive */
} pcnt_unit_count_sign_t;
PCNT_UNIT_ZERO_CROSS_POS_ZERO, /*!< start from positive value, end to zero, i.e. +N->0 */
PCNT_UNIT_ZERO_CROSS_NEG_ZERO, /*!< start from negative value, end to zero, i.e. -N->0 */
PCNT_UNIT_ZERO_CROSS_NEG_POS, /*!< start from negative value, end to positive value, i.e. -N->+M */
PCNT_UNIT_ZERO_CROSS_POS_NEG, /*!< start from positive value, end to negative value, i.e. +N->-M */
} pcnt_unit_zero_cross_mode_t;
#ifdef __cplusplus
}

View File

@ -1,16 +1,8 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The HAL layer for PCNT (common part)

View File

@ -1,21 +1,13 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
// 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 _SOC_PCNT_REG_H_
#define _SOC_PCNT_REG_H_
#include <stdint.h>
#include "soc/soc.h"
#include "soc.h"
#define PCNT_U0_CONF0_REG (DR_REG_PCNT_BASE + 0x0000)
/* PCNT_CH1_LCTRL_MODE_U0 : R/W ;bitpos:[31:30] ;default: 2'd0 ; */
/*description: This register is used to control the mode of channel1's low control
@ -1517,8 +1509,3 @@
#define PCNT_DATE_M ((PCNT_DATE_V)<<(PCNT_DATE_S))
#define PCNT_DATE_V 0xFFFFFFFF
#define PCNT_DATE_S 0
#endif /*_SOC_PCNT_REG_H_ */

View File

@ -1,18 +1,9 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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 _SOC_PCNT_STRUCT_H_
#define _SOC_PCNT_STRUCT_H_
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
@ -20,8 +11,8 @@
extern "C" {
#endif
typedef volatile struct pcnt_dev_s {
struct {
typedef struct pcnt_dev_t {
volatile struct {
union {
struct {
uint32_t filter_thres: 10; /*This register is used to filter pulse whose width is smaller than this value for unit0.*/
@ -44,27 +35,27 @@ typedef volatile struct pcnt_dev_s {
} conf0;
union {
struct {
uint32_t cnt_thres0:16; /*This register is used to configure thres0 value for unit0.*/
uint32_t cnt_thres1:16; /*This register is used to configure thres1 value for unit0.*/
uint32_t cnt_thres0: 16; /*This register is used to configure thres0 value for unit0.*/
uint32_t cnt_thres1: 16; /*This register is used to configure thres1 value for unit0.*/
};
uint32_t val;
} conf1;
union {
struct {
uint32_t cnt_h_lim:16; /*This register is used to configure thr_h_lim value for unit0.*/
uint32_t cnt_l_lim:16; /*This register is used to configure thr_l_lim value for unit0.*/
uint32_t cnt_h_lim: 16; /*This register is used to configure thr_h_lim value for unit0.*/
uint32_t cnt_l_lim: 16; /*This register is used to configure thr_l_lim value for unit0.*/
};
uint32_t val;
} conf2;
} conf_unit[8];
union {
volatile union {
struct {
uint32_t cnt_val : 16; /*This register stores the current pulse count value for unit0.*/
uint32_t reserved16: 16;
};
uint32_t val;
} cnt_unit[8];
union {
volatile union {
struct {
uint32_t cnt_thr_event_u0: 1; /*This is the interrupt raw bit for channel0 event.*/
uint32_t cnt_thr_event_u1: 1; /*This is the interrupt raw bit for channel1 event.*/
@ -78,7 +69,7 @@ typedef volatile struct pcnt_dev_s {
};
uint32_t val;
} int_raw;
union {
volatile union {
struct {
uint32_t cnt_thr_event_u0: 1; /*This is the interrupt status bit for channel0 event.*/
uint32_t cnt_thr_event_u1: 1; /*This is the interrupt status bit for channel1 event.*/
@ -92,7 +83,7 @@ typedef volatile struct pcnt_dev_s {
};
uint32_t val;
} int_st;
union {
volatile union {
struct {
uint32_t cnt_thr_event_u0: 1; /*This is the interrupt enable bit for channel0 event.*/
uint32_t cnt_thr_event_u1: 1; /*This is the interrupt enable bit for channel1 event.*/
@ -106,7 +97,7 @@ typedef volatile struct pcnt_dev_s {
};
uint32_t val;
} int_ena;
union {
volatile union {
struct {
uint32_t cnt_thr_event_u0: 1; /*Set this bit to clear channel0 event interrupt.*/
uint32_t cnt_thr_event_u1: 1; /*Set this bit to clear channel1 event interrupt.*/
@ -120,19 +111,19 @@ typedef volatile struct pcnt_dev_s {
};
uint32_t val;
} int_clr;
union {
volatile union {
struct {
uint32_t cnt_mode:2; /*0: positive value to zero; 1: negative value to zero; 2: counter value negative ; 3: counter value positive*/
uint32_t thres1_lat:1; /* counter value equals to thresh1*/
uint32_t thres0_lat:1; /* counter value equals to thresh0*/
uint32_t l_lim_lat:1; /* counter value reaches h_lim*/
uint32_t h_lim_lat:1; /* counter value reaches l_lim*/
uint32_t zero_lat:1; /* counter value equals zero*/
uint32_t reserved7:25;
uint32_t cnt_mode: 2; /*0: positive value to zero; 1: negative value to zero; 2: counter value negative ; 3: counter value positive*/
uint32_t thres1_lat: 1; /* counter value equals to thresh1*/
uint32_t thres0_lat: 1; /* counter value equals to thresh0*/
uint32_t l_lim_lat: 1; /* counter value reaches h_lim*/
uint32_t h_lim_lat: 1; /* counter value reaches l_lim*/
uint32_t zero_lat: 1; /* counter value equals zero*/
uint32_t reserved7: 25;
};
uint32_t val;
} status_unit[8];
union {
volatile union {
struct {
uint32_t cnt_rst_u0: 1; /*Set this bit to clear unit0's counter.*/
uint32_t cnt_pause_u0: 1; /*Set this bit to pause unit0's counter.*/
@ -173,12 +164,11 @@ typedef volatile struct pcnt_dev_s {
uint32_t reserved_f0;
uint32_t reserved_f4;
uint32_t reserved_f8;
uint32_t date; /**/
volatile uint32_t date;
} pcnt_dev_t;
extern pcnt_dev_t PCNT;
#ifdef __cplusplus
}
#endif
#endif /* _SOC_PCNT_STRUCT_H_ */

View File

@ -1,16 +1,8 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/pcnt_periph.h"
#include "soc/gpio_sig_map.h"

View File

@ -1,16 +1,7 @@
/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed 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.
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

View File

@ -1,16 +1,7 @@
/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed 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.
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@ -388,7 +379,7 @@ typedef union {
} pcnt_date_reg_t;
typedef struct {
typedef struct pcnt_dev_t {
volatile struct {
pcnt_un_conf0_reg_t conf0;
pcnt_un_conf1_reg_t conf1;

View File

@ -1,16 +1,8 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/pcnt_periph.h"
#include "soc/gpio_sig_map.h"

View File

@ -1,26 +0,0 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define SOC_PCNT_PORT_NUM (1)
#define SOC_PCNT_UNIT_NUM (4)
#ifdef __cplusplus
}
#endif

View File

@ -1,16 +1,7 @@
/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed 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.
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

View File

@ -1,16 +1,7 @@
/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed 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.
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@ -388,7 +379,7 @@ typedef union {
} pcnt_date_reg_t;
typedef struct {
typedef struct pcnt_dev_t {
volatile struct {
pcnt_un_conf0_reg_t conf0;
pcnt_un_conf1_reg_t conf1;

View File

@ -1,16 +1,8 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/pcnt_periph.h"
#include "soc/gpio_sig_map.h"

View File

@ -1,16 +1,8 @@
// Copyright 2019-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

View File

@ -818,7 +818,6 @@ components/hal/esp32/include/hal/dac_ll.h
components/hal/esp32/include/hal/i2c_ll.h
components/hal/esp32/include/hal/interrupt_controller_ll.h
components/hal/esp32/include/hal/mpu_ll.h
components/hal/esp32/include/hal/pcnt_ll.h
components/hal/esp32/include/hal/rtc_cntl_ll.h
components/hal/esp32/include/hal/rtc_io_ll.h
components/hal/esp32/include/hal/rwdt_ll.h
@ -901,7 +900,6 @@ components/hal/esp32s2/include/hal/interrupt_controller_ll.h
components/hal/esp32s2/include/hal/memprot_ll.h
components/hal/esp32s2/include/hal/memprot_peri_ll.h
components/hal/esp32s2/include/hal/mpu_ll.h
components/hal/esp32s2/include/hal/pcnt_ll.h
components/hal/esp32s2/include/hal/rtc_cntl_ll.h
components/hal/esp32s2/include/hal/rtc_io_ll.h
components/hal/esp32s2/include/hal/sha_ll.h
@ -927,7 +925,6 @@ components/hal/esp32s3/include/hal/i2c_ll.h
components/hal/esp32s3/include/hal/interrupt_controller_ll.h
components/hal/esp32s3/include/hal/memprot_ll.h
components/hal/esp32s3/include/hal/mpu_ll.h
components/hal/esp32s3/include/hal/pcnt_ll.h
components/hal/esp32s3/include/hal/rtc_cntl_ll.h
components/hal/esp32s3/include/hal/rwdt_ll.h
components/hal/esp32s3/include/hal/sha_ll.h
@ -958,8 +955,6 @@ components/hal/include/hal/mcpwm_hal.h
components/hal/include/hal/mcpwm_types.h
components/hal/include/hal/mpu_hal.h
components/hal/include/hal/mpu_types.h
components/hal/include/hal/pcnt_hal.h
components/hal/include/hal/pcnt_types.h
components/hal/include/hal/rmt_types.h
components/hal/include/hal/rtc_io_types.h
components/hal/include/hal/sdio_slave_hal.h
@ -987,7 +982,6 @@ components/hal/interrupt_controller_hal.c
components/hal/ledc_hal_iram.c
components/hal/mcpwm_hal.c
components/hal/mpu_hal.c
components/hal/pcnt_hal.c
components/hal/platform_port/include/hal/assert.h
components/hal/platform_port/include/hal/check.h
components/hal/platform_port/include/hal/log.h
@ -1315,8 +1309,6 @@ components/soc/esp32/include/soc/ledc_reg.h
components/soc/esp32/include/soc/ledc_struct.h
components/soc/esp32/include/soc/mmu.h
components/soc/esp32/include/soc/nrx_reg.h
components/soc/esp32/include/soc/pcnt_reg.h
components/soc/esp32/include/soc/pcnt_struct.h
components/soc/esp32/include/soc/pid.h
components/soc/esp32/include/soc/reset_reasons.h
components/soc/esp32/include/soc/rmt_reg.h
@ -1352,7 +1344,6 @@ components/soc/esp32/include/soc/wdev_reg.h
components/soc/esp32/interrupts.c
components/soc/esp32/ledc_periph.c
components/soc/esp32/mcpwm_periph.c
components/soc/esp32/pcnt_periph.c
components/soc/esp32/rmt_periph.c
components/soc/esp32/sdio_slave_periph.c
components/soc/esp32/sdmmc_periph.c
@ -1511,8 +1502,6 @@ components/soc/esp32s2/include/soc/ledc_struct.h
components/soc/esp32s2/include/soc/memprot_defs.h
components/soc/esp32s2/include/soc/mmu.h
components/soc/esp32s2/include/soc/nrx_reg.h
components/soc/esp32s2/include/soc/pcnt_reg.h
components/soc/esp32s2/include/soc/pcnt_struct.h
components/soc/esp32s2/include/soc/reset_reasons.h
components/soc/esp32s2/include/soc/rtc_cntl_reg.h
components/soc/esp32s2/include/soc/rtc_cntl_struct.h
@ -1556,7 +1545,6 @@ components/soc/esp32s2/include/soc/usbh_struct.h
components/soc/esp32s2/include/soc/wdev_reg.h
components/soc/esp32s2/interrupts.c
components/soc/esp32s2/ledc_periph.c
components/soc/esp32s2/pcnt_periph.c
components/soc/esp32s2/rmt_periph.c
components/soc/esp32s2/sigmadelta_periph.c
components/soc/esp32s2/spi_periph.c
@ -1605,9 +1593,6 @@ components/soc/esp32s3/include/soc/ledc_struct.h
components/soc/esp32s3/include/soc/mmu.h
components/soc/esp32s3/include/soc/mpu_caps.h
components/soc/esp32s3/include/soc/nrx_reg.h
components/soc/esp32s3/include/soc/pcnt_caps.h
components/soc/esp32s3/include/soc/pcnt_reg.h
components/soc/esp32s3/include/soc/pcnt_struct.h
components/soc/esp32s3/include/soc/peri_backup_reg.h
components/soc/esp32s3/include/soc/peri_backup_struct.h
components/soc/esp32s3/include/soc/reset_reasons.h
@ -1663,7 +1648,6 @@ components/soc/esp32s3/include/soc/world_controller_struct.h
components/soc/esp32s3/interrupts.c
components/soc/esp32s3/ledc_periph.c
components/soc/esp32s3/mcpwm_periph.c
components/soc/esp32s3/pcnt_periph.c
components/soc/esp32s3/rmt_periph.c
components/soc/esp32s3/rtc_io_periph.c
components/soc/esp32s3/sdio_slave_periph.c
@ -1683,7 +1667,6 @@ components/soc/include/soc/i2c_periph.h
components/soc/include/soc/interrupts.h
components/soc/include/soc/ledc_periph.h
components/soc/include/soc/mcpwm_periph.h
components/soc/include/soc/pcnt_periph.h
components/soc/include/soc/rmt_periph.h
components/soc/include/soc/rtc_cntl_periph.h
components/soc/include/soc/rtc_periph.h
@ -2223,10 +2206,6 @@ examples/peripherals/mcpwm/mcpwm_brushed_dc_control/main/mcpwm_brushed_dc_contro
examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/main/mcpwm_capture_hc_sr04.c
examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example_main.c
examples/peripherals/mcpwm/mcpwm_sync_example/main/mcpwm_sync_example.c
examples/peripherals/pcnt/pulse_count_event/main/pcnt_event_example_main.c
examples/peripherals/pcnt/rotary_encoder/components/rotary_encoder/include/rotary_encoder.h
examples/peripherals/pcnt/rotary_encoder/components/rotary_encoder/src/rotary_encoder_pcnt_ec11.c
examples/peripherals/pcnt/rotary_encoder/main/rotary_encoder_example_main.c
examples/peripherals/rmt/ir_protocols/components/infrared_tools/include/ir_timings.h
examples/peripherals/rmt/ir_protocols/components/infrared_tools/src/ir_builder_rmt_nec.c
examples/peripherals/rmt/ir_protocols/components/infrared_tools/src/ir_builder_rmt_rc5.c