Timer Task starts with no fixed timing

Hello,
I implemented FreeRTOS on KEAZ1284.
In my application I create 1 task and 2 timers. Of course also the Idle task is present.
I checked by using two debug pins:

  • PA2: toggle when the Idle task is executed,
  • PA3: toggle when the prvTimerTask is executed.

What I see is:

  • Idle Task is always running, starting a few ms from the POR.
    -prvTimerTask sometime starts with a long delay (more than 50 ms).

below the settings:
#define configUSE_PREEMPTION 1
#define configUSE_TIME_SLICING 1
#define configUSE_IDLE_HOOK 1
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ DEFAULT_SYSTEM_CLOCK
#define configTICK_RATE_HZ 1000u
#define configMAX_PRIORITIES 6
#define configMINIMAL_STACK_SIZE 0x80
#define configTOTAL_HEAP_SIZE 0x2000
#define configMAX_TASK_NAME_LEN 6
/* to have 32-bit TickType_t */
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
#define configUSE_TASK_NOTIFICATIONS 1
#define configUSE_MUTEXES 1

Do you have any suggestion?

Thank you for your help.

Best regards,
Alessandro Serafini

I guess configUSE_TIMERS, configTIMER_TASK_PRIORITY etc. are also set somewhere.
See FreeRTOS - Configuring a real time embedded application to enable the use of RTOS software timers for details. In short the timer task should have or usually has the highest prio in your application. Seems that’s not the case ?

I checked your suggestion and these are the setting for the timer already implemented:

#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY 5
#define configTIMER_QUEUE_LENGTH 5
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE<<1)

consider that
#define configMAX_PRIORITIES 6

The implemented tasks are created as follow:

/* create ADC task, priority is the second to last parameter */
(void)xTaskCreate(vADCTask, "ADC", configMINIMAL_STACK_SIZE, NULL_POINTER,
    2u, ADC_getTaskHandlePtr());

/* create a 2 ms auto-reload timer, callback function is the last parameter */
x2msTimer = xTimerCreate("T2ms", pdMS_TO_TICKS(2u), pdTRUE, NULL_POINTER,
    SWC_prv2msTimerCallback);
/* check the software timer was created */
if(x2msTimer != NULL_POINTER)
{
/* start the software timer, using a block time of 0, the scheduler has
 * not been started yet so any block time specified here would be
 * ignored anyway */
(void)xTimerStart(x2msTimer, 0u);
}

/* create a 10 ms auto-reload timer, callback function is the last parameter */
x10msTimer = xTimerCreate("T10ms", pdMS_TO_TICKS(10u), pdTRUE, NULL_POINTER,
    SWC_prv10msTimerCallback);
/* check the software timer was created */
if(x10msTimer != NULL_POINTER)
{
    /* start the software timer, using a block time of 0, the scheduler has
     * not been started yet so any block time specified here would be
     * ignored anyway */
    (void)xTimerStart(x10msTimer, 0u);
}

/* start the created tasks running */
vTaskStartScheduler();

so:
timer task → priority 5 >>> highest
ADC task → priority 2

What I don’t understand is: why sometimes the timer task starts later than normal?

Thank you for the help!

Where have you added the code to toggle these pins?

Here the toggles:

in prvTimerTask (module timers.c) see below:

static portTASK_FUNCTION( prvTimerTask, pvParameters )
{
TickType_t xNextExpireTime;
BaseType_t xListWasEmpty;

	/* Just to avoid compiler warnings. */
	( void ) pvParameters;

	#if( configUSE_DAEMON_TASK_STARTUP_HOOK == 1 )
	{
		extern void vApplicationDaemonTaskStartupHook( void );

		/* Allow the application writer to execute some code in the context of
		this task at the point the task starts executing.  This is useful if the
		application includes initialisation code that would benefit from
		executing after the scheduler has been started. */
		vApplicationDaemonTaskStartupHook();
	}
	#endif /* configUSE_DAEMON_TASK_STARTUP_HOOK */

	for( ;; )
	{
		/* Query the timers list to see if it contains any timers, and if so,
		obtain the time at which the next timer will expire. */

//EmPa >>>>>>>>>>>>>>TOGGLE A DEBUG LINE 
		static uint8_t x =0;
		if (x==0)
		{
			GPIO_SET_IN(PTA,PTA2);
			GPIO_ENABLE_PULLUP(PTA,PTA2);
			x=1;
		}else if(x==1)
		{
			GPIO_SET_OUT(PTA,PTA2);
			GPIO_SET_LOW(PTA,PTA2);
			x=0;
		}
//<<<<<<<<<<<<<<<<<TOGGLE A DEBUG LINE -END

		xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty );

		/* If a timer has expired, process it.  Otherwise, block this task
		until either a timer does expire, or a command is received. */
		prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );

		/* Empty the command queue. */
		prvProcessReceivedCommands();
	}
}

in prvIdleTask (module task.c) see below:

/*
 * -----------------------------------------------------------
 * The Idle task.
 * ----------------------------------------------------------
 *
 * The portTASK_FUNCTION() macro is used to allow port/compiler specific
 * language extensions.  The equivalent prototype for this function is:
 *
 * void prvIdleTask( void *pvParameters );
 *
 */
static portTASK_FUNCTION( prvIdleTask, pvParameters )
{
	/* Stop warnings. */
	( void ) pvParameters;

	/** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE
	SCHEDULER IS STARTED. **/

	/* In case a task that has a secure context deletes itself, in which case
	the idle task is responsible for deleting the task's secure context, if
	any. */
	portALLOCATE_SECURE_CONTEXT( configMINIMAL_SECURE_STACK_SIZE );

	for( ;; )
	{
		/* See if any tasks have deleted themselves - if so then the idle task
		is responsible for freeing the deleted task's TCB and stack. */
		prvCheckTasksWaitingTermination();

		#if ( configUSE_PREEMPTION == 0 )
		{

			/* If we are not using preemption we keep forcing a task switch to
			see if any other task has become available.  If we are using
			preemption we don't need to do this as any task becoming available
			will automatically get the processor anyway. */
			taskYIELD();
		}
		#endif /* configUSE_PREEMPTION */

		#if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )
		{
			/* When using preemption tasks of equal priority will be
			timesliced.  If a task that is sharing the idle priority is ready
			to run then the idle task should yield before the end of the
			timeslice.

			A critical region is not required here as we are just reading from
			the list, and an occasional incorrect value will not matter.  If
			the ready list at the idle priority contains more than one task
			then a task other than the idle task is ready to execute. */

//EmPa >>>>>>>>>>>>>>>>>>>>>>> TOGGLE A DEBUG LINE
 	 	 	 static uint8_t x =0;
			if (x==0)
			{
				GPIO_SET_IN(PTA,PTA3);
				GPIO_ENABLE_PULLUP(PTA,PTA3);
				x=1;
			}else if(x==1)
			{
				GPIO_SET_OUT(PTA,PTA3);
				GPIO_SET_LOW(PTA,PTA3);
				x=0;
			}
//<<<<<<<<<<<<<<<<<<<<<< TOGGLE A DEBUG LINE - END
			if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 )
			{
				taskYIELD();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */

		#if ( configUSE_IDLE_HOOK == 1 )
		{
			extern void vApplicationIdleHook( void );

			/* Call the user defined function from within the idle task.  This
			allows the application designer to add background functionality
			without the overhead of a separate task.
			NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,
			CALL A FUNCTION THAT MIGHT BLOCK. */
			vApplicationIdleHook();
		}
		#endif /* configUSE_IDLE_HOOK */

		/* This conditional compilation should use inequality to 0, not equality
		to 1.  This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when
		user defined low power mode	implementations require
		configUSE_TICKLESS_IDLE to be set to a value other than 1. */
		#if ( configUSE_TICKLESS_IDLE != 0 )
		{
		TickType_t xExpectedIdleTime;

			/* It is not desirable to suspend then resume the scheduler on
			each iteration of the idle task.  Therefore, a preliminary
			test of the expected idle time is performed without the
			scheduler suspended.  The result here is not necessarily
			valid. */
			xExpectedIdleTime = prvGetExpectedIdleTime();

			if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
			{
				vTaskSuspendAll();
				{
					/* Now the scheduler is suspended, the expected idle
					time can be sampled again, and this time its value can
					be used. */
					configASSERT( xNextTaskUnblockTime >= xTickCount );
					xExpectedIdleTime = prvGetExpectedIdleTime();

					/* Define the following macro to set xExpectedIdleTime to 0
					if the application does not want
					portSUPPRESS_TICKS_AND_SLEEP() to be called. */
					configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime );

					if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
					{
						traceLOW_POWER_IDLE_BEGIN();
						portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
						traceLOW_POWER_IDLE_END();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				( void ) xTaskResumeAll();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* configUSE_TICKLESS_IDLE */
	}
}
/*-----------------------------------------------------------*/

The above snippet seems odd. Should it not be the following -

/* Executed only once. */
GPIO_ENABLE_PULLUP(PTA,PTA3);
GPIO_SET_OUT(PTA,PTA3);

/* Toggle. */
static uint8_t x =0;
if( x == 0 )
{
    GPIO_SET_LOW(PTA,PTA3);
    x = 1;
}
else
{
    GPIO_SET_HIGH(PTA,PTA3);
    x = 0;
}

We fix the issue!

I resume the problem:
on a KEAZ1284 (Cortex M0 core) we observed that the timer task is starting with no fixed timing.

We fix it by writing the SYSTICK_CURRENT_VALUE register

*(portNVIC_SYSTICK_CURRENT_VALUE) = 1uL;

in void prvSetupTimerInterrupt( void ), after initializing the SYSTICK_LOAD register.

See the snippet below:

/*
 * Setup the systick timer to generate the tick interrupts at the required
 * frequency.
 */
void prvSetupTimerInterrupt( void )
{
    /* Calculate the constants required to configure the tick interrupt. */
#if( configUSE_TICKLESS_IDLE == 1 )
#if( configSYSTICK_USE_LOW_POWER_TIMER == 1 )
    ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
    ulStoppedTimerCompensation = 0;
    ulLPTimerCountsForOneTick = configSYSTICK_CLOCK_HZ / configLPTMR_RATE_HZ;
    xMaximumPossibleSuppressedTicks = portMAX_16_BIT_NUMBER / ulLPTimerCountsForOneTick;
    NVIC_EnableIRQ(TICKLESS_LPTMR_IRQn);
#else
    ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
    xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;
    ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
#endif/*configSYSTICK_USE_LOW_POWER_TIMER*/
#endif /* configUSE_TICKLESS_IDLE */

	/* Configure SysTick to interrupt at the requested rate. */
	*(portNVIC_SYSTICK_LOAD) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
	
	/* 2024.03.28.
	 *  Added initialization of SYSTICK_CURRENT_VALUE to be sure to start from know value 
	 */
	*(portNVIC_SYSTICK_CURRENT_VALUE) = 1uL;
	/* 2024.03.28 - End */
	
	*(portNVIC_SYSTICK_CTRL) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE;

}

Question:

why the original function void prvSetupTimerInterrupt( void ) in port.c module doesn’t clear the SYSTICK_CURRENT_VALUE ?

We use FreeRTOS V8.2.3.

The latest code does that - FreeRTOS-Kernel/portable/GCC/ARM_CM0/port.c at main · FreeRTOS/FreeRTOS-Kernel · GitHub.

Thank you for sharing your solution!