Merge branch 'feature/pcnt_driver_ng' into 'master'

 Driver-NG: Introduce a brand new driver for PCNT

Closes IDF-2196 and IDF-2896

See merge request espressif/esp-idf!10507
This commit is contained in:
morris
2022-03-04 12:25:35 +08:00
74 changed files with 2778 additions and 1475 deletions

View File

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

View File

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

View File

@@ -1,109 +1,23 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "freertos/FreeRTOS.h"
#include "esp_types.h"
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_intr_alloc.h"
#include "driver/gpio.h"
#include "soc/soc_caps.h"
#include "hal/pcnt_types.h"
#include "driver/pcnt_types_legacy.h"
#if !CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN
#warning "legacy pcnt driver is deprecated, please migrate to use driver/pulse_cnt.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define PCNT_PIN_NOT_USED (-1) /*!< When selected for a pin, this pin will not be used */
typedef intr_handle_t pcnt_isr_handle_t;
/**
* @brief PCNT port number, the max port number is (PCNT_PORT_MAX - 1).
*/
typedef enum {
PCNT_PORT_0, /*!< PCNT port 0 */
PCNT_PORT_MAX, /*!< PCNT port max */
} pcnt_port_t;
/**
* @brief Selection of all available PCNT units
*/
typedef enum {
PCNT_UNIT_0, /*!< PCNT unit 0 */
PCNT_UNIT_1, /*!< PCNT unit 1 */
PCNT_UNIT_2, /*!< PCNT unit 2 */
PCNT_UNIT_3, /*!< PCNT unit 3 */
#if SOC_PCNT_UNITS_PER_GROUP > 4
PCNT_UNIT_4, /*!< PCNT unit 4 */
PCNT_UNIT_5, /*!< PCNT unit 5 */
PCNT_UNIT_6, /*!< PCNT unit 6 */
PCNT_UNIT_7, /*!< PCNT unit 7 */
#endif
PCNT_UNIT_MAX,
} pcnt_unit_t;
/**
* @brief Selection of channels available for a single PCNT unit
*/
typedef enum {
PCNT_CHANNEL_0, /*!< PCNT channel 0 */
PCNT_CHANNEL_1, /*!< PCNT channel 1 */
PCNT_CHANNEL_MAX,
} pcnt_channel_t;
/**
* @brief Selection of counter's events the may trigger an interrupt
*/
typedef enum {
PCNT_EVT_THRES_1 = 1 << 2, /*!< PCNT watch point event: threshold1 value event */
PCNT_EVT_THRES_0 = 1 << 3, /*!< PCNT watch point event: threshold0 value event */
PCNT_EVT_L_LIM = 1 << 4, /*!< PCNT watch point event: Minimum counter value */
PCNT_EVT_H_LIM = 1 << 5, /*!< PCNT watch point event: Maximum counter value */
PCNT_EVT_ZERO = 1 << 6, /*!< PCNT watch point event: counter value zero event */
PCNT_EVT_MAX
} pcnt_evt_type_t;
/**
* @brief Selection of available modes that determine the counter's action depending on the state of the control signal's input GPIO
* @note Configuration covers two actions, one for high, and one for low level on the control input
*/
typedef pcnt_channel_level_action_t pcnt_ctrl_mode_t;
#define PCNT_MODE_KEEP PCNT_CHANNEL_LEVEL_ACTION_KEEP /*!< Control mode: won't change counter mode*/
#define PCNT_MODE_REVERSE PCNT_CHANNEL_LEVEL_ACTION_INVERSE /*!< Control mode: invert counter mode(increase -> decrease, decrease -> increase) */
#define PCNT_MODE_DISABLE PCNT_CHANNEL_LEVEL_ACTION_HOLD /*!< Control mode: Inhibit counter(counter value will not change in this condition) */
#define PCNT_MODE_MAX 3
/**
* @brief Selection of available modes that determine the counter's action on the edge of the pulse signal's input GPIO
* @note Configuration covers two actions, one for positive, and one for negative edge on the pulse input
*/
typedef pcnt_channel_edge_action_t pcnt_count_mode_t;
#define PCNT_COUNT_DIS PCNT_CHANNEL_EDGE_ACTION_HOLD /*!< Counter mode: Inhibit counter(counter value will not change in this condition) */
#define PCNT_COUNT_INC PCNT_CHANNEL_EDGE_ACTION_INCREASE /*!< Counter mode: Increase counter value */
#define PCNT_COUNT_DEC PCNT_CHANNEL_EDGE_ACTION_DECREASE /*!< Counter mode: Decrease counter value */
#define PCNT_COUNT_MAX 3
/**
* @brief Pulse Counter configuration for a single channel
*/
typedef struct {
int pulse_gpio_num; /*!< Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored */
int ctrl_gpio_num; /*!< Control signal input GPIO number, a negative value will be ignored */
pcnt_ctrl_mode_t lctrl_mode; /*!< PCNT low control mode */
pcnt_ctrl_mode_t hctrl_mode; /*!< PCNT high control mode */
pcnt_count_mode_t pos_mode; /*!< PCNT positive edge count mode */
pcnt_count_mode_t neg_mode; /*!< PCNT negative edge count mode */
int16_t counter_h_lim; /*!< Maximum counter value */
int16_t counter_l_lim; /*!< Minimum counter value */
pcnt_unit_t unit; /*!< PCNT unit number */
pcnt_channel_t channel; /*!< the PCNT channel */
} pcnt_config_t;
/**
* @brief Configure Pulse Counter unit
* @note

View File

@@ -0,0 +1,108 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_intr_alloc.h"
#include "soc/soc_caps.h"
#include "hal/pcnt_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#define PCNT_PIN_NOT_USED (-1) /*!< When selected for a pin, this pin will not be used */
/**
* @brief PCNT interrupt handle
*/
typedef intr_handle_t pcnt_isr_handle_t;
/**
* @brief PCNT port number, the max port number is (PCNT_PORT_MAX - 1).
*/
typedef enum {
PCNT_PORT_0, /*!< PCNT port 0 */
PCNT_PORT_MAX, /*!< PCNT port max */
} pcnt_port_t;
/**
* @brief Selection of all available PCNT units
*/
typedef enum {
PCNT_UNIT_0, /*!< PCNT unit 0 */
PCNT_UNIT_1, /*!< PCNT unit 1 */
PCNT_UNIT_2, /*!< PCNT unit 2 */
PCNT_UNIT_3, /*!< PCNT unit 3 */
#if SOC_PCNT_UNITS_PER_GROUP > 4
PCNT_UNIT_4, /*!< PCNT unit 4 */
PCNT_UNIT_5, /*!< PCNT unit 5 */
PCNT_UNIT_6, /*!< PCNT unit 6 */
PCNT_UNIT_7, /*!< PCNT unit 7 */
#endif
PCNT_UNIT_MAX,
} pcnt_unit_t;
/**
* @brief Selection of channels available for a single PCNT unit
*/
typedef enum {
PCNT_CHANNEL_0, /*!< PCNT channel 0 */
PCNT_CHANNEL_1, /*!< PCNT channel 1 */
PCNT_CHANNEL_MAX,
} pcnt_channel_t;
/**
* @brief Selection of counter's events the may trigger an interrupt
*/
typedef enum {
PCNT_EVT_THRES_1 = 1 << 2, /*!< PCNT watch point event: threshold1 value event */
PCNT_EVT_THRES_0 = 1 << 3, /*!< PCNT watch point event: threshold0 value event */
PCNT_EVT_L_LIM = 1 << 4, /*!< PCNT watch point event: Minimum counter value */
PCNT_EVT_H_LIM = 1 << 5, /*!< PCNT watch point event: Maximum counter value */
PCNT_EVT_ZERO = 1 << 6, /*!< PCNT watch point event: counter value zero event */
PCNT_EVT_MAX
} pcnt_evt_type_t;
/**
* @brief Selection of available modes that determine the counter's action depending on the state of the control signal's input GPIO
* @note Configuration covers two actions, one for high, and one for low level on the control input
*/
typedef pcnt_channel_level_action_t pcnt_ctrl_mode_t;
#define PCNT_MODE_KEEP PCNT_CHANNEL_LEVEL_ACTION_KEEP /*!< Control mode: won't change counter mode*/
#define PCNT_MODE_REVERSE PCNT_CHANNEL_LEVEL_ACTION_INVERSE /*!< Control mode: invert counter mode(increase -> decrease, decrease -> increase) */
#define PCNT_MODE_DISABLE PCNT_CHANNEL_LEVEL_ACTION_HOLD /*!< Control mode: Inhibit counter(counter value will not change in this condition) */
#define PCNT_MODE_MAX 3
/**
* @brief Selection of available modes that determine the counter's action on the edge of the pulse signal's input GPIO
* @note Configuration covers two actions, one for positive, and one for negative edge on the pulse input
*/
typedef pcnt_channel_edge_action_t pcnt_count_mode_t;
#define PCNT_COUNT_DIS PCNT_CHANNEL_EDGE_ACTION_HOLD /*!< Counter mode: Inhibit counter(counter value will not change in this condition) */
#define PCNT_COUNT_INC PCNT_CHANNEL_EDGE_ACTION_INCREASE /*!< Counter mode: Increase counter value */
#define PCNT_COUNT_DEC PCNT_CHANNEL_EDGE_ACTION_DECREASE /*!< Counter mode: Decrease counter value */
#define PCNT_COUNT_MAX 3
/**
* @brief Pulse Counter configuration for a single channel
*/
typedef struct {
int pulse_gpio_num; /*!< Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored */
int ctrl_gpio_num; /*!< Control signal input GPIO number, a negative value will be ignored */
pcnt_ctrl_mode_t lctrl_mode; /*!< PCNT low control mode */
pcnt_ctrl_mode_t hctrl_mode; /*!< PCNT high control mode */
pcnt_count_mode_t pos_mode; /*!< PCNT positive edge count mode */
pcnt_count_mode_t neg_mode; /*!< PCNT negative edge count mode */
int16_t counter_h_lim; /*!< Maximum counter value */
int16_t counter_l_lim; /*!< Minimum counter value */
pcnt_unit_t unit; /*!< PCNT unit number */
pcnt_channel_t channel; /*!< the PCNT channel */
} pcnt_config_t;
#ifdef __cplusplus
}
#endif

View File

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

View File

@@ -132,7 +132,9 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel);
/**
* @brief Set LEDC output gpio.
* @deprecated This function is redundant, please use ledc_channel_config to set gpio pins.
*
* @note This function only routes the LEDC signal to GPIO through matrix, other LEDC resources initialization are not involved.
* Please use `ledc_channel_config()` instead to fully configure a LEDC channel.
*
* @param gpio_num The LEDC output gpio
* @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.
@@ -142,8 +144,8 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel);
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel)
__attribute__((deprecated("use ledc_channel_config instead")));
esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel);
/**
* @brief LEDC stop.
* Disable LEDC output, and set idle level

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -8,7 +8,8 @@
#include "esp_check.h"
#include "soc/soc_caps.h"
#include "esp_private/periph_ctrl.h"
#include "driver/pcnt.h"
#include "driver/pcnt_types_legacy.h"
#include "driver/gpio.h"
#include "hal/pcnt_hal.h"
#include "hal/pcnt_ll.h"
#include "hal/gpio_hal.h"
@@ -54,6 +55,9 @@ static pcnt_isr_func_t *pcnt_isr_func = NULL;
static pcnt_isr_handle_t pcnt_isr_service = NULL;
static portMUX_TYPE pcnt_spinlock = portMUX_INITIALIZER_UNLOCKED;
esp_err_t pcnt_isr_register(void (*fun)(void *), void *arg, int intr_alloc_flags, pcnt_isr_handle_t *handle);
esp_err_t pcnt_isr_unregister(pcnt_isr_handle_t handle);
static inline esp_err_t _pcnt_set_mode(pcnt_port_t pcnt_port, pcnt_unit_t unit, pcnt_channel_t channel, pcnt_count_mode_t pos_mode, pcnt_count_mode_t neg_mode, pcnt_ctrl_mode_t hctrl_mode, pcnt_ctrl_mode_t lctrl_mode)
{
PCNT_OBJ_CHECK(pcnt_port);
@@ -537,7 +541,22 @@ esp_err_t pcnt_isr_service_install(int intr_alloc_flags)
return _pcnt_isr_service_install(PCNT_PORT_0, intr_alloc_flags);
}
void pcnt_isr_service_uninstall()
void pcnt_isr_service_uninstall(void)
{
_pcnt_isr_service_uninstall(PCNT_PORT_0);
}
/**
* @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_pcnt_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();
}
ESP_EARLY_LOGW(TAG, "legacy pcnt driver is deprecated, please migrate to use driver/pulse_cnt.h");
}

View File

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

View File

@@ -6,10 +6,6 @@
/* LEDC tested by PCNT in some case
* PCNT can get the LEDC waveform frequency
*
* test environment of UT_T1_LEDC:
* 1. connect GPIO18 with GPIO4
* 2. connect GPIO5 to 3.3v (in case of it is pulled down by default)
*
* some calculation related with duty:
* real duty = duty/2^duty_resolution
*/
@@ -24,15 +20,13 @@
#include "freertos/queue.h"
#include "unity.h"
#include "soc/ledc_periph.h"
#include "soc/gpio_periph.h"
#include "soc/io_mux_reg.h"
#include "esp_system.h"
#include "driver/ledc.h"
#include "driver/gpio.h"
#define PULSE_IO 18
#define PCNT_INPUT_IO 4
#define PCNT_CTRL_FLOATING_IO 5
#define HIGHEST_LIMIT 10000
#define LOWEST_LIMIT -10000
#define PULSE_IO 18
#define TEST_PWM_FREQ 2000
@@ -116,81 +110,6 @@ static void timer_duty_test(ledc_channel_t channel, ledc_timer_bit_t timer_bit,
timer_duty_set_get(ledc_ch_config.speed_mode, ledc_ch_config.channel, (1 << 13) - 2);
}
#if SOC_PCNT_SUPPORTED
#include "driver/pcnt.h" // TODO: C3 doesn't have PCNT peripheral
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3)
//no runners
// use PCNT to test the waveform of LEDC
static int16_t wave_count(int last_time)
{
int16_t test_counter;
pcnt_config_t pcnt_config = {
.pulse_gpio_num = PCNT_INPUT_IO,
.ctrl_gpio_num = PCNT_CTRL_FLOATING_IO,
.channel = PCNT_CHANNEL_0,
.unit = PCNT_UNIT_0,
.pos_mode = PCNT_COUNT_INC,
.neg_mode = PCNT_COUNT_DIS,
.lctrl_mode = PCNT_MODE_REVERSE,
.hctrl_mode = PCNT_MODE_KEEP,
.counter_h_lim = HIGHEST_LIMIT,
.counter_l_lim = LOWEST_LIMIT,
};
TEST_ESP_OK(pcnt_unit_config(&pcnt_config));
// initialize first
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
vTaskDelay(100 / portTICK_PERIOD_MS);
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
vTaskDelay(last_time / portTICK_PERIOD_MS);
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
return test_counter;
}
// the PCNT will count the frequency of it
static void frequency_set_get(ledc_mode_t speed_mode, ledc_timer_t timer, uint32_t freq_hz, int16_t real_freq, int16_t error)
{
int16_t count;
TEST_ESP_OK(ledc_set_freq(speed_mode, timer, freq_hz));
count = wave_count(1000);
TEST_ASSERT_INT16_WITHIN(error, count, real_freq);
TEST_ASSERT_EQUAL_INT32(ledc_get_freq(speed_mode, timer), real_freq);
}
static void timer_frequency_test(ledc_channel_t channel, ledc_timer_bit_t timer_bit, ledc_timer_t timer, ledc_mode_t speed_mode)
{
ledc_channel_config_t ledc_ch_config = {
.gpio_num = PULSE_IO,
.speed_mode = speed_mode,
.channel = channel,
.intr_type = LEDC_INTR_DISABLE,
.timer_sel = timer,
.duty = 4000,
.hpoint = 0,
};
ledc_timer_config_t ledc_time_config = {
.speed_mode = speed_mode,
.duty_resolution = timer_bit,
.timer_num = timer,
.freq_hz = 5000,
.clk_cfg = LEDC_USE_APB_CLK,
};
TEST_ESP_OK(ledc_channel_config(&ledc_ch_config));
TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 100, 100, 2);
frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 5000, 5000, 5);
frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 9000, 8993, 5);
}
#endif // !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3)
#endif // SOC_PCNT_SUPPORTED
TEST_CASE("LEDC channel config wrong gpio", "[ledc]")
{
ledc_channel_config_t ledc_ch_config = initialize_channel_config();
@@ -324,8 +243,8 @@ TEST_CASE("LEDC set and get duty", "[ledc]")
{
ledc_timer_t timer_list[4] = {LEDC_TIMER_0, LEDC_TIMER_1, LEDC_TIMER_2, LEDC_TIMER_3};
ledc_mode_t speed_mode_list[LEDC_SPEED_MODE_MAX] = SPEED_MODE_LIST;
for(int i=0; i<LEDC_TIMER_MAX-1; i++) {
for(int j=0; j<LEDC_SPEED_MODE_MAX; j++) {
for (int i = 0; i < LEDC_TIMER_MAX - 1; i++) {
for (int j = 0; j < LEDC_SPEED_MODE_MAX; j++) {
timer_duty_test(LEDC_CHANNEL_0, LEDC_TIMER_13_BIT, timer_list[i], speed_mode_list[j]);
}
}
@@ -473,10 +392,88 @@ TEST_CASE("LEDC fade stop test", "[ledc]")
#if SOC_PCNT_SUPPORTED
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3)
#include "driver/pulse_cnt.h"
TEST_CASE("LEDC set and get frequency", "[ledc][test_env=UT_T1_LEDC][timeout=60][ignore]")
#define HIGHEST_LIMIT 10000
#define LOWEST_LIMIT -10000
static pcnt_unit_handle_t pcnt_unit;
static pcnt_channel_handle_t pcnt_chan;
static void setup_testbench(void)
{
pcnt_unit_config_t unit_config = {
.high_limit = HIGHEST_LIMIT,
.low_limit = LOWEST_LIMIT,
};
TEST_ESP_OK(pcnt_new_unit(&unit_config, &pcnt_unit));
pcnt_chan_config_t chan_config = {
.edge_gpio_num = PULSE_IO,
.level_gpio_num = -1,
};
TEST_ESP_OK(pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan));
TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
}
static void tear_testbench(void)
{
TEST_ESP_OK(pcnt_del_channel(pcnt_chan));
TEST_ESP_OK(pcnt_del_unit(pcnt_unit));
}
// use PCNT to test the waveform of LEDC
static int wave_count(int last_time)
{
// The input ability of PULSE_IO is disabled after ledc driver install, so we need to reenable it again
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[PULSE_IO]);
int test_counter = 0;
TEST_ESP_OK(pcnt_unit_clear_count(pcnt_unit));
TEST_ESP_OK(pcnt_unit_start(pcnt_unit));
vTaskDelay(pdMS_TO_TICKS(last_time));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_get_count(pcnt_unit, &test_counter));
return test_counter;
}
// the PCNT will count the frequency of it
static void frequency_set_get(ledc_mode_t speed_mode, ledc_timer_t timer, uint32_t freq_hz, int16_t real_freq, int16_t error)
{
int count;
TEST_ESP_OK(ledc_set_freq(speed_mode, timer, freq_hz));
count = wave_count(1000);
TEST_ASSERT_INT16_WITHIN(error, count, real_freq);
TEST_ASSERT_EQUAL_INT32(real_freq, ledc_get_freq(speed_mode, timer));
}
static void timer_frequency_test(ledc_channel_t channel, ledc_timer_bit_t timer_bit, ledc_timer_t timer, ledc_mode_t speed_mode)
{
ledc_channel_config_t ledc_ch_config = {
.gpio_num = PULSE_IO,
.speed_mode = speed_mode,
.channel = channel,
.intr_type = LEDC_INTR_DISABLE,
.timer_sel = timer,
.duty = 4000,
.hpoint = 0,
};
ledc_timer_config_t ledc_time_config = {
.speed_mode = speed_mode,
.duty_resolution = timer_bit,
.timer_num = timer,
.freq_hz = 5000,
.clk_cfg = LEDC_USE_APB_CLK,
};
TEST_ESP_OK(ledc_channel_config(&ledc_ch_config));
TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 100, 100, 20);
frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 5000, 5000, 50);
frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 9000, 8992, 50);
}
TEST_CASE("LEDC set and get frequency", "[ledc][timeout=60][ignore]")
{
setup_testbench();
#if SOC_LEDC_SUPPORT_HS_MODE
timer_frequency_test(LEDC_CHANNEL_0, LEDC_TIMER_13_BIT, LEDC_TIMER_0, LEDC_HIGH_SPEED_MODE);
timer_frequency_test(LEDC_CHANNEL_0, LEDC_TIMER_13_BIT, LEDC_TIMER_1, LEDC_HIGH_SPEED_MODE);
@@ -487,10 +484,12 @@ TEST_CASE("LEDC set and get frequency", "[ledc][test_env=UT_T1_LEDC][timeout=60]
timer_frequency_test(LEDC_CHANNEL_0, LEDC_TIMER_13_BIT, LEDC_TIMER_1, LEDC_LOW_SPEED_MODE);
timer_frequency_test(LEDC_CHANNEL_0, LEDC_TIMER_13_BIT, LEDC_TIMER_2, LEDC_LOW_SPEED_MODE);
timer_frequency_test(LEDC_CHANNEL_0, LEDC_TIMER_13_BIT, LEDC_TIMER_3, LEDC_LOW_SPEED_MODE);
tear_testbench();
}
TEST_CASE("LEDC timer set", "[ledc][test_env=UT_T1_LEDC]")
TEST_CASE("LEDC timer set", "[ledc]")
{
setup_testbench();
const ledc_mode_t test_speed_mode = TEST_SPEED_MODE;
ledc_channel_config_t ledc_ch_config = {
.gpio_num = PULSE_IO,
@@ -513,7 +512,8 @@ TEST_CASE("LEDC timer set", "[ledc][test_env=UT_T1_LEDC]")
TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
uint32_t freq_get;
uint32_t count;
int count;
#if SOC_LEDC_SUPPORT_REF_TICK
//set timer 0 as 250Hz, use REF_TICK
TEST_ESP_OK(ledc_timer_set(test_speed_mode, LEDC_TIMER_0, 1000, 10, LEDC_REF_TICK));
TEST_ESP_OK(ledc_timer_rst(test_speed_mode, LEDC_TIMER_0));
@@ -521,6 +521,7 @@ TEST_CASE("LEDC timer set", "[ledc][test_env=UT_T1_LEDC]")
freq_get = ledc_get_freq(test_speed_mode, LEDC_TIMER_0);
count = wave_count(1000);
TEST_ASSERT_UINT32_WITHIN(10, count, freq_get);
#endif
//set timer 0 as 500Hz, use APB_CLK
TEST_ESP_OK(ledc_timer_set(test_speed_mode, LEDC_TIMER_0, 5000, 13, LEDC_APB_CLK));
@@ -539,12 +540,14 @@ TEST_CASE("LEDC timer set", "[ledc][test_env=UT_T1_LEDC]")
TEST_ESP_OK(ledc_stop(test_speed_mode, LEDC_CHANNEL_0, !current_level));
vTaskDelay(1000 / portTICK_PERIOD_MS);
TEST_ASSERT_EQUAL_INT32( LEDC.channel_group[test_speed_mode].channel[LEDC_CHANNEL_0].conf0.idle_lv, !current_level);
tear_testbench();
}
TEST_CASE("LEDC timer pause and resume", "[ledc][test_env=UT_T1_LEDC]")
TEST_CASE("LEDC timer pause and resume", "[ledc]")
{
setup_testbench();
const ledc_mode_t test_speed_mode = TEST_SPEED_MODE;
int16_t count;
int count;
ledc_channel_config_t ledc_ch_config = {
.gpio_num = PULSE_IO,
.speed_mode = test_speed_mode,
@@ -588,6 +591,7 @@ TEST_CASE("LEDC timer pause and resume", "[ledc][test_env=UT_T1_LEDC]")
TEST_ESP_OK(ledc_timer_rst(test_speed_mode, LEDC_TIMER_0));
vTaskDelay(100 / portTICK_PERIOD_MS);
TEST_ASSERT_UINT32_WITHIN(5, count, 5000);
tear_testbench();
}
static void ledc_cpu_reset_test_first_stage(void)
@@ -603,17 +607,18 @@ static void ledc_cpu_reset_test_first_stage(void)
static void ledc_cpu_reset_test_second_stage(void)
{
int count;
TEST_ASSERT_EQUAL(ESP_RST_SW, esp_reset_reason());
int16_t count;
setup_testbench();
// reconfigure the GPIO again, as the GPIO output ability has been disabled during initialize pcnt peripheral
ledc_set_pin(PULSE_IO, TEST_SPEED_MODE, LEDC_CHANNEL_0);
count = wave_count(1000);
TEST_ASSERT_UINT32_WITHIN(5, count, TEST_PWM_FREQ);
TEST_ASSERT_UINT32_WITHIN(5, TEST_PWM_FREQ, count);
tear_testbench();
}
TEST_CASE_MULTIPLE_STAGES("LEDC software reset test",
"[ledc][test_env=UT_T1_LEDC]",
TEST_CASE_MULTIPLE_STAGES("LEDC continue work after software reset", "[ledc]",
ledc_cpu_reset_test_first_stage,
ledc_cpu_reset_test_second_stage);
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3)
#endif // SOC_PCNT_SUPPORTED

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -15,12 +15,10 @@
#include "soc/rtc.h"
#if SOC_MCPWM_SUPPORTED
#include "soc/mcpwm_periph.h"
#include "driver/pcnt.h"
#include "driver/pulse_cnt.h"
#include "driver/mcpwm.h"
#include "driver/gpio.h"
#define TEST_PWMA_PCNT_UNIT (0)
#define TEST_PWMB_PCNT_UNIT (1)
#define TEST_PWMA_GPIO (2)
#define TEST_PWMB_GPIO (4)
#define TEST_FAULT_GPIO (21)
@@ -41,6 +39,11 @@ const static mcpwm_io_signals_t sync_io_sig_array[] = {MCPWM_SYNC_0, MCPWM_SYNC_
const static mcpwm_capture_signal_t cap_sig_array[] = {MCPWM_SELECT_CAP0, MCPWM_SELECT_CAP1, MCPWM_SELECT_CAP2};
const static mcpwm_io_signals_t cap_io_sig_array[] = {MCPWM_CAP_0, MCPWM_CAP_1, MCPWM_CAP_2};
static pcnt_unit_handle_t pcnt_unit_a;
static pcnt_channel_handle_t pcnt_chan_a;
static pcnt_unit_handle_t pcnt_unit_b;
static pcnt_channel_handle_t pcnt_chan_b;
// This GPIO init function is almost the same to public API `mcpwm_gpio_init()`, except that
// this function will configure all MCPWM GPIOs into output and input capable
// which is useful to simulate a trigger source
@@ -75,26 +78,9 @@ static esp_err_t test_mcpwm_gpio_init(mcpwm_unit_t mcpwm_num, mcpwm_io_signals_t
static void mcpwm_setup_testbench(mcpwm_unit_t group, mcpwm_timer_t timer, uint32_t pwm_freq, float pwm_duty,
unsigned long int group_resolution, unsigned long int timer_resolution)
{
// PWMA <--> PCNT UNIT0
pcnt_config_t pcnt_config = {
.pulse_gpio_num = TEST_PWMA_GPIO,
.ctrl_gpio_num = -1, // don't care level signal
.channel = PCNT_CHANNEL_0,
.unit = TEST_PWMA_PCNT_UNIT,
.pos_mode = PCNT_COUNT_INC,
.neg_mode = PCNT_COUNT_DIS,
.lctrl_mode = PCNT_MODE_KEEP,
.hctrl_mode = PCNT_MODE_KEEP,
.counter_h_lim = 10000,
.counter_l_lim = -10000,
};
TEST_ESP_OK(pcnt_unit_config(&pcnt_config));
mcpwm_io_signals_t mcpwm_a = pwma[timer];
TEST_ESP_OK(test_mcpwm_gpio_init(group, mcpwm_a, TEST_PWMA_GPIO));
// PWMB <--> PCNT UNIT1
pcnt_config.pulse_gpio_num = TEST_PWMB_GPIO;
pcnt_config.unit = TEST_PWMB_PCNT_UNIT;
TEST_ESP_OK(pcnt_unit_config(&pcnt_config));
mcpwm_io_signals_t mcpwm_b = pwmb[timer];
TEST_ESP_OK(test_mcpwm_gpio_init(group, mcpwm_b, TEST_PWMB_GPIO));
@@ -111,14 +97,53 @@ static void mcpwm_setup_testbench(mcpwm_unit_t group, mcpwm_timer_t timer, uint3
TEST_ESP_OK(mcpwm_init(group, timer, &pwm_config));
}
static uint32_t mcpwm_pcnt_get_pulse_number(pcnt_unit_t pwm_pcnt_unit, int capture_window_ms)
static void pcnt_setup_testbench(void)
{
int16_t count_value = 0;
TEST_ESP_OK(pcnt_counter_pause(pwm_pcnt_unit));
TEST_ESP_OK(pcnt_counter_clear(pwm_pcnt_unit));
TEST_ESP_OK(pcnt_counter_resume(pwm_pcnt_unit));
// PWMA <--> PCNT UNIT0
pcnt_unit_config_t unit_a_config = {
.high_limit = 10000,
.low_limit = -10000,
};
TEST_ESP_OK(pcnt_new_unit(&unit_a_config, &pcnt_unit_a));
pcnt_chan_config_t chan_a_config = {
.edge_gpio_num = TEST_PWMA_GPIO,
.level_gpio_num = -1, // don't care level signal
};
TEST_ESP_OK(pcnt_new_channel(pcnt_unit_a, &chan_a_config, &pcnt_chan_a));
TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan_a, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan_a, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
// PWMB <--> PCNT UNIT1
pcnt_unit_config_t unit_b_config = {
.high_limit = 10000,
.low_limit = -10000,
};
TEST_ESP_OK(pcnt_new_unit(&unit_b_config, &pcnt_unit_b));
pcnt_chan_config_t chan_b_config = {
.edge_gpio_num = TEST_PWMB_GPIO,
.level_gpio_num = -1, // don't care level signal
};
TEST_ESP_OK(pcnt_new_channel(pcnt_unit_b, &chan_b_config, &pcnt_chan_b));
TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan_b, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan_b, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
}
static void pcnt_tear_testbench(void)
{
TEST_ESP_OK(pcnt_del_channel(pcnt_chan_a));
TEST_ESP_OK(pcnt_del_channel(pcnt_chan_b));
TEST_ESP_OK(pcnt_del_unit(pcnt_unit_a));
TEST_ESP_OK(pcnt_del_unit(pcnt_unit_b));
}
static uint32_t pcnt_get_pulse_number(pcnt_unit_handle_t pwm_pcnt_unit, int capture_window_ms)
{
int count_value = 0;
TEST_ESP_OK(pcnt_unit_clear_count(pwm_pcnt_unit));
TEST_ESP_OK(pcnt_unit_start(pwm_pcnt_unit));
usleep(capture_window_ms * 1000);
TEST_ESP_OK(pcnt_get_counter_value(pwm_pcnt_unit, &count_value));
TEST_ESP_OK(pcnt_unit_stop(pwm_pcnt_unit));
TEST_ESP_OK(pcnt_unit_get_count(pwm_pcnt_unit, &count_value));
printf("count value: %d\r\n", count_value);
return (uint32_t)count_value;
}
@@ -163,24 +188,26 @@ static void mcpwm_start_stop_test(mcpwm_unit_t unit, mcpwm_timer_t timer)
{
uint32_t pulse_number = 0;
pcnt_setup_testbench();
mcpwm_setup_testbench(unit, timer, 1000, 50.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ); // Period: 1000us, 1ms
// count the pulse number within 100ms
pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMA_PCNT_UNIT, 100);
pulse_number = pcnt_get_pulse_number(pcnt_unit_a, 100);
TEST_ASSERT_INT_WITHIN(2, 100, pulse_number);
pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMB_PCNT_UNIT, 100);
pulse_number = pcnt_get_pulse_number(pcnt_unit_b, 100);
TEST_ASSERT_INT_WITHIN(2, 100, pulse_number);
TEST_ESP_OK(mcpwm_set_frequency(unit, timer, 100));
pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMB_PCNT_UNIT, 100);
pulse_number = pcnt_get_pulse_number(pcnt_unit_b, 100);
TEST_ASSERT_INT_WITHIN(2, 10, pulse_number);
// stop timer, then no pwm pulse should be generating
TEST_ESP_OK(mcpwm_stop(unit, timer));
usleep(10000); // wait until timer stopped
pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMA_PCNT_UNIT, 100);
pulse_number = pcnt_get_pulse_number(pcnt_unit_a, 100);
TEST_ASSERT_INT_WITHIN(2, 0, pulse_number);
pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMB_PCNT_UNIT, 100);
pulse_number = pcnt_get_pulse_number(pcnt_unit_b, 100);
TEST_ASSERT_INT_WITHIN(2, 0, pulse_number);
pcnt_tear_testbench();
}
TEST_CASE("MCPWM start and stop test", "[mcpwm]")
@@ -229,6 +256,7 @@ static void mcpwm_carrier_test(mcpwm_unit_t unit, mcpwm_timer_t timer, mcpwm_car
{
uint32_t pulse_number = 0;
pcnt_setup_testbench();
mcpwm_setup_testbench(unit, timer, 1000, 50.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ);
mcpwm_set_signal_high(unit, timer, MCPWM_GEN_A);
mcpwm_set_signal_high(unit, timer, MCPWM_GEN_B);
@@ -239,14 +267,15 @@ static void mcpwm_carrier_test(mcpwm_unit_t unit, mcpwm_timer_t timer, mcpwm_car
TEST_ESP_OK(mcpwm_carrier_oneshot_mode_enable(unit, timer, os_width));
vTaskDelay(pdMS_TO_TICKS(100));
pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMA_PCNT_UNIT, 10);
pulse_number = pcnt_get_pulse_number(pcnt_unit_a, 10);
TEST_ASSERT_INT_WITHIN(50, 2500, pulse_number);
usleep(10000);
pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMB_PCNT_UNIT, 10);
pulse_number = pcnt_get_pulse_number(pcnt_unit_b, 10);
TEST_ASSERT_INT_WITHIN(50, 2500, pulse_number);
TEST_ESP_OK(mcpwm_carrier_disable(unit, timer));
TEST_ESP_OK(mcpwm_stop(unit, timer));
pcnt_tear_testbench();
}
TEST_CASE("MCPWM carrier test", "[mcpwm]")
@@ -382,33 +411,37 @@ TEST_CASE("MCPWM timer GPIO sync test", "[mcpwm]")
}
}
static void mcpwm_swsync_test(mcpwm_unit_t unit) {
// used only in this area but need to be reset every time. mutex is not needed
// store timestamps captured from ISR callback
static uint64_t cap_timestamp[3];
// control the start of capture to avoid unstable data
static volatile bool log_cap;
// cb function, to update capture value
// only log when channel1 comes at first, then channel2, and do not log further more.
static bool test_mcpwm_capture_callback(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_channel, const cap_event_data_t *edata,
void *user_data)
{
if (log_cap && (cap_timestamp[1] == 0 || cap_timestamp[2] == 0)) {
if (cap_channel == MCPWM_SELECT_CAP1 && cap_timestamp[1] == 0) {
cap_timestamp[1] = edata->cap_value;
}
if (cap_channel == MCPWM_SELECT_CAP2 && cap_timestamp[1] != 0) {
cap_timestamp[2] = edata->cap_value;
}
}
return false;
}
static void mcpwm_swsync_test(mcpwm_unit_t unit)
{
const uint32_t test_sync_phase = 20;
// used only in this area but need to be reset every time. mutex is not needed
// store timestamps captured from ISR callback
static uint64_t cap_timestamp[3];
cap_timestamp[0] = 0;
cap_timestamp[1] = 0;
cap_timestamp[2] = 0;
// control the start of capture to avoid unstable data
static volatile bool log_cap;
log_cap = false;
// cb function, to update capture value
// only log when channel1 comes at first, then channel2, and do not log further more.
bool capture_callback(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_channel, const cap_event_data_t *edata,
void *user_data) {
if (log_cap && (cap_timestamp[1] == 0 || cap_timestamp[2] == 0)) {
if (cap_channel == MCPWM_SELECT_CAP1 && cap_timestamp[1] == 0) {
cap_timestamp[1] = edata->cap_value;
}
if (cap_channel == MCPWM_SELECT_CAP2 && cap_timestamp[1] != 0) {
cap_timestamp[2] = edata->cap_value;
}
}
return false;
}
// configure all timer output 10% PWM
for (int i = 0; i < 3; ++i) {
mcpwm_setup_testbench(unit, i, 1000, 10.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ);
@@ -420,7 +453,7 @@ static void mcpwm_swsync_test(mcpwm_unit_t unit) {
mcpwm_capture_config_t conf = {
.cap_edge = MCPWM_POS_EDGE,
.cap_prescale = 1,
.capture_cb = capture_callback,
.capture_cb = test_mcpwm_capture_callback,
.user_data = NULL,
};
TEST_ESP_OK(test_mcpwm_gpio_init(unit, MCPWM_CAP_0, TEST_SYNC_GPIO_0));
@@ -477,7 +510,8 @@ typedef struct {
TaskHandle_t task_hdl;
} test_capture_callback_data_t;
static bool test_mcpwm_intr_handler(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_sig, const cap_event_data_t *edata, void *arg) {
static bool test_mcpwm_intr_handler(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_sig, const cap_event_data_t *edata, void *arg)
{
BaseType_t high_task_wakeup = pdFALSE;
test_capture_callback_data_t *cb_data = (test_capture_callback_data_t *)arg;
vTaskNotifyGiveFromISR(cb_data->task_hdl, &high_task_wakeup);
@@ -497,10 +531,10 @@ static void mcpwm_capture_test(mcpwm_unit_t unit, mcpwm_capture_signal_t cap_cha
TEST_ESP_OK(test_mcpwm_gpio_init(unit, cap_io, TEST_CAP_GPIO));
mcpwm_capture_config_t conf = {
.cap_edge = MCPWM_POS_EDGE,
.cap_prescale = 1,
.capture_cb = test_mcpwm_intr_handler,
.user_data = &callback_data
.cap_edge = MCPWM_POS_EDGE,
.cap_prescale = 1,
.capture_cb = test_mcpwm_intr_handler,
.user_data = &callback_data
};
TEST_ESP_OK(mcpwm_capture_enable_channel(unit, cap_channel, &conf));
// generate an posage

View File

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

View File

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

View File

@@ -0,0 +1,5 @@
# 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(legacy_pcnt_driver_test)

View File

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

View File

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

View File

@@ -0,0 +1,40 @@
/*
* 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"
#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)
{
unity_run_menu();
}

View File

@@ -3,24 +3,12 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* this case is used for test PCNT
* prepare job for test environment UT_T1_PCNT:
* We use internal signals instead of external wiring, but please keep the following IO connections, or connect nothing to prevent the signal from being disturbed.
* 1. prepare one ESP-WROOM-32 board and connect it to PC.
* 2. connect GPIO21 with GPIO4
* 3. GPIO5 connect to 3.3v
* 4. GPIO19 connect to GND
* 5. logic analyzer will help too if possible
*
* the GPIO18 is the pulse producer, the GPIO4 is the input GPIO
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "soc/soc_caps.h"
#if SOC_PCNT_SUPPORTED
#include "driver/gpio.h"
#include "driver/pcnt.h"
#include "driver/ledc.h"
@@ -317,7 +305,7 @@ static void count_mode_test(gpio_num_t ctl_io)
}
// test PCNT basic configuration
TEST_CASE("PCNT test config", "[pcnt]")
TEST_CASE("PCNT_test_config", "[pcnt]")
{
pcnt_config_t pcnt_config = {
.pulse_gpio_num = PCNT_INPUT_IO,
@@ -380,7 +368,7 @@ TEST_CASE("PCNT test config", "[pcnt]")
* 2. resume counter
* 3. clear counter
* 4. check the counter value*/
TEST_CASE("PCNT basic function test", "[pcnt]")
TEST_CASE("PCNT_basic_function_test", "[pcnt]")
{
int16_t test_counter;
int16_t time = 0;
@@ -469,7 +457,7 @@ TEST_CASE("PCNT basic function test", "[pcnt]")
* 4. PCNT_EVT_H_LIM
* 5. PCNT_EVT_L_LIM
* */
TEST_CASE("PCNT interrupt method test(control IO is high)", "[pcnt][timeout=120]")
TEST_CASE("PCNT_interrupt_method_test_control_IO_high", "[pcnt][timeout=120]")
{
pcnt_config_t config = {
.pulse_gpio_num = PCNT_INPUT_IO,
@@ -569,7 +557,7 @@ TEST_CASE("PCNT interrupt method test(control IO is high)", "[pcnt][timeout=120]
vQueueDelete(pcnt_evt_queue);
}
TEST_CASE("PCNT interrupt method test(control IO is low)", "[pcnt][timeout=120]")
TEST_CASE("PCNT_interrupt_method_test_control_IO_low", "[pcnt][timeout=120]")
{
pcnt_config_t config = {
.pulse_gpio_num = PCNT_INPUT_IO,
@@ -671,7 +659,7 @@ TEST_CASE("PCNT interrupt method test(control IO is low)", "[pcnt][timeout=120]"
vQueueDelete(pcnt_evt_queue);
}
TEST_CASE("PCNT counting mode test", "[pcnt]")
TEST_CASE("PCNT_counting_mode_test", "[pcnt]")
{
printf("PCNT mode test for positive count\n");
count_mode_test(PCNT_CTRL_VCC_IO);
@@ -679,4 +667,6 @@ TEST_CASE("PCNT counting mode test", "[pcnt]")
count_mode_test(PCNT_CTRL_GND_IO);
}
#endif // #if SOC_PCNT_SUPPORTED
void test_app_include_legacy_pcnt(void)
{
}

View File

@@ -0,0 +1,22 @@
# 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',
[
'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(timeout=240)

View File

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

View File

@@ -0,0 +1,3 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n
CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN=y

View File

@@ -1,7 +1,6 @@
set(srcs "test_app_main.c"
"test_legacy_timer.c")
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES driver unity)
idf_component_register(SRCS ${srcs})
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u test_app_include_legacy_timer")

View File

@@ -11,6 +11,6 @@ from pytest_embedded import Dut
'release',
], indirect=True)
def test_legacy_timer_driver(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(timeout=120)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
@@ -22,6 +14,7 @@
#pragma once
#include <limits.h>
#include <stdlib.h>
#include <stdbool.h>
#include "soc/pcnt_struct.h"
@@ -31,19 +24,23 @@
extern "C" {
#endif
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
#define PCNT_LL_MAX_GLITCH_WIDTH 1023
#define PCNT_LL_MAX_LIM SHRT_MAX
#define PCNT_LL_MIN_LIN SHRT_MIN
typedef enum {
PCNT_LL_EVENT_THRES1,
PCNT_LL_EVENT_THRES0,
PCNT_LL_EVENT_LOW_LIMIT,
PCNT_LL_EVENT_HIGH_LIMIT,
PCNT_LL_EVENT_ZERO_CROSS,
PCNT_LL_EVENT_MAX
} pcnt_ll_event_id_t;
PCNT_LL_WATCH_EVENT_INVALID = -1,
PCNT_LL_WATCH_EVENT_THRES1,
PCNT_LL_WATCH_EVENT_THRES0,
PCNT_LL_WATCH_EVENT_LOW_LIMIT,
PCNT_LL_WATCH_EVENT_HIGH_LIMIT,
PCNT_LL_WATCH_EVENT_ZERO_CROSS,
PCNT_LL_WATCH_EVENT_MAX
} pcnt_ll_watch_event_id_t;
#define PCNT_LL_EVENT_MASK ((1 << PCNT_LL_EVENT_MAX) - 1)
#define PCNT_LL_WATCH_EVENT_MASK ((1 << PCNT_LL_WATCH_EVENT_MAX) - 1)
#define PCNT_LL_UNIT_WATCH_EVENT(unit_id) (1 << (unit_id))
/**
* @brief Set PCNT channel edge action
@@ -156,7 +153,8 @@ static inline void pcnt_ll_enable_intr(pcnt_dev_t *hw, uint32_t unit_mask, bool
* @param hw Peripheral PCNT hardware instance address.
* @return Interrupt status word
*/
__attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
{
return hw->int_st.val;
}
@@ -167,7 +165,8 @@ __attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pc
* @param hw Peripheral PCNT hardware instance address.
* @param status value to clear interrupt status
*/
__attribute__((always_inline)) static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
__attribute__((always_inline))
static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
{
hw->int_clr.val = status;
}
@@ -233,7 +232,7 @@ static inline void pcnt_ll_enable_thres_event(pcnt_dev_t *hw, uint32_t unit, uin
*/
static inline void pcnt_ll_disable_all_events(pcnt_dev_t *hw, uint32_t unit)
{
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_EVENT_MASK << 11);
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_WATCH_EVENT_MASK << 11);
}
/**
@@ -344,13 +343,14 @@ static inline uint32_t pcnt_ll_get_unit_status(pcnt_dev_t *hw, uint32_t unit)
}
/**
* @brief Get PCNT count sign
* @brief Get PCNT zero cross mode
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @return Count sign
* @return Zero cross mode
*/
static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint32_t unit)
__attribute__((always_inline))
static inline pcnt_unit_zero_cross_mode_t pcnt_ll_get_zero_cross_mode(pcnt_dev_t *hw, uint32_t unit)
{
return hw->status_unit[unit].val & 0x03;
}
@@ -362,6 +362,7 @@ static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint
* @param unit PCNT unit number
* @return Event status word
*/
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit)
{
return hw->status_unit[unit].val >> 2;
@@ -404,6 +405,18 @@ static inline void pcnt_ll_enable_glitch_filter(pcnt_dev_t *hw, uint32_t unit, b
hw->conf_unit[unit].conf0.filter_en = enable;
}
/**
* @brief Get interrupt status register address.
*
* @param hw Beginning address of the peripheral registers.
*
* @return Interrupt status register address
*/
static inline volatile void *pcnt_ll_get_intr_status_reg(pcnt_dev_t *hw)
{
return &hw->int_st.val;
}
#ifdef __cplusplus
}
#endif

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
@@ -22,6 +14,7 @@
#pragma once
#include <limits.h>
#include <stdlib.h>
#include <stdbool.h>
#include "soc/pcnt_struct.h"
@@ -31,19 +24,23 @@
extern "C" {
#endif
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
#define PCNT_LL_MAX_GLITCH_WIDTH 1023
#define PCNT_LL_MAX_LIM SHRT_MAX
#define PCNT_LL_MIN_LIN SHRT_MIN
typedef enum {
PCNT_LL_EVENT_THRES1,
PCNT_LL_EVENT_THRES0,
PCNT_LL_EVENT_LOW_LIMIT,
PCNT_LL_EVENT_HIGH_LIMIT,
PCNT_LL_EVENT_ZERO_CROSS,
PCNT_LL_EVENT_MAX
} pcnt_ll_event_id_t;
PCNT_LL_WATCH_EVENT_INVALID = -1,
PCNT_LL_WATCH_EVENT_THRES1,
PCNT_LL_WATCH_EVENT_THRES0,
PCNT_LL_WATCH_EVENT_LOW_LIMIT,
PCNT_LL_WATCH_EVENT_HIGH_LIMIT,
PCNT_LL_WATCH_EVENT_ZERO_CROSS,
PCNT_LL_WATCH_EVENT_MAX
} pcnt_ll_watch_event_id_t;
#define PCNT_LL_EVENT_MASK ((1 << PCNT_LL_EVENT_MAX) - 1)
#define PCNT_LL_WATCH_EVENT_MASK ((1 << PCNT_LL_WATCH_EVENT_MAX) - 1)
#define PCNT_LL_UNIT_WATCH_EVENT(unit_id) (1 << (unit_id))
/**
* @brief Set PCNT channel edge action
@@ -156,7 +153,8 @@ static inline void pcnt_ll_enable_intr(pcnt_dev_t *hw, uint32_t unit_mask, bool
* @param hw Peripheral PCNT hardware instance address.
* @return Interrupt status word
*/
__attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
{
return hw->int_st.val;
}
@@ -167,7 +165,8 @@ __attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pc
* @param hw Peripheral PCNT hardware instance address.
* @param status value to clear interrupt status
*/
__attribute__((always_inline)) static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
__attribute__((always_inline))
static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
{
hw->int_clr.val = status;
}
@@ -233,7 +232,7 @@ static inline void pcnt_ll_enable_thres_event(pcnt_dev_t *hw, uint32_t unit, uin
*/
static inline void pcnt_ll_disable_all_events(pcnt_dev_t *hw, uint32_t unit)
{
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_EVENT_MASK << 11);
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_WATCH_EVENT_MASK << 11);
}
/**
@@ -344,13 +343,14 @@ static inline uint32_t pcnt_ll_get_unit_status(pcnt_dev_t *hw, uint32_t unit)
}
/**
* @brief Get PCNT count sign
* @brief Get PCNT zero cross mode
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @return Count sign
* @return Zero cross mode
*/
static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint32_t unit)
__attribute__((always_inline))
static inline pcnt_unit_zero_cross_mode_t pcnt_ll_get_zero_cross_mode(pcnt_dev_t *hw, uint32_t unit)
{
return hw->status_unit[unit].val & 0x03;
}
@@ -362,6 +362,7 @@ static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint
* @param unit PCNT unit number
* @return Event status word
*/
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit)
{
return hw->status_unit[unit].val >> 2;
@@ -404,6 +405,18 @@ static inline void pcnt_ll_enable_glitch_filter(pcnt_dev_t *hw, uint32_t unit, b
hw->conf_unit[unit].conf0.filter_en_un = enable;
}
/**
* @brief Get interrupt status register address.
*
* @param hw Beginning address of the peripheral registers.
*
* @return Interrupt status register address
*/
static inline volatile void *pcnt_ll_get_intr_status_reg(pcnt_dev_t *hw)
{
return &hw->int_st.val;
}
#ifdef __cplusplus
}
#endif

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
@@ -22,6 +14,7 @@
#pragma once
#include <limits.h>
#include <stdlib.h>
#include <stdbool.h>
#include "soc/pcnt_struct.h"
@@ -31,19 +24,23 @@
extern "C" {
#endif
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
#define PCNT_LL_MAX_GLITCH_WIDTH 1023
#define PCNT_LL_MAX_LIM SHRT_MAX
#define PCNT_LL_MIN_LIN SHRT_MIN
typedef enum {
PCNT_LL_EVENT_THRES1,
PCNT_LL_EVENT_THRES0,
PCNT_LL_EVENT_LOW_LIMIT,
PCNT_LL_EVENT_HIGH_LIMIT,
PCNT_LL_EVENT_ZERO_CROSS,
PCNT_LL_EVENT_MAX
} pcnt_ll_event_id_t;
PCNT_LL_WATCH_EVENT_INVALID = -1,
PCNT_LL_WATCH_EVENT_THRES1,
PCNT_LL_WATCH_EVENT_THRES0,
PCNT_LL_WATCH_EVENT_LOW_LIMIT,
PCNT_LL_WATCH_EVENT_HIGH_LIMIT,
PCNT_LL_WATCH_EVENT_ZERO_CROSS,
PCNT_LL_WATCH_EVENT_MAX
} pcnt_ll_watch_event_id_t;
#define PCNT_LL_EVENT_MASK ((1 << PCNT_LL_EVENT_MAX) - 1)
#define PCNT_LL_WATCH_EVENT_MASK ((1 << PCNT_LL_WATCH_EVENT_MAX) - 1)
#define PCNT_LL_UNIT_WATCH_EVENT(unit_id) (1 << (unit_id))
/**
* @brief Set PCNT channel edge action
@@ -156,7 +153,8 @@ static inline void pcnt_ll_enable_intr(pcnt_dev_t *hw, uint32_t unit_mask, bool
* @param hw Peripheral PCNT hardware instance address.
* @return Interrupt status word
*/
__attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw)
{
return hw->int_st.val;
}
@@ -167,7 +165,8 @@ __attribute__((always_inline)) static inline uint32_t pcnt_ll_get_intr_status(pc
* @param hw Peripheral PCNT hardware instance address.
* @param status value to clear interrupt status
*/
__attribute__((always_inline)) static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
__attribute__((always_inline))
static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
{
hw->int_clr.val = status;
}
@@ -233,7 +232,7 @@ static inline void pcnt_ll_enable_thres_event(pcnt_dev_t *hw, uint32_t unit, uin
*/
static inline void pcnt_ll_disable_all_events(pcnt_dev_t *hw, uint32_t unit)
{
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_EVENT_MASK << 11);
hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_WATCH_EVENT_MASK << 11);
}
/**
@@ -344,13 +343,14 @@ static inline uint32_t pcnt_ll_get_unit_status(pcnt_dev_t *hw, uint32_t unit)
}
/**
* @brief Get PCNT count sign
* @brief Get PCNT zero cross mode
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @return Count sign
* @return Zero cross mode
*/
static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint32_t unit)
__attribute__((always_inline))
static inline pcnt_unit_zero_cross_mode_t pcnt_ll_get_zero_cross_mode(pcnt_dev_t *hw, uint32_t unit)
{
return hw->status_unit[unit].val & 0x03;
}
@@ -362,6 +362,7 @@ static inline pcnt_unit_count_sign_t pcnt_ll_get_count_sign(pcnt_dev_t *hw, uint
* @param unit PCNT unit number
* @return Event status word
*/
__attribute__((always_inline))
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit)
{
return hw->status_unit[unit].val >> 2;
@@ -404,6 +405,18 @@ static inline void pcnt_ll_enable_glitch_filter(pcnt_dev_t *hw, uint32_t unit, b
hw->conf_unit[unit].conf0.filter_en_un = enable;
}
/**
* @brief Get interrupt status register address.
*
* @param hw Beginning address of the peripheral registers.
*
* @return Interrupt status register address
*/
static inline volatile void *pcnt_ll_get_intr_status_reg(pcnt_dev_t *hw)
{
return &hw->int_st.val;
}
#ifdef __cplusplus
}
#endif

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* NOTICE
@@ -18,22 +10,19 @@
* See readme.md in hal/include/hal/readme.md
******************************************************************************/
// The HAL layer for PCNT.
// There is no parameter check in the hal layer, so the caller must ensure the correctness of the parameters.
#pragma once
#include "soc/pcnt_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct pcnt_dev_t *pcnt_soc_handle_t; // PCNT SOC layer handle
/**
* Context that should be maintained by both the driver and the HAL
*/
typedef struct {
pcnt_dev_t *dev; /*!< PCNT peripheral register base address */
pcnt_soc_handle_t dev; // PCNT SOC layer handle
} pcnt_hal_context_t;
/**

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@@ -20,7 +12,6 @@ extern "C" {
/**
* @brief PCNT channel action on control level
*
*/
typedef enum {
PCNT_CHANNEL_LEVEL_ACTION_KEEP, /*!< Keep current count mode */
@@ -30,7 +21,6 @@ typedef enum {
/**
* @brief PCNT channel action on signal edge
*
*/
typedef enum {
PCNT_CHANNEL_EDGE_ACTION_HOLD, /*!< Hold current count value */
@@ -39,15 +29,14 @@ typedef enum {
} pcnt_channel_edge_action_t;
/**
* @brief PCNT unit counter value's sign
*
* @brief PCNT unit zero cross mode
*/
typedef enum {
PCNT_UNIT_COUNT_SIGN_ZERO_POS, /*!< positive value to zero */
PCNT_UNIT_COUNT_SIGN_ZERO_NEG, /*!< negative value to zero */
PCNT_UNIT_COUNT_SIGN_NEG, /*!< counter value negative */
PCNT_UNIT_COUNT_SIGN_POS, /*!< counter value positive */
} pcnt_unit_count_sign_t;
PCNT_UNIT_ZERO_CROSS_POS_ZERO, /*!< start from positive value, end to zero, i.e. +N->0 */
PCNT_UNIT_ZERO_CROSS_NEG_ZERO, /*!< start from negative value, end to zero, i.e. -N->0 */
PCNT_UNIT_ZERO_CROSS_NEG_POS, /*!< start from negative value, end to positive value, i.e. -N->+M */
PCNT_UNIT_ZERO_CROSS_POS_NEG, /*!< start from positive value, end to negative value, i.e. +N->-M */
} pcnt_unit_zero_cross_mode_t;
#ifdef __cplusplus
}

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The HAL layer for PCNT (common part)

View File

@@ -1,21 +1,13 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _SOC_PCNT_REG_H_
#define _SOC_PCNT_REG_H_
#include <stdint.h>
#include "soc/soc.h"
#include "soc.h"
#define PCNT_U0_CONF0_REG (DR_REG_PCNT_BASE + 0x0000)
/* PCNT_CH1_LCTRL_MODE_U0 : R/W ;bitpos:[31:30] ;default: 2'd0 ; */
/*description: This register is used to control the mode of channel1's low control
@@ -1517,8 +1509,3 @@
#define PCNT_DATE_M ((PCNT_DATE_V)<<(PCNT_DATE_S))
#define PCNT_DATE_V 0xFFFFFFFF
#define PCNT_DATE_S 0
#endif /*_SOC_PCNT_REG_H_ */

View File

@@ -1,18 +1,9 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _SOC_PCNT_STRUCT_H_
#define _SOC_PCNT_STRUCT_H_
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
@@ -20,8 +11,8 @@
extern "C" {
#endif
typedef volatile struct pcnt_dev_s {
struct {
typedef struct pcnt_dev_t {
volatile struct {
union {
struct {
uint32_t filter_thres: 10; /*This register is used to filter pulse whose width is smaller than this value for unit0.*/
@@ -44,27 +35,27 @@ typedef volatile struct pcnt_dev_s {
} conf0;
union {
struct {
uint32_t cnt_thres0:16; /*This register is used to configure thres0 value for unit0.*/
uint32_t cnt_thres1:16; /*This register is used to configure thres1 value for unit0.*/
uint32_t cnt_thres0: 16; /*This register is used to configure thres0 value for unit0.*/
uint32_t cnt_thres1: 16; /*This register is used to configure thres1 value for unit0.*/
};
uint32_t val;
} conf1;
union {
struct {
uint32_t cnt_h_lim:16; /*This register is used to configure thr_h_lim value for unit0.*/
uint32_t cnt_l_lim:16; /*This register is used to configure thr_l_lim value for unit0.*/
uint32_t cnt_h_lim: 16; /*This register is used to configure thr_h_lim value for unit0.*/
uint32_t cnt_l_lim: 16; /*This register is used to configure thr_l_lim value for unit0.*/
};
uint32_t val;
} conf2;
} conf_unit[8];
union {
volatile union {
struct {
uint32_t cnt_val : 16; /*This register stores the current pulse count value for unit0.*/
uint32_t reserved16: 16;
};
uint32_t val;
} cnt_unit[8];
union {
volatile union {
struct {
uint32_t cnt_thr_event_u0: 1; /*This is the interrupt raw bit for channel0 event.*/
uint32_t cnt_thr_event_u1: 1; /*This is the interrupt raw bit for channel1 event.*/
@@ -78,7 +69,7 @@ typedef volatile struct pcnt_dev_s {
};
uint32_t val;
} int_raw;
union {
volatile union {
struct {
uint32_t cnt_thr_event_u0: 1; /*This is the interrupt status bit for channel0 event.*/
uint32_t cnt_thr_event_u1: 1; /*This is the interrupt status bit for channel1 event.*/
@@ -92,7 +83,7 @@ typedef volatile struct pcnt_dev_s {
};
uint32_t val;
} int_st;
union {
volatile union {
struct {
uint32_t cnt_thr_event_u0: 1; /*This is the interrupt enable bit for channel0 event.*/
uint32_t cnt_thr_event_u1: 1; /*This is the interrupt enable bit for channel1 event.*/
@@ -106,7 +97,7 @@ typedef volatile struct pcnt_dev_s {
};
uint32_t val;
} int_ena;
union {
volatile union {
struct {
uint32_t cnt_thr_event_u0: 1; /*Set this bit to clear channel0 event interrupt.*/
uint32_t cnt_thr_event_u1: 1; /*Set this bit to clear channel1 event interrupt.*/
@@ -120,19 +111,19 @@ typedef volatile struct pcnt_dev_s {
};
uint32_t val;
} int_clr;
union {
volatile union {
struct {
uint32_t cnt_mode:2; /*0: positive value to zero; 1: negative value to zero; 2: counter value negative ; 3: counter value positive*/
uint32_t thres1_lat:1; /* counter value equals to thresh1*/
uint32_t thres0_lat:1; /* counter value equals to thresh0*/
uint32_t l_lim_lat:1; /* counter value reaches h_lim*/
uint32_t h_lim_lat:1; /* counter value reaches l_lim*/
uint32_t zero_lat:1; /* counter value equals zero*/
uint32_t reserved7:25;
uint32_t cnt_mode: 2; /*0: positive value to zero; 1: negative value to zero; 2: counter value negative ; 3: counter value positive*/
uint32_t thres1_lat: 1; /* counter value equals to thresh1*/
uint32_t thres0_lat: 1; /* counter value equals to thresh0*/
uint32_t l_lim_lat: 1; /* counter value reaches h_lim*/
uint32_t h_lim_lat: 1; /* counter value reaches l_lim*/
uint32_t zero_lat: 1; /* counter value equals zero*/
uint32_t reserved7: 25;
};
uint32_t val;
} status_unit[8];
union {
volatile union {
struct {
uint32_t cnt_rst_u0: 1; /*Set this bit to clear unit0's counter.*/
uint32_t cnt_pause_u0: 1; /*Set this bit to pause unit0's counter.*/
@@ -173,12 +164,11 @@ typedef volatile struct pcnt_dev_s {
uint32_t reserved_f0;
uint32_t reserved_f4;
uint32_t reserved_f8;
uint32_t date; /**/
volatile uint32_t date;
} pcnt_dev_t;
extern pcnt_dev_t PCNT;
#ifdef __cplusplus
}
#endif
#endif /* _SOC_PCNT_STRUCT_H_ */

View File

@@ -1,16 +1,8 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/pcnt_periph.h"
#include "soc/gpio_sig_map.h"

View File

@@ -1,16 +1,7 @@
/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

View File

@@ -1,16 +1,7 @@
/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@@ -388,7 +379,7 @@ typedef union {
} pcnt_date_reg_t;
typedef struct {
typedef struct pcnt_dev_t {
volatile struct {
pcnt_un_conf0_reg_t conf0;
pcnt_un_conf1_reg_t conf1;

View File

@@ -1,16 +1,8 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/pcnt_periph.h"
#include "soc/gpio_sig_map.h"

View File

@@ -1,26 +0,0 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define SOC_PCNT_PORT_NUM (1)
#define SOC_PCNT_UNIT_NUM (4)
#ifdef __cplusplus
}
#endif

View File

@@ -1,16 +1,7 @@
/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

View File

@@ -1,16 +1,7 @@
/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@@ -388,7 +379,7 @@ typedef union {
} pcnt_date_reg_t;
typedef struct {
typedef struct pcnt_dev_t {
volatile struct {
pcnt_un_conf0_reg_t conf0;
pcnt_un_conf1_reg_t conf1;

View File

@@ -1,16 +1,8 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/pcnt_periph.h"
#include "soc/gpio_sig_map.h"

View File

@@ -1,16 +1,8 @@
// Copyright 2019-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once