After a rather difficult process of learning some things about interrupt priority, I am still a bit stuck trying to understand what values I am allowed to call for HAL_NVIC_SetPriority()
on the SysTick_IRQn
(which is the ISR calling the FreeRTOS scheduler every 1 ms).
TLDR;
Part of me thinks that anything between HAL_NVIC_SetPriority(SysTick_IRQn, 15 ,0U)
(lowest priority possible) and HAL_NVIC_SetPriority(SysTick_IRQn, 10 ,0U)
(a bit higher) are allowed, and part of me thinks that anything between HAL_NVIC_SetPriority(SysTick_IRQn, 15 ,0U)
(lowest priority possible) and HAL_NVIC_SetPriority(SysTick_IRQn, 5 ,0U)
(quite a bit higher) is allowed. This is assuming that configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
in FreeRTOSConfig.h is set to 5. The confusion lies in the fact that in FreeRTOS, higher numbers are higher priority, but in STM32, higher numbers are lower priority, and the documentation is pretty challenging to understand.
Details:
To prove I've made a valiant effort, and to help you help me fill in the blanks, here's my current understanding. I'm going to write a description of what I know to be true in such a way that it looks like I am teaching you, even though I'm seeking the answer to my above question, as well as correction, confirmation, or additional insight as you see fit.
Though this probably applies to many of the STM32 microcontrollers or families, let's discuss it in terms of the STM32F207ZG in particular.
Note: STM32CubeF2 download is here.
My understanding:
If you look at a standard FreeRTOSConfig.h file (ex: STM32Cube_FW_F2_V1.7.0/Projects/STM322xG_EVAL/Applications/FreeRTOS/FreeRTOS_ThreadCreation/Inc/FreeRTOSConfig.h) you'll see the following:
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4 /* 15 priority levels */
#endif
/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
/* 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) )
__NVIC_PRIO_BITS
is defined in STM32Cube_FW_F2_V1.7.0/Drivers/CMSIS/Device/ST/STM32F2xx/Include/stm32f217xx.h as 4U
since "STM32F2XX uses 4 Bits for the Priority Levels".
First off, this is interesting because it means that only 4 of the available 8 priority bits are actually used! The STM32 HAL library call HAL_NVIC_SetPriority()
has a header that looks as follows, and seems to indicate you have 8 bits to set (PreemptPriority
from 0 to 15 and SubPriority
from 0 to 15), but really you don't--you only have 4 bits to set.
/**
* @brief Sets the priority of an interrupt.
* @param IRQn: External interrupt number.
* This parameter can be an enumerator of IRQn_Type enumeration
* (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f2xxxx.h))
* @param PreemptPriority: The preemption priority for the IRQn channel.
* This parameter can be a value between 0 and 15
* A lower priority value indicates a higher priority
* @param SubPriority: the subpriority level for the IRQ channel.
* This parameter can be a value between 0 and 15
* A lower priority value indicates a higher priority.
* @retval None
*/
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
{
So, you only have 4 bits to set, but it turns out you can decide how many are PreemptPriority
bits and how many are SubPriority
bits, as follows:
HAL_NVIC_SetPriorityGrouping()
from UM1940, 9.2.4, p124/1371.
FreeRTOS says in their documentation:
If you are using an STM32 with the STM32 driver library then ensure all the priority bits are assigned to be preempt priority bits by calling NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ); before the RTOS is started.
So, one of the very first things you should do in your code (at least before starting the FreeRTOS scheduler via osKernelStart()
or vTaskStartScheduler()
is:
/* Set Interrupt Group Priority */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
This configures all 4 of your priority bits to be PreemptPriority
bits and none to be SubPriority
bits. This means any calls to the HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
function will now always use 0 as the right-most parameter.
You should then essentially call (note: this is called via HAL_InitTick()
:
/*Configure the SysTick IRQ priority */
HAL_NVIC_SetPriority(SysTick_IRQn, 15 ,0U);
where 15
is the SysTick tick priority. Since we have all 4 bits available, this gives us a priority range of 0 to 15, with 15 being the lowest interrupt priority and 0 being the highest priority.
Well, why are we setting SysTick to the lowest priority? Answer: because this is a good practice for the FreeRTOS scheduler, which the SysTick interrupt calls. As a matter of fact, giving it too high a priority will break FreeRTOS, according to their own documentation. Let's try to figure that out.
We know that our interrupt options now are 0 to 15 for the PreemptPriority
setting, but it's even narrower than that: we can only set our PreemptPriority for the SysTick interrupt to be 10 to 15 (I think--I need some help here). Why 10 to 15? Well, the FreeRTOS documentation (albeit this is super confusing) even expressly states:
FreeRTOS functions that end in "FromISR" are interrupt safe, but even these functions cannot be called from interrupts that have a logical priority above [ie: numerically lower] the priority defined by configMAX_SYSCALL_INTERRUPT_PRIORITY (configMAX_SYSCALL_INTERRUPT_PRIORITY is defined in the FreeRTOSConfig.h header file). Therefore, any interrupt service routine that uses an RTOS API function must have its priority manually set to a value that is numerically equal to or greater than the configMAX_SYSCALL_INTERRUPT_PRIORITY setting. This ensures the interrupt's logical priority is equal to or less than the configMAX_SYSCALL_INTERRUPT_PRIORITY setting.
Now, refer back to FreeRTOSConfig.h at the top of this page. We know __NVIC_PRIO_BITS
is 4 and we see that:
configMAX_SYSCALL_INTERRUPT_PRIORITY
is (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
, which means it is 5 << (8 - 4) = decimal 80 = 0b01010000
.
The FreeRTOS documentation offers some valuable insight into why the left-shift (see the section titled "Cortex-M Internal Priority Representation"), but I can't discern beyond that.
So now making some educated guesses and knowing our priority options are 0 to 15 with 0 being highest and 15 being lowest, and knowing that configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY is 5, we maybe are allowed to use 15 - 5 = 10 as highest priority and 15 as lowest, or maybe 5 to 15 is ok but 0 to 4 is off-limits? I don't know...I'm confused...