]> granicus.if.org Git - esp-idf/commitdiff
driver(pcnt): Add some APIs to support each unit can have it's own interrupt handler.
authorkooho <2229179028@qq.com>
Mon, 19 Mar 2018 09:22:48 +0000 (17:22 +0800)
committerkooho <2229179028@qq.com>
Wed, 11 Apr 2018 08:15:09 +0000 (16:15 +0800)
components/driver/include/driver/pcnt.h
components/driver/pcnt.c

index ad47e51ec3d5b3d97f71f0a7840bdc03201dc651..c751473baa656461b864d967419ab6f88f461951 100644 (file)
@@ -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
index 3f2d866c1c7f5ce61aacf17cca7a8c3112754b25..b859b495b122c84bc8227e5f27b49510800ecf98 100644 (file)
 #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);
+}