Andrei's hobby blog

"Timing is everything"


     This is a cooperative scheduler designed to be simple and lightweight. It provides cyclic tasks on bare metal embedded systems and also scales well. I used it many times from tiny microcontrollers with ridiculously small resources up to complex industrial automation equipment. Tasks are launched outside interrupts so the system remains responsive to events. The only part of the scheduler that runs in a cyclic interrupt is kept to a minimum.
     Tasks are stored into an array. Each element has a pointer to a function, a timer that decrements at each scheduler tick, and the reload value for timer. This value is also the tasks recurrence period.


typedef  struct {
	void (*task)(void);			
	unsigned int interval;		
	unsigned int *timer;        
}task_type;



Here is an example of three tasks. The three tasks have a recurrence of 10, 100 and 1000ms (assuming a scheduler tick of 1ms)


unsigned int tmr_task_10_ms = 0;
unsigned int tmr_task_100_ms = 0;
unsigned int tmr_task_1000_ms = 0;

void task_10_ms(void); 
void task_100_ms(void);
void task_1000_ms(void);

task_type task_list[] = {/*
FUNCTION NAME			    INTERVAL			TIMER					*/
&task_10_ms,				10,					&tmr_task_10_ms,			
&task_100_ms,				100,				&tmr_task_100_ms,
&task_1000_ms,				1000,				&tmr_task_1000_ms,
0,							0,					0						};


This is the main scheduler function. It checks if any of the tasks timer has expired. If yes then it launches the corresponding function and reloads the timer.

void scheduler(void) { 

	unsigned int i = 0;

	while ( task_list[i].task )                             /*loop through all tasks*/  
	{	
		if ( *(task_list[i].timer) == 0 )                   /*check if timer didn't expired*/
		{
			task_list[i].task();                            /*launch task*/
            *(task_list[i].timer) = task_list[i].interval;  
		}
		i++;
	}
}


This is an example of scheduler() function usage:

void main(void) {

	do_any_init_stuff();

		while(1) 
		{
			scheduler();
		}
	
}


This is the only function that should be called in a cyclic interrupt. It will update the timers of each task:
void scheduler_tmr(void) 
{
	if 	( tmr_task_10_ms )  		tmr_task_10_ms--;
	if 	( tmr_task_100_ms )			tmr_task_100_ms--;
	if	( tmr_task_1000_ms )		tmr_task_1000_ms--;
}


Being a cooperative scheduler it is important to plan the recurrence interval of each task according to its execution time. Keeping the tasks short will also minimize the jitter. To change a tasks priority just move it up or down in task_list[]



home