Add interrupt allocation scheme / interrupt sharing. Also modifies drivers and examples. Also allows interrupts

to be marked specifically as having a handler that's all in IRAM.
This commit is contained in:
Jeroen Domburg 2016-11-25 17:33:51 +08:00
parent 505282bab1
commit 655fd2986a
29 changed files with 1142 additions and 161 deletions

View file

@ -14,6 +14,7 @@
#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"
@ -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*/

View file

@ -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.

View file

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

View file

@ -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

View file

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

View file

@ -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.
*

View file

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

View file

@ -13,6 +13,7 @@
// 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"
@ -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)

View file

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

View file

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

View file

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

View file

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

View file

@ -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

View file

@ -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) {

View file

@ -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 <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

View file

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

View file

@ -27,6 +27,7 @@
#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"
@ -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<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);
@ -201,12 +202,7 @@ void esp_task_wdt_init() {
#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);
}

View file

@ -35,6 +35,7 @@
#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"
@ -305,19 +306,16 @@ static void IRAM_ATTR emac_process_intr(void *arg)
}
}
//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);
}

View file

@ -39,7 +39,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#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];

View file

@ -40,6 +40,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------------
*/
#if XT_USE_SWPRI
/* Warning - this is not multicore-compatible. */
.data
.global _xt_intdata
.align 8
@ -53,7 +56,7 @@ _xt_intdata:
_xt_intenable: .word 0 /* Virtual INTENABLE */
_xt_vpri_mask: .word 0xFFFFFFFF /* Virtual priority mask */
#endif
/*
-------------------------------------------------------------------------------
@ -124,7 +127,8 @@ _xt_exception_table:
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.
-------------------------------------------------------------------------------
*/
@ -137,7 +141,9 @@ _xt_exception_table:
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 */
@ -149,6 +155,13 @@ xt_ints_on:
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
@ -162,7 +175,8 @@ xt_ints_on:
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.
-------------------------------------------------------------------------------
*/
@ -176,6 +190,7 @@ xt_ints_off:
ENTRY0
#if XCHAL_HAVE_INTERRUPTS
#if XT_USE_SWPRI
movi a3, 0
movi a4, _xt_intdata
xsr a3, INTENABLE /* Disables all interrupts */
@ -188,6 +203,14 @@ xt_ints_off:
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

View file

@ -32,6 +32,7 @@
#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"
@ -59,10 +60,7 @@ static void rsa_isr_initialise()
{
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);
}
}

View file

@ -22,6 +22,7 @@
#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"
@ -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
}

View file

@ -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

View file

@ -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
##

84
docs/api/intr_alloc.rst Normal file
View file

@ -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 <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

View file

@ -110,6 +110,7 @@ Contents:
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>

View file

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

View file

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

View file

@ -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*/