Merge branch 'feature/pcnt_add_isr_manage_apis' into 'master'

Driver(pcnt) :  Add new feature that each unit can  has it's own interrupt handler.

See merge request idf/esp-idf!2086
This commit is contained in:
Angus Gratton 2018-04-12 09:06:56 +08:00
commit 234723c061
2 changed files with 136 additions and 6 deletions

View 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

View File

@ -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);
}