to be marked specifically as having a handler that's all in IRAM.
#include <esp_types.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/gpio.h"
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*/
* 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".
* - 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);
*/
/**
- *----------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
**/
/**
- *----------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
/**
*----------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.
/**
* @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
* - 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
* ----------------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}
/**
* @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
* @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.
* 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
* 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}
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;
/**
* @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.
* - 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.
*
* @brief register UART interrupt handler(ISR).\r
*\r
* @note UART ISR handler will be attached to the same CPU core that this function is running on.\r
- * Users should know that which CPU is running and then pick a INUM that is not used by system.\r
- * We can find the information of INUM and interrupt level in soc.h.\r
- *\r
- * @attention The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now.\r
*\r
* @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2\r
- * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details\r
* @param fn Interrupt handler function.\r
* @param arg parameter for handler function\r
+ * @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.\r
*\r
* @return\r
* - ESP_OK Success\r
* - ESP_FAIL Parameter error\r
*/\r
-esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg);\r
+esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, int intr_alloc_flags);\r
\r
/**\r
* @brief Set UART pin number\r
* @param tx_buffer_size UART TX ring buffer size.\r
* If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out..\r
* @param queue_size UART event queue size/depth.\r
- * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details\r
* @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue.\r
+ * @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.
*\r
* @return\r
* - ESP_OK Success\r
* - ESP_FAIL Parameter error\r
*/\r
-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);\r
+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);\r
\r
/**\r
* @brief Uninstall UART driver.\r
* //Set UART log level\r
* esp_log_level_set(TAG, ESP_LOG_INFO);\r
* //Install UART driver, and get the queue.\r
- * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue);\r
+ * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, &uart0_queue, 0);\r
* //Create a task to handler UART event from ISR\r
* xTaskCreate(uart_task, "uTask", 1024, (void*)uart_num, 10, NULL);\r
* }\r
// limitations under the License.
#include <esp_types.h>
#include "esp_intr.h"
+#include "esp_intr_alloc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/xtensa_api.h"
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)
// 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"
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);
}
#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"
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)
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) {
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) {
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;
}
#include "esp_log.h"\r
#include "esp_err.h"\r
#include "esp_intr.h"\r
+#include "esp_intr_alloc.h"\r
#include "freertos/FreeRTOS.h"\r
#include "freertos/xtensa_api.h"\r
#include "driver/timer.h"\r
return ESP_OK;\r
}\r
\r
-esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, int timer_intr_num,\r
- timer_intr_mode_t intr_type, void (*fn)(void*), void * arg)\r
+esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, \r
+ void (*fn)(void*), void * arg, int intr_alloc_flags)\r
{\r
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);\r
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);\r
TIMER_CHECK(fn != NULL, TIMER_PARAM_ADDR_ERROR, ESP_ERR_INVALID_ARG);\r
\r
- ESP_INTR_DISABLE(timer_intr_num);\r
int intr_source = 0;\r
switch(group_num) {\r
case TIMER_GROUP_0:\r
default:\r
- if(intr_type == TIMER_INTR_LEVEL) {\r
+ if((intr_alloc_flags & ESP_INTR_FLAG_EDGE) == 0) {\r
intr_source = ETS_TG0_T0_LEVEL_INTR_SOURCE + timer_num;\r
} else {\r
intr_source = ETS_TG0_T0_EDGE_INTR_SOURCE + timer_num;\r
}\r
break;\r
case TIMER_GROUP_1:\r
- if(intr_type == TIMER_INTR_LEVEL) {\r
+ if((intr_alloc_flags & ESP_INTR_FLAG_EDGE) == 0) {\r
intr_source = ETS_TG1_T0_LEVEL_INTR_SOURCE + timer_num;\r
} else {\r
intr_source = ETS_TG1_T0_EDGE_INTR_SOURCE + timer_num;\r
}\r
break;\r
}\r
- intr_matrix_set(xPortGetCoreID(), intr_source, timer_intr_num);\r
- xt_set_interrupt_handler(timer_intr_num, fn, arg);\r
- ESP_INTR_ENABLE(timer_intr_num);\r
- return ESP_OK;\r
+ return esp_intr_alloc(intr_source, intr_alloc_flags, fn, arg, NULL);\r
}\r
\r
esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, timer_config_t *config)\r
#include "esp_types.h"\r
#include "esp_attr.h"\r
#include "esp_intr.h"\r
+#include "esp_intr_alloc.h"\r
#include "esp_log.h"\r
+#include "esp_err.h"\r
#include "malloc.h"\r
#include "freertos/FreeRTOS.h"\r
#include "freertos/semphr.h"\r
uart_port_t uart_num; /*!< UART port number*/\r
int queue_size; /*!< UART event queue size*/\r
QueueHandle_t xQueueUart; /*!< UART queue handler*/\r
- int intr_num; /*!< UART interrupt number*/\r
+ int_handle_t intr_handle; /*!< UART interrupt handle*/\r
//rx parameters\r
SemaphoreHandle_t rx_mux; /*!< UART RX data mutex*/\r
int rx_buf_size; /*!< RX ring buffer size */\r
UART[uart_num]->conf1.txfifo_empty_thrhd = thresh & UART_TXFIFO_EMPTY_THRHD_V;\r
UART[uart_num]->int_ena.txfifo_empty = enable & 0x1;\r
UART_EXIT_CRITICAL(&uart_spinlock[uart_num]);\r
- ESP_INTR_ENABLE(p_uart_obj[uart_num]->intr_num);\r
return ESP_OK;\r
}\r
\r
-esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg)\r
+esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, int intr_alloc_flags)\r
{\r
+ int ret;
UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL);\r
+ UART_CHECK(((intr_alloc_flags & ESP_INTR_FLAG_SHARED)==0), "UART doesn't support shared interrupts", ESP_FAIL);\r
UART_ENTER_CRITICAL(&uart_spinlock[uart_num]);\r
- ESP_INTR_DISABLE(uart_intr_num);\r
switch(uart_num) {\r
case UART_NUM_1:\r
- intr_matrix_set(xPortGetCoreID(), ETS_UART1_INTR_SOURCE, uart_intr_num);\r
+ ret=esp_intr_alloc(ETS_UART1_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle);\r
break;\r
case UART_NUM_2:\r
- intr_matrix_set(xPortGetCoreID(), ETS_UART2_INTR_SOURCE, uart_intr_num);\r
+ ret=esp_intr_alloc(ETS_UART2_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle);\r
break;\r
case UART_NUM_0:\r
default:\r
- intr_matrix_set(xPortGetCoreID(), ETS_UART0_INTR_SOURCE, uart_intr_num);\r
+ ret=esp_intr_alloc(ETS_UART0_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle);\r
break;\r
}\r
- xt_set_interrupt_handler(uart_intr_num, fn, arg);\r
- ESP_INTR_ENABLE(uart_intr_num);\r
UART_EXIT_CRITICAL(&uart_spinlock[uart_num]);\r
- return ESP_OK;\r
+ return ret;\r
}\r
\r
//internal signal can be output to multiple GPIO pads\r
\r
//rx sem protect the ring buffer read related functions\r
xSemaphoreTake(p_uart->rx_mux, (portTickType)portMAX_DELAY);\r
- ESP_INTR_DISABLE(p_uart->intr_num);\r
+ esp_intr_disable(p_uart->intr_handle);\r
while(true) {\r
if(p_uart->rx_head_ptr) {\r
vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->rx_head_ptr);\r
p_uart->rx_ptr = NULL;\r
p_uart->rx_cur_remain = 0;\r
p_uart->rx_head_ptr = NULL;\r
- ESP_INTR_ENABLE(p_uart->intr_num);\r
+ esp_intr_enable(p_uart->intr_handle);\r
xSemaphoreGive(p_uart->rx_mux);\r
\r
if(p_uart->tx_buf_size > 0) {\r
xSemaphoreTake(p_uart->tx_mux, (portTickType)portMAX_DELAY);\r
- ESP_INTR_DISABLE(p_uart->intr_num);\r
+ esp_intr_disable(p_uart->intr_handle);\r
UART_ENTER_CRITICAL(&uart_spinlock[uart_num]);\r
UART[uart_num]->int_ena.txfifo_empty = 0;\r
UART[uart_num]->int_clr.txfifo_empty = 1;\r
p_uart->tx_ptr = NULL;\r
p_uart->tx_waiting_brk = 0;\r
p_uart->tx_waiting_fifo = false;\r
- ESP_INTR_ENABLE(p_uart->intr_num);\r
+ esp_intr_enable(p_uart->intr_handle);\r
xSemaphoreGive(p_uart->tx_mux);\r
}\r
uart_reset_fifo(uart_num);\r
return ESP_OK;\r
}\r
\r
-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)\r
+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)\r
{\r
UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL);\r
UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error", ESP_FAIL);\r
if(p_uart_obj[uart_num] == NULL) {\r
- ESP_INTR_DISABLE(uart_intr_num);\r
p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t));\r
if(p_uart_obj[uart_num] == NULL) {\r
ESP_LOGE(UART_TAG, "UART driver malloc error");\r
p_uart_obj[uart_num]->tx_brk_sem = xSemaphoreCreateBinary();\r
p_uart_obj[uart_num]->tx_mux = xSemaphoreCreateMutex();\r
p_uart_obj[uart_num]->rx_mux = xSemaphoreCreateMutex();\r
- p_uart_obj[uart_num]->intr_num = uart_intr_num;\r
p_uart_obj[uart_num]->queue_size = queue_size;\r
p_uart_obj[uart_num]->tx_ptr = NULL;\r
p_uart_obj[uart_num]->tx_head = NULL;\r
ESP_LOGE(UART_TAG, "UART driver already installed");\r
return ESP_FAIL;\r
}\r
- uart_isr_register(uart_num, uart_intr_num, uart_rx_intr_handler_default, p_uart_obj[uart_num]);\r
+ uart_isr_register(uart_num, uart_rx_intr_handler_default, p_uart_obj[uart_num], intr_alloc_flags);\r
uart_intr_config_t uart_intr = {\r
.intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M\r
| UART_RXFIFO_TOUT_INT_ENA_M\r
.txfifo_empty_intr_thresh = UART_EMPTY_THRESH_DEFAULT\r
};\r
uart_intr_config(uart_num, &uart_intr);\r
- ESP_INTR_ENABLE(uart_intr_num);\r
return ESP_OK;\r
}\r
\r
ESP_LOGI(UART_TAG, "ALREADY NULL");\r
return ESP_OK;\r
}\r
- ESP_INTR_DISABLE(p_uart_obj[uart_num]->intr_num);\r
+ esp_intr_free(p_uart_obj[uart_num]->intr_handle);
uart_disable_rx_intr(uart_num);\r
uart_disable_tx_intr(uart_num);\r
- uart_isr_register(uart_num, p_uart_obj[uart_num]->intr_num, NULL, NULL);\r
\r
if(p_uart_obj[uart_num]->tx_fifo_sem) {\r
vSemaphoreDelete(p_uart_obj[uart_num]->tx_fifo_sem);\r
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();
_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
#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"
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) {
--- /dev/null
+// 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 <stdint.h>
+#include <stdbool.h>
+#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<<level)
+#define ESP_INTR_FLAG_LEVEL1 (1<<1) ///< Accept a Level 1 interrupt vector
+#define ESP_INTR_FLAG_LEVEL2 (1<<2) ///< Accept a Level 2 interrupt vector
+#define ESP_INTR_FLAG_LEVEL3 (1<<3) ///< Accept a Level 3 interrupt vector
+#define ESP_INTR_FLAG_LEVEL4 (1<<4) ///< Accept a Level 4 interrupt vector
+#define ESP_INTR_FLAG_LEVEL5 (1<<5) ///< Accept a Level 5 interrupt vector
+#define ESP_INTR_FLAG_LEVEL6 (1<<6) ///< Accept a Level 6 interrupt vector
+#define ESP_INTR_FLAG_NMI (1<<7) ///< Accept a Level 7 interrupt vector
+#define ESP_INTR_FLAG_SHARED (1<<8) ///< Interrupt can be shared between ISRs
+#define ESP_INTR_FLAG_EDGE (1<<9) ///< Edge-triggered interrupt
+#define ESP_INTR_FLAG_IRAM (1<<10) ///< ISR can be called if cache is disabled
+
+#define ESP_INTR_FLAG_LOWMED (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3) ///< Low and medium prio interrupts. These can be handled in C.
+#define ESP_INTR_FLAG_HIGH (ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6|ESP_INTR_FLAG_NMI) ///< High level interrupts. Need to be handled in assembly.
+
+#define ESP_INTR_FLAG_LEVELMASK (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3| \
+ ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6| \
+ ESP_INTR_FLAG_NMI) ///< Mask for all level flags
+/**@}*/
+
+
+/** @addtogroup Intr_Alloc_Pseudo_Src
+ * @{
+ */
+
+/**
+ * The esp_intr_alloc* functions can allocate an int for all ETS_*_INTR_SOURCE interrupt sources that
+ * are routed through the interrupt mux. Apart from these sources, each core also has some internal
+ * sources that do not pass through the interrupt mux. To allocate an interrupt for these sources,
+ * pass these pseudo-sources to the functions.
+ */
+#define ETS_INTERNAL_TIMER0_INTR_SOURCE -1 ///< Xtensa timer 0 interrupt source
+#define ETS_INTERNAL_TIMER1_INTR_SOURCE -2 ///< Xtensa timer 1 interrupt source
+#define ETS_INTERNAL_TIMER2_INTR_SOURCE -3 ///< Xtensa timer 2 interrupt source
+#define ETS_INTERNAL_SW0_INTR_SOURCE -4 ///< Software int source 1
+#define ETS_INTERNAL_SW1_INTR_SOURCE -5 ///< Software int source 2
+#define ETS_INTERNAL_PROFILING_INTR_SOURCE -6 ///< Int source for profiling
+
+/**@}*/
+
+typedef void (*intr_handler_t)(void *arg);
+
+
+typedef struct int_handle_data_t int_handle_data_t;
+typedef int_handle_data_t* int_handle_t ;
+
+/**
+ * @brief Mark an interrupt as a shared interrupt
+ *
+ * This will mark a certain interrupt on the specified CPU as
+ * an interrupt that can be used to hook shared interrupt handlers
+ * to.
+ *
+ * @param intno The number of the interrupt (0-31)
+ * @param cpu CPU on which the interrupt should be marked as shared (0 or 1)
+ * @param is_in_iram Shared interrupt is for handlers that reside in IRAM and
+ * the int can be left enabled while the flash cache is out.
+ *
+ * @return ESP_ERR_INVALID_ARG if cpu or intno is invalid
+ * ESP_OK otherwise
+ */
+esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_in_iram);
+
+/**
+ * @brief Reserve an interrupt to be used outside of this framewoek
+ *
+ * This will mark a certain interrupt on the specified CPU as
+ * reserved, not to be allocated for any reason.
+ *
+ * @param intno The number of the interrupt (0-31)
+ * @param cpu CPU on which the interrupt should be marked as shared (0 or 1)
+ *
+ * @return ESP_ERR_INVALID_ARG if cpu or intno is invalid
+ * ESP_OK otherwise
+ */
+esp_err_t esp_intr_reserve(int intno, int cpu);
+
+/**
+ * @brief Allocate an interrupt with the given parameters.
+ *
+ * This finds an interrupt that matches the restrictions as given in the flags
+ * parameter, maps the given interrupt source to it and hooks up the given
+ * interrupt handler (with optional argument) as well. If needed, it can return
+ * a handle for the interrupt as well.
+ *
+ * The interrupt will always be allocated on the core that runs this function.
+ *
+ * @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 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(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
--- /dev/null
+// 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include <esp_types.h>
+#include "esp_err.h"
+#include "esp_log.h"
+#include "esp_intr.h"
+#include "esp_attr.h"
+#include "esp_intr_alloc.h"
+#include <limits.h>
+#include <assert.h>
+
+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<<int_desc[x].level))) {
+ ALCHLOG(TAG, "....Unusable: incompatible level");
+ continue;
+ }
+ //check if edge/level type matches what we want
+ if (((flags&ESP_INTR_FLAG_EDGE) && (int_desc[x].type==INTTP_LEVEL)) ||
+ (((!(flags&ESP_INTR_FLAG_EDGE)) && (int_desc[x].type==INTTP_EDGE)))) {
+ ALCHLOG(TAG, "....Unusable: incompatible trigger type");
+ continue;
+ }
+ //Check if interrupt already is allocated by xt_set_interrupt_handler
+ if (int_has_handler(x, cpu) && !(vd->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 (no<bestSharedCt || bestLevel>int_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<<intr);
+ } else {
+ vd->flags&=~VECDESC_FL_INIRAM;
+ non_iram_int_mask[cpu]|=(1<<intr);
+ }
+ if (source>=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");
+}
+
+
+
+
+
+
#include <esp_types.h>
#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"
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
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<portNUM_PROCESSORS; x++) {
- ets_printf(DRAM_STR("CPU %d: %s\n"), x, pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(x)));
+ ets_printf("CPU %d: %s\n", x, pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(x)));
}
#if CONFIG_TASK_WDT_PANIC
- ets_printf(DRAM_STR("Aborting.\n"));
+ ets_printf("Aborting.\n");
abort();
#endif
portEXIT_CRITICAL(&taskwdt_spinlock);
#if CONFIG_TASK_WDT_CHECK_IDLE_TASK
esp_register_freertos_idle_hook(idle_hook);
#endif
- ESP_INTR_DISABLE(ETS_T0_WDT_INUM);
- intr_matrix_set(xPortGetCoreID(), ETS_TG0_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM);
- xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL);
- TIMERG0.int_clr_timers.wdt=1;
- timer_group_intr_enable(TIMER_GROUP_0, TIMG_WDT_INT_ENA_M);
- ESP_INTR_ENABLE(ETS_T0_WDT_INUM);
+ esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, NULL);
}
#include "esp_err.h"
#include "esp_log.h"
#include "esp_eth.h"
+#include "esp_intr_alloc.h"
#include "emac_common.h"
#include "emac_desc.h"
}
}
+//ToDo: this should only be called once because this allocates the interrupt as well.
static void emac_enable_intr()
{
//init emac intr
- REG_SET_FIELD(DPORT_PRO_EMAC_INT_MAP_REG, DPORT_PRO_EMAC_INT_MAP, ETS_EMAC_INUM);
- xt_set_interrupt_handler(ETS_EMAC_INUM, emac_process_intr, NULL);
- xt_ints_on(1 << ETS_EMAC_INUM);
-
+ esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, 0, emac_process_intr, NULL, NULL);
REG_WRITE(EMAC_DMAINTERRUPT_EN_REG, EMAC_INTR_ENABLE_BIT);
}
static void emac_disable_intr()
{
- xt_ints_off(1 << ETS_EMAC_INUM);
REG_WRITE(EMAC_DMAINTERRUPT_EN_REG, 0);
}
#if XCHAL_HAVE_EXCEPTIONS
/* Handler table is in xtensa_intr_asm.S */
-// Todo: Make multicore - JD
extern xt_exc_handler _xt_exception_table[XCHAL_EXCCAUSE_NUM*portNUM_PROCESSORS];
-------------------------------------------------------------------------------
*/
+
+#if XT_USE_SWPRI
+/* Warning - this is not multicore-compatible. */
.data
.global _xt_intdata
.align 8
_xt_intenable: .word 0 /* Virtual INTENABLE */
_xt_vpri_mask: .word 0xFFFFFFFF /* Virtual priority mask */
-
+#endif
/*
-------------------------------------------------------------------------------
unsigned int xt_ints_on ( unsigned int mask )
Enables a set of interrupts. Does not simply set INTENABLE directly, but
- computes it as a function of the current virtual priority.
+ computes it as a function of the current virtual priority if XT_USE_SWPRI is
+ enabled.
Can be called from interrupt handlers.
-------------------------------------------------------------------------------
*/
xt_ints_on:
ENTRY0
+
#if XCHAL_HAVE_INTERRUPTS
+#if XT_USE_SWPRI
movi a3, 0
movi a4, _xt_intdata
xsr a3, INTENABLE /* Disables all interrupts */
and a5, a5, a6 /* a5 = _xt_intenable & _xt_vpri_mask */
wsr a5, INTENABLE /* Reenable interrupts */
mov a2, a3 /* Previous mask */
+#else
+ movi a3, 0
+ xsr a3, INTENABLE /* Disables all interrupts */
+ or a2, a3, a2 /* set bits in mask */
+ wsr a2, INTENABLE /* Re-enable ints */
+ mov a2, a3 /* return prev mask */
+#endif
#else
movi a2, 0 /* Return zero */
#endif
unsigned int xt_ints_off ( unsigned int mask )
Disables a set of interrupts. Does not simply set INTENABLE directly,
- but computes it as a function of the current virtual priority.
+ but computes it as a function of the current virtual priority if XT_USE_SWPRI is
+ enabled.
Can be called from interrupt handlers.
-------------------------------------------------------------------------------
*/
ENTRY0
#if XCHAL_HAVE_INTERRUPTS
+#if XT_USE_SWPRI
movi a3, 0
movi a4, _xt_intdata
xsr a3, INTENABLE /* Disables all interrupts */
and a5, a5, a6 /* a5 = _xt_intenable & _xt_vpri_mask */
wsr a5, INTENABLE /* Reenable interrupts */
mov a2, a3 /* Previous mask */
+#else
+ movi a3, 0
+ xsr a3, INTENABLE /* Disables all interrupts */
+ or a2, a3, a2 /* set bits in mask */
+ xor a2, a3, a2 /* invert bits in mask set in mask, essentially clearing them */
+ wsr a2, INTENABLE /* Re-enable ints */
+ mov a2, a3 /* return prev mask */
+#endif
#else
movi a2, 0 /* return zero */
#endif
#include "esp_system.h"
#include "esp_log.h"
#include "esp_intr.h"
+#include "esp_intr_alloc.h"
#include "esp_attr.h"
#include "soc/dport_reg.h"
{
if (op_complete_sem == NULL) {
op_complete_sem = xSemaphoreCreateBinary();
- intr_matrix_set(xPortGetCoreID(), ETS_RSA_INTR_SOURCE, CONFIG_MBEDTLS_MPI_INTERRUPT_NUM);
- xt_set_interrupt_handler(CONFIG_MBEDTLS_MPI_INTERRUPT_NUM, &rsa_complete_isr, NULL);
- xthal_set_intclear(1 << CONFIG_MBEDTLS_MPI_INTERRUPT_NUM);
- xt_ints_on(1 << CONFIG_MBEDTLS_MPI_INTERRUPT_NUM);
+ esp_intr_alloc(ETS_RSA_INTR_SOURCE, 0, rsa_complete_isr, NULL, NULL);
}
}
#include <sys/times.h>
#include <sys/lock.h>
#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"
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
}
#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"
{
// 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]);
}
// 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();
}
// 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]);
}
}
// Release API lock
spi_flash_op_unlock();
+ // Re-enable non-iram interrupts
+ esp_intr_noniram_enable();
}
#else // CONFIG_FREERTOS_UNICORE
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]);
}
{
spi_flash_restore_cache(0, s_flash_op_cache_state[0]);
spi_flash_op_unlock();
+ esp_intr_noniram_enable();
}
#endif // CONFIG_FREERTOS_UNICORE
../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
##
--- /dev/null
+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 <https://github.com/espressif/esp-idf/blob/master/components/esp32/include/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
Non-Volatile Storage <api/nvs_flash>
Virtual Filesystem <api/vfs>
Ethernet <api/esp_eth>
+ Interrupt Allocation <api/intr_alloc>
deep-sleep-stub
Template <api/template>
#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) */
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);
}
/*
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);
}
/**
#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 */
/*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);
}
/*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);
}
#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)
/*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*/