mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
pulse_cnt: new driver for PCNT peripheral
This commit is contained in:
parent
6e405cc209
commit
ec8defaa96
@ -48,7 +48,7 @@ if(CONFIG_SOC_RMT_SUPPORTED)
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_PCNT_SUPPORTED)
|
||||
list(APPEND srcs "pcnt.c")
|
||||
list(APPEND srcs "pcnt_legacy.c" "pulse_cnt.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_SDMMC_HOST_SUPPORTED)
|
||||
|
@ -22,7 +22,7 @@ menu "Driver configurations"
|
||||
endmenu # ADC Configuration
|
||||
|
||||
menu "MCPWM configuration"
|
||||
|
||||
depends on SOC_MCPWM_SUPPORTED
|
||||
config MCPWM_ISR_IN_IRAM
|
||||
bool "Place MCPWM ISR function into IRAM"
|
||||
default n
|
||||
@ -32,7 +32,6 @@ menu "Driver configurations"
|
||||
|
||||
Note that if this option is selected, all user registered ISR callbacks should never
|
||||
try to use cache as well. (with IRAM_ATTR)
|
||||
|
||||
endmenu # MCPWM Configuration
|
||||
|
||||
menu "SPI configuration"
|
||||
@ -158,8 +157,6 @@ menu "Driver configurations"
|
||||
endmenu # UART Configuration
|
||||
|
||||
menu "GPIO Configuration"
|
||||
visible if IDF_TARGET_ESP32
|
||||
|
||||
config GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL
|
||||
bool "Support light sleep GPIO pullup/pulldown configuration for ESP32"
|
||||
depends on IDF_TARGET_ESP32
|
||||
@ -168,10 +165,10 @@ menu "Driver configurations"
|
||||
pullup/pulldown mode in sleep.
|
||||
If this option is selected, chip will automatically emulate the behaviour of switching,
|
||||
and about 450B of source codes would be placed into IRAM.
|
||||
|
||||
endmenu # GPIO Configuration
|
||||
|
||||
menu "GDMA Configuration"
|
||||
depends on SOC_GDMA_SUPPORTED
|
||||
config GDMA_CTRL_FUNC_IN_IRAM
|
||||
bool "Place GDMA control functions into IRAM"
|
||||
default n
|
||||
@ -202,17 +199,56 @@ menu "Driver configurations"
|
||||
bool "GPTimer ISR IRAM-Safe"
|
||||
default n
|
||||
help
|
||||
This will ensure the GPTimer interrupt handle is IRAM-Safe, allow to avoid flash
|
||||
cache misses, and also be able to run whilst the cache is disabled.
|
||||
(e.g. SPI Flash write)
|
||||
Ensure the GPTimer interrupt is IRAM-Safe by allowing the interrupt handler to be
|
||||
executable when the cache is disabled (e.g. SPI Flash write).
|
||||
|
||||
config GPTIMER_SUPPRESS_DEPRECATE_WARN
|
||||
bool "Suppress leagcy driver deprecated warning"
|
||||
bool "Suppress legacy driver deprecated warning"
|
||||
default n
|
||||
help
|
||||
Wether to suppress the deprecation warnings when using legacy timer group driver (driver/timer.h).
|
||||
If you want to continue using the legacy driver, and don't want to see related deprecation warnings,
|
||||
you can enable this option.
|
||||
|
||||
config GPTIMER_ENABLE_DEBUG_LOG
|
||||
bool "Enable debug log"
|
||||
default n
|
||||
help
|
||||
Wether to enable the debug log message for GPTimer driver.
|
||||
Note that, this option only controls the GPTimer driver log, won't affect other drivers.
|
||||
endmenu # GPTimer Configuration
|
||||
|
||||
menu "PCNT Configuration"
|
||||
depends on SOC_PCNT_SUPPORTED
|
||||
config PCNT_CTRL_FUNC_IN_IRAM
|
||||
bool "Place PCNT control functions into IRAM"
|
||||
default n
|
||||
help
|
||||
Place PCNT control functions (like start/stop) into IRAM,
|
||||
so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context.
|
||||
Enabling this option can improve driver performance as well.
|
||||
|
||||
config PCNT_ISR_IRAM_SAFE
|
||||
bool "PCNT ISR IRAM-Safe"
|
||||
default n
|
||||
help
|
||||
Ensure the PCNT interrupt is IRAM-Safe by allowing the interrupt handler to be
|
||||
executable when the cache is disabled (e.g. SPI Flash write).
|
||||
|
||||
config PCNT_SUPPRESS_DEPRECATE_WARN
|
||||
bool "Suppress legacy driver deprecated warning"
|
||||
default n
|
||||
help
|
||||
Wether to suppress the deprecation warnings when using legacy PCNT driver (driver/pcnt.h).
|
||||
If you want to continue using the legacy driver, and don't want to see related deprecation warnings,
|
||||
you can enable this option.
|
||||
|
||||
config PCNT_ENABLE_DEBUG_LOG
|
||||
bool "Enable debug log"
|
||||
default n
|
||||
help
|
||||
Wether to enable the debug log message for PCNT driver.
|
||||
Note that, this option only controls the PCNT driver log, won't affect other drivers.
|
||||
endmenu # PCNT Configuration
|
||||
|
||||
endmenu # Driver configurations
|
||||
|
@ -4,10 +4,14 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG // uncomment this line to enable debug logs
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/lock.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_GPTIMER_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_err.h"
|
||||
@ -90,7 +94,7 @@ static gptimer_platform_t s_platform;
|
||||
static gptimer_group_t *gptimer_acquire_group_handle(int group_id);
|
||||
static void gptimer_release_group_handle(gptimer_group_t *group);
|
||||
static esp_err_t gptimer_select_periph_clock(gptimer_t *timer, gptimer_clock_source_t src_clk, uint32_t resolution_hz);
|
||||
IRAM_ATTR static void gptimer_default_isr(void *args);
|
||||
static void gptimer_default_isr(void *args);
|
||||
|
||||
esp_err_t gptimer_new_timer(const gptimer_config_t *config, gptimer_handle_t *ret_timer)
|
||||
{
|
||||
@ -205,7 +209,7 @@ esp_err_t gptimer_del_timer(gptimer_handle_t timer)
|
||||
|
||||
esp_err_t gptimer_set_raw_count(gptimer_handle_t timer, unsigned long long value)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE_ISR(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
portENTER_CRITICAL_SAFE(&timer->spinlock);
|
||||
timer_hal_set_counter_value(&timer->hal, value);
|
||||
@ -215,7 +219,7 @@ esp_err_t gptimer_set_raw_count(gptimer_handle_t timer, unsigned long long value
|
||||
|
||||
esp_err_t gptimer_get_raw_count(gptimer_handle_t timer, unsigned long long *value)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer && value, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE_ISR(timer && value, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
portENTER_CRITICAL_SAFE(&timer->spinlock);
|
||||
*value = timer_ll_get_counter_value(timer->hal.dev, timer->timer_id);
|
||||
@ -252,9 +256,9 @@ esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer
|
||||
}
|
||||
|
||||
// enable/disable GPTimer interrupt events
|
||||
portENTER_CRITICAL_SAFE(&group->spinlock);
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
timer_ll_enable_intr(timer->hal.dev, TIMER_LL_EVENT_ALARM(timer->timer_id), cbs->on_alarm != NULL); // enable timer interrupt
|
||||
portEXIT_CRITICAL_SAFE(&group->spinlock);
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
timer->on_alarm = cbs->on_alarm;
|
||||
timer->user_ctx = user_data;
|
||||
@ -263,11 +267,11 @@ esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer
|
||||
|
||||
esp_err_t gptimer_set_alarm_action(gptimer_handle_t timer, const gptimer_alarm_config_t *config)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE_ISR(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
if (config) {
|
||||
// When auto_reload is enabled, alarm_count should not be equal to reload_count
|
||||
bool valid_auto_reload = !config->flags.auto_reload_on_alarm || config->alarm_count != config->reload_count;
|
||||
ESP_RETURN_ON_FALSE(valid_auto_reload, ESP_ERR_INVALID_ARG, TAG, "reload count can't equal to alarm count");
|
||||
ESP_RETURN_ON_FALSE_ISR(valid_auto_reload, ESP_ERR_INVALID_ARG, TAG, "reload count can't equal to alarm count");
|
||||
|
||||
timer->reload_count = config->reload_count;
|
||||
timer->alarm_count = config->alarm_count;
|
||||
@ -292,15 +296,15 @@ esp_err_t gptimer_set_alarm_action(gptimer_handle_t timer, const gptimer_alarm_c
|
||||
|
||||
esp_err_t gptimer_start(gptimer_handle_t timer)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE_ISR(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
// acquire power manager lock
|
||||
if (timer->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(timer->pm_lock), TAG, "acquire APB_FREQ_MAX lock failed");
|
||||
ESP_RETURN_ON_ERROR_ISR(esp_pm_lock_acquire(timer->pm_lock), TAG, "acquire APB_FREQ_MAX lock failed");
|
||||
}
|
||||
// interrupt interupt service
|
||||
if (timer->intr) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_enable(timer->intr), TAG, "enable interrupt service failed");
|
||||
ESP_RETURN_ON_ERROR_ISR(esp_intr_enable(timer->intr), TAG, "enable interrupt service failed");
|
||||
}
|
||||
|
||||
portENTER_CRITICAL_SAFE(&timer->spinlock);
|
||||
@ -314,7 +318,7 @@ esp_err_t gptimer_start(gptimer_handle_t timer)
|
||||
|
||||
esp_err_t gptimer_stop(gptimer_handle_t timer)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE_ISR(timer, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
// disable counter, alarm, autoreload
|
||||
portENTER_CRITICAL_SAFE(&timer->spinlock);
|
||||
@ -325,11 +329,11 @@ esp_err_t gptimer_stop(gptimer_handle_t timer)
|
||||
|
||||
// disable interrupt service
|
||||
if (timer->intr) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_disable(timer->intr), TAG, "disable interrupt service failed");
|
||||
ESP_RETURN_ON_ERROR_ISR(esp_intr_disable(timer->intr), TAG, "disable interrupt service failed");
|
||||
}
|
||||
// release power manager lock
|
||||
if (timer->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_release(timer->pm_lock), TAG, "release APB_FREQ_MAX lock failed");
|
||||
ESP_RETURN_ON_ERROR_ISR(esp_pm_lock_release(timer->pm_lock), TAG, "release APB_FREQ_MAX lock failed");
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
@ -337,7 +341,9 @@ esp_err_t gptimer_stop(gptimer_handle_t timer)
|
||||
|
||||
static gptimer_group_t *gptimer_acquire_group_handle(int group_id)
|
||||
{
|
||||
// esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#if CONFIG_GPTIMER_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
bool new_group = false;
|
||||
gptimer_group_t *group = NULL;
|
||||
|
||||
|
296
components/driver/include/driver/pulse_cnt.h
Normal file
296
components/driver/include/driver/pulse_cnt.h
Normal 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
|
@ -9,3 +9,9 @@
|
||||
* the legacy timer group driver (deprecated/driver/timer.h) and the new gptimer driver (driver/gptimer.h).
|
||||
*/
|
||||
int timer_group_driver_init_count = 0;
|
||||
|
||||
/**
|
||||
* @brief This count is used to prevent the coexistence of
|
||||
* the legacy pcnt driver (deprecated/driver/pcnt.h) and the new pulse_cnt driver (driver/pulse_cnt.h).
|
||||
*/
|
||||
int pcnt_driver_init_count = 0;
|
||||
|
@ -18,3 +18,8 @@ entries:
|
||||
gdma: gdma_stop (noflash)
|
||||
gdma: gdma_append (noflash)
|
||||
gdma: gdma_reset (noflash)
|
||||
if PCNT_CTRL_FUNC_IN_IRAM = y:
|
||||
pulse_cnt: pcnt_unit_start (noflash)
|
||||
pulse_cnt: pcnt_unit_stop (noflash)
|
||||
pulse_cnt: pcnt_unit_clear_count (noflash)
|
||||
pulse_cnt: pcnt_unit_get_count (noflash)
|
||||
|
714
components/driver/pulse_cnt.c
Normal file
714
components/driver/pulse_cnt.c
Normal 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();
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ set(srcs "test_app_main.c"
|
||||
"test_gptimer.c"
|
||||
"test_gptimer_iram.c")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES driver unity spi_flash)
|
||||
idf_component_register(SRCS ${srcs})
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u test_app_include_gptimer" "-u test_app_include_gptimer_iram")
|
||||
|
@ -12,6 +12,6 @@ from pytest_embedded import Dut
|
||||
'release',
|
||||
], indirect=True)
|
||||
def test_gptimer(dut: Dut) -> None:
|
||||
dut.expect('Press ENTER to see the list of tests')
|
||||
dut.expect_exact('Press ENTER to see the list of tests')
|
||||
dut.write('*')
|
||||
dut.expect_unity_test_output()
|
||||
|
18
components/driver/test_apps/pulse_cnt/CMakeLists.txt
Normal file
18
components/driver/test_apps/pulse_cnt/CMakeLists.txt
Normal 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()
|
2
components/driver/test_apps/pulse_cnt/README.md
Normal file
2
components/driver/test_apps/pulse_cnt/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- |
|
@ -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")
|
51
components/driver/test_apps/pulse_cnt/main/test_app_main.c
Normal file
51
components/driver/test_apps/pulse_cnt/main/test_app_main.c
Normal 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();
|
||||
}
|
408
components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c
Normal file
408
components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c
Normal 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)
|
||||
{
|
||||
}
|
23
components/driver/test_apps/pulse_cnt/pytest_pulse_cnt.py
Normal file
23
components/driver/test_apps/pulse_cnt/pytest_pulse_cnt.py
Normal 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()
|
@ -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
|
@ -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
|
2
components/driver/test_apps/pulse_cnt/sdkconfig.defaults
Normal file
2
components/driver/test_apps/pulse_cnt/sdkconfig.defaults
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT=n
|
@ -1,16 +1,8 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* NOTICE
|
||||
@ -22,6 +14,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "soc/pcnt_struct.h"
|
||||
@ -31,19 +24,23 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
|
||||
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
|
||||
#define PCNT_LL_MAX_GLITCH_WIDTH 1023
|
||||
#define PCNT_LL_MAX_LIM SHRT_MAX
|
||||
#define PCNT_LL_MIN_LIN SHRT_MIN
|
||||
|
||||
typedef enum {
|
||||
PCNT_LL_EVENT_THRES1,
|
||||
PCNT_LL_EVENT_THRES0,
|
||||
PCNT_LL_EVENT_LOW_LIMIT,
|
||||
PCNT_LL_EVENT_HIGH_LIMIT,
|
||||
PCNT_LL_EVENT_ZERO_CROSS,
|
||||
PCNT_LL_EVENT_MAX
|
||||
} pcnt_ll_event_id_t;
|
||||
PCNT_LL_WATCH_EVENT_INVALID = -1,
|
||||
PCNT_LL_WATCH_EVENT_THRES1,
|
||||
PCNT_LL_WATCH_EVENT_THRES0,
|
||||
PCNT_LL_WATCH_EVENT_LOW_LIMIT,
|
||||
PCNT_LL_WATCH_EVENT_HIGH_LIMIT,
|
||||
PCNT_LL_WATCH_EVENT_ZERO_CROSS,
|
||||
PCNT_LL_WATCH_EVENT_MAX
|
||||
} pcnt_ll_watch_event_id_t;
|
||||
|
||||
#define PCNT_LL_EVENT_MASK ((1 << PCNT_LL_EVENT_MAX) - 1)
|
||||
#define PCNT_LL_WATCH_EVENT_MASK ((1 << PCNT_LL_WATCH_EVENT_MAX) - 1)
|
||||
#define PCNT_LL_UNIT_WATCH_EVENT(unit_id) (1 << (unit_id))
|
||||
|
||||
/**
|
||||
* @brief Set PCNT channel edge action
|
||||
@ -156,7 +153,8 @@ static inline void pcnt_ll_enable_intr(pcnt_dev_t *hw, uint32_t unit_mask, bool
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @return Interrupt status word
|
||||
*/
|
||||
__attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
|
||||
{
|
||||
return hw->int_st.val;
|
||||
}
|
||||
@ -167,7 +165,8 @@ __attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pc
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param status value to clear interrupt status
|
||||
*/
|
||||
__attribute__((always_inline)) static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
|
||||
__attribute__((always_inline))
|
||||
static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
|
||||
{
|
||||
hw->int_clr.val = status;
|
||||
}
|
||||
@ -233,7 +232,7 @@ static inline void pcnt_ll_enable_thres_event(pcnt_dev_t *hw, uint32_t unit, uin
|
||||
*/
|
||||
static inline void pcnt_ll_disable_all_events(pcnt_dev_t *hw, uint32_t unit)
|
||||
{
|
||||
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_EVENT_MASK << 11);
|
||||
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_WATCH_EVENT_MASK << 11);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -344,13 +343,14 @@ static inline uint32_t pcnt_ll_get_unit_status(pcnt_dev_t *hw, uint32_t unit)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get PCNT count sign
|
||||
* @brief Get PCNT zero cross mode
|
||||
*
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param unit PCNT unit number
|
||||
* @return Count sign
|
||||
* @return Zero cross mode
|
||||
*/
|
||||
static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint32_t unit)
|
||||
__attribute__((always_inline))
|
||||
static inline pcnt_unit_zero_cross_mode_t pcnt_ll_get_zero_cross_mode(pcnt_dev_t *hw, uint32_t unit)
|
||||
{
|
||||
return hw->status_unit[unit].val & 0x03;
|
||||
}
|
||||
@ -362,6 +362,7 @@ static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint
|
||||
* @param unit PCNT unit number
|
||||
* @return Event status word
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit)
|
||||
{
|
||||
return hw->status_unit[unit].val >> 2;
|
||||
@ -404,6 +405,18 @@ static inline void pcnt_ll_enable_glitch_filter(pcnt_dev_t *hw, uint32_t unit, b
|
||||
hw->conf_unit[unit].conf0.filter_en = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get interrupt status register address.
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
*
|
||||
* @return Interrupt status register address
|
||||
*/
|
||||
static inline volatile void *pcnt_ll_get_intr_status_reg(pcnt_dev_t *hw)
|
||||
{
|
||||
return &hw->int_st.val;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -1,16 +1,8 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* NOTICE
|
||||
@ -22,6 +14,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "soc/pcnt_struct.h"
|
||||
@ -31,19 +24,23 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
|
||||
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
|
||||
#define PCNT_LL_MAX_GLITCH_WIDTH 1023
|
||||
#define PCNT_LL_MAX_LIM SHRT_MAX
|
||||
#define PCNT_LL_MIN_LIN SHRT_MIN
|
||||
|
||||
typedef enum {
|
||||
PCNT_LL_EVENT_THRES1,
|
||||
PCNT_LL_EVENT_THRES0,
|
||||
PCNT_LL_EVENT_LOW_LIMIT,
|
||||
PCNT_LL_EVENT_HIGH_LIMIT,
|
||||
PCNT_LL_EVENT_ZERO_CROSS,
|
||||
PCNT_LL_EVENT_MAX
|
||||
} pcnt_ll_event_id_t;
|
||||
PCNT_LL_WATCH_EVENT_INVALID = -1,
|
||||
PCNT_LL_WATCH_EVENT_THRES1,
|
||||
PCNT_LL_WATCH_EVENT_THRES0,
|
||||
PCNT_LL_WATCH_EVENT_LOW_LIMIT,
|
||||
PCNT_LL_WATCH_EVENT_HIGH_LIMIT,
|
||||
PCNT_LL_WATCH_EVENT_ZERO_CROSS,
|
||||
PCNT_LL_WATCH_EVENT_MAX
|
||||
} pcnt_ll_watch_event_id_t;
|
||||
|
||||
#define PCNT_LL_EVENT_MASK ((1 << PCNT_LL_EVENT_MAX) - 1)
|
||||
#define PCNT_LL_WATCH_EVENT_MASK ((1 << PCNT_LL_WATCH_EVENT_MAX) - 1)
|
||||
#define PCNT_LL_UNIT_WATCH_EVENT(unit_id) (1 << (unit_id))
|
||||
|
||||
/**
|
||||
* @brief Set PCNT channel edge action
|
||||
@ -156,7 +153,8 @@ static inline void pcnt_ll_enable_intr(pcnt_dev_t *hw, uint32_t unit_mask, bool
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @return Interrupt status word
|
||||
*/
|
||||
__attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
|
||||
{
|
||||
return hw->int_st.val;
|
||||
}
|
||||
@ -167,7 +165,8 @@ __attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pc
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param status value to clear interrupt status
|
||||
*/
|
||||
__attribute__((always_inline)) static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
|
||||
__attribute__((always_inline))
|
||||
static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
|
||||
{
|
||||
hw->int_clr.val = status;
|
||||
}
|
||||
@ -233,7 +232,7 @@ static inline void pcnt_ll_enable_thres_event(pcnt_dev_t *hw, uint32_t unit, uin
|
||||
*/
|
||||
static inline void pcnt_ll_disable_all_events(pcnt_dev_t *hw, uint32_t unit)
|
||||
{
|
||||
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_EVENT_MASK << 11);
|
||||
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_WATCH_EVENT_MASK << 11);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -344,13 +343,14 @@ static inline uint32_t pcnt_ll_get_unit_status(pcnt_dev_t *hw, uint32_t unit)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get PCNT count sign
|
||||
* @brief Get PCNT zero cross mode
|
||||
*
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param unit PCNT unit number
|
||||
* @return Count sign
|
||||
* @return Zero cross mode
|
||||
*/
|
||||
static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint32_t unit)
|
||||
__attribute__((always_inline))
|
||||
static inline pcnt_unit_zero_cross_mode_t pcnt_ll_get_zero_cross_mode(pcnt_dev_t *hw, uint32_t unit)
|
||||
{
|
||||
return hw->status_unit[unit].val & 0x03;
|
||||
}
|
||||
@ -362,6 +362,7 @@ static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint
|
||||
* @param unit PCNT unit number
|
||||
* @return Event status word
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit)
|
||||
{
|
||||
return hw->status_unit[unit].val >> 2;
|
||||
@ -404,6 +405,18 @@ static inline void pcnt_ll_enable_glitch_filter(pcnt_dev_t *hw, uint32_t unit, b
|
||||
hw->conf_unit[unit].conf0.filter_en_un = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get interrupt status register address.
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
*
|
||||
* @return Interrupt status register address
|
||||
*/
|
||||
static inline volatile void *pcnt_ll_get_intr_status_reg(pcnt_dev_t *hw)
|
||||
{
|
||||
return &hw->int_st.val;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -1,16 +1,8 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* NOTICE
|
||||
@ -22,6 +14,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "soc/pcnt_struct.h"
|
||||
@ -31,19 +24,23 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
|
||||
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
|
||||
#define PCNT_LL_MAX_GLITCH_WIDTH 1023
|
||||
#define PCNT_LL_MAX_LIM SHRT_MAX
|
||||
#define PCNT_LL_MIN_LIN SHRT_MIN
|
||||
|
||||
typedef enum {
|
||||
PCNT_LL_EVENT_THRES1,
|
||||
PCNT_LL_EVENT_THRES0,
|
||||
PCNT_LL_EVENT_LOW_LIMIT,
|
||||
PCNT_LL_EVENT_HIGH_LIMIT,
|
||||
PCNT_LL_EVENT_ZERO_CROSS,
|
||||
PCNT_LL_EVENT_MAX
|
||||
} pcnt_ll_event_id_t;
|
||||
PCNT_LL_WATCH_EVENT_INVALID = -1,
|
||||
PCNT_LL_WATCH_EVENT_THRES1,
|
||||
PCNT_LL_WATCH_EVENT_THRES0,
|
||||
PCNT_LL_WATCH_EVENT_LOW_LIMIT,
|
||||
PCNT_LL_WATCH_EVENT_HIGH_LIMIT,
|
||||
PCNT_LL_WATCH_EVENT_ZERO_CROSS,
|
||||
PCNT_LL_WATCH_EVENT_MAX
|
||||
} pcnt_ll_watch_event_id_t;
|
||||
|
||||
#define PCNT_LL_EVENT_MASK ((1 << PCNT_LL_EVENT_MAX) - 1)
|
||||
#define PCNT_LL_WATCH_EVENT_MASK ((1 << PCNT_LL_WATCH_EVENT_MAX) - 1)
|
||||
#define PCNT_LL_UNIT_WATCH_EVENT(unit_id) (1 << (unit_id))
|
||||
|
||||
/**
|
||||
* @brief Set PCNT channel edge action
|
||||
@ -156,7 +153,8 @@ static inline void pcnt_ll_enable_intr(pcnt_dev_t *hw, uint32_t unit_mask, bool
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @return Interrupt status word
|
||||
*/
|
||||
__attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
|
||||
{
|
||||
return hw->int_st.val;
|
||||
}
|
||||
@ -167,7 +165,8 @@ __attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pc
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param status value to clear interrupt status
|
||||
*/
|
||||
__attribute__((always_inline)) static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
|
||||
__attribute__((always_inline))
|
||||
static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
|
||||
{
|
||||
hw->int_clr.val = status;
|
||||
}
|
||||
@ -233,7 +232,7 @@ static inline void pcnt_ll_enable_thres_event(pcnt_dev_t *hw, uint32_t unit, uin
|
||||
*/
|
||||
static inline void pcnt_ll_disable_all_events(pcnt_dev_t *hw, uint32_t unit)
|
||||
{
|
||||
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_EVENT_MASK << 11);
|
||||
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_WATCH_EVENT_MASK << 11);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -344,13 +343,14 @@ static inline uint32_t pcnt_ll_get_unit_status(pcnt_dev_t *hw, uint32_t unit)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get PCNT count sign
|
||||
* @brief Get PCNT zero cross mode
|
||||
*
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param unit PCNT unit number
|
||||
* @return Count sign
|
||||
* @return Zero cross mode
|
||||
*/
|
||||
static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint32_t unit)
|
||||
__attribute__((always_inline))
|
||||
static inline pcnt_unit_zero_cross_mode_t pcnt_ll_get_zero_cross_mode(pcnt_dev_t *hw, uint32_t unit)
|
||||
{
|
||||
return hw->status_unit[unit].val & 0x03;
|
||||
}
|
||||
@ -362,6 +362,7 @@ static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint
|
||||
* @param unit PCNT unit number
|
||||
* @return Event status word
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit)
|
||||
{
|
||||
return hw->status_unit[unit].val >> 2;
|
||||
@ -404,6 +405,18 @@ static inline void pcnt_ll_enable_glitch_filter(pcnt_dev_t *hw, uint32_t unit, b
|
||||
hw->conf_unit[unit].conf0.filter_en_un = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get interrupt status register address.
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
*
|
||||
* @return Interrupt status register address
|
||||
*/
|
||||
static inline volatile void *pcnt_ll_get_intr_status_reg(pcnt_dev_t *hw)
|
||||
{
|
||||
return &hw->int_st.val;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -1,16 +1,8 @@
|
||||
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* NOTICE
|
||||
@ -18,22 +10,19 @@
|
||||
* See readme.md in hal/include/hal/readme.md
|
||||
******************************************************************************/
|
||||
|
||||
// The HAL layer for PCNT.
|
||||
// There is no parameter check in the hal layer, so the caller must ensure the correctness of the parameters.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/pcnt_struct.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct pcnt_dev_t *pcnt_soc_handle_t; // PCNT SOC layer handle
|
||||
|
||||
/**
|
||||
* Context that should be maintained by both the driver and the HAL
|
||||
*/
|
||||
typedef struct {
|
||||
pcnt_dev_t *dev; /*!< PCNT peripheral register base address */
|
||||
pcnt_soc_handle_t dev; // PCNT SOC layer handle
|
||||
} pcnt_hal_context_t;
|
||||
|
||||
/**
|
||||
|
@ -1,16 +1,8 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -20,7 +12,6 @@ extern "C" {
|
||||
|
||||
/**
|
||||
* @brief PCNT channel action on control level
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
PCNT_CHANNEL_LEVEL_ACTION_KEEP, /*!< Keep current count mode */
|
||||
@ -30,7 +21,6 @@ typedef enum {
|
||||
|
||||
/**
|
||||
* @brief PCNT channel action on signal edge
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
PCNT_CHANNEL_EDGE_ACTION_HOLD, /*!< Hold current count value */
|
||||
@ -39,15 +29,14 @@ typedef enum {
|
||||
} pcnt_channel_edge_action_t;
|
||||
|
||||
/**
|
||||
* @brief PCNT unit counter value's sign
|
||||
*
|
||||
* @brief PCNT unit zero cross mode
|
||||
*/
|
||||
typedef enum {
|
||||
PCNT_UNIT_COUNT_SIGN_ZERO_POS, /*!< positive value to zero */
|
||||
PCNT_UNIT_COUNT_SIGN_ZERO_NEG, /*!< negative value to zero */
|
||||
PCNT_UNIT_COUNT_SIGN_NEG, /*!< counter value negative */
|
||||
PCNT_UNIT_COUNT_SIGN_POS, /*!< counter value positive */
|
||||
} pcnt_unit_count_sign_t;
|
||||
PCNT_UNIT_ZERO_CROSS_POS_ZERO, /*!< start from positive value, end to zero, i.e. +N->0 */
|
||||
PCNT_UNIT_ZERO_CROSS_NEG_ZERO, /*!< start from negative value, end to zero, i.e. -N->0 */
|
||||
PCNT_UNIT_ZERO_CROSS_NEG_POS, /*!< start from negative value, end to positive value, i.e. -N->+M */
|
||||
PCNT_UNIT_ZERO_CROSS_POS_NEG, /*!< start from positive value, end to negative value, i.e. +N->-M */
|
||||
} pcnt_unit_zero_cross_mode_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -1,16 +1,8 @@
|
||||
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// The HAL layer for PCNT (common part)
|
||||
|
||||
|
@ -1,21 +1,13 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef _SOC_PCNT_REG_H_
|
||||
#define _SOC_PCNT_REG_H_
|
||||
#include <stdint.h>
|
||||
#include "soc/soc.h"
|
||||
|
||||
|
||||
#include "soc.h"
|
||||
#define PCNT_U0_CONF0_REG (DR_REG_PCNT_BASE + 0x0000)
|
||||
/* PCNT_CH1_LCTRL_MODE_U0 : R/W ;bitpos:[31:30] ;default: 2'd0 ; */
|
||||
/*description: This register is used to control the mode of channel1's low control
|
||||
@ -1517,8 +1509,3 @@
|
||||
#define PCNT_DATE_M ((PCNT_DATE_V)<<(PCNT_DATE_S))
|
||||
#define PCNT_DATE_V 0xFFFFFFFF
|
||||
#define PCNT_DATE_S 0
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /*_SOC_PCNT_REG_H_ */
|
||||
|
@ -1,18 +1,9 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef _SOC_PCNT_STRUCT_H_
|
||||
#define _SOC_PCNT_STRUCT_H_
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@ -20,8 +11,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef volatile struct pcnt_dev_s {
|
||||
struct {
|
||||
typedef struct pcnt_dev_t {
|
||||
volatile struct {
|
||||
union {
|
||||
struct {
|
||||
uint32_t filter_thres: 10; /*This register is used to filter pulse whose width is smaller than this value for unit0.*/
|
||||
@ -44,27 +35,27 @@ typedef volatile struct pcnt_dev_s {
|
||||
} conf0;
|
||||
union {
|
||||
struct {
|
||||
uint32_t cnt_thres0:16; /*This register is used to configure thres0 value for unit0.*/
|
||||
uint32_t cnt_thres1:16; /*This register is used to configure thres1 value for unit0.*/
|
||||
uint32_t cnt_thres0: 16; /*This register is used to configure thres0 value for unit0.*/
|
||||
uint32_t cnt_thres1: 16; /*This register is used to configure thres1 value for unit0.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} conf1;
|
||||
union {
|
||||
struct {
|
||||
uint32_t cnt_h_lim:16; /*This register is used to configure thr_h_lim value for unit0.*/
|
||||
uint32_t cnt_l_lim:16; /*This register is used to configure thr_l_lim value for unit0.*/
|
||||
uint32_t cnt_h_lim: 16; /*This register is used to configure thr_h_lim value for unit0.*/
|
||||
uint32_t cnt_l_lim: 16; /*This register is used to configure thr_l_lim value for unit0.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} conf2;
|
||||
} conf_unit[8];
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t cnt_val : 16; /*This register stores the current pulse count value for unit0.*/
|
||||
uint32_t reserved16: 16;
|
||||
};
|
||||
uint32_t val;
|
||||
} cnt_unit[8];
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t cnt_thr_event_u0: 1; /*This is the interrupt raw bit for channel0 event.*/
|
||||
uint32_t cnt_thr_event_u1: 1; /*This is the interrupt raw bit for channel1 event.*/
|
||||
@ -78,7 +69,7 @@ typedef volatile struct pcnt_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} int_raw;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t cnt_thr_event_u0: 1; /*This is the interrupt status bit for channel0 event.*/
|
||||
uint32_t cnt_thr_event_u1: 1; /*This is the interrupt status bit for channel1 event.*/
|
||||
@ -92,7 +83,7 @@ typedef volatile struct pcnt_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} int_st;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t cnt_thr_event_u0: 1; /*This is the interrupt enable bit for channel0 event.*/
|
||||
uint32_t cnt_thr_event_u1: 1; /*This is the interrupt enable bit for channel1 event.*/
|
||||
@ -106,7 +97,7 @@ typedef volatile struct pcnt_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} int_ena;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t cnt_thr_event_u0: 1; /*Set this bit to clear channel0 event interrupt.*/
|
||||
uint32_t cnt_thr_event_u1: 1; /*Set this bit to clear channel1 event interrupt.*/
|
||||
@ -120,19 +111,19 @@ typedef volatile struct pcnt_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} int_clr;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t cnt_mode:2; /*0: positive value to zero; 1: negative value to zero; 2: counter value negative ; 3: counter value positive*/
|
||||
uint32_t thres1_lat:1; /* counter value equals to thresh1*/
|
||||
uint32_t thres0_lat:1; /* counter value equals to thresh0*/
|
||||
uint32_t l_lim_lat:1; /* counter value reaches h_lim*/
|
||||
uint32_t h_lim_lat:1; /* counter value reaches l_lim*/
|
||||
uint32_t zero_lat:1; /* counter value equals zero*/
|
||||
uint32_t reserved7:25;
|
||||
uint32_t cnt_mode: 2; /*0: positive value to zero; 1: negative value to zero; 2: counter value negative ; 3: counter value positive*/
|
||||
uint32_t thres1_lat: 1; /* counter value equals to thresh1*/
|
||||
uint32_t thres0_lat: 1; /* counter value equals to thresh0*/
|
||||
uint32_t l_lim_lat: 1; /* counter value reaches h_lim*/
|
||||
uint32_t h_lim_lat: 1; /* counter value reaches l_lim*/
|
||||
uint32_t zero_lat: 1; /* counter value equals zero*/
|
||||
uint32_t reserved7: 25;
|
||||
};
|
||||
uint32_t val;
|
||||
} status_unit[8];
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t cnt_rst_u0: 1; /*Set this bit to clear unit0's counter.*/
|
||||
uint32_t cnt_pause_u0: 1; /*Set this bit to pause unit0's counter.*/
|
||||
@ -173,12 +164,11 @@ typedef volatile struct pcnt_dev_s {
|
||||
uint32_t reserved_f0;
|
||||
uint32_t reserved_f4;
|
||||
uint32_t reserved_f8;
|
||||
uint32_t date; /**/
|
||||
volatile uint32_t date;
|
||||
} pcnt_dev_t;
|
||||
|
||||
extern pcnt_dev_t PCNT;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SOC_PCNT_STRUCT_H_ */
|
||||
|
@ -1,16 +1,8 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/pcnt_periph.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
|
@ -1,16 +1,7 @@
|
||||
/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
@ -1,16 +1,7 @@
|
||||
/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
@ -388,7 +379,7 @@ typedef union {
|
||||
} pcnt_date_reg_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
typedef struct pcnt_dev_t {
|
||||
volatile struct {
|
||||
pcnt_un_conf0_reg_t conf0;
|
||||
pcnt_un_conf1_reg_t conf1;
|
||||
|
@ -1,16 +1,8 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/pcnt_periph.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
|
@ -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
|
@ -1,16 +1,7 @@
|
||||
/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
@ -1,16 +1,7 @@
|
||||
/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
@ -388,7 +379,7 @@ typedef union {
|
||||
} pcnt_date_reg_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
typedef struct pcnt_dev_t {
|
||||
volatile struct {
|
||||
pcnt_un_conf0_reg_t conf0;
|
||||
pcnt_un_conf1_reg_t conf1;
|
||||
|
@ -1,16 +1,8 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/pcnt_periph.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
|
@ -1,16 +1,8 @@
|
||||
// Copyright 2019-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
@ -818,7 +818,6 @@ components/hal/esp32/include/hal/dac_ll.h
|
||||
components/hal/esp32/include/hal/i2c_ll.h
|
||||
components/hal/esp32/include/hal/interrupt_controller_ll.h
|
||||
components/hal/esp32/include/hal/mpu_ll.h
|
||||
components/hal/esp32/include/hal/pcnt_ll.h
|
||||
components/hal/esp32/include/hal/rtc_cntl_ll.h
|
||||
components/hal/esp32/include/hal/rtc_io_ll.h
|
||||
components/hal/esp32/include/hal/rwdt_ll.h
|
||||
@ -901,7 +900,6 @@ components/hal/esp32s2/include/hal/interrupt_controller_ll.h
|
||||
components/hal/esp32s2/include/hal/memprot_ll.h
|
||||
components/hal/esp32s2/include/hal/memprot_peri_ll.h
|
||||
components/hal/esp32s2/include/hal/mpu_ll.h
|
||||
components/hal/esp32s2/include/hal/pcnt_ll.h
|
||||
components/hal/esp32s2/include/hal/rtc_cntl_ll.h
|
||||
components/hal/esp32s2/include/hal/rtc_io_ll.h
|
||||
components/hal/esp32s2/include/hal/sha_ll.h
|
||||
@ -927,7 +925,6 @@ components/hal/esp32s3/include/hal/i2c_ll.h
|
||||
components/hal/esp32s3/include/hal/interrupt_controller_ll.h
|
||||
components/hal/esp32s3/include/hal/memprot_ll.h
|
||||
components/hal/esp32s3/include/hal/mpu_ll.h
|
||||
components/hal/esp32s3/include/hal/pcnt_ll.h
|
||||
components/hal/esp32s3/include/hal/rtc_cntl_ll.h
|
||||
components/hal/esp32s3/include/hal/rwdt_ll.h
|
||||
components/hal/esp32s3/include/hal/sha_ll.h
|
||||
@ -958,8 +955,6 @@ components/hal/include/hal/mcpwm_hal.h
|
||||
components/hal/include/hal/mcpwm_types.h
|
||||
components/hal/include/hal/mpu_hal.h
|
||||
components/hal/include/hal/mpu_types.h
|
||||
components/hal/include/hal/pcnt_hal.h
|
||||
components/hal/include/hal/pcnt_types.h
|
||||
components/hal/include/hal/rmt_types.h
|
||||
components/hal/include/hal/rtc_io_types.h
|
||||
components/hal/include/hal/sdio_slave_hal.h
|
||||
@ -987,7 +982,6 @@ components/hal/interrupt_controller_hal.c
|
||||
components/hal/ledc_hal_iram.c
|
||||
components/hal/mcpwm_hal.c
|
||||
components/hal/mpu_hal.c
|
||||
components/hal/pcnt_hal.c
|
||||
components/hal/platform_port/include/hal/assert.h
|
||||
components/hal/platform_port/include/hal/check.h
|
||||
components/hal/platform_port/include/hal/log.h
|
||||
@ -1315,8 +1309,6 @@ components/soc/esp32/include/soc/ledc_reg.h
|
||||
components/soc/esp32/include/soc/ledc_struct.h
|
||||
components/soc/esp32/include/soc/mmu.h
|
||||
components/soc/esp32/include/soc/nrx_reg.h
|
||||
components/soc/esp32/include/soc/pcnt_reg.h
|
||||
components/soc/esp32/include/soc/pcnt_struct.h
|
||||
components/soc/esp32/include/soc/pid.h
|
||||
components/soc/esp32/include/soc/reset_reasons.h
|
||||
components/soc/esp32/include/soc/rmt_reg.h
|
||||
@ -1352,7 +1344,6 @@ components/soc/esp32/include/soc/wdev_reg.h
|
||||
components/soc/esp32/interrupts.c
|
||||
components/soc/esp32/ledc_periph.c
|
||||
components/soc/esp32/mcpwm_periph.c
|
||||
components/soc/esp32/pcnt_periph.c
|
||||
components/soc/esp32/rmt_periph.c
|
||||
components/soc/esp32/sdio_slave_periph.c
|
||||
components/soc/esp32/sdmmc_periph.c
|
||||
@ -1511,8 +1502,6 @@ components/soc/esp32s2/include/soc/ledc_struct.h
|
||||
components/soc/esp32s2/include/soc/memprot_defs.h
|
||||
components/soc/esp32s2/include/soc/mmu.h
|
||||
components/soc/esp32s2/include/soc/nrx_reg.h
|
||||
components/soc/esp32s2/include/soc/pcnt_reg.h
|
||||
components/soc/esp32s2/include/soc/pcnt_struct.h
|
||||
components/soc/esp32s2/include/soc/reset_reasons.h
|
||||
components/soc/esp32s2/include/soc/rtc_cntl_reg.h
|
||||
components/soc/esp32s2/include/soc/rtc_cntl_struct.h
|
||||
@ -1556,7 +1545,6 @@ components/soc/esp32s2/include/soc/usbh_struct.h
|
||||
components/soc/esp32s2/include/soc/wdev_reg.h
|
||||
components/soc/esp32s2/interrupts.c
|
||||
components/soc/esp32s2/ledc_periph.c
|
||||
components/soc/esp32s2/pcnt_periph.c
|
||||
components/soc/esp32s2/rmt_periph.c
|
||||
components/soc/esp32s2/sigmadelta_periph.c
|
||||
components/soc/esp32s2/spi_periph.c
|
||||
@ -1605,9 +1593,6 @@ components/soc/esp32s3/include/soc/ledc_struct.h
|
||||
components/soc/esp32s3/include/soc/mmu.h
|
||||
components/soc/esp32s3/include/soc/mpu_caps.h
|
||||
components/soc/esp32s3/include/soc/nrx_reg.h
|
||||
components/soc/esp32s3/include/soc/pcnt_caps.h
|
||||
components/soc/esp32s3/include/soc/pcnt_reg.h
|
||||
components/soc/esp32s3/include/soc/pcnt_struct.h
|
||||
components/soc/esp32s3/include/soc/peri_backup_reg.h
|
||||
components/soc/esp32s3/include/soc/peri_backup_struct.h
|
||||
components/soc/esp32s3/include/soc/reset_reasons.h
|
||||
@ -1663,7 +1648,6 @@ components/soc/esp32s3/include/soc/world_controller_struct.h
|
||||
components/soc/esp32s3/interrupts.c
|
||||
components/soc/esp32s3/ledc_periph.c
|
||||
components/soc/esp32s3/mcpwm_periph.c
|
||||
components/soc/esp32s3/pcnt_periph.c
|
||||
components/soc/esp32s3/rmt_periph.c
|
||||
components/soc/esp32s3/rtc_io_periph.c
|
||||
components/soc/esp32s3/sdio_slave_periph.c
|
||||
@ -1683,7 +1667,6 @@ components/soc/include/soc/i2c_periph.h
|
||||
components/soc/include/soc/interrupts.h
|
||||
components/soc/include/soc/ledc_periph.h
|
||||
components/soc/include/soc/mcpwm_periph.h
|
||||
components/soc/include/soc/pcnt_periph.h
|
||||
components/soc/include/soc/rmt_periph.h
|
||||
components/soc/include/soc/rtc_cntl_periph.h
|
||||
components/soc/include/soc/rtc_periph.h
|
||||
@ -2223,10 +2206,6 @@ examples/peripherals/mcpwm/mcpwm_brushed_dc_control/main/mcpwm_brushed_dc_contro
|
||||
examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/main/mcpwm_capture_hc_sr04.c
|
||||
examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example_main.c
|
||||
examples/peripherals/mcpwm/mcpwm_sync_example/main/mcpwm_sync_example.c
|
||||
examples/peripherals/pcnt/pulse_count_event/main/pcnt_event_example_main.c
|
||||
examples/peripherals/pcnt/rotary_encoder/components/rotary_encoder/include/rotary_encoder.h
|
||||
examples/peripherals/pcnt/rotary_encoder/components/rotary_encoder/src/rotary_encoder_pcnt_ec11.c
|
||||
examples/peripherals/pcnt/rotary_encoder/main/rotary_encoder_example_main.c
|
||||
examples/peripherals/rmt/ir_protocols/components/infrared_tools/include/ir_timings.h
|
||||
examples/peripherals/rmt/ir_protocols/components/infrared_tools/src/ir_builder_rmt_nec.c
|
||||
examples/peripherals/rmt/ir_protocols/components/infrared_tools/src/ir_builder_rmt_rc5.c
|
||||
|
Loading…
Reference in New Issue
Block a user