From 223624955582c9def55b4b3c9e3e598d18670e6e Mon Sep 17 00:00:00 2001 From: kooho <2229179028@qq.com> Date: Mon, 19 Mar 2018 17:22:48 +0800 Subject: [PATCH] driver(pcnt): Add some APIs to support each unit can have it's own interrupt handler. --- components/driver/include/driver/pcnt.h | 55 ++++++++++++++++ components/driver/pcnt.c | 87 +++++++++++++++++++++++-- 2 files changed, 136 insertions(+), 6 deletions(-) diff --git a/components/driver/include/driver/pcnt.h b/components/driver/include/driver/pcnt.h index ad47e51ec..c751473ba 100644 --- a/components/driver/include/driver/pcnt.h +++ b/components/driver/include/driver/pcnt.h @@ -330,6 +330,61 @@ esp_err_t pcnt_set_mode(pcnt_unit_t unit, pcnt_channel_t channel, pcnt_count_mode_t pos_mode, pcnt_count_mode_t neg_mode, pcnt_ctrl_mode_t hctrl_mode, pcnt_ctrl_mode_t lctrl_mode); +/** + * @brief Add ISR handler for specified unit. + * + * Call this function after using pcnt_isr_service_install() to + * install the PCNT driver's ISR handler service. + * + * The ISR handlers do not need to be declared with IRAM_ATTR, + * unless you pass the ESP_INTR_FLAG_IRAM flag when allocating the + * ISR in pcnt_isr_service_install(). + * + * This ISR handler will be called from an ISR. So there is a stack + * size limit (configurable as "ISR stack size" in menuconfig). This + * limit is smaller compared to a global PCNT interrupt handler due + * to the additional level of indirection. + * + * @param unit PCNT unit number + * @param isr_handler Interrupt handler function. + * @param args Parameter for handler function + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t pcnt_isr_handler_add(pcnt_unit_t unit, void(*isr_handler)(void *), void *args); + +/** + * @brief Install PCNT ISR service. + * @note We can manage different interrupt service for each unit. + * Please do not use pcnt_isr_register if this function was called. + * + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * + * @return + * - ESP_OK Success + * - ESP_ERR_NO_MEM No memory to install this service + * - ESP_ERR_INVALID_STATE ISR service already installed + */ +esp_err_t pcnt_isr_service_install(int intr_alloc_flags); + +/** + * @brief Uninstall PCNT ISR service, freeing related resources. + */ +void pcnt_isr_service_uninstall(void); + +/** + * @brief Delete ISR handler for specified unit. + * + * @param unit PCNT unit number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t pcnt_isr_handler_remove(pcnt_unit_t unit); /** * @addtogroup pcnt-examples diff --git a/components/driver/pcnt.c b/components/driver/pcnt.c index 3f2d866c1..b859b495b 100644 --- a/components/driver/pcnt.c +++ b/components/driver/pcnt.c @@ -25,19 +25,24 @@ #define PCNT_CTRL_MODE_ERR_STR "PCNT CTRL MODE ERROR" #define PCNT_EVT_TYPE_ERR_STR "PCNT value type error" -static const char* PCNT_TAG = "pcnt"; +#define PCNT_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) +#define PCNT_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) + #define PCNT_CHECK(a, str, ret_val) \ if (!(a)) { \ ESP_LOGE(PCNT_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ return (ret_val); \ } -static portMUX_TYPE pcnt_spinlock = portMUX_INITIALIZER_UNLOCKED; +typedef struct{ + void(*fn)(void *args); /*!< isr function */ + void* args; /*!< isr function args */ +} pcnt_isr_func_t; -#define PCNT_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) -#define PCNT_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) -#define PCNT_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux) -#define PCNT_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux) +static pcnt_isr_func_t *pcnt_isr_func = NULL; +static pcnt_isr_handle_t pcnt_isr_service = NULL; +static portMUX_TYPE pcnt_spinlock = portMUX_INITIALIZER_UNLOCKED; +static const char* PCNT_TAG = "pcnt"; esp_err_t pcnt_unit_config(const pcnt_config_t *pcnt_config) { @@ -285,3 +290,73 @@ esp_err_t pcnt_isr_register(void (*fun)(void*), void * arg, int intr_alloc_flags return esp_intr_alloc(ETS_PCNT_INTR_SOURCE, intr_alloc_flags, fun, arg, handle); } +// pcnt interrupt service +static void IRAM_ATTR pcnt_intr_service(void* arg) +{ + uint32_t intr_status = PCNT.int_st.val; + for (int unit = 0; unit < PCNT_UNIT_MAX; unit++) { + if (intr_status & (BIT(unit))) { + if (pcnt_isr_func[unit].fn != NULL) { + (pcnt_isr_func[unit].fn)(pcnt_isr_func[unit].args); + } + PCNT.int_clr.val = BIT(unit); + } + } +} + +esp_err_t pcnt_isr_handler_add(pcnt_unit_t unit, void(*isr_handler)(void *), void *args) +{ + PCNT_CHECK(pcnt_isr_func != NULL, "ISR service is not installed, call pcnt_install_isr_service() first", ESP_ERR_INVALID_STATE); + PCNT_CHECK(unit < PCNT_UNIT_MAX, "PCNT unit error", ESP_ERR_INVALID_ARG); + PCNT_ENTER_CRITICAL(&pcnt_spinlock); + pcnt_intr_disable(unit); + if (pcnt_isr_func) { + pcnt_isr_func[unit].fn = isr_handler; + pcnt_isr_func[unit].args = args; + } + pcnt_intr_enable(unit); + PCNT_EXIT_CRITICAL(&pcnt_spinlock); + return ESP_OK; +} + +esp_err_t pcnt_isr_handler_remove(pcnt_unit_t unit) +{ + PCNT_CHECK(pcnt_isr_func != NULL, "ISR service is not installed", ESP_ERR_INVALID_STATE); + PCNT_CHECK(unit < PCNT_UNIT_MAX, "PCNT unit error", ESP_ERR_INVALID_ARG); + PCNT_ENTER_CRITICAL(&pcnt_spinlock); + pcnt_intr_disable(unit); + if (pcnt_isr_func) { + pcnt_isr_func[unit].fn = NULL; + pcnt_isr_func[unit].args = NULL; + } + PCNT_EXIT_CRITICAL(&pcnt_spinlock); + return ESP_OK; +} + +esp_err_t pcnt_isr_service_install(int intr_alloc_flags) +{ + PCNT_CHECK(pcnt_isr_func == NULL, "ISR service already installed", ESP_ERR_INVALID_STATE); + PCNT_ENTER_CRITICAL(&pcnt_spinlock); + esp_err_t ret = ESP_FAIL; + pcnt_isr_func = (pcnt_isr_func_t*) calloc(PCNT_UNIT_MAX, sizeof(pcnt_isr_func_t)); + if (pcnt_isr_func == NULL) { + ret = ESP_ERR_NO_MEM; + } else { + ret = pcnt_isr_register(pcnt_intr_service, NULL, intr_alloc_flags, &pcnt_isr_service); + } + PCNT_EXIT_CRITICAL(&pcnt_spinlock); + return ret; +} + +void pcnt_isr_service_uninstall(void) +{ + if (pcnt_isr_func == NULL) { + return; + } + PCNT_ENTER_CRITICAL(&pcnt_spinlock); + esp_intr_free(pcnt_isr_service); + free(pcnt_isr_func); + pcnt_isr_func = NULL; + pcnt_isr_service = NULL; + PCNT_EXIT_CRITICAL(&pcnt_spinlock); +}