The following requirements will help you to write the code for you reusable Scheduler, you can implemented on you PC or directly in you microcontroller using your preferred Base time mechanisms. Create two files Scheduler.h and Scheduler.c

  • The scheduler should have an initialization interface
void Scheduler_Init( SchedulerType *Scheduler, uint32_t TickBase, uint8_t Tasks, TaskType *TasksBuffer );

The function should initialize the Schedulerstructure with the values passed as parameters, the number of tasks to use, the tick value use a base time, the address of the array of task TCBs,and the amount of time the scheduler should run.

  • The scheduler should have an interface to register the tasks to run
uint8_t Scheduler_RegisterTask( SchedulerType *Scheduler, void (*InitPtr)(void), void (*TaskPtr)(void), uint32_t Period );

The function will set the task TCB with the following parameters, the address of the function to hold the init routine for the given task and the address for the actual routine that will run as the task, plus the periodicity in milliseconds of the task to register, if the task does not have an init routine a NULL parameter should be accepted by the function, another thing to validate is the Periodicity should not be less than the tick value and always be multiple. The function shall return a Task ID which will be a number from 1 to n task registered if the operation was a success, otherwise, it will return zero.

  • The scheduler should have an interface to stop any of the registered tasks from keeping running
uint8_t Scheduler_StopTask( SchedulerType *Scheduler, uint8_t Task );

By default after registering a task this will start running within the scheduler, this function can prevent it from being dispatched. The second parameter indicates the task to be stopped, which is a number from 1 to n task registered. Number zero is forbidden. the function will return a TRUE if the task was stopped otherwise returns FALSE

  • The scheduler should have an interface to start any of the registered tasks previously stopped
uint8_t Scheduler_StartTask( SchedulerType *Scheduler, uint8_t Task );

Once a task is stopped using the function Scheduler_StopTask, it can be active again using this function. The second parameter indicates the task to be started, which is a number from 1 to n task registered. Number zero is forbidden. the function will return a TRUE if the task was stopped otherwise returns FALSE

  • The scheduler should have an interface to change the task periodicity of any of the registered tasks
uint8_t Scheduler_PeriodTask( SchedulerType *Scheduler, uint8_t Task, uint32_t Period );

The new periodicity shall be a multiple of the tick value otherwise won’t be affected by the new period. The second parameter indicates the task to be started, which is a number from 1 to n task registered. Number zero is forbidden. the function will return a TRUE if the task was stopped otherwise returns FALSE.

  • The scheduler should have an interface that will run the different tasks that have been registered
void Scheduler_MainFunction( SchedulerType *Scheduler );

This is the function in charge of running the task init functions one single time, and actual run each registered task according to their periodicity in an infinite loop, the function will never return at least something wrong happens, but this will be considered a malfunction. In order to dispatch each task accordingly it will be necessary to use a Base time or Tick provided by a Hardware Timer, for instance; the Systick Timer in case of Cortex-M microcontrollers.

  • The scheduler control structure shall have the following elements
typedef struct _SchedulerType
{
    uint8_t Tasks;         /*number of task to handle*/
    uint32_t Tick;          /*the time base in ms*/
    uint32_t Timeout;       /*the number of milliseconds the scheduler should run*/
    uint8_t TasksCount;    /*internal task counter*/
    TaskType *TaskPtr;  /*Pointer to buffer for the TCB tasks*/
} SchedulerType;
  • The task control block ( TCB ) should have the following elements
typedef struct _TaskType 
{
    uint32_t Period;          /*How often the task shopud run in ms*/
    uint32_t Elapsed;         /*the cuurent elapsed time*/
    uint8_t StartFlag;        /*flag to run task*/
    void (*InitFunc)(void);   /*pointer to init task function*/
    void (*TaskFunc)(void);   /*pointer to task function*/
} TaskType;

This is an example of a program running on this scheduler, two simple tasks at 1000ms and 500ms with a tick of 100ms.

#include <stdio.h>
#include "Scheduler.h"

#define TASKS_N     2
#define TICK_VAL    100

static TaskType tasks[ TASKS_N ];
static SchedulerType Sche;

void Init_500ms(void);
void Init_1000ms(void);
void Task_500ms(void);
void Task_1000ms(void);

int main( void )
{
    unsigned char TaskID1;
    unsigned char TaskID2;
    
    /*init the scheduler with two tasks and a tick time of 100ms and run for 10 seconds only*/
    Scheduler_InitScheduler( &Sche, TICK_VAL, TASKS_N, &tasks );
    
    /*register two task with thier corresponding init fucntions and their periodicyt, 100ms and 500ms*/
    TaskID1 = Scheduler_RegisterTask( &Sche, Init_500ms, Task_500ms, 500 );
    TaskID2 = Scheduler_RegisterTask( &Sche, Init_1000ms, Task_1000ms, 1000 );

    /*run the scheduler forever*/
    Scheduler_MainFunction( &Sche );
    
    return 0;
}

void Init_500ms(void)
{
    printf("Init task 500 millisecond");
}

void Init_1000ms(void)
{
    printf("Init task 1000 millisecond");
}

void Task_500ms(void)
{
    static int loop = 0;
    printf("This is a counter from task 500ms: %d", loop++);
}

void Task_1000ms(void)
{
    static int loop = 0;
    printf("This is a counter from task 1000ms: %d", loop++);
}