diff --git a/components/driver/gpio.c b/components/driver/gpio.c index f8694597a8..2cba63ca6f 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -14,6 +14,7 @@ #include #include "esp_err.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "freertos/FreeRTOS.h" #include "freertos/xtensa_api.h" #include "driver/gpio.h" @@ -320,14 +321,10 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) return ESP_OK; } -esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg) +esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags) { GPIO_CHECK(fn, "GPIO ISR null", ESP_ERR_INVALID_ARG); - ESP_INTR_DISABLE(gpio_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_GPIO_INTR_SOURCE, gpio_intr_num); - xt_set_interrupt_handler(gpio_intr_num, fn, arg); - ESP_INTR_ENABLE(gpio_intr_num); - return ESP_OK; + return esp_intr_alloc(ETS_GPIO_INTR_SOURCE, intr_alloc_flags, fn, arg, NULL); } /*only level interrupt can be used for wake-up function*/ diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index de7525bd5f..59dd568578 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -343,8 +343,9 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * - * @param gpio_intr_num GPIO interrupt number,check the info in soc.h, and please see the core-isa.h for more details * @param fn Interrupt handler function. + * @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. * * @note * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". @@ -355,7 +356,7 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); * - ESP_OK Success ; * - ESP_ERR_INVALID_ARG GPIO error */ -esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg); +esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags); @@ -415,7 +416,7 @@ esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); */ /** - *----------EXAMPLE TO CONIFGURE GPIO AS OUTPUT ------------ * + *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * * @code{c} * gpio_config_t io_conf; * io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt @@ -428,7 +429,7 @@ esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); **/ /** - *----------EXAMPLE TO CONIFGURE GPIO AS OUTPUT ------------ * + *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * * @code{c} * io_conf.intr_type = GPIO_INTR_POSEDGE; //set posedge interrupt * io_conf.mode = GPIO_MODE_INPUT; //set as input @@ -441,8 +442,7 @@ esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); /** *----------EXAMPLE TO SET ISR HANDLER ---------------------- * @code{c} - * //the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. - * gpio_isr_register(18,gpio_intr_test,NULL); //hook the isr handler for GPIO interrupt + * gpio_isr_register(gpio_intr_test,NULL, 0); //hook the isr handler for GPIO interrupt * @endcode * @note * 1. user should arrange the INUMs that used, better not to use a same INUM for different interrupt. diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index e07787b2b1..a581f604ec 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -257,11 +257,11 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, /** * @brief register LEDC interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. - * @note - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. - * @param ledc_intr_num LEDC interrupt number, check the info in soc.h, and please see the core-isa.h for more details + * * @param fn Interrupt handler function. + * @param arg User-supplied argument passed to the handler function. + * @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. * @note * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". * @param arg Parameter for handler function @@ -270,7 +270,7 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, * - ESP_OK Success * - ESP_ERR_INVALID_ARG Function pointer error. */ -esp_err_t ledc_isr_register(uint32_t ledc_intr_num, void (*fn)(void*), void * arg); +esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags); /** * @brief configure LEDC settings @@ -398,13 +398,8 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint * ----------------EXAMPLE OF LEDC INTERRUPT ------------------ * @code{c} * //we have fade_end interrupt and counter overflow interrupt. we just give an example of fade_end interrupt here. - * ledc_isr_register(18, ledc_isr_handler, NULL); //hook the isr handler for LEDC interrupt + * ledc_isr_register(ledc_isr_handler, NULL, 0); //hook the isr handler for LEDC interrupt * @endcode - * @note - * 1. the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. - * 2. user should arrange the INUMs that used, better not to use a same INUM for different interrupt source. - * 3. do not pick the INUM that already occupied by the system. - * 4. refer to soc.h to check which INUMs that can be used. * * ----------------EXAMPLE OF INTERRUPT HANDLER --------------- * @code{c} diff --git a/components/driver/include/driver/pcnt.h b/components/driver/include/driver/pcnt.h index d2620cf15e..1ab2abae74 100644 --- a/components/driver/include/driver/pcnt.h +++ b/components/driver/include/driver/pcnt.h @@ -213,21 +213,19 @@ esp_err_t pcnt_get_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16 /** * @brief Register PCNT interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. - * @note - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. * - * @param pcnt_intr_num PCNT interrupt number, check the info in soc.h, and please see the core-isa.h for more details * @param fn Interrupt handler function. * @note * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". * @param arg Parameter for handler function + * @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_INVALID_ARG Function pointer error. */ -esp_err_t pcnt_isr_register(uint32_t pcnt_intr_num, void (*fn)(void*), void * arg); +esp_err_t pcnt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags); /** * @brief Configure PCNT pulse signal input pin and control input pin diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index 50a5c743dd..4c28406d80 100644 --- a/components/driver/include/driver/rmt.h +++ b/components/driver/include/driver/rmt.h @@ -566,27 +566,21 @@ esp_err_t rmt_config(rmt_config_t* rmt_param); * @brief register RMT interrupt handler, the handler is an ISR. * * The handler will be attached to the same CPU core that this function is running on. - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. * @note * If you already called rmt_driver_install to use system RMT driver, * please do not register ISR handler again. * - * @param rmt_intr_num RMT interrupt number, check the info in soc.h, and please see the core-isa.h for more details - * * @param fn Interrupt handler function. - * - * @note - * the handler function MUST be defined with attribution of "IRAM_ATTR". - * * @param arg Parameter for handler function + * @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_INVALID_ARG Function pointer error. * - ESP_FAIL System driver installed, can not register ISR handler for RMT */ -esp_err_t rmt_isr_register(uint8_t rmt_intr_num, void (* fn)(void* ), void * arg); +esp_err_t rmt_isr_register(void (* fn)(void* ), void * arg, int intr_alloc_flags); /** * @brief Fill memory data of channel with given RMT items. @@ -727,7 +721,7 @@ esp_err_t rmt_get_ringbuf_handler(rmt_channel_t channel, RingbufHandle_t* buf_ha * rmt_config(&rmt_tx); * * //install system RMT driver, disable rx ringbuffer for transmitter. - * rmt_driver_install(rmt_tx.channel, 0, RMT_INTR_NUM); + * rmt_driver_install(rmt_tx.channel, 0, 0); * } * * @endcode @@ -747,20 +741,16 @@ esp_err_t rmt_get_ringbuf_handler(rmt_channel_t channel, RingbufHandle_t* buf_ha * rmt_config(&rmt_rx); * * //install system RMT driver. - * rmt_driver_install(rmt_rx.channel, 1000, RMT_INTR_NUM); + * rmt_driver_install(rmt_rx.channel, 1000, 0); * } * * ----------------EXAMPLE OF RMT INTERRUPT ------------------ * @code{c} * - * rmt_isr_register(RMT_INTR_NUM, rmt_isr, NULL); //hook the ISR handler for RMT interrupt + * rmt_isr_register(rmt_isr, NULL, 0); //hook the ISR handler for RMT interrupt * @endcode * @note * 0. If you have called rmt_driver_install, you don't need to set ISR handler any more. - * 1. the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. - * 2. user should arrange the INUMs that used, better not to use a same INUM for different interrupt source. - * 3. do not pick the INUM that already occupied by the system. - * 4. refer to soc.h to check which INUMs that can be used. * * ----------------EXAMPLE OF INTERRUPT HANDLER --------------- * @code{c} diff --git a/components/driver/include/driver/timer.h b/components/driver/include/driver/timer.h index c0ad7116e4..e6cbbd3787 100644 --- a/components/driver/include/driver/timer.h +++ b/components/driver/include/driver/timer.h @@ -94,8 +94,8 @@ typedef enum { typedef struct { bool alarm_en; /*!< Timer alarm enable */ bool counter_en; /*!< Counter enable */ - timer_count_dir_t counter_dir; /*!< Counter direction */ timer_intr_mode_t intr_type; /*!< Interrupt mode */ + timer_count_dir_t counter_dir; /*!< Counter direction */ bool auto_reload; /*!< Timer auto-reload */ uint16_t divider; /*!< Counter clock divider*/ } timer_config_t; @@ -245,21 +245,17 @@ esp_err_t timer_set_alarm(timer_group_t group_num, timer_idx_t timer_num, timer_ /** * @brief register Timer interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. - * @note - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. * * @param group_num Timer group number * @param timer_num Timer index of timer group - * @param timer_intr_num TIMER interrupt number, check the info in soc.h, and please see the core-isa.h for more details - * @param intr_type Timer interrupt type * @param fn Interrupt handler function. * @note * Code inside the handler function can only call functions in IRAM, so cannot call other timer APIs. * Use direct register access to access timers from inside the ISR. * * @param arg Parameter for handler function - * + * @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_INVALID_ARG Function pointer error. @@ -268,7 +264,7 @@ esp_err_t timer_set_alarm(timer_group_t group_num, timer_idx_t timer_num, timer_ * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error */ -esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, int timer_intr_num, timer_intr_mode_t intr_type, void (*fn)(void*), void * arg); +esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, void (*fn)(void*), void * arg, int intr_alloc_flags); /** @brief Initializes and configure the timer. * diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 1ddfcad9fa..0e7a4b515b 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -366,21 +366,19 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); * @brief register UART interrupt handler(ISR). * * @note UART ISR handler will be attached to the same CPU core that this function is running on. - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. - * - * @attention The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details * @param fn Interrupt handler function. * @param arg parameter for handler function + * @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. Note that the UART + * driver at the moment does not work with a shared interrupt. * * @return * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg); +esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, int intr_alloc_flags); /** * @brief Set UART pin number @@ -461,14 +459,15 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ * @param tx_buffer_size UART TX ring buffer size. * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. * @param queue_size UART event queue size/depth. - * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. + * @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_FAIL Parameter error */ -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue); +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, void* uart_queue, int intr_alloc_flags); /** * @brief Uninstall UART driver. @@ -733,7 +732,7 @@ esp_err_t uart_flush(uart_port_t uart_num); * //Set UART log level * esp_log_level_set(TAG, ESP_LOG_INFO); * //Install UART driver, and get the queue. - * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue); + * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, &uart0_queue, 0); * //Create a task to handler UART event from ISR * xTaskCreate(uart_task, "uTask", 1024, (void*)uart_num, 10, NULL); * } diff --git a/components/driver/ledc.c b/components/driver/ledc.c index 893c78a6be..66c31da9b9 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -13,6 +13,7 @@ // limitations under the License. #include #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/xtensa_api.h" @@ -113,16 +114,14 @@ static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, return ESP_OK; } -esp_err_t ledc_isr_register(uint32_t ledc_intr_num, void (*fn)(void*), void * arg) +esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags) { + esp_err_t ret; LEDC_CHECK(fn, "ledc isr null", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); - ESP_INTR_DISABLE(ledc_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_LEDC_INTR_SOURCE, ledc_intr_num); - xt_set_interrupt_handler(ledc_intr_num, fn, arg); - ESP_INTR_ENABLE(ledc_intr_num); + ret=esp_intr_alloc(ETS_LEDC_INTR_SOURCE, intr_alloc_flags, fn, arg, NULL); portEXIT_CRITICAL(&ledc_spinlock); - return ESP_OK; + return ret; } esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) diff --git a/components/driver/pcnt.c b/components/driver/pcnt.c index e65d733c01..aedd22104a 100644 --- a/components/driver/pcnt.c +++ b/components/driver/pcnt.c @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. #include "esp_log.h" +#include "esp_intr_alloc.h" #include "driver/pcnt.h" #include "driver/periph_ctrl.h" @@ -266,13 +267,9 @@ esp_err_t pcnt_filter_disable(pcnt_unit_t unit) return ESP_OK; } -esp_err_t pcnt_isr_register(uint32_t pcnt_intr_num, void (*fun)(void*), void * arg) +esp_err_t pcnt_isr_register(void (*fun)(void*), void * arg, int intr_alloc_flags) { PCNT_CHECK(fun != NULL, PCNT_ADDRESS_ERR_STR, ESP_ERR_INVALID_ARG); - ESP_INTR_DISABLE(pcnt_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_PCNT_INTR_SOURCE, pcnt_intr_num); - xt_set_interrupt_handler(pcnt_intr_num, fun, arg); - ESP_INTR_ENABLE(pcnt_intr_num); - return ESP_OK; + return esp_intr_alloc(ETS_PCNT_INTR_SOURCE, intr_alloc_flags, fun, arg, NULL); } diff --git a/components/driver/rmt.c b/components/driver/rmt.c index f23fa257d0..ba70ffea60 100644 --- a/components/driver/rmt.c +++ b/components/driver/rmt.c @@ -21,6 +21,7 @@ #include "esp_intr.h" #include "esp_log.h" #include "esp_err.h" +#include "esp_intr_alloc.h" #include "soc/gpio_sig_map.h" #include "soc/rmt_struct.h" #include "driver/periph_ctrl.h" @@ -472,17 +473,15 @@ esp_err_t rmt_fill_tx_items(rmt_channel_t channel, rmt_item32_t* item, uint16_t return ESP_OK; } -esp_err_t rmt_isr_register(uint8_t rmt_intr_num, void (*fn)(void*), void * arg) +esp_err_t rmt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags) { + esp_err_t ret; RMT_CHECK((fn != NULL), RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK(s_rmt_driver_installed == false, "RMT DRIVER INSTALLED, CAN NOT REG ISR HANDLER", ESP_FAIL); portENTER_CRITICAL(&rmt_spinlock); - ESP_INTR_DISABLE(rmt_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_RMT_INTR_SOURCE, rmt_intr_num); - xt_set_interrupt_handler(rmt_intr_num, fn, arg); - ESP_INTR_ENABLE(rmt_intr_num); + ret=esp_intr_alloc(ETS_RMT_INTR_SOURCE, intr_alloc_flags, fn, arg, NULL); portEXIT_CRITICAL(&rmt_spinlock); - return ESP_OK; + return ret; } static int IRAM_ATTR rmt_get_mem_len(rmt_channel_t channel) @@ -619,7 +618,7 @@ esp_err_t rmt_driver_uninstall(rmt_channel_t channel) return ESP_OK; } -esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num) +esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int intr_alloc_flags) { RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); if(p_rmt_obj[channel] != NULL) { @@ -627,7 +626,6 @@ esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_ return ESP_FAIL; } - ESP_INTR_DISABLE(rmt_intr_num); p_rmt_obj[channel] = (rmt_obj_t*) malloc(sizeof(rmt_obj_t)); if(p_rmt_obj[channel] == NULL) { @@ -652,11 +650,10 @@ esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_ rmt_set_err_intr_en(channel, 1); } if(s_rmt_driver_installed == false) { - rmt_isr_register(rmt_intr_num, rmt_driver_isr_default, NULL); + rmt_isr_register(rmt_driver_isr_default, NULL, intr_alloc_flags); s_rmt_driver_installed = true; } rmt_set_tx_intr_en(channel, 1); - ESP_INTR_ENABLE(rmt_intr_num); return ESP_OK; } diff --git a/components/driver/timer.c b/components/driver/timer.c index b305a41468..023f731cc3 100644 --- a/components/driver/timer.c +++ b/components/driver/timer.c @@ -15,6 +15,7 @@ #include "esp_log.h" #include "esp_err.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "freertos/FreeRTOS.h" #include "freertos/xtensa_api.h" #include "driver/timer.h" @@ -167,36 +168,32 @@ esp_err_t timer_set_alarm(timer_group_t group_num, timer_idx_t timer_num, timer_ return ESP_OK; } -esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, int timer_intr_num, - timer_intr_mode_t intr_type, void (*fn)(void*), void * arg) +esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, + void (*fn)(void*), void * arg, int intr_alloc_flags) { TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG); TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG); TIMER_CHECK(fn != NULL, TIMER_PARAM_ADDR_ERROR, ESP_ERR_INVALID_ARG); - ESP_INTR_DISABLE(timer_intr_num); int intr_source = 0; switch(group_num) { case TIMER_GROUP_0: default: - if(intr_type == TIMER_INTR_LEVEL) { + if((intr_alloc_flags & ESP_INTR_FLAG_EDGE) == 0) { intr_source = ETS_TG0_T0_LEVEL_INTR_SOURCE + timer_num; } else { intr_source = ETS_TG0_T0_EDGE_INTR_SOURCE + timer_num; } break; case TIMER_GROUP_1: - if(intr_type == TIMER_INTR_LEVEL) { + if((intr_alloc_flags & ESP_INTR_FLAG_EDGE) == 0) { intr_source = ETS_TG1_T0_LEVEL_INTR_SOURCE + timer_num; } else { intr_source = ETS_TG1_T0_EDGE_INTR_SOURCE + timer_num; } break; } - intr_matrix_set(xPortGetCoreID(), intr_source, timer_intr_num); - xt_set_interrupt_handler(timer_intr_num, fn, arg); - ESP_INTR_ENABLE(timer_intr_num); - return ESP_OK; + return esp_intr_alloc(intr_source, intr_alloc_flags, fn, arg, NULL); } esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, timer_config_t *config) diff --git a/components/driver/uart.c b/components/driver/uart.c index 59b4904dcd..62979dd5a3 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -15,7 +15,9 @@ #include "esp_types.h" #include "esp_attr.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "esp_log.h" +#include "esp_err.h" #include "malloc.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -53,7 +55,7 @@ typedef struct { uart_port_t uart_num; /*!< UART port number*/ int queue_size; /*!< UART event queue size*/ QueueHandle_t xQueueUart; /*!< UART queue handler*/ - int intr_num; /*!< UART interrupt number*/ + int_handle_t intr_handle; /*!< UART interrupt handle*/ //rx parameters SemaphoreHandle_t rx_mux; /*!< UART RX data mutex*/ int rx_buf_size; /*!< RX ring buffer size */ @@ -283,31 +285,29 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh) UART[uart_num]->conf1.txfifo_empty_thrhd = thresh & UART_TXFIFO_EMPTY_THRHD_V; UART[uart_num]->int_ena.txfifo_empty = enable & 0x1; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); - ESP_INTR_ENABLE(p_uart_obj[uart_num]->intr_num); return ESP_OK; } -esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg) +esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, int intr_alloc_flags) { + int ret; UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK(((intr_alloc_flags & ESP_INTR_FLAG_SHARED)==0), "UART doesn't support shared interrupts", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); - ESP_INTR_DISABLE(uart_intr_num); switch(uart_num) { case UART_NUM_1: - intr_matrix_set(xPortGetCoreID(), ETS_UART1_INTR_SOURCE, uart_intr_num); + ret=esp_intr_alloc(ETS_UART1_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle); break; case UART_NUM_2: - intr_matrix_set(xPortGetCoreID(), ETS_UART2_INTR_SOURCE, uart_intr_num); + ret=esp_intr_alloc(ETS_UART2_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle); break; case UART_NUM_0: default: - intr_matrix_set(xPortGetCoreID(), ETS_UART0_INTR_SOURCE, uart_intr_num); + ret=esp_intr_alloc(ETS_UART0_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle); break; } - xt_set_interrupt_handler(uart_intr_num, fn, arg); - ESP_INTR_ENABLE(uart_intr_num); UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); - return ESP_OK; + return ret; } //internal signal can be output to multiple GPIO pads @@ -859,7 +859,7 @@ esp_err_t uart_flush(uart_port_t uart_num) //rx sem protect the ring buffer read related functions xSemaphoreTake(p_uart->rx_mux, (portTickType)portMAX_DELAY); - ESP_INTR_DISABLE(p_uart->intr_num); + esp_intr_disable(p_uart->intr_handle); while(true) { if(p_uart->rx_head_ptr) { vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->rx_head_ptr); @@ -876,12 +876,12 @@ esp_err_t uart_flush(uart_port_t uart_num) p_uart->rx_ptr = NULL; p_uart->rx_cur_remain = 0; p_uart->rx_head_ptr = NULL; - ESP_INTR_ENABLE(p_uart->intr_num); + esp_intr_enable(p_uart->intr_handle); xSemaphoreGive(p_uart->rx_mux); if(p_uart->tx_buf_size > 0) { xSemaphoreTake(p_uart->tx_mux, (portTickType)portMAX_DELAY); - ESP_INTR_DISABLE(p_uart->intr_num); + esp_intr_disable(p_uart->intr_handle); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->int_ena.txfifo_empty = 0; UART[uart_num]->int_clr.txfifo_empty = 1; @@ -901,19 +901,18 @@ esp_err_t uart_flush(uart_port_t uart_num) p_uart->tx_ptr = NULL; p_uart->tx_waiting_brk = 0; p_uart->tx_waiting_fifo = false; - ESP_INTR_ENABLE(p_uart->intr_num); + esp_intr_enable(p_uart->intr_handle); xSemaphoreGive(p_uart->tx_mux); } uart_reset_fifo(uart_num); return ESP_OK; } -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue) +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, void* uart_queue, int intr_alloc_flags) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error", ESP_FAIL); if(p_uart_obj[uart_num] == NULL) { - ESP_INTR_DISABLE(uart_intr_num); p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); if(p_uart_obj[uart_num] == NULL) { ESP_LOGE(UART_TAG, "UART driver malloc error"); @@ -926,7 +925,6 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b p_uart_obj[uart_num]->tx_brk_sem = xSemaphoreCreateBinary(); p_uart_obj[uart_num]->tx_mux = xSemaphoreCreateMutex(); p_uart_obj[uart_num]->rx_mux = xSemaphoreCreateMutex(); - p_uart_obj[uart_num]->intr_num = uart_intr_num; p_uart_obj[uart_num]->queue_size = queue_size; p_uart_obj[uart_num]->tx_ptr = NULL; p_uart_obj[uart_num]->tx_head = NULL; @@ -959,7 +957,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b ESP_LOGE(UART_TAG, "UART driver already installed"); return ESP_FAIL; } - uart_isr_register(uart_num, uart_intr_num, uart_rx_intr_handler_default, p_uart_obj[uart_num]); + uart_isr_register(uart_num, uart_rx_intr_handler_default, p_uart_obj[uart_num], intr_alloc_flags); uart_intr_config_t uart_intr = { .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M | UART_RXFIFO_TOUT_INT_ENA_M @@ -972,7 +970,6 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b .txfifo_empty_intr_thresh = UART_EMPTY_THRESH_DEFAULT }; uart_intr_config(uart_num, &uart_intr); - ESP_INTR_ENABLE(uart_intr_num); return ESP_OK; } @@ -984,10 +981,9 @@ esp_err_t uart_driver_delete(uart_port_t uart_num) ESP_LOGI(UART_TAG, "ALREADY NULL"); return ESP_OK; } - ESP_INTR_DISABLE(p_uart_obj[uart_num]->intr_num); + esp_intr_free(p_uart_obj[uart_num]->intr_handle); uart_disable_rx_intr(uart_num); uart_disable_tx_intr(uart_num); - uart_isr_register(uart_num, p_uart_obj[uart_num]->intr_num, NULL, NULL); if(p_uart_obj[uart_num]->tx_fifo_sem) { vSemaphoreDelete(p_uart_obj[uart_num]->tx_fifo_sem); diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 8802b7dcd7..d6088b0401 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -173,12 +173,6 @@ void start_cpu0_default(void) uart_div_modify(CONFIG_CONSOLE_UART_NUM, (APB_CLK_FREQ << 4) / CONFIG_CONSOLE_UART_BAUDRATE); #if CONFIG_BROWNOUT_DET esp_brownout_init(); -#endif -#if CONFIG_INT_WDT - esp_int_wdt_init(); -#endif -#if CONFIG_TASK_WDT - esp_task_wdt_init(); #endif esp_setup_time_syscalls(); esp_vfs_dev_uart_register(); @@ -194,6 +188,12 @@ void start_cpu0_default(void) _GLOBAL_REENT->_stderr = (FILE*) &__sf_fake_stderr; #endif do_global_ctors(); +#if CONFIG_INT_WDT + esp_int_wdt_init(); +#endif +#if CONFIG_TASK_WDT + esp_task_wdt_init(); +#endif #if !CONFIG_FREERTOS_UNICORE esp_crosscore_int_init(); #endif diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index 60f972a2a2..f7ea4f6a74 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -17,6 +17,7 @@ #include "esp_attr.h" #include "esp_err.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "rom/ets_sys.h" #include "rom/uart.h" @@ -72,14 +73,11 @@ void esp_crosscore_int_init() { portENTER_CRITICAL(&reasonSpinlock); reason[xPortGetCoreID()]=0; portEXIT_CRITICAL(&reasonSpinlock); - ESP_INTR_DISABLE(ETS_FROM_CPU_INUM); if (xPortGetCoreID()==0) { - intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR0_SOURCE, ETS_FROM_CPU_INUM); + esp_intr_alloc(ETS_FROM_CPU_INTR0_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); } else { - intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR1_SOURCE, ETS_FROM_CPU_INUM); + esp_intr_alloc(ETS_FROM_CPU_INTR1_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); } - xt_set_interrupt_handler(ETS_FROM_CPU_INUM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()]); - ESP_INTR_ENABLE(ETS_FROM_CPU_INUM); } void esp_crosscore_int_send_yield(int coreId) { diff --git a/components/esp32/include/esp_intr_alloc.h b/components/esp32/include/esp_intr_alloc.h new file mode 100644 index 0000000000..ea7d597678 --- /dev/null +++ b/components/esp32/include/esp_intr_alloc.h @@ -0,0 +1,262 @@ +// 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 __ESP_INTR_ALLOC_H__ +#define __ESP_INTR_ALLOC_H__ + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @addtogroup Intr_Alloc + * @{ + */ + + +/** @brief Interrupt allocation flags + * + * These flags can be used to specify which interrupt qualities the + * code calling esp_intr_alloc* needs. + * + */ + +//Keep the LEVELx values as they are here; they match up with (1<3 + * is requested, because these types of interrupts aren't C-callable. + * @param arg Optional argument for passed to the interrupt handler + * @param ret_handle Pointer to an int_handle_t to store a handle that can later be + * used to request details or free the interrupt. Can be NULL if no handle + * is required. + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_ERR_NOT_FOUND No free interrupt found with the specified flags + * ESP_OK otherwise + */ +esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, int_handle_t *ret_handle); + + +/** + * @brief Allocate an interrupt with the given parameters. + * + * + * This essentially does the same as esp_intr_alloc, but allows specifying a register and mask + * combo. For shared interrupts, the handler is only called if a read from the specified + * register, ANDed with the mask, returns non-zero. By passing an interrupt status register + * address and a fitting mask, this can be used to accelerate interrupt handling in the case + * a shared interrupt is triggered; by checking the interrupt statuses first, the code can + * decide which ISRs can be skipped + * + * @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux + * sources, as defined in soc/soc.h, or one of the internal + * ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header. + * @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the + * choice of interrupts that this routine can choose from. If this value + * is 0, it will default to allocating a non-shared interrupt of level + * 1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared + * interrupt of level 1. + * @param intrstatusreg The address of an interrupt status register + * @param intrstatusmask A mask. If a read of address intrstatusreg has any of the bits + * that are 1 in the mask set, the ISR will be called. If not, it will be + * skipped. + * @param handler The interrupt handler. Must be NULL when an interrupt of level >3 + * is requested, because these types of interrupts aren't C-callable. + * @param arg Optional argument for passed to the interrupt handler + * @param ret_handle Pointer to an int_handle_t to store a handle that can later be + * used to request details or free the interrupt. Can be NULL if no handle + * is required. + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_ERR_NOT_FOUND No free interrupt found with the specified flags + * ESP_OK otherwise + */ +esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, void *arg, int_handle_t *ret_handle); + + +/** + * @brief Disable and free an interrupt. + * + * Use an interrupt handle to disable the interrupt (if non-shared) and release the resources + * associated with it. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if handle is invalid, or esp_intr_free runs on another core than + * where the interrupt is allocated on. + * ESP_OK otherwise + */ +esp_err_t esp_intr_free(int_handle_t handle); + + +/** + * @brief Get CPU number an interrupt is tied to + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return The core number where the interrupt is allocated + */ +int esp_intr_get_cpu(int_handle_t handle); + +/** + * @brief Get the allocated interrupt for a certain handle + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return The interrupt number + */ +int esp_intr_get_intno(int_handle_t handle); + + +/** + * @brief Disable the interrupt associated with the handle + * + * @note This function can only disable non-shared inteerupts allocated on the CPU that runs this function. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_OK otherwise + */ +esp_err_t esp_intr_disable(int_handle_t handle); + +/** + * @brief Ensable the interrupt associated with the handle + * + * @note This function can only enable non-shared inteerupts allocated on the CPU that runs this function. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_OK otherwise + */ +esp_err_t esp_intr_enable(int_handle_t handle); + + +/** + * @brief Disable interrupts that aren't specifically marked as running from IRAM + */ +void esp_intr_noniram_disable(); + + +/** + * @brief Re-enable interrupts disabled by esp_intr_noniram_disable + */ +void esp_intr_noniram_enable(); + +/**@}*/ + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/components/esp32/intr_alloc.c b/components/esp32/intr_alloc.c new file mode 100644 index 0000000000..bd7a7e2c25 --- /dev/null +++ b/components/esp32/intr_alloc.c @@ -0,0 +1,654 @@ +// 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. + + + +#include "sdkconfig.h" +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include "esp_err.h" +#include "esp_log.h" +#include "esp_intr.h" +#include "esp_attr.h" +#include "esp_intr_alloc.h" +#include +#include + +static const char* TAG = "intr_alloc"; + + +#define ETS_INTERNAL_TIMER0_INTR_NO 6 +#define ETS_INTERNAL_TIMER1_INTR_NO 15 +#define ETS_INTERNAL_TIMER2_INTR_NO 16 +#define ETS_INTERNAL_SW0_INTR_NO 7 +#define ETS_INTERNAL_SW1_INTR_NO 29 +#define ETS_INTERNAL_PROFILING_INTR_NO 11 + + +/* +Define this to debug the choices made when allocating the interrupt. This leads to much debugging +output within a critical region, which can lead to weird effects like e.g. the interrupt watchdog +being triggered, that is why it is separate from the normal LOG* scheme. +*/ +//define DEBUG_INT_ALLOC_DECISIONS +#ifdef DEBUG_INT_ALLOC_DECISIONS +# define ALCHLOG(...) ESP_EARLY_LOGD(TAG, __VA_ARGS__) +#else +# define ALCHLOG(...) do {} while (0) +#endif + + +typedef enum { + INTDESC_NORMAL=0, + INTDESC_RESVD, + INTDESC_SPECIAL //for xtensa timers / software ints +} int_desc_flag_t; + +typedef enum { + INTTP_LEVEL=0, + INTTP_EDGE, + INTTP_NA +} int_type_t; + +typedef struct { + int level; + int_type_t type; + int_desc_flag_t cpuflags[2]; +} int_desc_t; + + +//We should mark the interrupt for the timer used by FreeRTOS as reserved. The specific timer +//is selectable using menuconfig; we use these cpp bits to convert that into something we can use in +//the table below. +#if CONFIG_FREERTOS_CORETIMER_0 +#define INT6RES INTDESC_RESVD +#else +#define INT6RES INTDESC_SPECIAL +#endif + +#if CONFIG_FREERTOS_CORETIMER_1 +#define INT15RES INTDESC_RESVD +#else +#define INT15RES INTDESC_SPECIAL +#endif + +#if CONFIG_FREERTOS_CORETIMER_2 +#define INT16RES INTDESC_RESVD +#else +#define INT16RES INTDESC_SPECIAL +#endif + +//This is basically a software-readable version of the interrupt usage table in include/soc/soc.h +const static int_desc_t int_desc[32]={ + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //0 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //1 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //2 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //3 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //4 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //5 + { 1, INTTP_NA, {INT6RES, INT6RES } }, //6 + { 1, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //7 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //8 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //9 + { 1, INTTP_EDGE , {INTDESC_RESVD, INTDESC_NORMAL} }, //10 + { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //11 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //12 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //13 + { 7, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //14, NMI + { 3, INTTP_NA, {INT15RES, INT15RES } }, //15 + { 5, INTTP_NA, {INT16RES, INT16RES } }, //16 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //17 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //18 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //19 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //20 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //21 + { 3, INTTP_EDGE, {INTDESC_RESVD, INTDESC_NORMAL} }, //22 + { 3, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //23 + { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //24 + { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //25 + { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //26 + { 3, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //27 + { 4, INTTP_EDGE, {INTDESC_NORMAL, INTDESC_NORMAL} }, //28 + { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //29 + { 4, INTTP_EDGE, {INTDESC_RESVD, INTDESC_RESVD } }, //30 + { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //31 +}; + + +//For memory usage and to get an unique ID for every int on every CPU core, the +//intrs and cpus are stored in in one int. These functions handle that. +inline static int to_intno_cpu(int intno, int cpu) +{ + return intno+cpu*32; +} + +inline static int to_intno(int intno_cpu) +{ + return (intno_cpu)&31; +} + +inline static int to_cpu(int intno_cpu) +{ + return (intno_cpu)/32; +} + +typedef struct shared_vector_desc_t shared_vector_desc_t; +typedef struct vector_desc_t vector_desc_t; + +struct shared_vector_desc_t { + volatile uint32_t *statusreg; + uint32_t statusmask; + intr_handler_t isr; + void *arg; + shared_vector_desc_t *next; +}; + + +#define VECDESC_FL_RESERVED (1<<0) +#define VECDESC_FL_INIRAM (1<<1) +#define VECDESC_FL_SHARED (1<<2) +#define VECDESC_FL_NONSHARED (1<<3) + +struct vector_desc_t { + int intno_cpu; //intno+cpu*32 + int flags; //OR of VECDESC_FLAG_* defines + shared_vector_desc_t *shared_vec_info; //used when VECDESC_FL_SHARED + vector_desc_t *next; +}; + +struct int_handle_data_t { + vector_desc_t *vector_desc; + shared_vector_desc_t *shared_vector_desc; +}; + + +//Linked list of vector descriptions, sorted by intno_cpu value +static vector_desc_t *vector_desc_head; + +//This bitmask has an 1 if the int should be disabled when the flash is disabled. +static uint32_t non_iram_int_mask[portNUM_PROCESSORS]; +//This bitmask has 1 in it if the int was disabled using esp_intr_noniram_disable. +static uint32_t non_iram_int_disabled[portNUM_PROCESSORS]; +static bool non_iram_int_disabled_flag[portNUM_PROCESSORS]; + + +static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; + +//Inserts an item into vector_desc list so that the list is sorted +//with an incrementing intno_cpu value. +static void insert_vector_desc(vector_desc_t *to_insert) +{ + vector_desc_t *vd=vector_desc_head; + vector_desc_t *prev=NULL; + while(vd!=NULL) { + if (vd->intno_cpu >= to_insert->intno_cpu) break; + prev=vd; + vd=vd->next; + } + if (vd==NULL && prev==NULL) { + //First item + vector_desc_head=to_insert; + vector_desc_head->next=NULL; + } else { + prev->next=to_insert; + to_insert->next=vd; + } +} + +//Returns a vector_desc entry for an intno/cpu, or NULL if none exists. +static vector_desc_t *find_desc_for_int(int intno, int cpu) +{ + vector_desc_t *vd=vector_desc_head; + while(vd!=NULL) { + if (vd->intno_cpu==to_intno_cpu(intno, cpu)) break; + vd=vd->next; + } + return vd; +} + +//Returns a vector_desc entry for an intno/cpu. +//Either returns a preexisting one or allocates a new one and inserts +//it into the list. +static vector_desc_t *get_desc_for_int(int intno, int cpu) +{ + vector_desc_t *vd=find_desc_for_int(intno, cpu); + if (vd==NULL) { + vector_desc_t *newvd=malloc(sizeof(vector_desc_t)); + memset(newvd, 0, sizeof(vector_desc_t)); + newvd->intno_cpu=to_intno_cpu(intno, cpu); + insert_vector_desc(newvd); + return newvd; + } else { + return vd; + } +} + +esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram) +{ + if (intno>31) return ESP_ERR_INVALID_ARG; + if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + vector_desc_t *vd=get_desc_for_int(intno, cpu); + vd->flags=VECDESC_FL_SHARED; + if (is_int_ram) vd->flags|=VECDESC_FL_INIRAM; + portEXIT_CRITICAL(&spinlock); + + return ESP_OK; +} + +esp_err_t esp_intr_reserve(int intno, int cpu) +{ + if (intno>31) return ESP_ERR_INVALID_ARG; + if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + vector_desc_t *vd=get_desc_for_int(intno, cpu); + vd->flags=VECDESC_FL_RESERVED; + portEXIT_CRITICAL(&spinlock); + + return ESP_OK; +} + +//Interrupt handler table and unhandled uinterrupt routine. Duplicated +//from xtensa_intr.c... it's supposed to be private, but we need to look +//into it in order to see if someone allocated an int using +//xt_set_interrupt_handler. +typedef struct xt_handler_table_entry { + void * handler; + void * arg; +} xt_handler_table_entry; +extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS*portNUM_PROCESSORS]; +extern void xt_unhandled_interrupt(void * arg); + +//Returns true if handler for interrupt is not the default unhandled interrupt handler +static bool int_has_handler(int intr, int cpu) +{ + return (_xt_interrupt_table[intr*portNUM_PROCESSORS+cpu].handler != xt_unhandled_interrupt); +} + + +//Locate a free interrupt compatible with the flags given. +//The 'force' argument can be -1, or 0-31 to force checking a certain interrupt. +static int get_free_int(int flags, int cpu, int force) +{ + int x; + int best=-1; + int bestLevel=9; + int bestSharedCt=INT_MAX; + //Default vector desc, for vectors not in the linked list + vector_desc_t empty_vect_desc; + memset(&empty_vect_desc, 0, sizeof(vector_desc_t)); + //Level defaults to any low/med interrupt + if (!(flags&ESP_INTR_FLAG_LEVELMASK)) flags|=ESP_INTR_FLAG_LOWMED; + + ALCHLOG(TAG, "get_free_int: start looking. Current cpu: %d", cpu); + //Iterate over the 32 possible interrupts + for (x=0; x!=31; x++) { + //Grab the vector_desc for this vector. + vector_desc_t *vd=find_desc_for_int(x, cpu); + if (vd==NULL) vd=&empty_vect_desc; + //See if we have a forced interrupt; if so, bail out if this is not it. + if (force!=-1 && force!=x) { + ALCHLOG(TAG, "Ignoring int %d: forced to %d", x, force); + continue; + } + ALCHLOG(TAG, "Int %d reserved %d level %d %s hasIsr %d", + x, int_desc[x].cpuflags[cpu]==INTDESC_RESVD, int_desc[x].level, + int_desc[x].type==INTTP_LEVEL?"LEVEL":"EDGE", int_has_handler(x, cpu)); + //Check if interrupt is not reserved by design + if (int_desc[x].cpuflags[cpu]==INTDESC_RESVD) { //ToDo: Check for SPECIAL and force!=-1 + ALCHLOG(TAG, "....Unusable: reserved"); + continue; + } + //Check if the interrupt level is acceptable + if (!(flags&(1<flags&VECDESC_FL_SHARED)) { + ALCHLOG(TAG, "....Unusable: already allocated"); + continue; + } + //Ints can't be both shared and non-shared. + assert(!((vd->flags&VECDESC_FL_SHARED)&&(vd->flags&VECDESC_FL_NONSHARED))); + //check if interrupt is reserved at runtime + if (vd->flags&VECDESC_FL_RESERVED) { + ALCHLOG(TAG, "....Unusable: reserved at runtime."); + continue; + } + //check if interrupt already is in use by a non-shared interrupt + if (vd->flags&VECDESC_FL_NONSHARED) { + ALCHLOG(TAG, "....Unusable: already in (non-shared) use."); + continue; + } + if (flags&ESP_INTR_FLAG_SHARED) { + //We're allocating a shared int. + bool in_iram_flag=((flags&ESP_INTR_FLAG_IRAM)!=0); + bool desc_in_iram_flag=((vd->flags&VECDESC_FL_INIRAM)!=0); + //Bail out if int is shared, but iram property doesn't match what we want. + if ((vd->flags&VECDESC_FL_SHARED) && (desc_in_iram_flag!=in_iram_flag)) { + ALCHLOG(TAG, "....Unusable: shared but iram prop doesn't match"); + continue; + } + //See if int already is used as a shared interrupt. + if (vd->flags&VECDESC_FL_SHARED) { + //We can use this already-marked-as-shared interrupt. Count the already attached isrs in order to see + //how useful it is. + int no=0; + shared_vector_desc_t *svdesc=vd->shared_vec_info; + while (svdesc!=NULL) { + no++; + svdesc=svdesc->next; + } + if (noint_desc[x].level) { + //Seems like this shared vector is both okay and has the least amount of ISRs already attached to it. + best=x; + bestSharedCt=no; + bestLevel=int_desc[x].level; + ALCHLOG(TAG, "...int %d more usable as a shared int: has %d existing vectors", x, no); + } else { + ALCHLOG(TAG, "...worse than int %d", best); + } + } else { + if (best==-1) { + //We haven't found a feasible shared interrupt yet. This one is still free and usable, even if + //not marked as shared. + //Remember it in case we don't find any other shared interrupt that qualifies. + if (bestLevel>int_desc[x].level) { + best=x; + bestLevel=int_desc[x].level; + ALCHLOG(TAG, "...int %d usable as a new shared int", x); + } + } else { + ALCHLOG(TAG, "...already have a shared int"); + } + } + } else { + //We need an unshared IRQ; can't use shared ones; bail out if this is shared. + if (vd->flags&VECDESC_FL_SHARED) { + ALCHLOG(TAG, "...Unusable: int is shared, we need non-shared."); + continue; + } + //Seems this interrupt is feasible. Select it and break out of the loop; no need to search further. + if (bestLevel>int_desc[x].level) { + best=x; + bestLevel=int_desc[x].level; + } else { + ALCHLOG(TAG, "...worse than int %d", best); + } + } + } + ALCHLOG(TAG, "get_free_int: using int %d", best); + + //Okay, by now we have looked at all potential interrupts and hopefully have selected the best one in best. + return best; +} + + +//Common shared isr handler. Chain-call all ISRs. +static void IRAM_ATTR shared_intr_isr(void *arg) +{ + vector_desc_t *vd=(vector_desc_t*)arg; + shared_vector_desc_t *sh_vec=vd->shared_vec_info; + portENTER_CRITICAL(&spinlock); + while(sh_vec) { + if ((sh_vec->statusreg == NULL) || (*sh_vec->statusreg & sh_vec->statusmask)) { + sh_vec->isr(sh_vec->arg); + sh_vec=sh_vec->next; + } + } + portEXIT_CRITICAL(&spinlock); +} + + +//We use ESP_EARLY_LOG* here because this can be called before the scheduler is running. +esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, + void *arg, int_handle_t *ret_handle) +{ + int force=-1; + ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): checking args", xPortGetCoreID()); + //Shared interrupts should be level-triggered. + if ((flags&ESP_INTR_FLAG_SHARED) && (flags&ESP_INTR_FLAG_EDGE)) return ESP_ERR_INVALID_ARG; + //You can't set an handler / arg for a non-C-callable interrupt. + if ((flags&ESP_INTR_FLAG_HIGH) && (handler)) return ESP_ERR_INVALID_ARG; + //Shared ints should have handler + if ((flags&ESP_INTR_FLAG_SHARED) && (!handler)) return ESP_ERR_INVALID_ARG; + //Only shared interrupts can have status reg / mask + if (intrstatusreg && (!(flags&ESP_INTR_FLAG_SHARED))) return ESP_ERR_INVALID_ARG; + //Statusreg should have a mask + if (intrstatusreg && !intrstatusmask) return ESP_ERR_INVALID_ARG; + + //Default to prio 1 for shared interrupts. Default to prio 1, 2 or 3 for non-shared interrupts. + if ((flags&ESP_INTR_FLAG_LEVELMASK)==0) { + if (flags&ESP_INTR_FLAG_SHARED) { + flags|=ESP_INTR_FLAG_LEVEL1; + } else { + flags|=ESP_INTR_FLAG_LOWMED; + } + } + ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): Args okay. Resulting flags 0x%X", xPortGetCoreID(), flags); + + //Check 'special' interrupt sources. These are tied to one specific interrupt, so we + //have to force get_free_int to only look at that. + if (source==ETS_INTERNAL_TIMER0_INTR_SOURCE) force=ETS_INTERNAL_TIMER0_INTR_NO; + if (source==ETS_INTERNAL_TIMER1_INTR_SOURCE) force=ETS_INTERNAL_TIMER1_INTR_NO; + if (source==ETS_INTERNAL_TIMER2_INTR_SOURCE) force=ETS_INTERNAL_TIMER2_INTR_NO; + if (source==ETS_INTERNAL_SW0_INTR_SOURCE) force=ETS_INTERNAL_SW0_INTR_NO; + if (source==ETS_INTERNAL_SW1_INTR_SOURCE) force=ETS_INTERNAL_SW1_INTR_NO; + if (source==ETS_INTERNAL_PROFILING_INTR_SOURCE) force=ETS_INTERNAL_PROFILING_INTR_NO; + + portENTER_CRITICAL(&spinlock); + int cpu=xPortGetCoreID(); + //See if we can find an interrupt that matches the flags. + int intr=get_free_int(flags, cpu, force); + if (intr==-1) { + //None found. Bail out. + portEXIT_CRITICAL(&spinlock); + return ESP_ERR_NOT_FOUND; + } + //Get an int vector desc for int. + vector_desc_t *vd=get_desc_for_int(intr, cpu); + + //Allocate that int! + if (flags&ESP_INTR_FLAG_SHARED) { + //Populate vector entry and add to linked list. + shared_vector_desc_t *sh_vec=malloc(sizeof(shared_vector_desc_t)); + memset(sh_vec, 0, sizeof(shared_vector_desc_t)); + sh_vec->statusreg=(uint32_t*)intrstatusreg; + sh_vec->statusmask=intrstatusmask; + sh_vec->isr=handler; + sh_vec->arg=arg; + sh_vec->next=vd->shared_vec_info; + vd->shared_vec_info=sh_vec; + vd->flags|=VECDESC_FL_SHARED; + //(Re-)set shared isr handler to new value. + xt_set_interrupt_handler(intr, shared_intr_isr, vd); + } else { + //Mark as unusable for other interrupt sources. This is ours now! + vd->flags=VECDESC_FL_NONSHARED; + if (handler) { + xt_set_interrupt_handler(intr, handler, arg); + } + if (flags&ESP_INTR_FLAG_EDGE) xthal_set_intclear(1 << intr); + } + if (flags&ESP_INTR_FLAG_IRAM) { + vd->flags|=VECDESC_FL_INIRAM; + non_iram_int_mask[cpu]&=~(1<flags&=~VECDESC_FL_INIRAM; + non_iram_int_mask[cpu]|=(1<=0) { + intr_matrix_set(cpu, source, intr); + } + //If we should return a handle, allocate it here. + if (ret_handle!=NULL) { + int_handle_data_t *ret; + ret=malloc(sizeof(int_handle_data_t)); + ret->vector_desc=vd; + ret->shared_vector_desc=vd->shared_vec_info; + *ret_handle=ret; + } + + //We enable the interrupt in any case. For shared interrupts, the interrupts are enabled as soon as we exit + //the critical region anyway, so this is consistent. + portEXIT_CRITICAL(&spinlock); + ESP_EARLY_LOGD(TAG, "Connected src %d to int %d (cpu %d)", source, intr, cpu); + ESP_INTR_ENABLE(intr); + return ESP_OK; +} + +esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, int_handle_t *ret_handle) +{ + /* + As an optimization, we can create a table with the possible interrupt status registers and masks for every single + source there is. We can then add code here to look up an applicable value and pass that to the + esp_intr_alloc_intrstatus function. + */ + return esp_intr_alloc_intrstatus(source, flags, 0, 0, handler, arg, ret_handle); +} + + +esp_err_t esp_intr_free(int_handle_t handle) +{ + bool free_shared_vector=false; + if (!handle) return ESP_ERR_INVALID_ARG; + //This routine should be called from the interrupt the task is scheduled on. + if (to_cpu(handle->vector_desc->intno_cpu)!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + if (handle->vector_desc->flags&VECDESC_FL_SHARED) { + //Find and kill the shared int + shared_vector_desc_t *svd=handle->vector_desc->shared_vec_info; + shared_vector_desc_t *prevsvd=NULL; + assert(svd); //should be something in there for a shared int + while (svd!=NULL) { + if (svd==handle->shared_vector_desc) { + //Found it. Now kill it. + if (prevsvd) { + prevsvd->next=svd->next; + } else { + handle->vector_desc->shared_vec_info=svd->next; + } + free(svd); + break; + } + prevsvd=svd; + svd=svd->next; + } + //If nothing left, disable interrupt. + if (handle->vector_desc->shared_vec_info==NULL) free_shared_vector=true; + ESP_LOGV(TAG, "esp_intr_free: Deleting shared int: %s. Shared int is %s", svd?"not found or last one":"deleted", free_shared_vector?"empty now.":"still in use"); + } + + if ((handle->vector_desc->flags&VECDESC_FL_NONSHARED) || free_shared_vector) { + ESP_LOGV(TAG, "esp_intr_free: Disabling int, killing handler"); + //Interrupt is not shared. Just disable it and revert to the default interrupt handler. + ESP_INTR_DISABLE(to_intno(handle->vector_desc->intno_cpu)); + xt_set_interrupt_handler(to_intno(handle->vector_desc->intno_cpu), xt_unhandled_interrupt, NULL); + //Theoretically, we could free the vector_desc... not sure if that's worth the few bytes of memory + //we save.(We can also not use the same exit path for empty shared ints anymore if we delete + //the desc.) For now, just mark it as free. + handle->vector_desc->flags&=!(VECDESC_FL_NONSHARED|VECDESC_FL_RESERVED); + //Also kill non_iram mask bit. + non_iram_int_mask[to_cpu(handle->vector_desc->intno_cpu)]&=~(1<<(to_intno(handle->vector_desc->intno_cpu))); + } + portEXIT_CRITICAL(&spinlock); + free(handle); + return ESP_OK; +} + +int esp_intr_get_intno(int_handle_t handle) +{ + return to_intno(handle->vector_desc->intno_cpu); +} + +int esp_intr_get_cpu(int_handle_t handle) +{ + return to_cpu(handle->vector_desc->intno_cpu); +} + +esp_err_t esp_intr_enable(int_handle_t handle) +{ + if (!handle) return ESP_ERR_INVALID_ARG; + if (handle->shared_vector_desc) return ESP_ERR_INVALID_ARG; //Shared ints can't be enabled using this function. + if (to_cpu(handle->vector_desc->intno_cpu)!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; //Can only enable ints on this cpu + ESP_INTR_ENABLE(to_intno(handle->vector_desc->intno_cpu)); + return ESP_OK; +} + +esp_err_t esp_intr_disable(int_handle_t handle) +{ + if (!handle) return ESP_ERR_INVALID_ARG; + if (handle->shared_vector_desc) return ESP_ERR_INVALID_ARG; //Shared ints can't be disabled using this function. + if (to_cpu(handle->vector_desc->intno_cpu)!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; //Can only disable ints on this cpu + ESP_INTR_DISABLE(to_intno(handle->vector_desc->intno_cpu)); + return ESP_OK; +} + + +void esp_intr_noniram_disable() +{ + int oldint; + int cpu=xPortGetCoreID(); + int intmask=~non_iram_int_mask[cpu]; + assert(non_iram_int_disabled_flag[cpu]==false); + non_iram_int_disabled_flag[cpu]=true; + asm volatile ( + "movi %0,0\n" + "xsr %0,INTENABLE\n" //disable all ints first + "rsync\n" + "and a3,%0,%1\n" //mask ints that need disabling + "wsr a3,INTENABLE\n" //write back + "rsync\n" + :"=r"(oldint):"r"(intmask):"a3"); + //Save which ints we did disable + non_iram_int_disabled[cpu]=oldint&non_iram_int_mask[cpu]; +} + +void esp_intr_noniram_enable() +{ + int cpu=xPortGetCoreID(); + int intmask=non_iram_int_disabled[cpu]; + assert(non_iram_int_disabled_flag[cpu]==true); + non_iram_int_disabled_flag[cpu]=false; + asm volatile ( + "movi a3,0\n" + "xsr a3,INTENABLE\n" + "rsync\n" + "or a3,a3,%0\n" + "wsr a3,INTENABLE\n" + "rsync\n" + ::"r"(intmask):"a3"); +} + + + + + + diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index 860556b8c5..8585260e2f 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -27,6 +27,7 @@ #include #include "esp_err.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "esp_attr.h" #include "esp_freertos_hooks.h" #include "soc/timer_group_struct.h" @@ -51,7 +52,7 @@ static wdt_task_t *wdt_task_list=NULL; static portMUX_TYPE taskwdt_spinlock = portMUX_INITIALIZER_UNLOCKED; -static void IRAM_ATTR task_wdt_isr(void *arg) { +static void task_wdt_isr(void *arg) { wdt_task_t *wdttask; const char *cpu; //Feed the watchdog so we do not reset @@ -71,21 +72,21 @@ static void IRAM_ATTR task_wdt_isr(void *arg) { return; } //Watchdog got triggered because at least one task did not report in. - ets_printf(DRAM_STR("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n")); + ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n"); for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { if (!wdttask->fed_watchdog) { cpu=xTaskGetAffinity(wdttask->task_handle)==0?DRAM_STR("CPU 0"):DRAM_STR("CPU 1"); if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu=DRAM_STR("CPU 0/1"); - ets_printf(DRAM_STR(" - %s (%s)\n"), pcTaskGetTaskName(wdttask->task_handle), cpu); + ets_printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); } } ets_printf(DRAM_STR("Tasks currently running:\n")); for (int x=0; x #include #include "esp_attr.h" +#include "esp_intr_alloc.h" #include "soc/soc.h" #include "soc/rtc_cntl_reg.h" #include "soc/frc_timer_reg.h" @@ -105,9 +106,7 @@ void esp_setup_time_syscalls() SET_PERI_REG_MASK(FRC_TIMER_CTRL_REG(0), FRC_TIMER_ENABLE | \ FRC_TIMER_INT_ENABLE); - intr_matrix_set(xPortGetCoreID(), ETS_TIMER1_INTR_SOURCE, ETS_FRC1_INUM); - xt_set_interrupt_handler(ETS_FRC1_INUM, &frc_timer_isr, NULL); - xt_ints_on(1 << ETS_FRC1_INUM); + esp_intr_alloc(ETS_TIMER1_INTR_SOURCE, 0, &frc_timer_isr, NULL, NULL); #endif // WITH_FRC1 } diff --git a/components/spi_flash/cache_utils.c b/components/spi_flash/cache_utils.c index 904007b316..f30db80cd8 100644 --- a/components/spi_flash/cache_utils.c +++ b/components/spi_flash/cache_utils.c @@ -27,6 +27,7 @@ #include "sdkconfig.h" #include "esp_ipc.h" #include "esp_attr.h" +#include "esp_intr_alloc.h" #include "esp_spi_flash.h" #include "esp_log.h" @@ -60,6 +61,8 @@ void IRAM_ATTR spi_flash_op_block_func(void* arg) { // Disable scheduler on this CPU vTaskSuspendAll(); + // Restore interrupts that aren't located in IRAM + esp_intr_noniram_disable(); uint32_t cpuid = (uint32_t) arg; // Disable cache so that flash operation can start spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]); @@ -70,6 +73,8 @@ void IRAM_ATTR spi_flash_op_block_func(void* arg) } // Flash operation is complete, re-enable cache spi_flash_restore_cache(cpuid, s_flash_op_cache_state[cpuid]); + // Restore interrupts that aren't located in IRAM + esp_intr_noniram_enable(); // Re-enable scheduler xTaskResumeAll(); } @@ -104,6 +109,8 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() // occupied by highest priority task assert(xPortGetCoreID() == cpuid); } + // Kill interrupts that aren't located in IRAM + esp_intr_noniram_disable(); // Disable cache on this CPU as well spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]); } @@ -130,6 +137,8 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() } // Release API lock spi_flash_op_unlock(); + // Re-enable non-iram interrupts + esp_intr_noniram_enable(); } #else // CONFIG_FREERTOS_UNICORE @@ -151,6 +160,7 @@ void spi_flash_op_unlock() void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() { + esp_intr_noniram_disable(); spi_flash_op_lock(); spi_flash_disable_cache(0, &s_flash_op_cache_state[0]); } @@ -159,6 +169,7 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() { spi_flash_restore_cache(0, s_flash_op_cache_state[0]); spi_flash_op_unlock(); + esp_intr_noniram_enable(); } #endif // CONFIG_FREERTOS_UNICORE diff --git a/docs/Doxyfile b/docs/Doxyfile index c1aebcd308..bdb91a4dce 100755 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -27,7 +27,8 @@ INPUT = ../components/esp32/include/esp_wifi.h \ ../components/esp32/include/esp_task_wdt.h \ ../components/app_update/include/esp_ota_ops.h \ ../components/ethernet/include/esp_eth.h \ - ../components/ulp/include/esp32/ulp.h + ../components/ulp/include/esp32/ulp.h \ + ../components/esp32/include/esp_intr_alloc.h ## Get warnings for functions that have no documentation for their parameters or return value ## diff --git a/docs/api/intr_alloc.rst b/docs/api/intr_alloc.rst new file mode 100644 index 0000000000..e4e9bdb4ad --- /dev/null +++ b/docs/api/intr_alloc.rst @@ -0,0 +1,84 @@ +Interrupt allocation +==================== + +Overview +-------- + +The ESP32 has two cores, with 32 interrupts each. Each interrupt has a certain priority level, most (but not all) interrupts are connected +to the interrupt mux. Because there are more interrupt sources than interrupts, sometimes it makes sense to share an interrupt in +multiple drivers. The esp_intr_alloc abstraction exists to hide all these implementation details. + +A driver can allocate an interrupt for a certain peripheral by calling esp_intr_alloc (or esp_intr_alloc_sintrstatus). It can use +the flags passed to this function to set the type of interrupt allocated, specifying a specific level or trigger method. The +interrupt allocation code will then find an applicable interrupt, use the interrupt mux to hook it up to the peripheral, and +install the given interrupt handler and ISR to it. + +This code has two different types of interrupts it handles differently: Shared interrupts and non-shared interrupts. The simplest +of the two are non-shared interrupts: a separate interrupt is allocated per esp_intr_alloc call and this interrupt is solely used for +the peripheral attached to it, with only one ISR that will get called. Non-shared interrupts can have multiple peripherals triggering +it, with multiple ISRs being called when one of the peripherals attached signals an interrupt. Thus, ISRs that are intended for shared +interrupts should check the interrupt status of the peripheral they service in order to see if any action is required. + +Non-shared interrupts can be either level- or edge-triggered. Shared interrupts can +only be level interrupts (because of the chance of missed interrupts when edge interrupts are +used.) +(The logic behind this: DevA and DevB share an int. DevB signals an int. Int line goes high. ISR handler +calls code for DevA -> does nothing. ISR handler calls code for DevB, but while doing that, +DevA signals an int. ISR DevB is done, clears int for DevB, exits interrupt code. Now an +interrupt for DevA is still pending, but because the int line never went low (DevA kept it high +even when the int for DevB was cleared) the interrupt is never serviced.) + + + + +Application Example +------------------- + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `esp_intr_alloc.h `_ + + +Macros +^^^^^^ + +.. doxygendefine:: ESP_INTR_FLAG_LEVEL1 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL2 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL3 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL4 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL5 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL6 +.. doxygendefine:: ESP_INTR_FLAG_NMI +.. doxygendefine:: ESP_INTR_FLAG_LOWMED +.. doxygendefine:: ESP_INTR_FLAG_HIGH +.. doxygendefine:: ESP_INTR_FLAG_SHARED +.. doxygendefine:: ESP_INTR_FLAG_EDGE +.. doxygendefine:: ESP_INTR_FLAG_IRAM + +Type Definitions +^^^^^^^^^^^^^^^^ + +Enumerations +^^^^^^^^^^^^ + +Structures +^^^^^^^^^^ + +Functions +^^^^^^^^^ + +.. doxygenfunction:: esp_intr_mark_shared +.. doxygenfunction:: esp_intr_reserve +.. doxygenfunction:: esp_intr_alloc +.. doxygenfunction:: esp_intr_alloc_intrstatus +.. doxygenfunction:: esp_intr_free +.. doxygenfunction:: esp_intr_get_cpu +.. doxygenfunction:: esp_intr_get_intno +.. doxygenfunction:: esp_intr_disable +.. doxygenfunction:: esp_intr_enable +.. doxygenfunction:: esp_intr_noniram_disable +.. doxygenfunction:: esp_intr_noniram_enable diff --git a/docs/index.rst b/docs/index.rst index 4c48255ae2..4e58cfe02f 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -110,6 +110,7 @@ Contents: Non-Volatile Storage Virtual Filesystem Ethernet + Interrupt Allocation deep-sleep-stub Template diff --git a/examples/11_rmt_nec_tx_rx/main/infrared_nec.c b/examples/11_rmt_nec_tx_rx/main/infrared_nec.c index dbd11fbae5..d2f7b091fa 100644 --- a/examples/11_rmt_nec_tx_rx/main/infrared_nec.c +++ b/examples/11_rmt_nec_tx_rx/main/infrared_nec.c @@ -43,7 +43,6 @@ static const char* NEC_TAG = "NEC"; #define RMT_TX_GPIO_NUM 16 /*!< GPIO number for transmitter signal */ #define RMT_RX_CHANNEL 0 /*!< RMT channel for receiver */ #define RMT_RX_GPIO_NUM 19 /*!< GPIO number for receiver */ -#define RMT_INTR_NUM 19 /*!< RMT interrupt number, select from soc.h */ #define RMT_CLK_DIV 100 /*!< RMT counter clock divider */ #define RMT_TICK_10_US (80000000/RMT_CLK_DIV/100000) /*!< RMT counter value for 10 us.(Source clock is APB clock) */ @@ -254,7 +253,7 @@ static void rmt_tx_init() rmt_tx.tx_config.idle_output_en = true; rmt_tx.rmt_mode = 0; rmt_config(&rmt_tx); - rmt_driver_install(rmt_tx.channel, 0, RMT_INTR_NUM); + rmt_driver_install(rmt_tx.channel, 0, 0); } /* @@ -272,7 +271,7 @@ void rmt_rx_init() rmt_rx.rx_config.filter_ticks_thresh = 100; rmt_rx.rx_config.idle_threshold = rmt_item32_tIMEOUT_US / 10 * (RMT_TICK_10_US); rmt_config(&rmt_rx); - rmt_driver_install(rmt_rx.channel, 1000, RMT_INTR_NUM); + rmt_driver_install(rmt_rx.channel, 1000, 0); } /** diff --git a/examples/13_timer_group/main/timer_group.c b/examples/13_timer_group/main/timer_group.c index 15d1ca2c05..e91efe767d 100644 --- a/examples/13_timer_group/main/timer_group.c +++ b/examples/13_timer_group/main/timer_group.c @@ -16,8 +16,6 @@ #include "driver/periph_ctrl.h" #include "driver/timer.h" -#define TIMER_INTR_NUM_0 17 /*!< Interrupt number for hardware timer 0 */ -#define TIMER_INTR_NUM_1 18 /*!< Interrupt number for hardware timer 1*/ #define TIMER_INTR_SEL TIMER_INTR_LEVEL /*!< Timer level interrupt */ #define TIMER_GROUP TIMER_GROUP_0 /*!< Test on timer group 0 */ #define TIMER_DIVIDER 16 /*!< Hardware timer clock divider */ @@ -157,7 +155,7 @@ void tg0_timer0_init() /*Enable timer interrupt*/ timer_enable_intr(timer_group, timer_idx); /*Set ISR handler*/ - timer_isr_register(timer_group, timer_idx, TIMER_INTR_NUM_0, TIMER_INTR_SEL, timer_group0_isr, (void*) timer_idx); + timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, 0); /*Start timer counter*/ timer_start(timer_group, timer_idx); } @@ -187,7 +185,7 @@ void tg0_timer1_init() /*Enable timer interrupt*/ timer_enable_intr(timer_group, timer_idx); /*Set ISR handler*/ - timer_isr_register(timer_group, timer_idx, TIMER_INTR_NUM_1, TIMER_INTR_SEL, timer_group0_isr, (void*) timer_idx); + timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, 0); /*Start timer counter*/ timer_start(timer_group, timer_idx); } diff --git a/examples/16_pcnt/main/pcnt_test.c b/examples/16_pcnt/main/pcnt_test.c index 1da4cca56e..31d8f28870 100644 --- a/examples/16_pcnt/main/pcnt_test.c +++ b/examples/16_pcnt/main/pcnt_test.c @@ -42,7 +42,6 @@ static const char* TAG = "PCNT_TEST"; #define PCNT_L_LIM_VAL (-10) #define PCNT_THRESH1_VAL (5) #define PCNT_THRESH0_VAL (-5) -#define PCNT_INTR_NUM (18) #define PCNT_INPUT_SIG_IO (4) #define PCNT_INPUT_CTRL_IO (5) #define LEDC_OUPUT_IO (18) @@ -177,7 +176,7 @@ static void pcnt_init(void) /*Reset counter value*/ pcnt_counter_clear(PCNT_TEST_UNIT); /*Register ISR handler*/ - pcnt_isr_register(PCNT_INTR_NUM, pcnt_intr_handler, NULL); + pcnt_isr_register(pcnt_intr_handler, NULL, 0); /*Enable interrupt for PCNT unit*/ pcnt_intr_enable(PCNT_TEST_UNIT); /*Resume counting*/