Learning Multi-tasking (Concurrent Executions) in Embedded Controller using "Arduino UNO R3 and Arduino_FreeRTOS.h"

The following setup of Fig-1 is composed of "Arduino UNO R3 + Arduino_FreeRTOS" Platform; where, tasks would be running cocurrently and would be outputting messages at often on Serial Monitor (SM). The Tasks are:
1. LED2 blinks continuously at 4-sec interval.

2. When Pot1 is at fully CCW position, LED1 blinks at 1 Hz interval; when Pot1 is at fully CW position, LED1 blinks at 20 Hz interval.

3. When button K1 is pressed, the MCU is interrupted and L (built-in LED of UNO) blinks for 5 times at 1-sec interval.

led3sw1Frtos
Figure-1:

Sketch:
To implement the objectives of Fig-1, I have prepared the following sketch by consulting the beautiful Examples of the Arduino IDE. The skecth is working fine and responds very well to interrupt switch (K1) and Pot1.

#include <Arduino_FreeRTOS.h>
#include <queue.h>
#include <semphr.h>

QueueHandle_t integerQueue;
SemaphoreHandle_t interruptSemaphore;
#define INT0 2

// define three tasks for Blink & AnalogRead
void TaskBlinkL( void *pvParameters ); //blinks L at 1 sec interval
void TaskBlinkLED2( void *pvParameters ); //blinks LED2 at 2 sec interval
void TaskAnalogReadA1( void *pvParameters );  //task that publiseh value of A1-pin in Queue
void TaskPwm( void *pvParameters ); //blink rate of LED1 depends on value of A1-pin

void setup()
{
  Serial.begin(9600);

  while (!Serial)
  {
    ; // wait for serial port to connect.
  }
  pinMode(13, OUTPUT);
  pinMode(INT0, INPUT_PULLUP);
  //----initialize PWM (Mode 14 FPWM) at DPin-9 -----
  pinMode(9, OUTPUT); //Ch-A
  TCCR1A = 0x00;   //reset
  TCCR1B = 0x00;   //TC1 reset and OFF
  //fOC1A/B = clckSys/(N*(1+ICR1); Mode-14 FPWM; OCR1A controls duty cycle
  // 1 Hz = 16000000/(256*(1+ICR1) N=1,8,64,256,1024==> ICR1 = 62499
  // 20 Hz ; ICR1 = 3124
  TCCR1A |= (1 << WGM11); //Mode-14 Fast PWM
  TCCR1B |= (1 << WGM13) | (1 << WGM12);    //Mode-14 Fast PWM
  TCCR1A |= (1 << COM1A1) | (0 << COM1A0);  //Non-invert: HIGH-LOW

  ICR1 = 62499;   // TOP for 1Hz frequnecy
  OCR1A = 31250;  //= 50% duty cycle
  TCNT1 = 0;

  TCCR1B |= (1 << CS12);//TC1 statrt with N = 256;
  //--------------------------------------------
  // Now set up tasks to run independently.
  interruptSemaphore = xSemaphoreCreateBinary();
  if (interruptSemaphore != NULL)
  {
    attachInterrupt(digitalPinToInterrupt(2), interruptHandler, LOW);// Attach interrupt
  }
  //-----------------------------------------------

  integerQueue = xQueueCreate(
                   10,
                   sizeof (int));  //Queue length, queue item size

  if (integerQueue != NULL)
  {
    xTaskCreate( // Create task that consumes the queue if it was created.
      TaskPwm, // Task function
      "Serial", // A name just for humans
      128,  // This stack size can be checked & adjusted by reading the Stack Highwater
      NULL,
      1, // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
      NULL);

    xTaskCreate(  // Create task that publish data in the queue if it was created.
      TaskAnalogReadA1,
      "AnalogReadA1",
      128,  // Stack size
      NULL,
      1,  // Priority
      NULL );
  }
  //----------------------------------------------------

  xTaskCreate(
    TaskBlinkL,
    "BlinkL",   // A name just for humans
    128,  // This stack size can be checked & adjusted by reading the Stack Highwater
    NULL,
    1,  // Priority, with 4 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    NULL );

  xTaskCreate(
    TaskBlinkLED2,
    "BlinkLED2",   // A name just for humans
    128,  // This stack size can be checked & adjusted by reading the Stack Highwater
    NULL,
    1, // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    NULL );

  xTaskCreate(
    TaskPwm,
    "PWMLED1",
    128,  // Stack size
    NULL,
    1,  // Priority
    NULL );
  // Now the task scheduler, which takes over control
  //of scheduling individual tasks, is automatically started.
}

void loop()  //called upon by Task Idle of FreeRTOS
{

}

//---------------------------------------------

void TaskBlinkLED2(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  pinMode(11, OUTPUT);

  while (true) // A Task shall never return or exit.
  {
    digitalWrite(11, HIGH);   // turn the LED on (HIGH is the voltage level)
    vTaskDelay( 2000 / portTICK_PERIOD_MS ); // wait for 2 second
    digitalWrite(11, LOW);    // turn the LED off by making the voltage LOW
    vTaskDelay( 2000 / portTICK_PERIOD_MS ); // wait for 2 second
  }
}

//--------------------------------------------
void TaskAnalogReadA1(void *pvParameters)
{
  (void) pvParameters;

  while (true)
  {
    // Read the input on analog pin 0:
    int adcValue = analogRead(A1);
    xQueueSend(integerQueue, &adcValue, portMAX_DELAY);
    vTaskDelay(1);// One tick delay (15ms) in between reads for stability
  }
}

void TaskPwm(void * pvParameters)
{
  (void) pvParameters;
  int valueFromQueue = 0;

  while (true)
  {
    if (xQueueReceive(integerQueue, &valueFromQueue, portMAX_DELAY) == pdPASS)
    {
      Serial.print("ADC-A1 value received via Queue: ");
      Serial.println(valueFromQueue, DEC); //0 to 1023
      Serial.println("===================");
      ICR1 = map(valueFromQueue, 0, 1023, 62499, 3124); //1Hz (FCCW) - 20Hz(FCW)
      OCR1A = ICR1 / 2; //maintains 50% duty cycle
      vTaskDelay(1000 / portTICK_PERIOD_MS );
    }
  }
}
//------------------------------

void interruptHandler()
{
  xSemaphoreGiveFromISR(interruptSemaphore, NULL);
}
//-------------------------------

void TaskBlinkL(void *pvParameters)
{
  (void) pvParameters;

  while (true)
  {
    if (xSemaphoreTake(interruptSemaphore, portMAX_DELAY) == pdPASS)
    {
      for (int i = 0; i < 5; i++)
      {
        digitalWrite(13, HIGH);
        vTaskDelay(500 / portTICK_PERIOD_MS ); //500 ms delay
        digitalWrite(13, LOW);
        vTaskDelay(500 / portTICK_PERIOD_MS );
      }
    }
  }
}

The opportunity for learning various vocabularies and principles of FreeRTOS will appear when a general discussion comes on in the form of questions/answers and opinions/comments.

2 Likes

20.1 What is multi-tasking in an embedded controller

An embedded controller like the ATmega328P MCU has multi-tasking capability which means that the controller can be programmed (with the help of a specialized software called Scheduler or Real Time Operating System, RTOS) to execute many tasks concurrently/simultaneously. Concurrency is not true parallelism but semi-parallelism. In true parallelism, one MCU/processor is dedicated for each tasks. If there are two tasks, then there are two processors, and hence the name Dual Core.

By nature, the MCU is a serial device. It has no ability to execute two tasks in parallel (at the same time). It has to perform them in sequence meaning one after another. This causes a serious problem in practical situation as the second task has to wait until the first task is completed. For example:

The first task (Task-1) is the acquisition, processing, display, and transmission of a Boiler Temperature and say it takes about 100 ms; the second task (Task-2) is to monitor the temperature of a hazardous area for possible breakout of fire and then initiate "alarm".

In the above example, the waiting for 100 ms completion time for Task-1 could be too long and should not be tolerated; because, in the meantime, the hazardous area might has started smoking indicating a possible breakout of fire.

The above unwanted situation can be easily avoided by programming the MCU (with the help of RTOS) to execute both tasks concurrently where the MCU spends a "tiny time slice (say: 15 ms Tick, Fig-1)" for each task in round-robin basis. This way, the execution phase of each task gradually proceeds towards end.

Here, in fact, the tasks are being executed one-after-another; but, the RTOS makes us believing that the tasks are being executed concurrently (semi parallelism) by switching the MCU from one task to another at tremendous speed (Fig-1).


Figure-1:

20.2 What does MCU do during 15 ms time slice when it turns to Task-1?
During compilation process, the sketch of Task-1 is converted into binary codes which are loaded into the flash memory of the MCU. Taking the average execution time of one instruction (on a 16 MHz Arduino UNOR3) as 1 cycle (0.0000000625 sec), the MCU executes .015/0.0000000625 = 240,000 instructions within the 15 ms time slice period before switching to Task-2.

Some instructions may take 2 cycles time for execution; if so, then the MCU will always execute an integral number of instructions whose total execution time must be <= 0.015 sec. The rest of the time just goes unused (I guess).

20.3 What is the role of Scheduler/FreeRTOS?
In FreeRTOS Arduino UNOR3 platform, the time slice is 15 ms, and a time slice is known as a “Tick.” A hardware timer is configured to generate an interrupt every 15 ms. The ISR for that timer runs the scheduler/FreeRTOS, which chooses the task to run next. At each Tick interrupt, the task with the highest priority is chosen to run. The Scheduler is a complex program loaded into the flash memory of UNO along with the sketch.

FreeRTOS allows us to set priorities for tasks, which allows the scheduler to preempt lower priority tasks with higher priority tasks. The scheduler is a piece of software inside the FreeRTOS operating system in charge of figuring out which task should run at eachTtick.

20.4 Conceptual view of a multi-task (multi-thread) program under FreeRTOS
multitask-2
Figure-2:

In Arduino IDE environment, after startup of the MCU, the control program executes the main() function and then enters into the setup() function.

int main(void) // Normal Arduino main.cpp. Normal execution order.
{
   init();
   initVariant();  // Our initVariant() diverts execution from here.`
}

void initVariant(void)
{
   setup();   // The Arduino setup() function.`
   vTaskStartScheduler();   // Initialise and run the FreeRTOS scheduler. 
}

void setup(void)
{
    xTaskCreate(Task_A, "TaskA", 128, NULL,  4, NULL );
    xTaskCreate(Task_B, "TaskB", 128, NULL,  4, NULL );
    xTaskCreate(Task_C, "TaskC", 128, NULL,  4, NULL );
}

If Task A, Task B, and Task C (Fig-2) are created in the Setup() function using xTaskCreate() function of the Arduino_FreeRTOS.h Library, the Arduino UNO system begins to work in multi-tasking environment by triggering the Scheduler/FreeRTOS. Task A, Task B, and Task C (NOT Task loop()) appear to run concurrently in their respective endless local while(true){} loops assuming that a task in not ended after a single execution.

If the tasks are assigned equal priority, then the scheduler/FreeRTOS serves them in round-robin fashion spending 15 ms (Tick Period) for each task. The Scheduler/FreeRTOS configures a hardware timer of the MCU to generate an interrupting signal at 15 ms (tick) interval, which triggers the scheduler to switch across the tasks.

If no task is created in the setup() function using xTaskCreate() function of the Arduino_FreeRTOS.h Library, the control program executes the setup() function and then enters into the loop() function. The system begins to work in "normal fashion" under Arduino IDE.

int main(void) // Normal Arduino main.cpp. Normal execution order.
{
   init();
   initVariant(); // Our initVariant() diverts execution from here.
   setup(); // The Arduino setup() function.
 
   for (;;)
   {
      loop(); // The Arduino loop() function.
      if (serialEventRun) serialEventRun();
   }
   return 0;
}

The FreeRTOS idle task is used to run the loop() function whenever there is no unblocked FreeRTOS task available to run. In the trivial case, where there are no configured FreeRTOS tasks, the loop() function will be run exactly as normal, with the exception that a short scheduler interrupt will occur every 15 milli-seconds (configurable).

void vApplicationIdleHook( void )
{
   loop(); // The Arduino loop() function.
   if (serialEventRun) serialEventRun();
}

20.5 Task State Diagram in FreeRTOS


Figure-3:

20.6 Example-1
Create Arduino_FreeRTOS.h based sketch to blink the two LEDs of the following circuit (Fig-4) concurrently; where, LED1 will blink at 1-sec interval and LED2 will blink at 2-sec interval.

led2
Figure-4:

Sketch:

#include <Arduino_FreeRTOS.h>

TaskHandle_t myTaskA;
TaskHandle_t myTaskB;
#define 	LED1	5
#define	    LED2	6

void setup()
{
  pinMode(LED1, OUTPUT); 		//LED1
  pinMode(LED2, OUTPUT);  		//LED3

  xTaskCreate(myTaskFunctionA, "MyTaskA", 128, NULL, 1, &myTaskA);
  xTaskCreate(myTaskFunctionB, "MyTaskB", 128, NULL, 1, &myTaskB);
}

void loop()   
{
  //leave it empty for Arduino UNO
}

void myTaskFunctionA(void *pvParameters)//LED1
{
  for (;;)
  {
    digitalWrite(LED1, HIGH);
    vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 1/2 second
    digitalWrite(LED1, LOW);
    vTaskDelay(pdMS_TO_TICKS(500)); 
  }
}

void myTaskFunctionB(void *pvParameters)//LED2
{
  for (;;)
  {
    digitalWrite(LED2, HIGH);
    vTaskDelay(pdMS_TO_TICKS(1000)); // Delay for 1 second
    digitalWrite(LED2, LOW);
    vTaskDelay(pdMS_TO_TICKS(1000)); 
  }
}

Timing Diagram:


Figure-5:

When tasks are created, they are included in the “ready to run list”. Both tasks are created with equal priority; so, they will be executed on round-robin basis.

At time t1, myTaskA is executed to turn On LED1. After that the instruction vTaskDelay(pdMS_TO_TICKS(500)) is executed. The task enters into blocked state, and it is marked as unready in the “ready to run list”. The task remains in blocked state until the 500 ms time delay is elapsed.

At time t2, the 15 ms apart Time Tick arrives; the Scheduler finds myTaskB as a ready task, and the task is executed; LD2 is turned On. After that the instruction vTaskDelay(pdMS_TO_TICKS(500)) is executed. The task enters into blocked state, and it is marked as unready in the “ready to run list”. The task remains in blocked state until the 500 ms time delay is elapsed.

From time t3 to t4, the Tick has interrupted the MCU multiple times; the Scheduler has been alerted, but it has not executed any tasks as they are in unready states.

At t4, myTaskA is ready; at Tick point, the Scheduler will execute the instructions digitalWrite(LED1, LOW) to turn Off LED1. After that, the task will enter into blocked state.

During the blocked period, the MCU just spends time doing nothing. Is it possible to assign some tasks to the MCU?

20.7 Example-2

The following setup of Fig-20.1 is composed of "Arduino UNO R3 + Arduino_FreeRTOS" Platform; where tasks would be running concurrently and would be outputting messages at often on Serial Monitor (SM). The Tasks are:
1. LED2 blinks at 4-sec interval.

2. When Pot1 is at fully CCW position, LED1 blinks at 1 Hz interval; when Pot1 is at fully CW position, LED1 blinks at 20 Hz interval.

3. When button K1 is pressed, the MCU is interrupted and L (built-in LED of UNO) blinks for 5 times at 1-sec interval.

led3SW1FRTOS
Figure-6:

Sketch:
To implement the objectives of Fig-1, I have prepared the following sketch by consulting the beautiful Examples of the Arduino IDE. The sketch is working fine and responds very well to interrupt switch (K1) and Pot1.

#include <Arduino_FreeRTOS.h>
#include <queue.h>
#include <semphr.h>

QueueHandle_t integerQueue;
SemaphoreHandle_t interruptSemaphore;
#define INT0 2

// define tasks for Blink & AnalogRead
void TaskBlinkL( void *pvParameters ); //blinks L at 1 sec interval
void TaskBlinkLED2( void *pvParameters ); //blinks LED2 at 2 sec interval
void TaskAnalogReadA1( void *pvParameters );  //task that publiseh value of A1-pin in Queue
void TaskPwm( void *pvParameters ); //blink rate of LED1 depends on value of A1-pin

void setup()
{
  Serial.begin(9600);

  while (!Serial)
  {
    ; // wait for serial port to connect.
  }
  pinMode(13, OUTPUT);
  pinMode(INT0, INPUT_PULLUP);
  //----initialize PWM (Mode 14 FPWM) at DPin-9 -----
  pinMode(9, OUTPUT); //Ch-A
  TCCR1A = 0x00;   //reset
  TCCR1B = 0x00;   //TC1 reset and OFF
  //fOC1A/B = clckSys/(N*(1+ICR1); Mode-14 FPWM; OCR1A controls duty cycle
  // 1 Hz = 16000000/(256*(1+ICR1) N=1,8,64,256,1024==> ICR1 = 62499
  // 20 Hz ; ICR1 = 3124
  TCCR1A |= (1 << WGM11); //Mode-14 Fast PWM
  TCCR1B |= (1 << WGM13) | (1 << WGM12);    //Mode-14 Fast PWM
  TCCR1A |= (1 << COM1A1) | (0 << COM1A0);  //Non-invert: HIGH-LOW

  ICR1 = 62499;   // TOP for 1Hz frequnecy
  OCR1A = 31250;  //= 50% duty cycle
  TCNT1 = 0;

  TCCR1B |= (1 << CS12);//TC1 statrt with N = 256;
  //--------------------------------------------
  // Now set up tasks to run independently.
  interruptSemaphore = xSemaphoreCreateBinary();
  if (interruptSemaphore != NULL)
  {
    attachInterrupt(digitalPinToInterrupt(2), interruptHandler, LOW);// Attach interrupt
  }
  //-----------------------------------------------

  integerQueue = xQueueCreate(
                   10,
                   sizeof (int));  //Queue length, queue item size

  if (integerQueue != NULL)
  {
    xTaskCreate( // Create task that consumes the queue if it was created.
      TaskPwm, // Task function
      "Serial", // A name just for humans
      128,  // This stack size can be checked & adjusted by reading the Stack Highwater
      NULL,
      1, // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
      NULL);

    xTaskCreate(  // Create task that publish data in the queue if it was created.
      TaskAnalogReadA1,
      "AnalogReadA1",
      128,  // Stack size
      NULL,
      1,  // Priority
      NULL );
  }
  //----------------------------------------------------

  xTaskCreate(
    TaskBlinkL,
    "BlinkL",   // A name just for humans
    128,  // This stack size can be checked & adjusted by reading the Stack Highwater
    NULL,
    1,  // Priority, with 4 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    NULL );

  xTaskCreate(
    TaskBlinkLED2,
    "BlinkLED2",   // A name just for humans
    128,  // This stack size can be checked & adjusted by reading the Stack Highwater
    NULL,
    1, // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    NULL );

  xTaskCreate(
    TaskPwm,
    "PWMLED1",
    128,  // Stack size
    NULL,
    1,  // Priority
    NULL );
  // Now the task scheduler, which takes over control
  //of scheduling individual tasks, is automatically started.
}

void loop()  //called upon by Task Idle of FreeRTOS
{

}

//---------------------------------------------

void TaskBlinkLED2(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  pinMode(11, OUTPUT);

  while (true) // A Task shall never return or exit.
  {
    digitalWrite(11, HIGH);   // turn the LED on (HIGH is the voltage level)
    vTaskDelay( 2000 / portTICK_PERIOD_MS ); // wait for 2 second
    digitalWrite(11, LOW);    // turn the LED off by making the voltage LOW
    vTaskDelay( 2000 / portTICK_PERIOD_MS ); // wait for 2 second
  }
}

//--------------------------------------------




void TaskAnalogReadA1(void *pvParameters)
{
  (void) pvParameters;

  while (true)
  {
    // Read the input on analog pin 0:
    int adcValue = analogRead(A1);
    xQueueSend(integerQueue, &adcValue, portMAX_DELAY);
    vTaskDelay(1);// One tick delay (15ms) in between reads for stability
  }
}

void TaskPwm(void * pvParameters)
{
  (void) pvParameters;
  int valueFromQueue = 0;

  while (true)
  {
    if (xQueueReceive(integerQueue, &valueFromQueue, portMAX_DELAY) == pdPASS)
    {
      Serial.print("ADC-A1 value received via Queue: ");
      Serial.println(valueFromQueue, DEC); //0 to 1023
      Serial.println("===================");
      ICR1 = map(valueFromQueue, 0, 1023, 62499, 3124); //1Hz (FCCW) - 20Hz(FCW)
      OCR1A = ICR1 / 2; //maintains 50% duty cycle
      vTaskDelay(1000 / portTICK_PERIOD_MS );
    }
  }
}
//------------------------------

void interruptHandler()
{
  xSemaphoreGiveFromISR(interruptSemaphore, NULL);
}
//-------------------------------

void TaskBlinkL(void *pvParameters)
{
  (void) pvParameters;

  while (true)
  {
    if (xSemaphoreTake(interruptSemaphore, portMAX_DELAY) == pdPASS)
    {
      for (int i = 0; i < 5; i++)
      {
        digitalWrite(13, HIGH);
        vTaskDelay(500 / portTICK_PERIOD_MS ); //500 ms delay
        digitalWrite(13, LOW);
        vTaskDelay(500 / portTICK_PERIOD_MS );
      }
    }
  }
}

20.8 Questions

... continue

... reserved.

which UNO ??

It is Arduino UNO.

For more clarity, I am editing my post.

I hope that you will come up with more quuestions and voluntarilly explaining the working principles of many of the critical instructions of the sketch of posT #1 for the education of the OP and others.

At present, in response to interrupt button (K1), the L blinks for five times in the ISR() and suspends the excutions of other independent tasks. How can I blink L in the loop() function so that other tasks remain active?

R3 or R4?

Very importatnt question -- it is R3.

By the by, I purchased two Arduino UNO R4 WiFi hearing that they also support RTOS, but I never tried.

a link to "Arduino_FreeRTOS" would be useful. This is not the typical R3 environment...

is this Arduino FreeRTOS | Gone Bush ?

This is the one (file attached) I have included in my IDE. I have got the reference of the file from the sketch of post #1 of this thread.
Arduino_FreeRTOS_Library-master.zip (338.5 KB)

Now, more good questions are coming in my mind becasue I have played for sometimes with RTOS for the Dual Core ESP32 where I can observe that two tasks from two cores do really compete for a common hardware resource - the Serial Monitor. In UNOR3, I do not experience such situation as there is always one MCU with multiple tasks which are essentially being executed in sequence thorough the Arduino_FreeRTOS makes me obedient to believe that they are being executed concurrently by distributing time slices among the tasks.

Thank you very much for the link. I have now better knowledge how FreeRTOS has been integrated with the Arduino IDE.

@GolamMostafa , I have tried another implementation for this post. I have used Software Timers from RTOS and Binary Semaphores to synchronize the Task 1 and Task 3 (Interrupt and LED Blinking every 4 seconds). Here is the code sample:

#include <Arduino_FreeRTOS.h>
#include <event_groups.h>
#include <semphr.h>
#include <timers.h>

//Define three tasks that we need to perform here
void TaskBlink1(void *pvParameters);
void TaskBlink2(void *pvParameters);
void TaskBlink3(void *pvParameters);

//Declaring a variable to hold the event group which will be created
EventGroupHandle_t xCreatedEventGroup;

//define bit for event - Potentiometer
#define BIT_0 (1<<0)

//define bit for event - Simulated Inpterrupt
#define BIT_1 (1<<1)

//Decalring a variable of type Semaphore (Binary)
SemaphoreHandle_t xBinarySema;

//Declaring a varibale for Software Timer for LED3 running 4 seconds
TimerHandle_t xTimer1,xTimer2;

//Declare a timer callback
void timerCallback1(TimerHandle_t xTimer);

//Global Variables for Potentiometer
int currVal = 100, prevVal = 50;

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB, on LEONARDO, MICRO, YUN, and other 32u4 based boards.
  }

    //We create the binary semaphore -  This is to Synchronize the TaskBlink1 and TaskBlink3. 
  xBinarySema = xSemaphoreCreateBinary();

      //If the Semaphore was not created successfully.
    if(xBinarySema == NULL){
      Serial.println("Failed to create semaphore.");
      } else {
       Serial.println("Semaphore got created"); 
         xSemaphoreGive(xBinarySema);
        }
  
  //If the event group was not created successfully.
  xCreatedEventGroup = xEventGroupCreate(); 
  if(xCreatedEventGroup == NULL){
  Serial.println("The event group did not get created.");
  }
  //if the event group was created succesfully.
  else {
   Serial.println("The event group did get created.");
        }
    
    xTimer1 = xTimerCreate("Timer",pdMS_TO_TICKS(1000),pdTRUE,(void *) 0, timerCallback1);
    xTimer2 = xTimerCreate("Timer",pdMS_TO_TICKS(4000),pdTRUE,(void *) 0, timerCallback2); 
    
    if((xTimer1 == NULL) || (xTimer2 == NULL)){
  Serial.println("The timers did not get created."); 
      }
      else{
        xTimerStart(xTimer1,0);
        xTimerStart(xTimer2,0);
        }
     xTaskCreate(TaskBlink1,"Blink",128,NULL,1,NULL);
     xTaskCreate(TaskBlink2,"Blink",128,NULL,1,NULL);
     xTaskCreate(TaskBlink3,"Blink",128,NULL,2,NULL);

     vTaskStartScheduler();
     for(;;);
}



void loop() {
  // put your main code here, to run repeatedly:
}

void TaskBlink1(void *pvParameters){
  EventBits_t uxBit;
  int i = 0;
  for(;;){
    if(xSemaphoreTake(xBinarySema, portMAX_DELAY)){
    for (i = 0;i<5;i++){
      uxBit = xEventGroupWaitBits(xCreatedEventGroup, BIT_0, pdTRUE, pdFALSE, portMAX_DELAY);
  if((uxBit & BIT_0) != 0){   
    digitalWrite(10, HIGH);
    vTaskDelay(pdMS_TO_TICKS(150));
    digitalWrite(10, LOW);
    vTaskDelay(pdMS_TO_TICKS(150));
   }
    }
     uxBit = xEventGroupClearBits(xCreatedEventGroup, BIT_0);
     xSemaphoreGive(xBinarySema);
     }
     }
  }

void TaskBlink2(void *pvParameters){
   //EventBits_t uxBit;
  for(;;){
    currVal = analogRead(A0);
  if(currVal != prevVal){  
   int pwmVal = map(currVal,0,1023,0,255);
   analogWrite(12,pwmVal);
   prevVal = currVal;
  }
  }
    }

void TaskBlink3(void *pvParameters){
  EventBits_t  uxBit;
  for (;;){
      uxBit = xEventGroupWaitBits(xCreatedEventGroup, BIT_1, pdTRUE, pdFALSE, portMAX_DELAY);
   if(xSemaphoreTake(xBinarySema, portMAX_DELAY)){
  if((uxBit & BIT_1) != 0){   
    digitalWrite(11, HIGH);
    vTaskDelay(pdMS_TO_TICKS(800));
    digitalWrite(11, LOW);
    vTaskDelay(pdMS_TO_TICKS(800));
  uxBit = xEventGroupClearBits(xCreatedEventGroup, BIT_1);
   }
   xSemaphoreGive(xBinarySema);
    }
     }
      }

  void timerCallback1(TimerHandle_t xTimer){
    xEventGroupSetBits(xCreatedEventGroup,BIT_0);
    }

  void timerCallback2(TimerHandle_t xTimer){
    xEventGroupSetBits(xCreatedEventGroup,BIT_1);
    }
1 Like

I will certainlt study your sketch of post #12 and will try to come with meaningful questions.

By-the-by, why have you left the loop() function empty? Could you place codes of a task in this loop()?

Sounds good, @GolamMostafa . I left the loop() function empty because I just wanted to run the code with Tasks. I suppose when we run the code inside of loop() that runs in a serial fashion.

Fig-1 is the official diagram with which I have added my conceptaul understanding of Task Idle and codefilled loop() and the result is FIg-2 of of post #3.

Fig-1 indeed shows the concept of task switching among the participating freeRTOS taskes which are not clear in you diagram of post #4.

I would suggest to re-draw your diagram of Post #4 keeping the spirit of the official diagram (Fig-1)--I mean super impose your concept of empty loop() and Task Idle on Fig-1.


Figue-1:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.