From ec8defaa96c5ff4487737fc169435a16dfc12049 Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 17 Jan 2022 14:42:09 +0800 Subject: [PATCH] pulse_cnt: new driver for PCNT peripheral --- components/driver/CMakeLists.txt | 2 +- components/driver/Kconfig | 54 +- components/driver/gptimer.c | 38 +- components/driver/include/driver/pulse_cnt.h | 296 ++++++++ components/driver/legacy_new_driver_coexist.c | 6 + components/driver/linker.lf | 5 + components/driver/pulse_cnt.c | 714 ++++++++++++++++++ .../test_apps/gptimer/main/CMakeLists.txt | 3 +- .../test_apps/gptimer/pytest_gptimer.py | 2 +- .../driver/test_apps/pulse_cnt/CMakeLists.txt | 18 + .../driver/test_apps/pulse_cnt/README.md | 2 + .../test_apps/pulse_cnt/main/CMakeLists.txt | 6 + .../test_apps/pulse_cnt/main/test_app_main.c | 51 ++ .../test_apps/pulse_cnt/main/test_pulse_cnt.c | 408 ++++++++++ .../test_apps/pulse_cnt/pytest_pulse_cnt.py | 23 + .../pulse_cnt/sdkconfig.ci.iram_safe | 6 + .../test_apps/pulse_cnt/sdkconfig.ci.release | 5 + .../test_apps/pulse_cnt/sdkconfig.defaults | 2 + components/hal/esp32/include/hal/pcnt_ll.h | 69 +- components/hal/esp32s2/include/hal/pcnt_ll.h | 69 +- components/hal/esp32s3/include/hal/pcnt_ll.h | 69 +- components/hal/include/hal/pcnt_hal.h | 27 +- components/hal/include/hal/pcnt_types.h | 33 +- components/hal/pcnt_hal.c | 18 +- components/soc/esp32/include/soc/pcnt_reg.h | 29 +- .../soc/esp32/include/soc/pcnt_struct.h | 66 +- components/soc/esp32/pcnt_periph.c | 18 +- components/soc/esp32s2/include/soc/pcnt_reg.h | 15 +- .../soc/esp32s2/include/soc/pcnt_struct.h | 17 +- components/soc/esp32s2/pcnt_periph.c | 18 +- .../soc/esp32s3/include/soc/pcnt_caps.h | 26 - components/soc/esp32s3/include/soc/pcnt_reg.h | 15 +- .../soc/esp32s3/include/soc/pcnt_struct.h | 17 +- components/soc/esp32s3/pcnt_periph.c | 18 +- components/soc/include/soc/pcnt_periph.h | 18 +- tools/ci/check_copyright_ignore.txt | 21 - 36 files changed, 1829 insertions(+), 375 deletions(-) create mode 100644 components/driver/include/driver/pulse_cnt.h create mode 100644 components/driver/pulse_cnt.c create mode 100644 components/driver/test_apps/pulse_cnt/CMakeLists.txt create mode 100644 components/driver/test_apps/pulse_cnt/README.md create mode 100644 components/driver/test_apps/pulse_cnt/main/CMakeLists.txt create mode 100644 components/driver/test_apps/pulse_cnt/main/test_app_main.c create mode 100644 components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c create mode 100644 components/driver/test_apps/pulse_cnt/pytest_pulse_cnt.py create mode 100644 components/driver/test_apps/pulse_cnt/sdkconfig.ci.iram_safe create mode 100644 components/driver/test_apps/pulse_cnt/sdkconfig.ci.release create mode 100644 components/driver/test_apps/pulse_cnt/sdkconfig.defaults delete mode 100644 components/soc/esp32s3/include/soc/pcnt_caps.h diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index d8d1c6d6cf..bddd65e1be 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -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) diff --git a/components/driver/Kconfig b/components/driver/Kconfig index ba7fc12f65..d0f0f8542b 100644 --- a/components/driver/Kconfig +++ b/components/driver/Kconfig @@ -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 diff --git a/components/driver/gptimer.c b/components/driver/gptimer.c index 9c816260be..381b32025c 100644 --- a/components/driver/gptimer.c +++ b/components/driver/gptimer.c @@ -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 #include +#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; diff --git a/components/driver/include/driver/pulse_cnt.h b/components/driver/include/driver/pulse_cnt.h new file mode 100644 index 0000000000..17fa06bb9f --- /dev/null +++ b/components/driver/include/driver/pulse_cnt.h @@ -0,0 +1,296 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#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 diff --git a/components/driver/legacy_new_driver_coexist.c b/components/driver/legacy_new_driver_coexist.c index 3896ee2d5e..0bbdcf5c5b 100644 --- a/components/driver/legacy_new_driver_coexist.c +++ b/components/driver/legacy_new_driver_coexist.c @@ -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; diff --git a/components/driver/linker.lf b/components/driver/linker.lf index eb3eea8506..554b843a70 100644 --- a/components/driver/linker.lf +++ b/components/driver/linker.lf @@ -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) diff --git a/components/driver/pulse_cnt.c b/components/driver/pulse_cnt.c new file mode 100644 index 0000000000..a2d0d9794b --- /dev/null +++ b/components/driver/pulse_cnt.c @@ -0,0 +1,714 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#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(); + } +} diff --git a/components/driver/test_apps/gptimer/main/CMakeLists.txt b/components/driver/test_apps/gptimer/main/CMakeLists.txt index a3b22adfb3..252a616260 100644 --- a/components/driver/test_apps/gptimer/main/CMakeLists.txt +++ b/components/driver/test_apps/gptimer/main/CMakeLists.txt @@ -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") diff --git a/components/driver/test_apps/gptimer/pytest_gptimer.py b/components/driver/test_apps/gptimer/pytest_gptimer.py index a72a78e308..19a5284685 100644 --- a/components/driver/test_apps/gptimer/pytest_gptimer.py +++ b/components/driver/test_apps/gptimer/pytest_gptimer.py @@ -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() diff --git a/components/driver/test_apps/pulse_cnt/CMakeLists.txt b/components/driver/test_apps/pulse_cnt/CMakeLists.txt new file mode 100644 index 0000000000..a978a789f0 --- /dev/null +++ b/components/driver/test_apps/pulse_cnt/CMakeLists.txt @@ -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() diff --git a/components/driver/test_apps/pulse_cnt/README.md b/components/driver/test_apps/pulse_cnt/README.md new file mode 100644 index 0000000000..b88a9825d9 --- /dev/null +++ b/components/driver/test_apps/pulse_cnt/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | diff --git a/components/driver/test_apps/pulse_cnt/main/CMakeLists.txt b/components/driver/test_apps/pulse_cnt/main/CMakeLists.txt new file mode 100644 index 0000000000..37a2f532da --- /dev/null +++ b/components/driver/test_apps/pulse_cnt/main/CMakeLists.txt @@ -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") diff --git a/components/driver/test_apps/pulse_cnt/main/test_app_main.c b/components/driver/test_apps/pulse_cnt/main/test_app_main.c new file mode 100644 index 0000000000..71e240d551 --- /dev/null +++ b/components/driver/test_apps/pulse_cnt/main/test_app_main.c @@ -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(); +} diff --git a/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c b/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c new file mode 100644 index 0000000000..e6dc900e04 --- /dev/null +++ b/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c @@ -0,0 +1,408 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#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) +{ +} diff --git a/components/driver/test_apps/pulse_cnt/pytest_pulse_cnt.py b/components/driver/test_apps/pulse_cnt/pytest_pulse_cnt.py new file mode 100644 index 0000000000..8e1f892417 --- /dev/null +++ b/components/driver/test_apps/pulse_cnt/pytest_pulse_cnt.py @@ -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() diff --git a/components/driver/test_apps/pulse_cnt/sdkconfig.ci.iram_safe b/components/driver/test_apps/pulse_cnt/sdkconfig.ci.iram_safe new file mode 100644 index 0000000000..093e3f620b --- /dev/null +++ b/components/driver/test_apps/pulse_cnt/sdkconfig.ci.iram_safe @@ -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 diff --git a/components/driver/test_apps/pulse_cnt/sdkconfig.ci.release b/components/driver/test_apps/pulse_cnt/sdkconfig.ci.release new file mode 100644 index 0000000000..91d93f163e --- /dev/null +++ b/components/driver/test_apps/pulse_cnt/sdkconfig.ci.release @@ -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 diff --git a/components/driver/test_apps/pulse_cnt/sdkconfig.defaults b/components/driver/test_apps/pulse_cnt/sdkconfig.defaults new file mode 100644 index 0000000000..b308cb2ddd --- /dev/null +++ b/components/driver/test_apps/pulse_cnt/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT=n diff --git a/components/hal/esp32/include/hal/pcnt_ll.h b/components/hal/esp32/include/hal/pcnt_ll.h index 40d4aff973..5d601b80c7 100644 --- a/components/hal/esp32/include/hal/pcnt_ll.h +++ b/components/hal/esp32/include/hal/pcnt_ll.h @@ -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 #include #include #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 diff --git a/components/hal/esp32s2/include/hal/pcnt_ll.h b/components/hal/esp32s2/include/hal/pcnt_ll.h index b9286fa92f..67a8ebafda 100644 --- a/components/hal/esp32s2/include/hal/pcnt_ll.h +++ b/components/hal/esp32s2/include/hal/pcnt_ll.h @@ -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 #include #include #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 diff --git a/components/hal/esp32s3/include/hal/pcnt_ll.h b/components/hal/esp32s3/include/hal/pcnt_ll.h index a8b32df50c..58e428ad8b 100644 --- a/components/hal/esp32s3/include/hal/pcnt_ll.h +++ b/components/hal/esp32s3/include/hal/pcnt_ll.h @@ -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 #include #include #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 diff --git a/components/hal/include/hal/pcnt_hal.h b/components/hal/include/hal/pcnt_hal.h index 3d10c8d8c6..7d2e1fe484 100644 --- a/components/hal/include/hal/pcnt_hal.h +++ b/components/hal/include/hal/pcnt_hal.h @@ -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; /** diff --git a/components/hal/include/hal/pcnt_types.h b/components/hal/include/hal/pcnt_types.h index 0696e756cc..8af94e879e 100644 --- a/components/hal/include/hal/pcnt_types.h +++ b/components/hal/include/hal/pcnt_types.h @@ -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 } diff --git a/components/hal/pcnt_hal.c b/components/hal/pcnt_hal.c index 93db4765dd..827804bb9e 100644 --- a/components/hal/pcnt_hal.c +++ b/components/hal/pcnt_hal.c @@ -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) diff --git a/components/soc/esp32/include/soc/pcnt_reg.h b/components/soc/esp32/include/soc/pcnt_reg.h index 6d21d52ef2..ec64469627 100644 --- a/components/soc/esp32/include/soc/pcnt_reg.h +++ b/components/soc/esp32/include/soc/pcnt_reg.h @@ -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 +#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_ */ diff --git a/components/soc/esp32/include/soc/pcnt_struct.h b/components/soc/esp32/include/soc/pcnt_struct.h index ecb79f3f4f..f2a8947184 100644 --- a/components/soc/esp32/include/soc/pcnt_struct.h +++ b/components/soc/esp32/include/soc/pcnt_struct.h @@ -1,18 +1,9 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#ifndef _SOC_PCNT_STRUCT_H_ -#define _SOC_PCNT_STRUCT_H_ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once #include @@ -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_ */ diff --git a/components/soc/esp32/pcnt_periph.c b/components/soc/esp32/pcnt_periph.c index 23673810b4..e2f98ff64c 100644 --- a/components/soc/esp32/pcnt_periph.c +++ b/components/soc/esp32/pcnt_periph.c @@ -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" diff --git a/components/soc/esp32s2/include/soc/pcnt_reg.h b/components/soc/esp32s2/include/soc/pcnt_reg.h index b6198c86fb..64907cd1ec 100644 --- a/components/soc/esp32s2/include/soc/pcnt_reg.h +++ b/components/soc/esp32s2/include/soc/pcnt_reg.h @@ -1,16 +1,7 @@ -/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/components/soc/esp32s2/include/soc/pcnt_struct.h b/components/soc/esp32s2/include/soc/pcnt_struct.h index 1d65a7309b..73794dc33a 100644 --- a/components/soc/esp32s2/include/soc/pcnt_struct.h +++ b/components/soc/esp32s2/include/soc/pcnt_struct.h @@ -1,16 +1,7 @@ -/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -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; diff --git a/components/soc/esp32s2/pcnt_periph.c b/components/soc/esp32s2/pcnt_periph.c index bb163ccd62..83c3424a0f 100644 --- a/components/soc/esp32s2/pcnt_periph.c +++ b/components/soc/esp32s2/pcnt_periph.c @@ -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" diff --git a/components/soc/esp32s3/include/soc/pcnt_caps.h b/components/soc/esp32s3/include/soc/pcnt_caps.h deleted file mode 100644 index b72d302c2e..0000000000 --- a/components/soc/esp32s3/include/soc/pcnt_caps.h +++ /dev/null @@ -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 diff --git a/components/soc/esp32s3/include/soc/pcnt_reg.h b/components/soc/esp32s3/include/soc/pcnt_reg.h index b6198c86fb..64907cd1ec 100644 --- a/components/soc/esp32s3/include/soc/pcnt_reg.h +++ b/components/soc/esp32s3/include/soc/pcnt_reg.h @@ -1,16 +1,7 @@ -/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/components/soc/esp32s3/include/soc/pcnt_struct.h b/components/soc/esp32s3/include/soc/pcnt_struct.h index 1d65a7309b..73794dc33a 100644 --- a/components/soc/esp32s3/include/soc/pcnt_struct.h +++ b/components/soc/esp32s3/include/soc/pcnt_struct.h @@ -1,16 +1,7 @@ -/** Copyright 2021 Espressif Systems (Shanghai) PTE LTD +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -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; diff --git a/components/soc/esp32s3/pcnt_periph.c b/components/soc/esp32s3/pcnt_periph.c index bb163ccd62..83c3424a0f 100644 --- a/components/soc/esp32s3/pcnt_periph.c +++ b/components/soc/esp32s3/pcnt_periph.c @@ -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" diff --git a/components/soc/include/soc/pcnt_periph.h b/components/soc/include/soc/pcnt_periph.h index 966950c846..f6e5a179cc 100644 --- a/components/soc/include/soc/pcnt_periph.h +++ b/components/soc/include/soc/pcnt_periph.h @@ -1,16 +1,8 @@ -// Copyright 2019-2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index 020430934f..a881acd0bc 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -818,7 +818,6 @@ components/hal/esp32/include/hal/dac_ll.h components/hal/esp32/include/hal/i2c_ll.h components/hal/esp32/include/hal/interrupt_controller_ll.h components/hal/esp32/include/hal/mpu_ll.h -components/hal/esp32/include/hal/pcnt_ll.h components/hal/esp32/include/hal/rtc_cntl_ll.h components/hal/esp32/include/hal/rtc_io_ll.h components/hal/esp32/include/hal/rwdt_ll.h @@ -901,7 +900,6 @@ components/hal/esp32s2/include/hal/interrupt_controller_ll.h components/hal/esp32s2/include/hal/memprot_ll.h components/hal/esp32s2/include/hal/memprot_peri_ll.h components/hal/esp32s2/include/hal/mpu_ll.h -components/hal/esp32s2/include/hal/pcnt_ll.h components/hal/esp32s2/include/hal/rtc_cntl_ll.h components/hal/esp32s2/include/hal/rtc_io_ll.h components/hal/esp32s2/include/hal/sha_ll.h @@ -927,7 +925,6 @@ components/hal/esp32s3/include/hal/i2c_ll.h components/hal/esp32s3/include/hal/interrupt_controller_ll.h components/hal/esp32s3/include/hal/memprot_ll.h components/hal/esp32s3/include/hal/mpu_ll.h -components/hal/esp32s3/include/hal/pcnt_ll.h components/hal/esp32s3/include/hal/rtc_cntl_ll.h components/hal/esp32s3/include/hal/rwdt_ll.h components/hal/esp32s3/include/hal/sha_ll.h @@ -958,8 +955,6 @@ components/hal/include/hal/mcpwm_hal.h components/hal/include/hal/mcpwm_types.h components/hal/include/hal/mpu_hal.h components/hal/include/hal/mpu_types.h -components/hal/include/hal/pcnt_hal.h -components/hal/include/hal/pcnt_types.h components/hal/include/hal/rmt_types.h components/hal/include/hal/rtc_io_types.h components/hal/include/hal/sdio_slave_hal.h @@ -987,7 +982,6 @@ components/hal/interrupt_controller_hal.c components/hal/ledc_hal_iram.c components/hal/mcpwm_hal.c components/hal/mpu_hal.c -components/hal/pcnt_hal.c components/hal/platform_port/include/hal/assert.h components/hal/platform_port/include/hal/check.h components/hal/platform_port/include/hal/log.h @@ -1315,8 +1309,6 @@ components/soc/esp32/include/soc/ledc_reg.h components/soc/esp32/include/soc/ledc_struct.h components/soc/esp32/include/soc/mmu.h components/soc/esp32/include/soc/nrx_reg.h -components/soc/esp32/include/soc/pcnt_reg.h -components/soc/esp32/include/soc/pcnt_struct.h components/soc/esp32/include/soc/pid.h components/soc/esp32/include/soc/reset_reasons.h components/soc/esp32/include/soc/rmt_reg.h @@ -1352,7 +1344,6 @@ components/soc/esp32/include/soc/wdev_reg.h components/soc/esp32/interrupts.c components/soc/esp32/ledc_periph.c components/soc/esp32/mcpwm_periph.c -components/soc/esp32/pcnt_periph.c components/soc/esp32/rmt_periph.c components/soc/esp32/sdio_slave_periph.c components/soc/esp32/sdmmc_periph.c @@ -1511,8 +1502,6 @@ components/soc/esp32s2/include/soc/ledc_struct.h components/soc/esp32s2/include/soc/memprot_defs.h components/soc/esp32s2/include/soc/mmu.h components/soc/esp32s2/include/soc/nrx_reg.h -components/soc/esp32s2/include/soc/pcnt_reg.h -components/soc/esp32s2/include/soc/pcnt_struct.h components/soc/esp32s2/include/soc/reset_reasons.h components/soc/esp32s2/include/soc/rtc_cntl_reg.h components/soc/esp32s2/include/soc/rtc_cntl_struct.h @@ -1556,7 +1545,6 @@ components/soc/esp32s2/include/soc/usbh_struct.h components/soc/esp32s2/include/soc/wdev_reg.h components/soc/esp32s2/interrupts.c components/soc/esp32s2/ledc_periph.c -components/soc/esp32s2/pcnt_periph.c components/soc/esp32s2/rmt_periph.c components/soc/esp32s2/sigmadelta_periph.c components/soc/esp32s2/spi_periph.c @@ -1605,9 +1593,6 @@ components/soc/esp32s3/include/soc/ledc_struct.h components/soc/esp32s3/include/soc/mmu.h components/soc/esp32s3/include/soc/mpu_caps.h components/soc/esp32s3/include/soc/nrx_reg.h -components/soc/esp32s3/include/soc/pcnt_caps.h -components/soc/esp32s3/include/soc/pcnt_reg.h -components/soc/esp32s3/include/soc/pcnt_struct.h components/soc/esp32s3/include/soc/peri_backup_reg.h components/soc/esp32s3/include/soc/peri_backup_struct.h components/soc/esp32s3/include/soc/reset_reasons.h @@ -1663,7 +1648,6 @@ components/soc/esp32s3/include/soc/world_controller_struct.h components/soc/esp32s3/interrupts.c components/soc/esp32s3/ledc_periph.c components/soc/esp32s3/mcpwm_periph.c -components/soc/esp32s3/pcnt_periph.c components/soc/esp32s3/rmt_periph.c components/soc/esp32s3/rtc_io_periph.c components/soc/esp32s3/sdio_slave_periph.c @@ -1683,7 +1667,6 @@ components/soc/include/soc/i2c_periph.h components/soc/include/soc/interrupts.h components/soc/include/soc/ledc_periph.h components/soc/include/soc/mcpwm_periph.h -components/soc/include/soc/pcnt_periph.h components/soc/include/soc/rmt_periph.h components/soc/include/soc/rtc_cntl_periph.h components/soc/include/soc/rtc_periph.h @@ -2223,10 +2206,6 @@ examples/peripherals/mcpwm/mcpwm_brushed_dc_control/main/mcpwm_brushed_dc_contro examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/main/mcpwm_capture_hc_sr04.c examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example_main.c examples/peripherals/mcpwm/mcpwm_sync_example/main/mcpwm_sync_example.c -examples/peripherals/pcnt/pulse_count_event/main/pcnt_event_example_main.c -examples/peripherals/pcnt/rotary_encoder/components/rotary_encoder/include/rotary_encoder.h -examples/peripherals/pcnt/rotary_encoder/components/rotary_encoder/src/rotary_encoder_pcnt_ec11.c -examples/peripherals/pcnt/rotary_encoder/main/rotary_encoder_example_main.c examples/peripherals/rmt/ir_protocols/components/infrared_tools/include/ir_timings.h examples/peripherals/rmt/ir_protocols/components/infrared_tools/src/ir_builder_rmt_nec.c examples/peripherals/rmt/ir_protocols/components/infrared_tools/src/ir_builder_rmt_rc5.c