title: What are valid values of `HAL_NVIC_SetPriority()` when using STM32 and FreeRTOS?
body: <p>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 <code>HAL_NVIC_SetPriority()</code> on the <code>SysTick_IRQn</code> (which is the ISR calling the FreeRTOS scheduler every 1 ms). </p>
  
  <h1>TLDR;</h1>
  
  <p>Part of me thinks that anything between <code>HAL_NVIC_SetPriority(SysTick_IRQn, 15 ,0U)</code> (lowest priority possible) and <code>HAL_NVIC_SetPriority(SysTick_IRQn, 10 ,0U)</code> (a bit higher) are allowed, and part of me thinks that anything between <code>HAL_NVIC_SetPriority(SysTick_IRQn, 15 ,0U)</code> (lowest priority possible) and <code>HAL_NVIC_SetPriority(SysTick_IRQn, 5 ,0U)</code> (quite a bit higher) is allowed. This is assuming that <code>configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY</code> 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 <em>lower</em> priority, and the documentation is pretty challenging to understand. </p>
  
  <hr>
  
  <h1>Details:</h1>
  
  <p>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 <em>I</em> am teaching <em>you</em>, even though I'm seeking the answer to my above question, as well as  <strong>correction</strong>, <strong>confirmation</strong>, or <strong>additional insight</strong> as you see fit.</p>
  
  <p>Though this probably applies to many of the STM32 microcontrollers or families, let's discuss it in terms of the <a href="http://www.st.com/en/microcontrollers/stm32f207zg.html" rel="noreferrer">STM32F207ZG</a> in particular. </p>
  
  <p>Note: <a href="http://www.st.com/en/embedded-software/stm32cubef2.html#getsoftware-scroll" rel="noreferrer">STM32CubeF2 download is here</a>.</p>
  
  <h2>My understanding:</h2>
  
  <p>If you look at a standard <strong>FreeRTOSConfig.h</strong> file (ex: STM32Cube_FW_F2_V1.7.0/Projects/STM322xG_EVAL/Applications/FreeRTOS/FreeRTOS_ThreadCreation/Inc/FreeRTOSConfig.h) you'll see the following:</p>
  
  <pre><code>/* 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 &lt;&lt; (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 &lt;&lt; (8 - configPRIO_BITS) )
  </code></pre>
  
  <p><code>__NVIC_PRIO_BITS</code> is defined in <em>STM32Cube_FW_F2_V1.7.0/Drivers/CMSIS/Device/ST/STM32F2xx/Include/stm32f217xx.h</em> as <code>4U</code> since "STM32F2XX uses 4 Bits for the Priority Levels". </p>
  
  <p>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 <code>HAL_NVIC_SetPriority()</code> has a header that looks as follows, and seems to indicate you have 8 bits to set (<code>PreemptPriority</code> from 0 to 15 and <code>SubPriority</code> from 0 to 15), but really you don't--you only have 4 bits to set.</p>
  
  <pre><code>/**
    * @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)
  { 
  </code></pre>
  
  <p>So, you only have 4 bits to set, but it turns out you can decide how many are <code>PreemptPriority</code> bits and how many are <code>SubPriority</code> bits, as follows:</p>
  
  <p><code>HAL_NVIC_SetPriorityGrouping()</code> from <a href="http://www.st.com/content/ccc/resource/technical/document/user_manual/56/32/53/cb/69/86/49/0e/DM00223149.pdf/files/DM00223149.pdf/jcr:content/translations/en.DM00223149.pdf" rel="noreferrer">UM1940</a>, 9.2.4, p124/1371.
  <a href="https://i.stack.imgur.com/5szUf.png" rel="noreferrer"><img src="https://i.stack.imgur.com/5szUf.png" alt="enter image description here"></a></p>
  
  <p><a href="https://www.freertos.org/RTOS-Cortex-M3-M4.html" rel="noreferrer">FreeRTOS says in their documentation</a>:</p>
  
  <blockquote>
    <p>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.</p>
  </blockquote>
  
  <p>So, one of the very first things you should do in your code (at least before starting the FreeRTOS scheduler via <code>osKernelStart()</code> or <code>vTaskStartScheduler()</code> is:</p>
  
  <pre><code>/* Set Interrupt Group Priority */
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
  </code></pre>
  
  <p>This configures all 4 of your priority bits to be <code>PreemptPriority</code> bits and none to be <code>SubPriority</code> bits. This means any calls to the <code>HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)</code> function will now <em>always use 0 as the right-most parameter.</em></p>
  
  <p>You should then essentially call (note: this is called via <code>HAL_InitTick()</code>:</p>
  
  <pre><code>/*Configure the SysTick IRQ priority */
  HAL_NVIC_SetPriority(SysTick_IRQn, 15 ,0U);
  </code></pre>
  
  <p>where <code>15</code> 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 <em>lowest</em> interrupt priority and 0 being the <em>highest</em> priority. </p>
  
  <p>Well, why are we setting SysTick to the <em>lowest</em> 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.</p>
  
  <p>We know that our interrupt options now are 0 to 15 for the <code>PreemptPriority</code> setting, but it's even narrower than that: we can only set our PreemptPriority for the SysTick interrupt to be <strong>10 to 15</strong> (I think--I need some help here). Why 10 to 15? Well, the <a href="https://www.freertos.org/RTOS-Cortex-M3-M4.html" rel="noreferrer">FreeRTOS documentation</a> (albeit this is super confusing) even expressly states:</p>
  
  <blockquote>
    <p>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.</p>
  </blockquote>
  
  <p>Now, refer back to <strong>FreeRTOSConfig.h</strong> at the top of this page. We know <code>__NVIC_PRIO_BITS</code> is 4 and we see that:</p>
  
  <p><code>configMAX_SYSCALL_INTERRUPT_PRIORITY</code> is <code>(configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY &lt;&lt; (8 - configPRIO_BITS))</code>, which means it is <code>5 &lt;&lt; (8 - 4) = decimal 80 = 0b01010000</code>. </p>
  
  <p>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. </p>
  
  <p>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...</p>