2025-09-19 10:07 AM - last edited on 2025-09-19 12:58 PM by mƎALLEm
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.
2025-09-19 11:19 AM
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.
2025-09-19 12:00 PM
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 );
}
}
}
2025-09-19 12:12 PM
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:
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.
2025-09-19 1:08 PM
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.
2025-09-19 1:29 PM - edited 2025-09-19 2:25 PM
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.
2025-09-20 1:31 AM - edited 2025-09-20 2:11 PM
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
2025-09-21 1:51 AM
> 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' ... )
2025-09-21 9:53 AM
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.
2025-09-22 1:19 AM
I wonder if anyone can confirm the above claim about configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY having a backwards meaning?