GPIO Interrupt on STM32F4 causes FreeRTOS timer go haywire

aarbee wrote on Tuesday, March 27, 2018:

Friends,
I am working on STM32F4 and using FreeRTOS.
When I enable the EXTI1 interrupt and there are interrupts coming on this pin, I see that the FreeRTOS timers start expiring sooner than the scheduled time. When there are no interrupts coming every timer is on time. It is when interrupts come that is when I see freeRTOS going into this state. I have already confirmed that I have:

configASSERT is define,
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ); is defined
and everything said here: https://www.freertos.org/RTOS-Cortex-M3-M4.html is taken care of.

What could it be?

rtel wrote on Wednesday, March 28, 2018:

When you say timers, do you mean software timers (as in FreeRTOS’s
software timer feature), or do you mean just block times are expiring
too soon in general.

It is hard to see what correlation there could be. I think if all
software timers are expiring early, or all block times are expiring
early, then it must be that the tick interrupt is executing more often
than it should - or at least the increment tick function is executing
more often than it should. That could be because the clock driving the
hardware sped up, or for some other software reason.

aarbee wrote on Wednesday, March 28, 2018:

Thank you for responding.
Yes all software timers expires sooner. I was guessing the same i.e., the tick timer is expiring too soon when interrupts kick in. What are “block times”?

Timers are defined as anybody would:

TimerHandle_t pulse_counter_timer =
xTimerCreate((const char ) “pulse_counter_timer”,
5000,
pdTRUE,
(void
) 0,
pulse_harvest_timer_callback);

if (pulse_counter_timer != NULL) {
xTimerStart(pulse_counter_timer, 0); // Start the Timer
} else {
printf(“Could not start Pulse Counter Timer\n”);
return;
}

rtel wrote on Wednesday, March 28, 2018:

By block time I mean any timeout specified in a call such as
vTaskDelay() or xQueueReceive().

Do block times also expire too soon? For example, if you call
vTaskDelay( pdMS_TO_TICKS( 500 ) ) does it expire every 500ms, or are
only software timers effected.

Software timers are stored with a pre-calculated expire time - there is
no single place in a data structure that could get corrupted that would
cause them all to expire too soon - unless the tick count value itself
was somehow corrupt or incrementing too quickly.

How are you ascertaining that the timers are expiring too early? Are
you 100% sure that is the symptom you are observing? Have you otherwise
validated that the tick interrupt is executing at the frequency you
expect both before and after this issue occurs?

aarbee wrote on Wednesday, March 28, 2018:

I am 100% sure that the timers are expiring. I tested it via:
a) Printing the timestamp in task that is woken up by the Timer (by releasing the semaphore).
b) Using a stop clock (Visual observation).
c) Toggling a GPIO low and high very time the timer expires and observing the GPIO Toggle on the oscilloscope. The time delta betweent he GPIO pulses gives very accurate representation of what is happening.

Once the interrupts stop, timers go back to normal.

I have not observed vTaskDelay though. That is what I will do next and post here.

aarbee wrote on Wednesday, March 28, 2018:

Yes, it impacts vTaskDelay(…) as well.

rtel wrote on Wednesday, March 28, 2018:

Well without knowing more or being able to experiment myself it sounds
like something is messing with the timing that the tick is occurring -
did you try toggling an LED or something from the tick hook so you can
see the frequency it is executing at?

aarbee wrote on Wednesday, March 28, 2018:

Thanks a lot for taking time to respond. I will try that and post the results as I debug it.

aarbee wrote on Friday, March 30, 2018:

This is what I have discovered until now.

Task A during it’s execution creates another task B and when Task A is done with what was needed from Task B, it deletes the task B. To delete the task it updates a global variable (lets call it terminate_now). Task B is looping on terminate_now and as soon as Task A sets the variable terminate_now to 1, Task B calls vTaskDelete(NULL); to kill itself.

**As soon that happens, the timers start going haywire.
**
If Task A kills Task B by directly calling vTaskDelete(Handle_to_task_B), everything is fine!

Why?

rtel wrote on Friday, March 30, 2018:

Trying to replicate now.

rtel wrote on Friday, March 30, 2018:

I’ve not been able to replicate this using the code below, so would be grateful if you can send me your example so I can see exactly what your code is doing in the hope that I can set up a similar case ehre. Please take out everything that is not necessary to show the issue - hopefully you can strip it back ot just the Task A and Task B mentioned in your previous post. Also please let me know the compiler, compiler version, FreeRTOS version, and any other information pertinent to replicating your system. You can zip the project up and send it to r dot barry at FreeRTOS dot org.

static void prvQueueReceiveTask( void *pvParameters )
{
    /* Prevent the compiler warning about the unused parameter. */
    ( void ) pvParameters;

    for( ;; )
    {
        /* Create the other task, checking it is created successfully. */
        configASSERT( xTaskCreate( prvQueueSendTask,
                      "TX",
                      configMINIMAL_STACK_SIZE,
                      NULL, mainQUEUE_SEND_TASK_PRIORITY,
                      NULL ) );

        /* Let it run a bit. */
        vTaskDelay( 4 );
        /* Tell it to delete itself. */
        xDeleteNow = pdTRUE;
        /* Let it delete itself. */
        vTaskDelay( 4 );

        configASSERT( xDeleteNow == pdFALSE );
    }
}
/*-----------------------------------------------------------*/

static void prvQueueSendTask( void *pvParameters )
{
    ( void ) pvParameters;

    for( ;; )
    {
        if( xDeleteNow != pdFALSE )
        {
            xDeleteNow = pdFALSE;
            vTaskDelete( NULL );
        }
    }
}
/*-----------------------------------------------------------*/

static void prvQueueSendTimerCallback( TimerHandle_t xTimerHandle )
{
    /* Print from the timer callback so the frequency can be seen. */
    ulTimers = 0;
    printf( "%d, ", (int)xPortGetFreeHeapSize() );
}
/*-----------------------------------------------------------*/

rtel wrote on Friday, March 30, 2018:

(ignore the names of the tasks in the code I posted - I just adapted an existing ‘hello world’ style project I had)

aarbee wrote on Friday, March 30, 2018:

Thanks I will post my code. Please note that the timers get messed up only when there are continuous interrupts occurring on the GPIOs. Otherwise everything appears to be normal.
As soon as interrupts stop, timers get back to normal. Also if interrupts are not enabled on the GPIO, there are no issues. Even when GPIO interrupt is enabled, there is no issue as long as interrupt is not triggered.

I have Anemometer connected to the GPIO so I can easily spin it to generated a series of pulses causing interrupts on the GPIO to reproduce the issue.

aarbee wrote on Monday, April 02, 2018:

It definitely appears to be linked to deleting a task from another task. No matter how I delete the task vTaskDelete(NULL) or vTaskDelete(task_handle), it ends up messing the timers when interrupts starts coming right after deletion. If I don’t kill the task (let it running) no issues or if I Delete the task but don’t have interrupts coming, no issues either.

rtel wrote on Monday, April 02, 2018:

Are you any closer to being able to send me a cut-down project?

aarbee wrote on Sunday, April 08, 2018:

Richard,
I removed a lot of code to do some more debugging and here is where I am. More confused.
Here is the code

    int init_synctask_freeRTOS(void)
      {
          synctask_semaphore = xSemaphoreCreateBinary();

          if (synctask_semaphore == NULL) {
              printf("Synctask Semaphore couldn't be created.\n");
              return pdFALSE;
          }

          xSemaphoreTake(sync_freq_mutex, portMAX_DELAY);
          xSyncSoftwareTimer = xTimerCreate((const char *) "SyncTimer",
                                            (sync_frequency * 60000),
                                            pdTRUE,   
                                            (void*) 0,
                                            vSyncTimerCallback);
          xSemaphoreGive(sync_freq_mutex);

         if (xSyncSoftwareTimer == NULL) {
             return pdFALSE;
         }

         int ret = xTaskCreate(synctask_thread,
                               "synctask_thread",
                               configMINIMAL_STACK_SIZE,
                               NULL,
                               &synctask_handle);

         if (ret != pdTRUE) {
             printf("Cannot create synctask_thread\n");
             return pdFALSE;
         }

         return ret;
     }

     void vSyncTimerCallback(TimerHandle_t xTimer)
     {
         xSemaphoreGive(synctask_semaphore);
     }

     void synctask_thread(void *thread_id)
     {
         synctask_active = true;

         if (othertask_isActive()) {
              PP_PRINTF("setting var to kill task..\n");
              // This func call should cause the othertask
              // to kill itself..
             othertask_setvar(1);
         }
         
         xTimerStart(xSyncSoftwareTimer, 0);
         while (1) {
                 xSemaphoreTake(synctask_semaphore, portMAX_DELAY);
                // Do some stuff here
         }
     }

If I call the following two together, All timers start expiring before the scheduled time as soon as the interrupts start showing up at the GPIO. Faster the interrupts, sooner the timers would expire…

       othertask_setvar(1);   <-- Kill the task
       xSemaphoreTake(synctask_semaphore, portMAX_DELAY); <- wait on semaphore

If I call only one of these two everything is fine…

rtel wrote on Sunday, April 08, 2018:

Is init_synctask_freeRTOS() called before the scheduler is started? If
so, then don’t try blocking on a semaphore because there is nothing else
that can run and you couldn’t block anyway (as the scheduler is not
running and no tasks are running yet). I’m not sure why you would take
then give the semaphore around the call to xTimerCreate() anyway.

Can you please also post the code for the “other task” (the one that
kills itself), including the code that creates the task so I can see its
priority, etc. - and also show the code for the ISR. Thanks.

aarbee wrote on Sunday, April 08, 2018:

xSemaphoreTake(sync_freq_mutex, portMAX_DELAY);
This mutex is taken because the variable ‘sync_frequency’ may be in use in some other task.

init_synctask_freeRTOS is not called before the scheduler is started. The main function creates a Task called inittask as:

int main(void) {
  uint8_t ret;

  HAL_Init();
  SEGGER_RTT_Init();
  ret = xTaskCreate(init_task,                                                         
                 "inittask", 
                  4 * configMINIMAL_STACK_SIZE, 
                  NULL, 1, NULL);
  if (ret == pdTRUE) {
    printf("Starting scheduler!\n");
    vTaskStartScheduler();  // should never return
  } else {
    printf("System Error!\n");
  }
  for (;;) {}
}

init_task would then call init_synctask_freeRTOS to create the sync_task_thread and init_other_task to create the other task. Other Task creation and execution looks like:

int othertask_isActive(void)
{
    return other_task_running;
}

void othertask_setvar(bool var)
{
    if (var)  {
        waitvar = true;
    } else {
        waitvar = false;
    }
}
void other_task(void *p)
{
   while (!waitvar) {
       vTaskDelay(5);
   }
    printf("Other task Killing itself\n");
    vTaskDelete(0);
   }

int init_other_task(void)
{
    other_task_running = true;
    if (xTaskCreate(other_task,
                    "other_task",
                    configMINIMAL_STACK_SIZE,
                    NULL,
                    1,
                    &other_task_handle) != pdTRUE) {
        PP_ERROR("Cannot create other task\n");
        return pdFALSE;
    }
    return pdTRUE;
}

aarbee wrote on Monday, April 09, 2018:

Interrupt handler is:

void interrupt_handler(void)
{
   if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_1) != RESET) {
        __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_1);
        if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_1) == GPIO_PIN_SET) {
           rising_edges++;
        } else {
           falling_edges++;
        }
    }
}

Interrupt is configured as:

HAL_NVIC_DisableIRQ(EXTI1_IRQn);

GPIO_InitTypeDef gpio;
gpio.Pin  = GPIO_PIN_1;
gpio.Mode = GPIO_MODE_IT_RISING_FALLING;
gpio.Pull = GPIO_PULLUP;
gpio.Speed = GPIO_SPEED_FAST;
HAL_GPIO_Init(GPIOD, &gpio);

HAL_NVIC_SetPriority(EXTI1_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY,1);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);

aarbee wrote on Monday, April 09, 2018:

Plot thickens.
Only this other task, if deleted causes trouble. I delete other tasks and no issues. Not only this, if I create another task while creating this ‘other task’, no issues. This “other_task” is the first task created by init_task when the device boots.

synctask_	R	1	1769  11
inittask 	R	1	1564  1
IDLE     	R	0	485	  2
Tmr Svc  	B	2	972	  3
other_tas  	B	1	406   4<--- Killing this task from synctask, trouble!
change_sy	B	3	981	  10
button   	B	1	467   9

Now I create another task named dummytask along with other_task and there is no issues killing this task from synctask… Interrupts don’t cause any issues anymore!

inittask 	R	1	1714  1
synctask_	R	1	1758  12
datalogge	R	1	460   13
IDLE     	R	0	485	  2
dummytask	B	1	477	  4 <--Magically fixes problem while doing nothing!
Tmr Svc  	B	2	972   3
other_tas  	B	1	405	  5 <---Killing this ok since I have dummytask!
change_sy	B	3	981	  11
button   	B	1	467   10