Interrupt allocation¶
Overview¶
The ESP32-C3 has one core, with 31 interrupts. Each interrupt has a programmable priority level.
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_intrstatus()
). 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. 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.)
IRAM-Safe Interrupt Handlers¶
The ESP_INTR_FLAG_IRAM
flag registers an interrupt handler that always runs from IRAM (and reads all its data from DRAM), and therefore does not need to be disabled during flash erase and write operations.
This is useful for interrupts which need a guaranteed minimum execution latency, as flash write and erase operations can be slow (erases can take tens or hundreds of milliseconds to complete).
It can also be useful to keep an interrupt handler in IRAM if it is called very frequently, to avoid flash cache misses.
Refer to the SPI flash API documentation for more details.
Multiple Handlers Sharing A Source¶
Several handlers can be assigned to a same source, given that all handlers are allocated using the ESP_INTR_FLAG_SHARED
flag.
They’ll be all allocated to the interrupt, which the source is attached to, and called sequentially when the source is active.
The handlers can be disabled and freed individually. The source is attached to the interrupt (enabled), if one or more handlers are enabled, otherwise detached.
A handler will never be called when disabled, while its source may still be triggered if any one of its handler enabled.
Sources attached to non-shared interrupt do not support this feature.
Though the framework support this feature, you have to use it very carefully. There usually exist 2 ways to stop a interrupt from being triggered: disable the source or mask peripheral interrupt status. IDF only handles the enabling and disabling of the source itself, leaving status and mask bits to be handled by users. Status bits should always be masked before the handler responsible for it is disabled, or the status should be handled in other enabled interrupt properly. You may leave some status bits unhandled if you just disable one of all the handlers without masking the status bits, which causes the interrupt to trigger infinitely resulting in a system crash.
API Reference¶
Functions¶
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.
- Return
ESP_ERR_INVALID_ARG if cpu or intno is invalid ESP_OK otherwise
- Parameters
intno
: The number of the interrupt (0-31)cpu
: CPU on which the interrupt should be marked as shared (0 or 1)is_in_iram
: Shared interrupt is for handlers that reside in IRAM and the int can be left enabled while the flash cache is disabled.
-
esp_err_t
esp_intr_reserve
(int intno, int cpu)¶ Reserve an interrupt to be used outside of this framework.
This will mark a certain interrupt on the specified CPU as reserved, not to be allocated for any reason.
- Return
ESP_ERR_INVALID_ARG if cpu or intno is invalid ESP_OK otherwise
- Parameters
intno
: The number of the interrupt (0-31)cpu
: CPU on which the interrupt should be marked as shared (0 or 1)
-
esp_err_t
esp_intr_alloc
(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle)¶ 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.
If ESP_INTR_FLAG_IRAM flag is used, and handler address is not in IRAM or RTC_FAST_MEM, then ESP_ERR_INVALID_ARG is returned.
- 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
- Parameters
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.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. Setting ESP_INTR_FLAG_INTRDISABLED will return from this function with the interrupt disabled.handler
: The interrupt handler. Must be NULL when an interrupt of level >3 is requested, because these types of interrupts aren’t C-callable.arg
: Optional argument for passed to the interrupt handlerret_handle
: Pointer to an intr_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.
-
esp_err_t
esp_intr_alloc_intrstatus
(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, void *arg, intr_handle_t *ret_handle)¶ 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
- 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
- Parameters
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.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. Setting ESP_INTR_FLAG_INTRDISABLED will return from this function with the interrupt disabled.intrstatusreg
: The address of an interrupt status registerintrstatusmask
: 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.handler
: The interrupt handler. Must be NULL when an interrupt of level >3 is requested, because these types of interrupts aren’t C-callable.arg
: Optional argument for passed to the interrupt handlerret_handle
: Pointer to an intr_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.
-
esp_err_t
esp_intr_free
(intr_handle_t handle)¶ Disable and free an interrupt.
Use an interrupt handle to disable the interrupt and release the resources associated with it. If the current core is not the core that registered this interrupt, this routine will be assigned to the core that allocated this interrupt, blocking and waiting until the resource is successfully released.
- Note
When the handler shares its source with other handlers, the interrupt status bits it’s responsible for should be managed properly before freeing it. see
esp_intr_disable
for more details. Please do not call this function inesp_ipc_call_blocking
.- Return
ESP_ERR_INVALID_ARG the handle is NULL ESP_FAIL failed to release this handle ESP_OK otherwise
- Parameters
handle
: The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
-
int
esp_intr_get_cpu
(intr_handle_t handle)¶ Get CPU number an interrupt is tied to.
- Return
The core number where the interrupt is allocated
- Parameters
handle
: The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
-
int
esp_intr_get_intno
(intr_handle_t handle)¶ Get the allocated interrupt for a certain handle.
- Return
The interrupt number
- Parameters
handle
: The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
-
esp_err_t
esp_intr_disable
(intr_handle_t handle)¶ Disable the interrupt associated with the handle.
- Note
For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the CPU the interrupt is allocated on. Other interrupts have no such restriction.
When several handlers sharing a same interrupt source, interrupt status bits, which are handled in the handler to be disabled, should be masked before the disabling, or handled in other enabled interrupts properly. Miss of interrupt status handling will cause infinite interrupt calls and finally system crash.
- Return
ESP_ERR_INVALID_ARG if the combination of arguments is invalid. ESP_OK otherwise
- Parameters
handle
: The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
-
esp_err_t
esp_intr_enable
(intr_handle_t handle)¶ Enable the interrupt associated with the handle.
- Note
For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the CPU the interrupt is allocated on. Other interrupts have no such restriction.
- Return
ESP_ERR_INVALID_ARG if the combination of arguments is invalid. ESP_OK otherwise
- Parameters
handle
: The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
-
esp_err_t
esp_intr_set_in_iram
(intr_handle_t handle, bool is_in_iram)¶ Set the “in IRAM” status of the handler.
- Note
Does not work on shared interrupts.
- Return
ESP_ERR_INVALID_ARG if the combination of arguments is invalid. ESP_OK otherwise
- Parameters
handle
: The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatusis_in_iram
: Whether the handler associated with this handle resides in IRAM. Handlers residing in IRAM can be called when cache is disabled.
-
void
esp_intr_noniram_disable
(void)¶ Disable interrupts that aren’t specifically marked as running from IRAM.
-
void
esp_intr_noniram_enable
(void)¶ Re-enable interrupts disabled by esp_intr_noniram_disable.
-
void
esp_intr_enable_source
(int inum)¶ enable the interrupt source based on its number
- Parameters
inum
: interrupt number from 0 to 31
-
void
esp_intr_disable_source
(int inum)¶ disable the interrupt source based on its number
- Parameters
inum
: interrupt number from 0 to 31
-
static int
esp_intr_flags_to_level
(int flags)¶ Get the lowest interrupt level from the flags.
- Parameters
flags
: The same flags that pass toesp_intr_alloc_intrstatus
API
Macros¶
-
ESP_INTR_FLAG_LEVEL1
¶ Interrupt allocation flags.
These flags can be used to specify which interrupt qualities the code calling esp_intr_alloc* needs.Accept a Level 1 interrupt vector (lowest priority)
-
ESP_INTR_FLAG_LEVEL2
¶ Accept a Level 2 interrupt vector.
-
ESP_INTR_FLAG_LEVEL3
¶ Accept a Level 3 interrupt vector.
-
ESP_INTR_FLAG_LEVEL4
¶ Accept a Level 4 interrupt vector.
-
ESP_INTR_FLAG_LEVEL5
¶ Accept a Level 5 interrupt vector.
-
ESP_INTR_FLAG_LEVEL6
¶ Accept a Level 6 interrupt vector.
-
ESP_INTR_FLAG_NMI
¶ Accept a Level 7 interrupt vector (highest priority)
-
ESP_INTR_FLAG_SHARED
¶ Interrupt can be shared between ISRs.
-
ESP_INTR_FLAG_EDGE
¶ Edge-triggered interrupt.
-
ESP_INTR_FLAG_IRAM
¶ ISR can be called if cache is disabled.
-
ESP_INTR_FLAG_INTRDISABLED
¶ Return with this interrupt disabled.
-
ESP_INTR_FLAG_LOWMED
¶ Low and medium prio interrupts. These can be handled in C.
-
ESP_INTR_FLAG_HIGH
¶ High level interrupts. Need to be handled in assembly.
-
ESP_INTR_FLAG_LEVELMASK
¶ Mask for all level flags.
-
ETS_INTERNAL_TIMER0_INTR_SOURCE
¶ Platform timer 0 interrupt source.
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.
-
ETS_INTERNAL_TIMER1_INTR_SOURCE
¶ Platform timer 1 interrupt source.
-
ETS_INTERNAL_TIMER2_INTR_SOURCE
¶ Platform timer 2 interrupt source.
-
ETS_INTERNAL_SW0_INTR_SOURCE
¶ Software int source 1.
-
ETS_INTERNAL_SW1_INTR_SOURCE
¶ Software int source 2.
-
ETS_INTERNAL_PROFILING_INTR_SOURCE
¶ Int source for profiling.
-
ETS_INTERNAL_INTR_SOURCE_OFF
¶ Provides SystemView with positive IRQ IDs, otherwise scheduler events are not shown properly
-
ESP_INTR_ENABLE
(inum)¶ Enable interrupt by interrupt number
-
ESP_INTR_DISABLE
(inum)¶ Disable interrupt by interrupt number
Type Definitions¶
-
typedef void (*
intr_handler_t
)(void *arg)¶ Function prototype for interrupt handler function
-
typedef struct intr_handle_data_t
intr_handle_data_t
¶ Interrupt handler associated data structure
-
typedef intr_handle_data_t *
intr_handle_t
¶ Handle to an interrupt handler