Thursday, June 29, 2017

Arduino ESP32 FreeRTOS 5: How to use Event Group to synchronize multiple tasks - broadcast events

1. Introduction
Benefits of event group:
- A task will be in Blocked state to wait for a combination of one or more events. it helps synchronizing multiple tasks, broadcasting events (by setting event bits of tasks to notify that event occurred) to more than one task.
- Unblock all the tasks that were waiting for same event/action or a combination of events/actions to occur/complete.
- Event Flag can be 0 or 1. It is used to indicate whether an event occur or not. And is represented by a bit in EventBits_t data type.
- Event Group is a set of event flags.
- An EventBits_t has the value 0x92 (1001 0010). It means event bits 1, 4 and 7 are set.
- You have to define the meaning of each event bits. For example: bit 0 is to indicate that the sending message event is finished or not.
- Let 's make 2 demos:
 + Demo 1 ("A task will be in Blocked state to wait for a combination of one or more events"): a timer is running, a counter will be increased when timer is timeout and 3 tasks that are waiting for counter event.  If counter is equal 2, task 1 will leave Blocked state. if counter is equal 3, both task 2 and task 3 will leave Blocked state and counter will be reset to 0.
+ Demo 2 ("synchronizing multiple tasks"): create 3 tasks in which task 1 (highest priority) will print "task 1 done" and then wait until task 2 finishes printing "task 2 done" and task 3 finishes printing "task 3 done"
2. Demo
2.1 Demo 1
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
#include "freertos/event_groups.h"

/* define event bits */
#define TASK_1_BIT        ( 1 << 0 ) //1
#define TASK_2_BIT        ( 1 << 1 ) //10
#define TASK_3_BIT        ( 1 << 2 ) //100
#define ALL_SYNC_BITS (TASK_1_BIT | TASK_2_BIT | TASK_3_BIT) //111


/* create a hardware timer */
hw_timer_t * timer = NULL;
/* create event group */
EventGroupHandle_t eg;
int count = 0;

/* timer ISR callback */
void IRAM_ATTR onTimer(){
  BaseType_t xHigherPriorityTaskWoken;
  count++;
  if(count == 2){
    /* if counter is equal 2 then set event bit of task1 */
    xEventGroupSetBitsFromISR(eg,TASK_1_BIT, &xHigherPriorityTaskWoken);
  }else if(count == 3){
    /* if counter is equal 3 then set event bit of task 2 and 3 */
    xEventGroupSetBitsFromISR(eg,TASK_2_BIT | TASK_3_BIT, &xHigherPriorityTaskWoken);
  }else if(count == 4){
    /* reset counter to start again */
    count = 0;
  }
}

void setup() {

  Serial.begin(112500);
  eg = xEventGroupCreate();

  /* Use 1st timer of 4 */
  /* 1 tick take 1/(80MHZ/80) = 1us so we set divider 80 and count up */
  timer = timerBegin(0, 80, true);

  /* Attach onTimer function to our timer */
  timerAttachInterrupt(timer, &onTimer, true);

  /* Set alarm to call onTimer function every second 1 tick is 1us
  => 1 second is 1000000us */
  /* Repeat the alarm (third parameter) */
  timerAlarmWrite(timer, 1000000, true);

  /* Start an alarm */
  timerAlarmEnable(timer);
  Serial.println("start timer");
  
  xTaskCreate(
      task1,           /* Task function. */
      "task1",        /* name of task. */
      10000,                    /* Stack size of task */
      NULL,                     /* parameter of the task */
      1,                        /* priority of the task */
      NULL);                    /* Task handle to keep track of created task */
  xTaskCreate(
      task2,           /* Task function. */
      "task2",        /* name of task. */
      10000,                    /* Stack size of task */
      NULL,                     /* parameter of the task */
      1,                        /* priority of the task */
      NULL);                    /* Task handle to keep track of created task */
  xTaskCreate(
      task3,           /* Task function. */
      "task3",        /* name of task. */
      10000,                    /* Stack size of task */
      NULL,                     /* parameter of the task */
      1,                        /* priority of the task */
      NULL);                    /* Task handle to keep track of created task */
}

void loop() {

}

void task1( void * parameter )
{
  for(;;){
    /* wait forever until event bit of task 1 is set */
    EventBits_t xbit = xEventGroupWaitBits(eg, TASK_1_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
    Serial.print("task1 has even bit: ");
    Serial.println(xbit);
  }
  vTaskDelete( NULL );
}
/* this task is similar to sendTask1 */
void task2( void * parameter )
{

  for(;;){
    /* wait forever until event bit of task 2 is set */
    EventBits_t xbit = xEventGroupWaitBits(eg, TASK_2_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
    Serial.print("task2 has even bit: ");
    Serial.println(xbit);
  }
  vTaskDelete( NULL );
}
void task3( void * parameter )
{
  for(;;){
    /* wait forever until event bit of task 3 is set */
    EventBits_t xbit = xEventGroupWaitBits(eg, TASK_3_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
    Serial.print("task3 has even bit: ");
    Serial.println(xbit);
  }
  vTaskDelete( NULL );
}
Figure: tasks are waiting for event bits are set
2.2 Demo 2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include "freertos/event_groups.h"

/* define event bits */
#define TASK_1_BIT        ( 1 << 0 ) //1
#define TASK_2_BIT        ( 1 << 1 ) //10
#define TASK_3_BIT        ( 1 << 2 ) //100
#define ALL_SYNC_BITS     (TASK_1_BIT | TASK_2_BIT | TASK_3_BIT) //111

/* create event group */
EventGroupHandle_t eg;

void setup() {

  Serial.begin(112500);
  eg = xEventGroupCreate();
  
  xTaskCreate(
      task1,           /* Task function. */
      "task1",        /* name of task. */
      10000,                    /* Stack size of task */
      NULL,                     /* parameter of the task */
      3,                        /* priority of the task */
      NULL);                    /* Task handle to keep track of created task */
  xTaskCreate(
      task2,           /* Task function. */
      "task2",        /* name of task. */
      10000,                    /* Stack size of task */
      NULL,                     /* parameter of the task */
      1,                        /* priority of the task */
      NULL);                    /* Task handle to keep track of created task */
  xTaskCreate(
      task3,           /* Task function. */
      "task3",        /* name of task. */
      10000,                    /* Stack size of task */
      NULL,                     /* parameter of the task */
      1,                        /* priority of the task */
      NULL);                    /* Task handle to keep track of created task */
}

void loop() {

}

void task1( void * parameter )
{
  for(;;){
    Serial.println("task1 done");
    /* task 2 finishes printing so set its event bit and wait until other tasks finish */
    EventBits_t uxBits = xEventGroupSync(eg, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY );
    /* if other tasks finished then all event bits would be set*/
    if( ( uxBits & ALL_SYNC_BITS ) == ALL_SYNC_BITS ){
        Serial.println("task 1 - all task done !!!");
    }
  }
  vTaskDelete( NULL );
}
/* this task is similar to sendTask1 */
void task2( void * parameter )
{

  for(;;){
    Serial.println("task2 done");
    /* task 2 finishes printing so set its event bit */
    EventBits_t uxBits = xEventGroupSync( eg, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY );
  }
  vTaskDelete( NULL );
}
void task3( void * parameter )
{
  for(;;){
    Serial.println("task3 done");
    /* task 3 finishes printing so set its event bit */
    EventBits_t uxBits = xEventGroupSync( eg, TASK_3_BIT, ALL_SYNC_BITS, portMAX_DELAY );
  }
  vTaskDelete( NULL );
}
Figure: task 1 wait until task 2 and task 3 finish

0 comments: