5.20 NVIC Guide
This chapter covers the files of the NVIC (misc) driver in the N32H49x standard peripheral library, its 5 public functions, and the NVIC_VectorTable_Relocation, NVIC_IRQ_Mask, and NVIC_IRQ_Priority examples. Read it together with the “System” chapter of the user manual for the interrupt priority grouping, vector table relocation, and core exception behavior.
5.20.1 Related files
| Relative path | Purpose |
|---|---|
firmware/n32h49x_std_periph_driver/inc/misc.h |
Defines the NVIC_InitType structure and the NVIC priority-group, low-power condition, and SysTick clock-source selection macros;declares the 5 public functions |
firmware/n32h49x_std_periph_driver/src/misc.c |
Implements those functions: priority grouping, NVIC channel initialization, vector table relocation, low-power condition selection, and SysTick clock-source configuration |
firmware/CMSIS/device/n32h49x.h |
Defines the IRQn_Type interrupt number enumeration and the CMSIS core interfaces such as NVIC_SetPriority, NVIC_GetActive, and NVIC_EncodePriority |
projects/n32h49x_EVAL/examples/NVIC/ |
NVIC example root, containing the NVIC_VectorTable_Relocation, NVIC_IRQ_Mask, and NVIC_IRQ_Priority examples |
projects/n32h49x_EVAL/examples/NVIC/NVIC_VectorTable_Relocation/ |
Project layout of that example: MDK-ARM/, EWARM/, inc/, src/, readme.txt |
projects/n32h49x_EVAL/bsp/ |
Board support files used by the example: log initialization and print UART remapping |
5.20.2 API reference
The 5 public functions below follow the declaration order of misc.h and fall into 3 groups; each entry gives a summary and the original comment directly above the function in misc.c.
Priority grouping and initialization
NVIC_PriorityGroupConfig, NVIC_Init
Vector table
NVIC_SetVectorTable
Low power and SysTick
NVIC_SystemLPConfig, SysTick_CLKSourceConfig
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)Configures the priority grouping: pre-emption priority and subpriority.
name NVIC_PriorityGroupConfig
fun Configures the priority grouping: pre-emption priority and subpriority.
param NVIC_PriorityGroup :
- NVIC_PriorityGroup_0 0 bits for pre-emption priority 4 bits for subpriority
- NVIC_PriorityGroup_1 1 bits for pre-emption priority 3 bits for subpriority
- NVIC_PriorityGroup_2 2 bits for pre-emption priority 2 bits for subpriority
- NVIC_PriorityGroup_3 3 bits for pre-emption priority 1 bits for subpriority
- NVIC_PriorityGroup_4 4 bits for pre-emption priority 0 bits for subpriority
return none
void NVIC_Init(NVIC_InitType* NVIC_InitStruct)Initializes the NVIC peripheral according to the specified parameters in the NVIC_structure_initializes.
name NVIC_Init
fun Initializes the NVIC peripheral according to the specified parameters in the NVIC_structure_initializes.
param NVIC_InitStruct :
- NVIC_IRQChannel :
- IRQn_Type Interrupt Number Definition
if NVIC_PriorityGroup is NVIC_PriorityGroup_0 :
- NVIC_IRQChannelPreemptionPriority :
- NVIC_PRE_PRIORITY_0
- NVIC_IRQChannelSubPriority :
- NVIC_SUB_PRIORITY_0
- NVIC_SUB_PRIORITY_1
- NVIC_SUB_PRIORITY_2
- NVIC_SUB_PRIORITY_3
- NVIC_SUB_PRIORITY_4
- NVIC_SUB_PRIORITY_5
- NVIC_SUB_PRIORITY_6
- NVIC_SUB_PRIORITY_7
- NVIC_SUB_PRIORITY_8
- NVIC_SUB_PRIORITY_9
- NVIC_SUB_PRIORITY_10
- NVIC_SUB_PRIORITY_11
- NVIC_SUB_PRIORITY_12
- NVIC_SUB_PRIORITY_13
- NVIC_SUB_PRIORITY_14
- NVIC_SUB_PRIORITY_15
if NVIC_PriorityGroup is NVIC_PriorityGroup_1 :
- NVIC_IRQChannelPreemptionPriority :
- NVIC_PRE_PRIORITY_0
- NVIC_PRE_PRIORITY_1
- NVIC_IRQChannelSubPriority :
- NVIC_SUB_PRIORITY_0 to NVIC_SUB_PRIORITY_7
if NVIC_PriorityGroup is NVIC_PriorityGroup_2 :
- NVIC_IRQChannelPreemptionPriority :
- NVIC_PRE_PRIORITY_0 to NVIC_PRE_PRIORITY_3
- NVIC_IRQChannelSubPriority :
- NVIC_SUB_PRIORITY_0 to NVIC_SUB_PRIORITY_3
if NVIC_PriorityGroup is NVIC_PriorityGroup_3 :
- NVIC_IRQChannelPreemptionPriority :
- NVIC_PRE_PRIORITY_0 to NVIC_PRE_PRIORITY_7
- NVIC_IRQChannelSubPriority :
- NVIC_SUB_PRIORITY_0
- NVIC_SUB_PRIORITY_1
if NVIC_PriorityGroup is NVIC_PriorityGroup_4 :
- NVIC_IRQChannelPreemptionPriority :
- NVIC_PRE_PRIORITY_0
- NVIC_PRE_PRIORITY_1
- NVIC_PRE_PRIORITY_2
- NVIC_PRE_PRIORITY_3
- NVIC_PRE_PRIORITY_4
- NVIC_PRE_PRIORITY_5
- NVIC_PRE_PRIORITY_6
- NVIC_PRE_PRIORITY_7
- NVIC_PRE_PRIORITY_8
- NVIC_PRE_PRIORITY_9
- NVIC_PRE_PRIORITY_10
- NVIC_PRE_PRIORITY_11
- NVIC_PRE_PRIORITY_12
- NVIC_PRE_PRIORITY_13
- NVIC_PRE_PRIORITY_14
- NVIC_PRE_PRIORITY_15
- NVIC_IRQChannelSubPriority :
- NVIC_SUB_PRIORITY_0
- NVIC_IRQChannelCmd :
- ENABLE
- DISABLE
return none
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset)Sets the vector table location and Offset.
name NVIC_SetVectorTable
fun Sets the vector table location and Offset.
param NVIC_vecter_table specifies if the vector table is in RAM or FLASH memory.
This parameter can be one of the following values:
- NVIC_VectTab_RAM
- NVIC_VectTab_FLASH
param offset Vector Table base offset field. This value must be a multiple of 0x200.
return none
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState Cmd)Selects the condition for the system to enter low power mode.
name NVIC_SystemLPConfig
fun Selects the condition for the system to enter low power mode.
param LowPowerMode Specifies the new mode for the system to enter low power mode.
This parameter can be one of the following values:
- NVIC_LP_SEVONPEND
- NVIC_LP_SLEEPDEEP
- NVIC_LP_SLEEPONEXIT
param - Cmd :
- ENABLE
- DISABLE
return none
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)Configures the SysTick clock source.
name SysTick_CLKSourceConfig
fun Configures the SysTick clock source.
param SysTick_CLKSource :
- SysTick_CLKSource_HCLK_Div8 External clock selected as SysTick clock source.
- SysTick_CLKSource_HCLK AHB clock selected as SysTick clock source.
return none
5.20.3 Demo descriptions
This section covers the three NVIC examples: NVIC_VectorTable_Relocation relocates the interrupt vector table from FLASH to SRAM, NVIC_IRQ_Mask shows the effect of global interrupt masking on configurable-priority interrupts, and NVIC_IRQ_Priority shows how preemption priority affects interrupt nesting.
5.20.3.1 NVIC_VectorTable_Relocation Vector table relocation
Example directory: projects/n32h49x_EVAL/examples/NVIC/NVIC_VectorTable_Relocation/
Initialize the print UART and configure PA15 as an input with an external interrupt→A falling edge on PA15 enters the EXTI4 interrupt→The handler sets the key flag and clears the pending bit→The main loop relocates the vector table from FLASH to SRAM after seeing the flag→The program keeps running after relocation and prints a message
| Item | Source analysis |
|---|---|
| Function | Demonstrates NVIC interrupt vector table relocation: at startup the vector table is in FLASH at offset 0x00000000; after a key press, NVIC_SetVectorTable relocates it to internal SRAM at 0x00010000 and a message is printed. |
| Source files | src/main.c: main, KeyInputExtiInit, and EXTI4_IRQHandler;inc/main.h: the vector table offset macros VECT_TAB_SRAM_OFFSET/VECT_TAB_FLASH_OFFSET and the key, EXTI, and NVIC macros;src/n32h49x_it.c: the core exception handlers;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | PA15 is configured as GPIO_MODE_INPUT with GPIO_PULL_UP;the external interrupt line is EXTI_LINE4 with EXTI_Trigger_Falling;the priority group is NVIC_PriorityGroup_2, and EXTI4 uses preemption priority 1 and subpriority 0;the system clock is 240 MHz and the print UART is USART, TX-PA9 and RX-PA10 at 115200. |
| Program and data path | main calls log_init to print NVIC VectorTable Relocation, then KeyInputExtiInit enables the GPIOA and AFIO clocks, configures PA15 as an input with pull-up, connects EXTI_LINE4 to PA15 with falling-edge triggering, and finally configures the NVIC channel;the main loop waits for Key_Status to be set, then prints the message and performs the vector table relocation. |
| Main APIs | NVIC_SetVectorTable, NVIC_PriorityGroupConfig, NVIC_Init, GPIO_ConfigEXTILine |
| Verification | After reset the vector table is in FLASH and the program runs normally; a key press enters EXTI4_IRQHandler and prints EXTI4 IRQHandler is executed(FLASH), then the main loop prints the relocation message and moves the vector table to SRAM, after which the program keeps running. |
| Use note | The offset passed to NVIC_SetVectorTable must be a multiple of 0x200;after relocation all interrupt vectors come from the new location, so SRAM must already hold a valid vector table at that address. Toolchains are KEIL MDK-ARM V5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example uses a key external interrupt to trigger a vector table relocation, moving the interrupt vector table from FLASH to internal SRAM and continuing to run.
Data path: inc/main.h defines the vector table offset and key resource macros → KeyInputExtiInit configures PA15, EXTI_LINE4, and the NVIC channel → EXTI4_IRQHandler sets Key_Status and clears the pending bit → the main loop calls NVIC_SetVectorTable to relocate the vector table to SRAM
1. Macros define the vector table offset and key resources. Two offset macros distinguish the FLASH and SRAM vector table locations, and the key port, pin, EXTI line, and interrupt number are described by macros as well.
#define VECT_TAB_FLASH_OFFSET ((uint32_t)0x00000000)
#define VECT_TAB_SRAM_OFFSET ((uint32_t)0x00010000)
#define KEY_INPUT_PORT_CLK RCC_AHB_PERIPHEN_GPIOA
#define KEY_INPUT_PORT GPIOA
#define KEY_INPUT_PIN GPIO_PIN_15
#define KEY_INPUT_EXTI_LINE EXTI_LINE4
#define KEY_INPUT_LIN_SOURCE EXTI_LINE_SOURCE4
#define KEY_INPUT_PORT_SOURCE GPIOA_PORT_SOURCE
#define KEY_INPUT_PIN_SOURCE GPIO_PIN_SOURCE15
#define KEY_INPUT_IRQn EXTI4_IRQn
VECT_TAB_SRAM_OFFSET is 0x00010000, a multiple of 0x200 as NVIC_SetVectorTable requires.
2. KeyInputExtiInit configures EXTI4 and the NVIC channel. The function first enables the GPIOA and AFIO clocks, configures PA15 as an input with pull-up, connects EXTI4 to PA15 with falling-edge triggering, and finally sets the priority group and the NVIC channel.
/*Configure key EXTI Line to key input Pin*/
GPIO_ConfigEXTILine(KEY_INPUT_LIN_SOURCE, KEY_INPUT_PORT_SOURCE,KEY_INPUT_PIN_SOURCE);
/*Configure key EXTI line*/
EXTI_InitStruct(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line = KEY_INPUT_EXTI_LINE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitPeripheral(&EXTI_InitStructure);
/* Configure two bits for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/*Set key input interrupt priority*/
NVIC_InitStructure.NVIC_IRQChannel = KEY_INPUT_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_PriorityGroup_2 assigns 2 bits to preemption priority and 2 bits to subpriority; here EXTI4 uses preemption priority 1 and subpriority 0.
3. EXTI4_IRQHandler sets the flag and clears the pending bit. The handler checks the EXTI4 status, sets Key_Status, and uses VECT_TAB_SRAM as a compile-time branch to distinguish the two vector table locations.
if (EXTI_GetStatusFlag(KEY_INPUT_EXTI_LINE) != RESET)
{
#ifdef VECT_TAB_SRAM
log_info("EXTI4 IRQHandler is executed(SRAM) \r\n");
#else
/* Key_Status */
Key_Status = ENABLE;
log_info("EXTI4 IRQHandler is executed(FLASH) \r\n");
#endif
/* Clears the SEL Button EXTI line pending bits */
EXTI_ClrStatusFlag(KEY_INPUT_EXTI_LINE);
}
EXTI_ClrStatusFlag clears the pending bit so the handler does not re-enter, and Key_Status is the flag shared between the main loop and the handler.
4. The main loop performs the vector table relocation. Once the flag is set it prints a message and calls NVIC_SetVectorTable to move the vector table to SRAM at 0x00010000, then enters an empty loop.
if (Key_Status == ENABLE)
{
log_info("Sets the vector table location and Offset \r\n");
/* Vector Table Relocation in Internal SRAM. */
NVIC_SetVectorTable(NVIC_VectTab_RAM, VECT_TAB_SRAM_OFFSET);
while (1)
;
}
NVIC_VectTab_RAM selects RAM as the vector table base and the offset is given by VECT_TAB_SRAM_OFFSET.
Corresponding source files: src/main.c, inc/main.h
5.20.3.2 NVIC_IRQ_Mask Global interrupt masking
Example directory: projects/n32h49x_EVAL/examples/NVIC/NVIC_IRQ_Mask/
Initialize the print UART, a 1-second timer update interrupt, and a PA15 key interrupt→The timer enters the update interrupt once per second and prints→The main loop waits for a key event to toggle the key flag→After seeing the flag it disables global interrupts and prints→It polls the key through one press and release, then re-enables global interrupts→The timer stops printing while masked and resumes afterwards
| Item | Source analysis |
|---|---|
| Function | Demonstrates the masking effect of __disable_irq() and __enable_irq() on configurable-priority interrupts: GTIM3 generates an update interrupt once per second and prints;after a key press the example disables global interrupts first and re-enables them once the key is released. |
| Source files | src/main.c: main, KeyInputExtiInit, TIM_Config, EXTI4_IRQHandler, and GTIM3_IRQHandler;inc/main.h: the key, EXTI, and NVIC macros and the function declarations;src/n32h49x_it.c: the core exception handlers;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | The priority group is NVIC_PriorityGroup_2;the GTIM3 update interrupt has a 1-second period (Period 50000-1, Prescaler HclkFreq/50000-1) with preemption priority 0 and subpriority 0; PA15 is an input with pull-up, EXTI_LINE4 uses falling-edge triggering with preemption priority 1 and subpriority 0. |
| Program and data path | main configures the GTIM3 update interrupt and the PA15 key interrupt, then enters the main loop;EXTI4_IRQHandler toggles Key_Status each time, and GTIM3_IRQHandler clears the update flag and prints GTIM3 IRQHandler. |
| Main APIs | NVIC_PriorityGroupConfig, NVIC_Init, GPIO_ReadInputDataBit, __disable_irq, __enable_irq |
| Verification | After reset the UART prints GTIM3 IRQHandler once per second;a key press prints Disable irq and the GTIM3 printing stops, and releasing the key prints enable irq and the printing resumes. |
| Use note | __disable_irq() raises the execution priority to 0 and masks all configurable-priority interrupts, leaving only HardFault and similar escalation;do not poll or block for long while interrupts are disabled, to avoid hurting the real-time response. Toolchains are KEIL MDK-ARM V5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example uses the one-second GTIM3 update interrupt to show whether interrupts are masked, with a key switching between “disable interrupts, poll the key, enable interrupts”.
Data path: TIM_Config sets up the GTIM3 update interrupt and enables the NVIC channel → GTIM3_IRQHandler clears the flag and prints once per second → EXTI4_IRQHandler toggles Key_Status → the main loop calls __disable_irq, polls the key, and calls __enable_irq based on Key_Status
1. TIM_Config sets up the one-second update interrupt and enables the NVIC. The Period and Prescaler are derived from the HCLK frequency to give a 1-second period, after which the update interrupt and the counter are enabled.
TIM_InitTimBaseStruct(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.Period = 50000-1;
TIM_TimeBaseStructure.Prescaler = ((RCC_Clocks.HclkFreq / 50000) - 1);
TIM_TimeBaseStructure.ClkDiv = TIM_CLK_DIV1;
TIM_TimeBaseStructure.CounterMode = TIM_CNT_MODE_UP;
TIM_InitTimeBase(GTIM3, &TIM_TimeBaseStructure);
/* Immediate load of GTIM3 Precaler values */
TIM_ConfigPrescaler(GTIM3, ((RCC_Clocks.HclkFreq / 50000) - 1),TIM_PSC_RELOAD_MODE_IMMEDIATE);
/* Clear GTIM3 update pending flags */
TIM_ClearFlag(GTIM3, TIM_FLAG_UPDATE);
/* Enable the GTIM3 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = GTIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Enable GTIM3 Update interrupts */
TIM_ConfigInt(GTIM3, TIM_INT_UPDATE,ENABLE);
/* GTIM3 enable counters */
TIM_Enable(GTIM3,ENABLE);
TIM_ConfigInt opens the peripheral-side update interrupt and NVIC_Init opens the NVIC channel; without either one GTIM3_IRQHandler is never entered.
2. The main loop disables and enables global interrupts. After the key flag is seen it calls __disable_irq, polls the key through one press and release, and finally calls __enable_irq to recover.
while (Key_Status == DISABLE)
{
}
log_info("Disable irq \r\n");
/* This instruction raises the execution priority to 0. This prevents all
exceptions with configurable priority from activating, other than through
the HardFault fault escalation mechanism. */
__disable_irq();
while(GPIO_ReadInputDataBit(KEY_INPUT_PORT,KEY_INPUT_PIN) == RESET)
{
}
while(GPIO_ReadInputDataBit(KEY_INPUT_PORT,KEY_INPUT_PIN) == SET)
{
}
log_info("enable irq \r\n");
Once __disable_irq raises the execution priority to 0, the GTIM3 update interrupt is pended instead of executed; after __enable_irq it enters again on its period.
3. EXTI4_IRQHandler toggles the key flag. Each interrupt flips Key_Status between ENABLE and DISABLE as the trigger for the main loop.
if (EXTI_GetStatusFlag(KEY_INPUT_EXTI_LINE) != RESET)
{
if (Key_Status == ENABLE)
{
Key_Status = DISABLE;
}
else
{
Key_Status = ENABLE;
}
/* Clears the SEL Button EXTI line pending bits. */
EXTI_ClrStatusFlag(KEY_INPUT_EXTI_LINE);
}
The main loop leaves the wait only after the flag flips, so disabling interrupts happens after a real key event.
4. GTIM3_IRQHandler clears the flag and prints. The timer interrupt only clears the flag and prints, which makes it easy to see whether interrupts are masked.
/* Clear GTIM3 update interrupt */
TIM_ClearFlag(GTIM3, TIM_INT_UPDATE);
/* GTIM3 IRQHandler */
log_info("GTIM3 IRQHandler \r\n");
If the update flag is not cleared the interrupt re-enters immediately and the prints run together.
Corresponding source files: src/main.c, inc/main.h
5.20.3.3 NVIC_IRQ_Priority Preemption priority
Example directory: projects/n32h49x_EVAL/examples/NVIC/NVIC_IRQ_Priority/
Initialize the print UART and the PA15 key external interrupt→Set the SysTick preemption priority to 0 and start its periodic interrupt→The main loop prints the status and delays each round→A key press pends a SysTick exception from the EXTI4 handler→SysTick preempts and checks whether it interrupted EXTI4→The main loop changes the SysTick preemption priority according to the status and prints the end
| Item | Source analysis |
|---|---|
| Function | Demonstrates how NVIC priority affects preemption: SysTick starts at preemption priority 0, higher than EXTI4 at 1; after a key press the EXTI4 handler pends SysTick on purpose, so you can see whether SysTick preempts EXTI4 while it runs. |
| Source files | src/main.c: main, KeyInputExtiInit, EXTI4_IRQHandler, and Delay;src/n32h49x_it.c: the core exception handlers including SysTick_Handler;inc/main.h: the key, EXTI, and NVIC macros and the Status type declaration;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | The priority group is NVIC_PriorityGroup_2;EXTI4 uses preemption priority 1 and subpriority 0; SysTick is set to preemption priority 0 with NVIC_SetPriority and NVIC_EncodePriority. |
| Program and data path | main initializes the key interrupt and sets the SysTick preemption priority to 0, then the main loop prints Key_Status and calls Delay(0x8FFFFF) each round;EXTI4_IRQHandler pends SysTick by writing SCB->ICSR, and SysTick_Handler sets Key_Status to 1 or 2 based on NVIC_GetActive(KEY_INPUT_IRQn). |
| Main APIs | NVIC_SetPriority, NVIC_EncodePriority, NVIC_GetPriorityGrouping, NVIC_Init |
| Verification | After reset the UART prints Key_Status = 0;a key press reaches EXTI4, then the main loop changes the SysTick priority to 2 and prints Key_Status = 1;a further trigger prints NVIC IRQ Priority Test End. |
| Use note | Preemption priority decides whether an interrupt can preempt a running one, and subpriority only orders interrupts that are pending at the same time; changing the SysTick priority affects SysTick-based delays, so recheck the system tick afterwards. Toolchains are KEIL MDK-ARM V5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example uses the preemption relationship of two interrupts to show what priority does: EXTI4 pends SysTick on purpose, and SysTick then checks whether it preempted EXTI4.
Data path: main sets the SysTick preemption priority with NVIC_SetPriority → a key enters EXTI4_IRQHandler, which writes SCB->ICSR to pend SysTick → SysTick_Handler uses NVIC_GetActive to check whether it interrupted EXTI4 and sets Key_Status → the main loop calls NVIC_SetPriority again based on Key_Status
1. Set the SysTick priority with the CMSIS interface. After initializing the key interrupt, the example sets the SysTick preemption priority to 0 so it is higher than EXTI4 at 1.
KeyInputExtiInit(KEY_INPUT_PORT, KEY_INPUT_PIN);
/* Configure the SysTick Handler Priority: Preemption priority(0) and subpriority */
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), PreemptionPriorityValue, 0));
NVIC_EncodePriority encodes the preemption and subpriority values into the register according to the current group, avoiding manual shifts.
2. EXTI4_IRQHandler pends SysTick on purpose. The handler writes the pend bit in SCB->ICSR to request a SysTick exception, creating the preemption scenario.
log_info("EXTI4 IRQHandler Start \r\n");
if (EXTI_GetStatusFlag(KEY_INPUT_EXTI_LINE) != RESET)
{
/* Generate SysTick exception */
SCB->ICSR |= 0x04000000;
/* Clears the SEL Button EXTI line pending bits */
EXTI_ClrStatusFlag(KEY_INPUT_EXTI_LINE);
}
log_info("EXTI4 IRQHandler End \r\n");
If the SysTick preemption priority is higher than EXTI4’s, SysTick enters before EXTI4 returns; otherwise it runs only after EXTI4 returns.
3. SysTick_Handler checks whether preemption happened. The handler reads whether EXTI4 is still active with NVIC_GetActive and sets Key_Status to 1 or 2 accordingly.
void SysTick_Handler(void)
{
log_info("SysTick IRQHandler \r\n");
/* If the EXTI0 IRQ Handler was preempted by SysTick Handler */
if (NVIC_GetActive(KEY_INPUT_IRQn) != 0)
{
Key_Status = 1;
}
else
{
Key_Status = 2;
}
}
A non-zero NVIC_GetActive return means EXTI4 is still active, so SysTick preempted EXTI4.
4. The main loop reads the status and adjusts the priority. main prints according to Key_Status and, when the status is 1, changes the SysTick preemption priority to PreemptionPriorityValue + 2.
while (1)
{
if (Key_Status == 1)
{
log_info("Key_Status = 1 \r\n");
/* Configure the SysTick Handler Priority: Preemption priority(2) and subpriority */
NVIC_SetPriority(SysTick_IRQn,
NVIC_EncodePriority(NVIC_GetPriorityGrouping(), PreemptionPriorityValue + 2, 0));
}
else if (Key_Status == 2)
{
log_info("NVIC IRQ Priority Test End \r\n");
}
else
{
log_info("Key_Status = 0 \r\n");
}
/* Insert delay Time */
Delay(0x8FFFFF);
}
Once the SysTick preemption priority is raised to 2 it is no longer higher than EXTI4’s 1, so on the next trigger SysTick waits for EXTI4 to return and Key_Status becomes 2.
Corresponding source files: src/main.c, src/n32h49x_it.c
5.20.4 Notes and troubleshooting
| Scenario | Check & action | Source |
|---|---|---|
| Number of interrupt channels and priority bits | The N32H49x provides 112 maskable interrupt channels (excluding the 16 Cortex-M4F interrupt lines); the priority field is 4 bits wide, giving 16 programmable priority levels. |
User manual |
| Enabling the interrupt channel | Before using any peripheral interrupt, enable the matching channel in the NVIC and set its priority correctly; enabling the interrupt only on the peripheral side without the NVIC channel does not reach the interrupt handler. |
User manual |
| Priority grouping | Each interrupt priority is made of a preemption part and a subpriority part whose widths are set by NVIC_PriorityGroupConfig;preemption priority decides whether an interrupt can preempt a running one, and subpriority only orders interrupts pending at the same time. |
User manual |
| Vector table relocation | On reset the CPU takes the initial stack value from 0x00000000 and the reset vector from 0x00000004;NVIC_SetVectorTable can move the vector table to SRAM, and the offset must be a multiple of 0x200. |
User manual |
| SysTick clock source | SysTick can use HCLK/8 or HCLK as its clock source, and defaults to HCLK/8 after reset; after switching the clock source, recompute the reload value so the counting period stays as intended. |
User manual |