Functions without parameters

Let begin from the easiest one: the queues for function pointers without parameters.

A definition:

//-8<--8<--8<--ANTIRTOS_C BEGIN--8<--8<--8<--8<---
#define fQ(q, Q_SIZE)                            \
    volatile int q##_last = 0;                   \
    int q##_first = 0;                           \
    void (*q##_Queue[Q_SIZE])(void);             \
    int q##_Push(void (*pointerQ)(void)) {       \
        if ((q##_last + 1) % Q_SIZE == q##_first)\
            return 1; /* Queue is full */        \
        q##_Queue[q##_last++] = pointerQ;        \
        q##_last %= Q_SIZE;                      \
        return 0; /* Success */                  \
    }                                            \
    int (*q##_Pull(void))(void) {                \
        if (q##_last == q##_first)               \
            return 1; /* Queue is empty */       \
        q##_Queue[q##_first++]();                \
        q##_first %= Q_SIZE;                     \
        return 0;                                \
    }
//-8<--8<--8<--ANTIRTOS_C END --8<--8<--8<--8<---

Usage:

Define your tasks:

void yourTaskOne(){
//put here what ever you want to execute
}

void yourTaskTwo(){
//put here what ever you want to execute
}

Initialize queues like:

  fQ(Q1,8); // define first queue (type fQ) with name Q1, 8 elements length
  fQ(Q2,8);   // define second queue (type fQ) with name Q2, 8 elements length
......

In main loop:

Q1_Pull();
Q2_Pull();
...........

In first of your interrupts routines:

void yourInterruptRoutineOne(void){
    Q1_Push(yourTaskOne);  // just push your task into queue!
}

In second one:

void yourInterruptRoutineTwo(void){
    Q2_Push(yourTaskTwo);  // just push your task into queue!
}

This is it! Every task is handled. Interrupts kept blazing fast.

The simplicity matter!

Functions with parameters

OK, now it is time to involve parameters if we need them to pass to the functions in queue, let define queue for functions with parameters:

#define fQP(q, Q_SIZE, param_type)                                              \
    void (*q##_funcs[Q_SIZE])(param_type);                                      \
    param_type q##_params[Q_SIZE];                                              \
    volatile int q##_last = 0;                                                  \
    int q##_first = 0;                                                          \
    int q##_Push(void (*func)(param_type), param_type params) {                 \
        if ((q##_last + 1) % Q_SIZE == q##_first)                               \
            return 1; /* Queue is full */                                       \
        q##_funcs[q##_last] = func;                                             \
        q##_params[q##_last++] = params;                                        \
        q##_last %= Q_SIZE;                                                     \
        return 0; /* Success */                                                 \
    }                                                                           \
    int q##_Pull(void) {                                                        \
        if (q##_last == q##_first)                                              \
            return 1; /* Queue is empty */                                      \
        q##_funcs[q##_first](q##_params[q##_first++]);                          \
        q##_first %= Q_SIZE;                                                    \
        return 0; /* Success */                                                 \
    }

Usage

Define all your tasks:

void blinkLED(int led){
  switch(led){
    case 0: 
      HAL_GPIO_TogglePin(LED_YELLOW_PORT, LED_YELLOW_PIN);
      break;
    case 1:
      HAL_GPIO_TogglePin(LED_GREEN_PORT, LED_GREEN_PIN);
      break;    
  }

void printSymbol(char ch){
 printf("Number: %c\n", ch);
}

Define all your queues:

  fQP(Q1,8,int);  //8 elements length, type fQP, functions receive int
  fQP(Q2,8,char); //8 elements length, type fQP, functions receive char

In main loop just add:

Q1_Pull();
Q2_Pull();

Where you want just push the tasks into queues and pass them parameters:

  Q1_Push(blinkLED, 0);
  Q2_Push(printSymbol, 'a');

More parameters

Assume you function require more parameters, just wrap them into your structure, for example:

typedef struct {
    int index;
    int logic;
} pinout;

 So task function will receive here type pinout:

void myTask(pinout p){
// put here what ever you want, your task
}


Now you may initialize your queues, like:

  fQP(Q3,8,pinout);  //8 elements length, type fQP, functions receive type 'pinout'

You may push parameters now like:

 Q3_Push(myTask, (pinout){0,1}); //passing arguments

 In main loop as usual:

Q3_Pull();

Delayed conveyers of function pointers without parameters

Ok, now adding delay functionality to make possible to delay our functions from execution to implement task scheduling:

#define del_fQ(q, Q_SIZE)                                           \
    int q##_time = 0;                                               \
    void (*q##_del_fQueue[Q_SIZE])(void);                           \
    int q##_execArr[Q_SIZE] = { 0 };                                \
    int q##_execTime[Q_SIZE];                                       \
    fQ(q, Q_SIZE)                                                   \
    int q##_Push_delayed(void (*pointerF)(void), int delayTime){    \
        int q##_fullQ = 1;                                          \
        for (int i = 0; i < Q_SIZE; i++) {                          \
            if (!q##_execArr[i]) {                                  \
                q##_del_fQueue[i] = pointerF;                       \
                q##_execArr[i]...
Read more »