vTaskNotifyGiveFromISR does not working anymore after some seconds

Hi,
I have troubles to call a task from a timer interrupt.

First of all, I use a STM32F103C8T6 uE and CMSISv2 API with FreeRTOS 10.0.1
I use the CubeMX to generated the Hardware Layer Code in C.

I have set the Timer3 to create an overflow interrupt after ca. 100us.
I tested it without FreeRTOS and is working.

Now I enabled the FreeRTOS and the call of the task from the interrupt does not work.

CubeMX enables the timer interupt with the following Code

    HAL_NVIC_SetPriority(TIM3_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);

Here I also tried the Macro configMAX_SYSCALL_INTERRUPT_PRIORITY as priority but the behaviour was the same.

After the overflow of the timer counter the controller goes in this function,

void TIM3_IRQHandler(void)
{
  /* USER CODE BEGIN TIM3_IRQn 0 */

 /* USER CODE END TIM3_IRQn 0 */
 HAL_TIM_IRQHandler(&htim3);
 /* USER CODE BEGIN TIM3_IRQn 1 */

 /* USER CODE END TIM3_IRQn 1 */
} 

and enters in the function HAL_TIM_IRQHandler(&htim3);

this function calls void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

and I wrote my own function like that,

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
 if(htim->Instance==TIM3)
 {
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  vTaskNotifyGiveFromISR(ComputationINTHandle,&xHigherPriorityTaskWoken);
  portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
 }

and I wrote also my code in the ComputationINTHandle like this,

void ComputationINTfunc(void *argument)
{
  /* USER CODE BEGIN ComputationINTfunc */
HAL_TIM_Base_Start_IT(&htim3);
 /* Infinite loop */
 for(;;)
{
  /* Will be woken up by timer interrupt*/
  ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
  HAL_GPIO_WritePin(TestPinC13_GPIO_Port, TestPinC13_Pin, GPIO_PIN_SET);
  for(int i=0;i<10;i++) asm("nop");
  HAL_GPIO_WritePin(TestPinC13_GPIO_Port, TestPinC13_Pin, GPIO_PIN_RESET);
}
  /* USER CODE END ComputationINTfunc */
}

Here I enable the timer interupt in the first execution and in loop I toggle a pin to debug it with my oscilloscope.

At this time I have the troubles, for a few seconds the program is running good but after some seconds, the software stops.

I use the debug to see what is going wrong and I have seen that the Code is stucking in the routine portASSERT_IF_INTERRUPT_PRIORITY_INVALID();

I analyzed step by step with the debugger and I see the following,
in the first steps, in the function vPortValidateInterruptPriority the variableulCurrentInterrupt was equal to 45, but in code from STM32 has the number 29. (I did not understand if this numbers should be equal)
the variable ucCurrentPriority was at the begging equal to 80 and in the following lines is coming the check configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); and in my program the ucMaxSysCallPriority is equal to 80. So in this moment everything is working very good.

But after some seconds, the variable ucMaxSysCallPriority changes to 84 or 180 and in this moment the code configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); is becoming false and boom, Software is stacking in the configASSERT.

If I commented out the configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); then everything is working perfect, or seems to be perfect.

Can any one explain me why the ucMaxSysCallPriority changes and how can I fix this problem?

Thank you in advance.

My guess is that something in your program is overwriting the variable, maybe as a wild write through some bad pointer.

Could it be that this is a different interrupt to the one that was executing before? Please post the code that implements the vector table for this chip so we can see which peripheral is generating interrupt number 45.

/*!< Interrupt Number Definition */
typedef enum
{  
   /******  Cortex-M3 Processor Exceptions Numbers   ***************************************************/
 NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable Interrupt                             */
 HardFault_IRQn              = -13,    /*!< 3 Cortex-M3 Hard Fault Interrupt                     */
 MemoryManagement_IRQn       = -12,    /*!< 4 Cortex-M3 Memory Management Interrupt              */
 BusFault_IRQn               = -11,    /*!< 5 Cortex-M3 Bus Fault Interrupt                      */
 UsageFault_IRQn             = -10,    /*!< 6 Cortex-M3 Usage Fault Interrupt                    */
 SVCall_IRQn                 = -5,     /*!< 11 Cortex-M3 SV Call Interrupt                       */
 DebugMonitor_IRQn           = -4,     /*!< 12 Cortex-M3 Debug Monitor Interrupt                 */
 PendSV_IRQn                 = -2,     /*!< 14 Cortex-M3 Pend SV Interrupt                       */
 SysTick_IRQn                = -1,     /*!< 15 Cortex-M3 System Tick Interrupt                   */

/******  STM32 specific Interrupt Numbers *********************************************************/
 WWDG_IRQn                   = 0,      /*!< Window WatchDog Interrupt                            */
 PVD_IRQn                    = 1,      /*!< PVD through EXTI Line detection Interrupt            */
 TAMPER_IRQn                 = 2,      /*!< Tamper Interrupt                                     */
 RTC_IRQn                    = 3,      /*!< RTC global Interrupt                                 */
 FLASH_IRQn                  = 4,      /*!< FLASH global Interrupt                               */
 RCC_IRQn                    = 5,      /*!< RCC global Interrupt                                 */
 EXTI0_IRQn                  = 6,      /*!< EXTI Line0 Interrupt                                 */
 EXTI1_IRQn                  = 7,      /*!< EXTI Line1 Interrupt                                 */
 EXTI2_IRQn                  = 8,      /*!< EXTI Line2 Interrupt                                 */
 EXTI3_IRQn                  = 9,      /*!< EXTI Line3 Interrupt                                 */
 EXTI4_IRQn                  = 10,     /*!< EXTI Line4 Interrupt                                 */
 DMA1_Channel1_IRQn          = 11,     /*!< DMA1 Channel 1 global Interrupt                      */
 DMA1_Channel2_IRQn          = 12,     /*!< DMA1 Channel 2 global Interrupt                      */
 DMA1_Channel3_IRQn          = 13,     /*!< DMA1 Channel 3 global Interrupt                      */
 DMA1_Channel4_IRQn          = 14,     /*!< DMA1 Channel 4 global Interrupt                      */
 DMA1_Channel5_IRQn          = 15,     /*!< DMA1 Channel 5 global Interrupt                      */
 DMA1_Channel6_IRQn          = 16,     /*!< DMA1 Channel 6 global Interrupt                      */
 DMA1_Channel7_IRQn          = 17,     /*!< DMA1 Channel 7 global Interrupt                      */
 ADC1_2_IRQn                 = 18,     /*!< ADC1 and ADC2 global Interrupt                       */
 USB_HP_CAN1_TX_IRQn         = 19,     /*!< USB Device High Priority or CAN1 TX Interrupts       */
 USB_LP_CAN1_RX0_IRQn        = 20,     /*!< USB Device Low Priority or CAN1 RX0 Interrupts       */
CAN1_RX1_IRQn               = 21,     /*!< CAN1 RX1 Interrupt                                   */
CAN1_SCE_IRQn               = 22,     /*!< CAN1 SCE Interrupt                                   */
EXTI9_5_IRQn                = 23,     /*!< External Line[9:5] Interrupts                        */
TIM1_BRK_IRQn               = 24,     /*!< TIM1 Break Interrupt                                 */
TIM1_UP_IRQn                = 25,     /*!< TIM1 Update Interrupt                                */
TIM1_TRG_COM_IRQn           = 26,     /*!< TIM1 Trigger and Commutation Interrupt               */
TIM1_CC_IRQn                = 27,     /*!< TIM1 Capture Compare Interrupt                       */
TIM2_IRQn                   = 28,     /*!< TIM2 global Interrupt                                */
TIM3_IRQn                   = 29,     /*!< TIM3 global Interrupt                                */
TIM4_IRQn                   = 30,     /*!< TIM4 global Interrupt                                */
I2C1_EV_IRQn                = 31,     /*!< I2C1 Event Interrupt                                 */
I2C1_ER_IRQn                = 32,     /*!< I2C1 Error Interrupt                                 */
I2C2_EV_IRQn                = 33,     /*!< I2C2 Event Interrupt                                 */
I2C2_ER_IRQn                = 34,     /*!< I2C2 Error Interrupt                                 */
SPI1_IRQn                   = 35,     /*!< SPI1 global Interrupt                                */
SPI2_IRQn                   = 36,     /*!< SPI2 global Interrupt                                */
USART1_IRQn                 = 37,     /*!< USART1 global Interrupt                              */
USART2_IRQn                 = 38,     /*!< USART2 global Interrupt                              */
USART3_IRQn                 = 39,     /*!< USART3 global Interrupt                              */
EXTI15_10_IRQn              = 40,     /*!< External Line[15:10] Interrupts                      */
RTC_Alarm_IRQn              = 41,     /*!< RTC Alarm through EXTI Line Interrupt                */
USBWakeUp_IRQn              = 42,     /*!< USB Device WakeUp from suspend through EXTI Line    Interrupt */
} IRQn_Type;`

Sorry - I misread your original post and thought it was ulCurrentInterrupt that was changing, hence my suggestion that perhaps it was a different interrupt that was executing. Re-reading now I see it is ucMaxSysCallPriority that is changing, and that calculated once and then should not change, so agree with richard-damon that maybe this is just being corrupted.

100us is very fast to be switching to a task. Does the operation of the task always complete before the next interrupt occurs?

I want to implement a BLDC Motor driver, so I calculated that for 100.000 RPM and a comutaion of 6 steps I need ca. 100uS Task. In Osciloscope I can see that the task is needed 4-5 uS because in thins example I only change a pin state.
If is it a bad pointer, then it should be a bug in FreeRTOS, CMIS or in HAL Code. I used STM32cubeIDE to generate a complet new Project and my code is only the call of the task from the ISR and the functionality of the Task, I dont have any other user code.

Thw whole code is under git hub https://github.com/ntosis/Nyx/tree/master/SW/STM32CubeIDE_prj

I don’t have time to go through and read your code, but I have done similar things. My first comment is that this pin toggling every 100us is simple enough that the logic for it can/should be in the ISR itself, and not deferred to a task except occasionally for the task to update major state as part of the control law.

I will also add that I try to avoid using the CubeMX code as much as possible, as I find it very hard to understand and to verify.

I want to make a BLDC Software, so I need a Task every 100uS to drive the commutation. So it is not possible to pack my code in the ISR. The logic of toggle a pin is only made to see if the FreeRTOS is working properly with a minimal code/example and in the next days I will start with the productive code.

I have built BLDC code using similar processors and it is quite possible to perform all the high speed commutation code in the ISR, and defer the somewhat slower motion control to a task.

If you look at the ST Micro BLDC demonstration code (and can understand what it does somewhat) you will find that they do ALL their motor control in the ISRs, even the user interface, as the program after doing its initialization ends up in a while(1) {} loop doing nothing.

Ok, I understand but this is a workaround or an other implementation, but the Problem in FreeRTOS is still open and without a reasonable explanation.

Normal recommendation for BLDC is to perform the operation in an interrupt that has a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY to ensure critical sections in the application code does not introduce any timing jitter - it does mean that you cannot call any FreeRTOS API functions from inside the ISR though.

I searched in the map file and I found the address of the variable ucMaxSysCallPriority to be the 0x20000d14, I saw that this address is changing during the software execution.
I added a watchpoint and the debuger stops in two Functions, the
Screenshot from 2020-03-16 19-57-58

and in the
Screenshot from 2020-03-16 19-59-00

In the first picture, in TIM3_IRQHandler the registers are r7 = 0x20000d28 sp = 0x20004fd8
and in the function PendSV_Handler r0 = 0x20000d10 PSP = 0x20000d10

this is the snippet from the map file
Screenshot from 2020-03-16 20-06-51

The problem is that the debugger stops after the memory address has been changed and I cannot find which assembly command writes this address.

Hooray, I changed the Heap implementation from Heap_4 to Heap_3 and now is working without a problem! :slight_smile: Seems to be a bug in Heap or wrong heap option for my controller.

Could just be a stack overflow? Did you go through all the items mentioned here https://www.freertos.org/FAQHelp.html ref checking for stack overflows, configASSERT(), malloc failed hooks, etc.?

No, I did not do that. I used 128 Byte Stack for the Task, how it will be possible to have a stack overflow when I togle only a single pin?

Hi Nikolaos, I had exactly the same problem with FreeRTOS Kernel V10.3.1 with FatFS for a uSD.

Doing a Watchpoint on ucMaxSysCallPriority and monitoring the memory space showed that the SDIO_SendCommand() corrupted ucMaxSysCallPriority. I counted the stack to be 12 levels deep at this point.

Tried to bump up the heap 50% from 0x4000 to 0x6000 but that didnt help.

Like in your case using heap_3 memory allocation implementations solved my problems. Do you know at this point whether this is a freeRTOS bug ?

Thanks a lot for debugging this and sharing your resolution to the problem.

Why is SDIO_SendCommand corrupting ucMaxSysCallPriority ? Possibly a stack overflow. If that is the case, you should try increasing the stack size of the task and not the heap.

Thanks.