Skip to main content
Graduate
September 19, 2025
Question

32F417: trying to interrupt from PE12 rising edge

  • September 19, 2025
  • 13 replies
  • 1038 views

Post edited by the ST moderator to be inline with ST community rules especially for the code. Please use </> button to paste your code. 

 

Hello :)

I almost have this working. In fact I had the ISR running and incrementing an integer. Then I went to the next stage and put in the xTaskResumeFromISR() but that crashes in configASSERT( xTaskToResume ).

Can anyone see any problems with this code?

Init:

__disable_irq();

GPIO_InitTypeDef GPIO_InitStruct = {0};

// Configure PE12

GPIO_InitStruct.Pin = GPIO_PIN_12;

GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; // Interrupt on rising edge

GPIO_InitStruct.Pull = GPIO_PULLDOWN; // pulldown

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // This does nothing on an input pin

HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

// Configure NVIC

HAL_NVIC_SetPriority(EXTI15_10_IRQn, 10, 0); // Maybe the priority is not suitable for xTaskResumeFromISR ?

HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

__enable_irq();

Vector table piece:

.word USART2_IRQHandler /* USART2 */

.word USART3_IRQHandler /* USART3 */

.word EXTI15_10_IRQHandler /* EXTI15_10_IRQHandler External Line[15:10] used for PE12 (TE) interrupt */

.word 0 /* RTC_Alarm_IRQHandler RTC Alarm (A and B) through EXTI Line */

.word 0 /* OTG_FS_WKUP_IRQHandler USB OTG FS Wakeup through EXTI line */

ISR:

// Handler for TE (PE12=1) interrupt.

// This restarts the xLCDHandle task which was suspended with vTaskSuspend(NULL).

void EXTI15_10_IRQHandler(void)

{

// Check if interrupt is from EXTI12 (PE12)

if (EXTI->PR & (1 << 12))

{

// Clear the interrupt pending bit

EXTI->PR = (1 << 12);

// Restart the suspended LCD task. If task not suspended, nothing happens

//xTaskResumeFromISR( xLCDHandle ); // uncommenting this breaks it

}

}

Currently I have no tasks suspended (so nothing to resume) but xTaskResumeFromISR should then just return False.

Digging around I found this

// Restart the suspended LCD task. If task not suspended, nothing happens

xYieldRequired = xTaskResumeFromISR( xLCDHandle );

if ( xYieldRequired == pdTRUE )

{

portYIELD_FROM_ISR( xYieldRequired );

}

but it doesn't help because the crash happens inside xTaskResumeFromISR.

Thank you very much for any input.

 

 

    This topic has been closed for replies.

    13 replies

    Graduate
    September 19, 2025

    ISR priorities must not be higher than the RTOS system interrupt priority defined by configSYSTEM_INTERRUPT_PRIORITY for any interrupt service routine that calls RTOS functions.

    PHolt.1Author
    Graduate
    September 19, 2025

    Thank you!

    I can't find that value.

    I have these

    /* The lowest interrupt priority that can be used in a call to a "set priority"
    function. */
    #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
    
    /* The highest interrupt priority that can be used by any interrupt service
    routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
    INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
    PRIORITY THAN THIS! (higher priorities are lower numeric values. */
    #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
    
    /* Interrupt priorities used by the kernel port layer itself. These are generic
    to all Cortex-M ports, and do not rely on any particular library functions. */
    #define configKERNEL_INTERRUPT_PRIORITY 		( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
    /* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
    See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
    #define configMAX_SYSCALL_INTERRUPT_PRIORITY 	( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

    I have tried the ISR with various priorities all the way down to 15 which is Systick.

    Also, would this be correct for the ISR?

    void EXTI15_10_IRQHandler(void)
    {
    
    	BaseType_t xYieldRequired;
    
     // Check if interrupt is from EXTI12 (PE12)
     if (EXTI->PR & (1 << 12))
     {
    
     // Clear the interrupt pending bit
     EXTI->PR = (1 << 12);
    
     // Restart the suspended LCD task. If task not suspended, nothing happens
     xYieldRequired = xTaskResumeFromISR( xLCDHandle );
     if ( xYieldRequired == pdTRUE )
     {
     	portYIELD_FROM_ISR( xYieldRequired );
     }
     }
    
    }
    
    Graduate
    September 19, 2025

    In your system: 

    configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5

    so your ISR must have priority >= 5.

    Example from: https://www.freertos.org/Documentation/02-Kernel/04-API-references/02-Task-control/08-xTaskResumeFromISR

    void vAnExampleISR( void )
    {
     BaseType_t xYieldRequired;
    
     // Resume the suspended task.
     xYieldRequired = xTaskResumeFromISR( xHandle );
    
     // We should switch context so the ISR returns to a different task.
     // NOTE: How this is done depends on the port you are using. Check
     // the documentation and examples for your port.
    
     portYIELD_FROM_ISR( xYieldRequired );
    }

    They recommend against this in general:

    xTaskResumeFromISR()

    is generally considered a dangerous function because its actions are not latched. For this reason it should definitely not be used to synchronise a task with an interrupt if there is a chance that the interrupt could arrive prior to the task being suspended, and therefore the interrupt being lost. Use of a semaphore, or preferable a direct to task notification, would avoid this eventuality. A worked example that uses a direct to task notification is provided. 

     

    Super User
    September 19, 2025

    It looks like the interrupt occurs for the 1st time before the scheduler has been activated. Thus xTaskToResume is invalid. Your IRQ priority is 10 which is lower than configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY (5), so OK.

     

    PHolt.1Author
    Graduate
    September 19, 2025

    The interrupt is initialised inside the RTOS task, so should not occur before RTOS is started. I have it working now, though not sure how exactly!

    Task is defined with this

    xTaskCreate(vLCDTask, "LCD", configMINIMAL_STACK_SIZE*2, NULL, osPriorityLow, &xLCDHandle);

    Then inside the task I set up the ISR with this

    	GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    	// Configure PE12
    	GPIO_InitStruct.Pin = GPIO_PIN_12;
    	GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; // Interrupt on rising edge
    	GPIO_InitStruct.Pull = GPIO_PULLDOWN; 	// pulldown
    	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;	// This does nothing on an input pin
    	HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
    	// Configure NVIC
    	HAL_NVIC_SetPriority(EXTI15_10_IRQn, 2, 0);	// Priority must be <=configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY (5)
    	HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

     The ISR is now this (I did a lot of googling and also used Claude to dig up more (Claude is >50% garbage as we know, but so are google hits to code fragments posted on forums)

    // Handler for TE (PE12=1) interrupt.
    // This restarts the xLCDHandle task which was suspended with vTaskSuspend(xLCDHandle).
    // Exec time of this ISR is 1.5us.
    
    void EXTI15_10_IRQHandler(void)
    {
    	BaseType_t xYieldRequired;
    	// Check if interrupt is from EXTI12 (PE12)
    	if (EXTI->PR & (1 << 12))
    	{
    		// Clear the interrupt pending bit
    		EXTI->PR = (1 << 12);
    
    		// Check if task handle is valid before using it
    		if (xLCDHandle != NULL)
    		{
     xYieldRequired = xTaskResumeFromISR(xLCDHandle);
     portYIELD_FROM_ISR(xYieldRequired);
    		}
    
    	}
    }

    and it is running!

    I am interrupting from the TE=1 vertical blanking output of an LCD controller. If you do all display writing during TE=1 (1ms long) then you get no flicker etc. So I am using

    vTaskSuspend(xLCDHandle);

    right before drawing any graphics. The ISR from TE rising edge then unsuspends the graphics activity. I have to manually ensure it completes within that 1ms (though there are some tricks one can do, using the knowledge of which way the display controller will scan its video RAM, etc).

    The ISR priority is a mystery because 2,3,4 or 5 works but 6+ blows it up in xtaskresume. This is the opposite of what is documented.

    Thank you all for your help.

    Let me add that this is a product under development for several years. It is rock solid, running about 30 FreeRTOS tasks, MbedTLS, ADCs, DACs, counters, all kinds of weird stuff, and I have just been adding the LCD functionality, which works great but I thought I would try to implement the video writing sync with the vertical sync.

    If anyone has any hints about making the code more reliable, I am all ears.

    There are loads of interrupts, some complex like USB and ETH, but this ISR is the only one from an external pin (PE12). I have never done that before.

    PHolt.1Author
    Graduate
    September 20, 2025

    I may have discovered the problem:

    HAL_NVIC_SetPriority()

    is not the same as

    NVIC_SetPriority()

    but the FR wording refers to the latter: the actual NVIC value.

    The actual priority in the NVIC is 0-255:

    The issue comes from how ARM Cortex-M priority values are interpreted differently at different layers.
    ARM Cortex-M Priority System
    Hardware Level (NVIC Register):

    Lower numbers = Higher priority
    Priority 0 = Highest priority
    Priority 255 = Lowest priority

    The HAL_ function shifts the value left by 4 bits.

    More from google:

    // configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY = 5 (raw NVIC value)
    // With priority group 4: HAL_value = NVIC_value >> 4
    // So HAL priority should be: 5 >> 4 = 0 (but this rounds to 0)

    // Actually, for NVIC value 5 to work with Priority Group 4:
    // You need HAL priority 0 (which gives NVIC value 0)
    // But FreeRTOS wants NVIC >= 5, so use:

    HAL_NVIC_SetPriority(EXTI15_10_IRQn, 1, 0); // Gives NVIC value 16
    // or
    HAL_NVIC_SetPriority(EXTI15_10_IRQn, 2, 0); // Gives NVIC value 32

    So this may explain why values like 2 work (because 2 produces actual NVIC priority of 32 or so, which is > the 5 required), but it still doesn't explain why a value of 6+ blows it up.

    I posted some more here and the answer may be in a zero priority grouping having been set

    https://www.eevblog.com/forum/microcontrollers/freertos-and-using-an-isr-to-unsuspend-a-suspended-task-(32f417)/

    Super User
    September 21, 2025

    > HAL_NVIC_SetPriority() is not the same as NVIC_SetPriority()

    Yes. The argument for HAL_NVIC_SetPriority is the preemption priority and sub-priority (which usually is 0 anyway). For NVIC_SetPriority you have to convert the priority and sub-priority to one value with NVIC_EncodePriority(prioritygroup, PreemptPriority, SubPriority).

    ST library provides convenient shortcut for this.

    Also, in your latter code snippet 

    > HAL_NVIC_SetPriority(EXTI15_10_IRQn, 2, 0); // Priority must be <=configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY (5)

    remember that lower numeric value means HIGHER priority in ARM Cortex, so 2 is higher than 5 (not good!)

    These small things unfortunately should be kept in mind (aka 'devil is in details' ... )

     

    PHolt.1Author
    Graduate
    September 21, 2025

    It was partly something else.

    Firstly I have priority grouping disabled, with

    HAL_NVIC_SetPriorityGrouping(0);

    I don't remember the reason this was done, 2 years ago, but it does produce a system where no ISR can be interrupted by another ISR, so no ISR can see an RTOS task switch within it, so you end up with a more deterministic system.

    Secondly I found this:

    The Confusing Name
    configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY is poorly named. It doesn't mean "maximum priority that can call syscalls."
    It means: "The LOWEST priority (highest number) that can call FreeRTOS APIs"

    If true, that explains why I get a crash if I set that ISR interrupt priority to 6 or more i.e. lower priority than 5.

    It also means the only priorities available in my system are 1,2,3,4,5, which is adequate. Systick is 15 which is also fine. 6-14 are not usable for anything and certainly not for ISRs. I could change configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY from its standard value of 5 to say 10 if necessary.

     

    PHolt.1Author
    Graduate
    September 22, 2025

    I wonder if anyone can confirm the above claim about configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY having a backwards meaning?

    Super User
    September 22, 2025

    It means: "The LOWEST priority (highest number) that can call FreeRTOS APIs"

    It means the highest priority (lowest number) that can call RTOS API. At this level the synchronization occurs: this level and lower (greater numbers) are masked inside RTOS calls. Higher priority handlers (smaller numbers) are not masked so they run undelayed but cannot call the RTOS.