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

Search API5 functions

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.
Source comment
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.
Source comment
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.
Source comment
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.
Source comment
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.
Source comment
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.

inc/main.hinc/main.h
#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.

src/main.csrc/main.c · KeyInputExtiInit
    /*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.

src/main.csrc/main.c · EXTI4_IRQHandler
    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.

src/main.csrc/main.c · main
        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.

src/main.csrc/main.c · TIM_Config
    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.

src/main.csrc/main.c · main
        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.

src/main.csrc/main.c · EXTI4_IRQHandler
    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.

src/main.csrc/main.c · GTIM3_IRQHandler
    /* 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.

src/main.csrc/main.c · main
    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.

src/main.csrc/main.c · EXTI4_IRQHandler
    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.

src/n32h49x_it.csrc/n32h49x_it.c · SysTick_Handler
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.

src/main.csrc/main.c · main
    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
Nations Technologies DevKit Docs

Datasheets, SDK, driver APIs and demo guides for Nations Technologies MCU development kits, organized by chip series.

© 2026 Nations Technologies Inc. All rights reserved.粤ICP备11069485号This site uses cookie-free anonymous analytics.