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() endif()
if(CONFIG_SOC_PCNT_SUPPORTED) if(CONFIG_SOC_PCNT_SUPPORTED)
list(APPEND srcs "pcnt.c") list(APPEND srcs "pcnt_legacy.c" "pulse_cnt.c")
endif() endif()
if(CONFIG_SOC_SDMMC_HOST_SUPPORTED) if(CONFIG_SOC_SDMMC_HOST_SUPPORTED)

View File

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

View File

@@ -4,10 +4,14 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
// #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG // uncomment this line to enable debug logs
#include <stdlib.h> #include <stdlib.h>
#include <sys/lock.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 "freertos/FreeRTOS.h"
#include "esp_attr.h" #include "esp_attr.h"
#include "esp_err.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 gptimer_group_t *gptimer_acquire_group_handle(int group_id);
static void gptimer_release_group_handle(gptimer_group_t *group); 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); 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) 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_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); portENTER_CRITICAL_SAFE(&timer->spinlock);
timer_hal_set_counter_value(&timer->hal, value); 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_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); portENTER_CRITICAL_SAFE(&timer->spinlock);
*value = timer_ll_get_counter_value(timer->hal.dev, timer->timer_id); *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 // 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 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->on_alarm = cbs->on_alarm;
timer->user_ctx = user_data; 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_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) { if (config) {
// When auto_reload is enabled, alarm_count should not be equal to reload_count // 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; 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->reload_count = config->reload_count;
timer->alarm_count = config->alarm_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_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 // acquire power manager lock
if (timer->pm_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 // interrupt interupt service
if (timer->intr) { 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); 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_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 // disable counter, alarm, autoreload
portENTER_CRITICAL_SAFE(&timer->spinlock); portENTER_CRITICAL_SAFE(&timer->spinlock);
@@ -325,11 +329,11 @@ esp_err_t gptimer_stop(gptimer_handle_t timer)
// disable interrupt service // disable interrupt service
if (timer->intr) { 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 // release power manager lock
if (timer->pm_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; 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) 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; bool new_group = false;
gptimer_group_t *group = NULL; 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). * the legacy timer group driver (deprecated/driver/timer.h) and the new gptimer driver (driver/gptimer.h).
*/ */
int timer_group_driver_init_count = 0; 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_stop (noflash)
gdma: gdma_append (noflash) gdma: gdma_append (noflash)
gdma: gdma_reset (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.c"
"test_gptimer_iram.c") "test_gptimer_iram.c")
idf_component_register(SRCS ${srcs} idf_component_register(SRCS ${srcs})
PRIV_REQUIRES driver unity spi_flash)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u test_app_include_gptimer" "-u test_app_include_gptimer_iram") 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', 'release',
], indirect=True) ], indirect=True)
def test_gptimer(dut: Dut) -> None: 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.write('*')
dut.expect_unity_test_output() 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 /*
// * 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. * SPDX-License-Identifier: Apache-2.0
// 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.
/******************************************************************************* /*******************************************************************************
* NOTICE * NOTICE
@@ -22,6 +14,7 @@
#pragma once #pragma once
#include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include "soc/pcnt_struct.h" #include "soc/pcnt_struct.h"
@@ -33,17 +26,21 @@ extern "C" {
#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_GLITCH_WIDTH 1023
#define PCNT_LL_MAX_LIM SHRT_MAX
#define PCNT_LL_MIN_LIN SHRT_MIN
typedef enum { typedef enum {
PCNT_LL_EVENT_THRES1, PCNT_LL_WATCH_EVENT_INVALID = -1,
PCNT_LL_EVENT_THRES0, PCNT_LL_WATCH_EVENT_THRES1,
PCNT_LL_EVENT_LOW_LIMIT, PCNT_LL_WATCH_EVENT_THRES0,
PCNT_LL_EVENT_HIGH_LIMIT, PCNT_LL_WATCH_EVENT_LOW_LIMIT,
PCNT_LL_EVENT_ZERO_CROSS, PCNT_LL_WATCH_EVENT_HIGH_LIMIT,
PCNT_LL_EVENT_MAX PCNT_LL_WATCH_EVENT_ZERO_CROSS,
} pcnt_ll_event_id_t; 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 * @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. * @param hw Peripheral PCNT hardware instance address.
* @return Interrupt status word * @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; 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 hw Peripheral PCNT hardware instance address.
* @param status value to clear interrupt status * @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; 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) 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 hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number * @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; 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 * @param unit PCNT unit number
* @return Event status word * @return Event status word
*/ */
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit) static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit)
{ {
return hw->status_unit[unit].val >> 2; 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; 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 #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,16 +1,8 @@
// Copyright 2015-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. * SPDX-License-Identifier: Apache-2.0
// 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.
/******************************************************************************* /*******************************************************************************
* NOTICE * NOTICE
@@ -22,6 +14,7 @@
#pragma once #pragma once
#include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include "soc/pcnt_struct.h" #include "soc/pcnt_struct.h"
@@ -33,17 +26,21 @@ extern "C" {
#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_GLITCH_WIDTH 1023
#define PCNT_LL_MAX_LIM SHRT_MAX
#define PCNT_LL_MIN_LIN SHRT_MIN
typedef enum { typedef enum {
PCNT_LL_EVENT_THRES1, PCNT_LL_WATCH_EVENT_INVALID = -1,
PCNT_LL_EVENT_THRES0, PCNT_LL_WATCH_EVENT_THRES1,
PCNT_LL_EVENT_LOW_LIMIT, PCNT_LL_WATCH_EVENT_THRES0,
PCNT_LL_EVENT_HIGH_LIMIT, PCNT_LL_WATCH_EVENT_LOW_LIMIT,
PCNT_LL_EVENT_ZERO_CROSS, PCNT_LL_WATCH_EVENT_HIGH_LIMIT,
PCNT_LL_EVENT_MAX PCNT_LL_WATCH_EVENT_ZERO_CROSS,
} pcnt_ll_event_id_t; 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 * @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. * @param hw Peripheral PCNT hardware instance address.
* @return Interrupt status word * @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; 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 hw Peripheral PCNT hardware instance address.
* @param status value to clear interrupt status * @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; 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) 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 hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number * @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; 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 * @param unit PCNT unit number
* @return Event status word * @return Event status word
*/ */
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit) static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit)
{ {
return hw->status_unit[unit].val >> 2; 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; 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 #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,16 +1,8 @@
// Copyright 2015-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. * SPDX-License-Identifier: Apache-2.0
// 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.
/******************************************************************************* /*******************************************************************************
* NOTICE * NOTICE
@@ -22,6 +14,7 @@
#pragma once #pragma once
#include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include "soc/pcnt_struct.h" #include "soc/pcnt_struct.h"
@@ -33,17 +26,21 @@ extern "C" {
#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_GLITCH_WIDTH 1023
#define PCNT_LL_MAX_LIM SHRT_MAX
#define PCNT_LL_MIN_LIN SHRT_MIN
typedef enum { typedef enum {
PCNT_LL_EVENT_THRES1, PCNT_LL_WATCH_EVENT_INVALID = -1,
PCNT_LL_EVENT_THRES0, PCNT_LL_WATCH_EVENT_THRES1,
PCNT_LL_EVENT_LOW_LIMIT, PCNT_LL_WATCH_EVENT_THRES0,
PCNT_LL_EVENT_HIGH_LIMIT, PCNT_LL_WATCH_EVENT_LOW_LIMIT,
PCNT_LL_EVENT_ZERO_CROSS, PCNT_LL_WATCH_EVENT_HIGH_LIMIT,
PCNT_LL_EVENT_MAX PCNT_LL_WATCH_EVENT_ZERO_CROSS,
} pcnt_ll_event_id_t; 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 * @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. * @param hw Peripheral PCNT hardware instance address.
* @return Interrupt status word * @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; 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 hw Peripheral PCNT hardware instance address.
* @param status value to clear interrupt status * @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; 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) 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 hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number * @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; 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 * @param unit PCNT unit number
* @return Event status word * @return Event status word
*/ */
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit) static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit)
{ {
return hw->status_unit[unit].val >> 2; 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; 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 #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2019 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. * SPDX-License-Identifier: Apache-2.0
// 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.
/******************************************************************************* /*******************************************************************************
* NOTICE * NOTICE
@@ -18,22 +10,19 @@
* See readme.md in hal/include/hal/readme.md * 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 #pragma once
#include "soc/pcnt_struct.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #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 * Context that should be maintained by both the driver and the HAL
*/ */
typedef struct { typedef struct {
pcnt_dev_t *dev; /*!< PCNT peripheral register base address */ pcnt_soc_handle_t dev; // PCNT SOC layer handle
} pcnt_hal_context_t; } pcnt_hal_context_t;
/** /**

View File

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

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2019 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. * SPDX-License-Identifier: Apache-2.0
// 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.
// The HAL layer for PCNT (common part) // The HAL layer for PCNT (common part)

View File

@@ -1,21 +1,13 @@
// Copyright 2015-2016 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. * SPDX-License-Identifier: Apache-2.0
// You may obtain a copy of the License at */
#pragma once
// http://www.apache.org/licenses/LICENSE-2.0 #include <stdint.h>
// #include "soc/soc.h"
// 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 "soc.h"
#define PCNT_U0_CONF0_REG (DR_REG_PCNT_BASE + 0x0000) #define PCNT_U0_CONF0_REG (DR_REG_PCNT_BASE + 0x0000)
/* PCNT_CH1_LCTRL_MODE_U0 : R/W ;bitpos:[31:30] ;default: 2'd0 ; */ /* 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 /*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_M ((PCNT_DATE_V)<<(PCNT_DATE_S))
#define PCNT_DATE_V 0xFFFFFFFF #define PCNT_DATE_V 0xFFFFFFFF
#define PCNT_DATE_S 0 #define PCNT_DATE_S 0
#endif /*_SOC_PCNT_REG_H_ */

View File

@@ -1,18 +1,9 @@
// Copyright 2015-2016 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. * SPDX-License-Identifier: Apache-2.0
// You may obtain a copy of the License at */
#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_STRUCT_H_
#define _SOC_PCNT_STRUCT_H_
#include <stdint.h> #include <stdint.h>
@@ -20,8 +11,8 @@
extern "C" { extern "C" {
#endif #endif
typedef volatile struct pcnt_dev_s { typedef struct pcnt_dev_t {
struct { volatile struct {
union { union {
struct { struct {
uint32_t filter_thres: 10; /*This register is used to filter pulse whose width is smaller than this value for unit0.*/ uint32_t filter_thres: 10; /*This register is used to filter pulse whose width is smaller than this value for unit0.*/
@@ -57,14 +48,14 @@ typedef volatile struct pcnt_dev_s {
uint32_t val; uint32_t val;
} conf2; } conf2;
} conf_unit[8]; } conf_unit[8];
union { volatile union {
struct { struct {
uint32_t cnt_val : 16; /*This register stores the current pulse count value for unit0.*/ uint32_t cnt_val : 16; /*This register stores the current pulse count value for unit0.*/
uint32_t reserved16: 16; uint32_t reserved16: 16;
}; };
uint32_t val; uint32_t val;
} cnt_unit[8]; } cnt_unit[8];
union { volatile union {
struct { struct {
uint32_t cnt_thr_event_u0: 1; /*This is the interrupt raw bit for channel0 event.*/ 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.*/ 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; uint32_t val;
} int_raw; } int_raw;
union { volatile union {
struct { struct {
uint32_t cnt_thr_event_u0: 1; /*This is the interrupt status bit for channel0 event.*/ 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.*/ 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; uint32_t val;
} int_st; } int_st;
union { volatile union {
struct { struct {
uint32_t cnt_thr_event_u0: 1; /*This is the interrupt enable bit for channel0 event.*/ 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.*/ 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; uint32_t val;
} int_ena; } int_ena;
union { volatile union {
struct { struct {
uint32_t cnt_thr_event_u0: 1; /*Set this bit to clear channel0 event interrupt.*/ 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.*/ uint32_t cnt_thr_event_u1: 1; /*Set this bit to clear channel1 event interrupt.*/
@@ -120,7 +111,7 @@ typedef volatile struct pcnt_dev_s {
}; };
uint32_t val; uint32_t val;
} int_clr; } int_clr;
union { volatile union {
struct { 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 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 thres1_lat: 1; /* counter value equals to thresh1*/
@@ -132,7 +123,7 @@ typedef volatile struct pcnt_dev_s {
}; };
uint32_t val; uint32_t val;
} status_unit[8]; } status_unit[8];
union { volatile union {
struct { struct {
uint32_t cnt_rst_u0: 1; /*Set this bit to clear unit0's counter.*/ 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.*/ 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_f0;
uint32_t reserved_f4; uint32_t reserved_f4;
uint32_t reserved_f8; uint32_t reserved_f8;
uint32_t date; /**/ volatile uint32_t date;
} pcnt_dev_t; } pcnt_dev_t;
extern pcnt_dev_t PCNT; extern pcnt_dev_t PCNT;
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif /* _SOC_PCNT_STRUCT_H_ */

View File

@@ -1,16 +1,8 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD /*
// * SPDX-FileCopyrightText: 2020-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. * SPDX-License-Identifier: Apache-2.0
// 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.
#include "soc/pcnt_periph.h" #include "soc/pcnt_periph.h"
#include "soc/gpio_sig_map.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"); * SPDX-License-Identifier: Apache-2.0
* 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 #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"); * SPDX-License-Identifier: Apache-2.0
* 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 #pragma once
@@ -388,7 +379,7 @@ typedef union {
} pcnt_date_reg_t; } pcnt_date_reg_t;
typedef struct { typedef struct pcnt_dev_t {
volatile struct { volatile struct {
pcnt_un_conf0_reg_t conf0; pcnt_un_conf0_reg_t conf0;
pcnt_un_conf1_reg_t conf1; pcnt_un_conf1_reg_t conf1;

View File

@@ -1,16 +1,8 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD /*
// * SPDX-FileCopyrightText: 2020-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. * SPDX-License-Identifier: Apache-2.0
// 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.
#include "soc/pcnt_periph.h" #include "soc/pcnt_periph.h"
#include "soc/gpio_sig_map.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"); * SPDX-License-Identifier: Apache-2.0
* 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 #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"); * SPDX-License-Identifier: Apache-2.0
* 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 #pragma once
@@ -388,7 +379,7 @@ typedef union {
} pcnt_date_reg_t; } pcnt_date_reg_t;
typedef struct { typedef struct pcnt_dev_t {
volatile struct { volatile struct {
pcnt_un_conf0_reg_t conf0; pcnt_un_conf0_reg_t conf0;
pcnt_un_conf1_reg_t conf1; pcnt_un_conf1_reg_t conf1;

View File

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

View File

@@ -1,16 +1,8 @@
// Copyright 2019-2020 Espressif Systems (Shanghai) PTE LTD /*
// * SPDX-FileCopyrightText: 2019-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. * SPDX-License-Identifier: Apache-2.0
// 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 #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/i2c_ll.h
components/hal/esp32/include/hal/interrupt_controller_ll.h components/hal/esp32/include/hal/interrupt_controller_ll.h
components/hal/esp32/include/hal/mpu_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_cntl_ll.h
components/hal/esp32/include/hal/rtc_io_ll.h components/hal/esp32/include/hal/rtc_io_ll.h
components/hal/esp32/include/hal/rwdt_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_ll.h
components/hal/esp32s2/include/hal/memprot_peri_ll.h components/hal/esp32s2/include/hal/memprot_peri_ll.h
components/hal/esp32s2/include/hal/mpu_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_cntl_ll.h
components/hal/esp32s2/include/hal/rtc_io_ll.h components/hal/esp32s2/include/hal/rtc_io_ll.h
components/hal/esp32s2/include/hal/sha_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/interrupt_controller_ll.h
components/hal/esp32s3/include/hal/memprot_ll.h components/hal/esp32s3/include/hal/memprot_ll.h
components/hal/esp32s3/include/hal/mpu_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/rtc_cntl_ll.h
components/hal/esp32s3/include/hal/rwdt_ll.h components/hal/esp32s3/include/hal/rwdt_ll.h
components/hal/esp32s3/include/hal/sha_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/mcpwm_types.h
components/hal/include/hal/mpu_hal.h components/hal/include/hal/mpu_hal.h
components/hal/include/hal/mpu_types.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/rmt_types.h
components/hal/include/hal/rtc_io_types.h components/hal/include/hal/rtc_io_types.h
components/hal/include/hal/sdio_slave_hal.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/ledc_hal_iram.c
components/hal/mcpwm_hal.c components/hal/mcpwm_hal.c
components/hal/mpu_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/assert.h
components/hal/platform_port/include/hal/check.h components/hal/platform_port/include/hal/check.h
components/hal/platform_port/include/hal/log.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/ledc_struct.h
components/soc/esp32/include/soc/mmu.h components/soc/esp32/include/soc/mmu.h
components/soc/esp32/include/soc/nrx_reg.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/pid.h
components/soc/esp32/include/soc/reset_reasons.h components/soc/esp32/include/soc/reset_reasons.h
components/soc/esp32/include/soc/rmt_reg.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/interrupts.c
components/soc/esp32/ledc_periph.c components/soc/esp32/ledc_periph.c
components/soc/esp32/mcpwm_periph.c components/soc/esp32/mcpwm_periph.c
components/soc/esp32/pcnt_periph.c
components/soc/esp32/rmt_periph.c components/soc/esp32/rmt_periph.c
components/soc/esp32/sdio_slave_periph.c components/soc/esp32/sdio_slave_periph.c
components/soc/esp32/sdmmc_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/memprot_defs.h
components/soc/esp32s2/include/soc/mmu.h components/soc/esp32s2/include/soc/mmu.h
components/soc/esp32s2/include/soc/nrx_reg.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/reset_reasons.h
components/soc/esp32s2/include/soc/rtc_cntl_reg.h components/soc/esp32s2/include/soc/rtc_cntl_reg.h
components/soc/esp32s2/include/soc/rtc_cntl_struct.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/include/soc/wdev_reg.h
components/soc/esp32s2/interrupts.c components/soc/esp32s2/interrupts.c
components/soc/esp32s2/ledc_periph.c components/soc/esp32s2/ledc_periph.c
components/soc/esp32s2/pcnt_periph.c
components/soc/esp32s2/rmt_periph.c components/soc/esp32s2/rmt_periph.c
components/soc/esp32s2/sigmadelta_periph.c components/soc/esp32s2/sigmadelta_periph.c
components/soc/esp32s2/spi_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/mmu.h
components/soc/esp32s3/include/soc/mpu_caps.h components/soc/esp32s3/include/soc/mpu_caps.h
components/soc/esp32s3/include/soc/nrx_reg.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_reg.h
components/soc/esp32s3/include/soc/peri_backup_struct.h components/soc/esp32s3/include/soc/peri_backup_struct.h
components/soc/esp32s3/include/soc/reset_reasons.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/interrupts.c
components/soc/esp32s3/ledc_periph.c components/soc/esp32s3/ledc_periph.c
components/soc/esp32s3/mcpwm_periph.c components/soc/esp32s3/mcpwm_periph.c
components/soc/esp32s3/pcnt_periph.c
components/soc/esp32s3/rmt_periph.c components/soc/esp32s3/rmt_periph.c
components/soc/esp32s3/rtc_io_periph.c components/soc/esp32s3/rtc_io_periph.c
components/soc/esp32s3/sdio_slave_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/interrupts.h
components/soc/include/soc/ledc_periph.h components/soc/include/soc/ledc_periph.h
components/soc/include/soc/mcpwm_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/rmt_periph.h
components/soc/include/soc/rtc_cntl_periph.h components/soc/include/soc/rtc_cntl_periph.h
components/soc/include/soc/rtc_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_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_servo_control/main/mcpwm_servo_control_example_main.c
examples/peripherals/mcpwm/mcpwm_sync_example/main/mcpwm_sync_example.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/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_nec.c
examples/peripherals/rmt/ir_protocols/components/infrared_tools/src/ir_builder_rmt_rc5.c examples/peripherals/rmt/ir_protocols/components/infrared_tools/src/ir_builder_rmt_rc5.c