ESP32:Примеры/Создание задач для использования обоих ядер ESP32: различия между версиями

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску
 
Нет описания правки
 
(не показаны 3 промежуточные версии этого же участника)
Строка 1: Строка 1:
{{ESP32 панель перехода}}
{{ESP32 панель перехода}}
{{Перевод от Сubewriter}}
{{Перевод от Сubewriter}}
{{Myagkij-редактор}}
{{Myagkij-редактор}}
{{Черновик}}


=Создание задач для использования обоих ядер ESP32=
=Создание задач для использования обоих ядер ESP32=
Строка 10: Строка 7:
Плата [[ESP32]] оснащена двумя 32-битными микропроцессорами [[Xtensa LX6]] – ядром 0 и ядром 1. То есть, это 2-ядерная плата. По умолчанию код [[IDE Arduino]] запускается на ядре 1. В этом руководстве мы расскажем, как запустить код на втором ядре [[ESP32]] при помощи создания заданий. Благодаря этому вы сможете запускать код одновременно на обоих ядрах, что сделает [[ESP32]] многозадачной.
Плата [[ESP32]] оснащена двумя 32-битными микропроцессорами [[Xtensa LX6]] – ядром 0 и ядром 1. То есть, это 2-ядерная плата. По умолчанию код [[IDE Arduino]] запускается на ядре 1. В этом руководстве мы расскажем, как запустить код на втором ядре [[ESP32]] при помощи создания заданий. Благодаря этому вы сможете запускать код одновременно на обоих ядрах, что сделает [[ESP32]] многозадачной.


'''Примечание:''' Для того, чтобы добиться многозадачности, необязательно запускать оба ядра.
{{Примечание1|Для того, чтобы добиться многозадачности, необязательно запускать оба ядра.}}


== Необходимые компоненты ==
== Необходимые компоненты ==
Строка 35: Строка 32:
Но есть функция, с помощью которой можно узнать, на каком именно ядре запущен код:
Но есть функция, с помощью которой можно узнать, на каком именно ядре запущен код:


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
xPortGetCoreID()
xPortGetCoreID()
</syntaxhighlight>
</syntaxhighlight>
Строка 41: Строка 38:
Воспользовавшись этой функцией в скетче [[IDE Arduino]], вы увидите, что оба главных блока скетча – [[setup()]] и [[loop()]] – выполняются на ядре 1. Можете проверить это, загрузив на [[ESP32]] код ниже.  
Воспользовавшись этой функцией в скетче [[IDE Arduino]], вы увидите, что оба главных блока скетча – [[setup()]] и [[loop()]] – выполняются на ядре 1. Можете проверить это, загрузив на [[ESP32]] код ниже.  


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
/*********
/*********
   Руи Сантос
   Руи Сантос
Строка 75: Строка 72:
* Создаем идентификатор задачи. Например, '''«Task1»''':
* Создаем идентификатор задачи. Например, '''«Task1»''':


:: <syntaxhighlight lang="c" enclose="div">
:: <syntaxhighlight lang="c">
TaskHandle_t Task1;
TaskHandle_t Task1;
</syntaxhighlight>
</syntaxhighlight>
Строка 81: Строка 78:
* В блоке [[setup()]] при помощи функции [[xTaskCreatePinnedToCore()]] создаем задачу и привязываем ее к ''ядру 0'' (последний параметр). Кроме того, в параметрах этой функции задаются код самой задачи, приоритет и т.д. Выглядит она вот так:
* В блоке [[setup()]] при помощи функции [[xTaskCreatePinnedToCore()]] создаем задачу и привязываем ее к ''ядру 0'' (последний параметр). Кроме того, в параметрах этой функции задаются код самой задачи, приоритет и т.д. Выглядит она вот так:


:: <syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
:: <syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
xTaskCreatePinnedToCore(
xTaskCreatePinnedToCore(
  Task1code, /* Функция, содержащая код задачи */
  Task1code, /* Функция, содержащая код задачи */
Строка 94: Строка 91:
* После создания задачи нам нужно создать функцию, содержащую код для созданной задачи. В нашем случае нужно создать функцию [[Task1code()]]. Выглядит она вот так:
* После создания задачи нам нужно создать функцию, содержащую код для созданной задачи. В нашем случае нужно создать функцию [[Task1code()]]. Выглядит она вот так:


:: <syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
:: <syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
Void Task1code( void * parameter) {
Void Task1code( void * parameter) {
   for(;;){
   for(;;){
Строка 107: Строка 104:
Если вы во время выполнения кода хотите удалить созданную задачу, это можно сделать при помощи функции [[vTaskDelete()]], указав в ее параметре идентификатор задачи ('''Task1'''):
Если вы во время выполнения кода хотите удалить созданную задачу, это можно сделать при помощи функции [[vTaskDelete()]], указав в ее параметре идентификатор задачи ('''Task1'''):


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
vTaskDelete(Task1);
vTaskDelete(Task1);
</syntaxhighlight>
</syntaxhighlight>
Строка 119: Строка 116:
[[File:esp32_two_leds_task_code_1.PNG|center]]
[[File:esp32_two_leds_task_code_1.PNG|center]]


{{Спойлер|На этой схеме изображена 36-контактная версия платы [[ESP32 DEVKIT DOIT V1]]. Если у вас какая-то другая модель, обязательно сверьтесь с ее распиновкой.}}
{{Примечание1|На этой схеме изображена 36-контактная версия платы [[ESP32 DEVKIT DOIT V1]]. Если у вас какая-то другая модель, обязательно сверьтесь с ее распиновкой.}}


Мы создадим две задачи, каждая из которых будет выполняться на собственном ядре.
Мы создадим две задачи, каждая из которых будет выполняться на собственном ядре.
Строка 129: Строка 126:
Загрузите скетч ниже на [[ESP32]].  
Загрузите скетч ниже на [[ESP32]].  


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
/*********
/*********
   Руи Сантос
   Руи Сантос
Строка 209: Строка 206:
=== Как работает этот код ===
=== Как работает этот код ===


'''Примечание:''' В этом коде создается две задачи. Выполнение одной из них привязывается к ядру 0, а другое – к ядру 1. По умолчанию [[Arduino-скетч]]и выполняются на ядре 1, поэтому код для задачи '''Task2''' можно просто написать в блок [[loop()]], не создавая для него отдельной задачи. Но в нашем случае мы создадим обе задачи – в обучающих целях. Впрочем, такой подход – организация кода по задачам – порой более практичен, но это зависит от особенностей проекта.
{{Примечание1|1=В этом коде создается две задачи. Выполнение одной из них привязывается к ядру 0, а другое – к ядру 1. По умолчанию [[Arduino-скетч]]и выполняются на ядре 1, поэтому код для задачи '''Task2''' можно просто написать в блок [[loop()]], не создавая для него отдельной задачи. Но в нашем случае мы создадим обе задачи – в обучающих целях. Впрочем, такой подход – организация кода по задачам – порой более практичен, но это зависит от особенностей проекта.}}


Код ниже начинается с создания идентификаторов для обеих задач – '''Task1''' и '''Task2'''.
Код ниже начинается с создания идентификаторов для обеих задач – '''Task1''' и '''Task2'''.


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
TaskHandle_t Task1;
TaskHandle_t Task1;
TaskHandle_t Task2;
TaskHandle_t Task2;
Строка 220: Строка 217:
Задаем контакты GPIO2 и GPIO4 как контакты для подключения [[светодиод]]ов.
Задаем контакты GPIO2 и GPIO4 как контакты для подключения [[светодиод]]ов.


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
const int led1 = 2;
const int led1 = 2;
const int led2 = 4;
const int led2 = 4;
Строка 227: Строка 224:
В блоке [[setup()]] инициализируем монитор порта на скорости [[115200 бод]].
В блоке [[setup()]] инициализируем монитор порта на скорости [[115200 бод]].


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
Serial.begin(115200);
Serial.begin(115200);
</syntaxhighlight>
</syntaxhighlight>
Строка 233: Строка 230:
Переключаем контакты [[светодиод]]ов в режим '''«[[OUTPUT]]»''' (т.е. в режим вывода данных).
Переключаем контакты [[светодиод]]ов в режим '''«[[OUTPUT]]»''' (т.е. в режим вывода данных).


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
pinMode(led1, OUTPUT);
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led2, OUTPUT);
Строка 240: Строка 237:
Затем создаем задачу '''Task1''' при помощи функции [[xTaskCreatePinnedToCore()]].
Затем создаем задачу '''Task1''' при помощи функции [[xTaskCreatePinnedToCore()]].


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
xTaskCreatePinnedToCore(
xTaskCreatePinnedToCore(
                     Task1code,  /* Функция задачи */
                     Task1code,  /* Функция задачи */
Строка 256: Строка 253:
Далее аналогичным образом создаем задачу '''Task2'''.
Далее аналогичным образом создаем задачу '''Task2'''.


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
xTaskCreatePinnedToCore(
xTaskCreatePinnedToCore(
                     Task2code,  /* Функция задачи */
                     Task2code,  /* Функция задачи */
Строка 270: Строка 267:
Создав задачи, создаем функции с кодом для этих задач.
Создав задачи, создаем функции с кодом для этих задач.


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
// Функция Task1code: мигает светодиодом каждые 1000 мс:
// Функция Task1code: мигает светодиодом каждые 1000 мс:
void Task1code( void * pvParameters ){
void Task1code( void * pvParameters ){
Строка 288: Строка 285:
Функция в задаче '''Task1''' называется '''Task1code()''', но вы можете назвать ее как заблагорассудится. В целях отладки сначала она напечатает в мониторе порта ядро, на котором выполняется задача:
Функция в задаче '''Task1''' называется '''Task1code()''', но вы можете назвать ее как заблагорассудится. В целях отладки сначала она напечатает в мониторе порта ядро, на котором выполняется задача:


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
Serial.print("Task1 running on core ");
Serial.print("Task1 running on core ");
         //  "Задача Task1 выполняется на ядре "
         //  "Задача Task1 выполняется на ядре "
Строка 298: Строка 295:
То же самое происходит и в функции для задачи '''Task2''', но время мигания [[светодиод]]а – другое.  
То же самое происходит и в функции для задачи '''Task2''', но время мигания [[светодиод]]а – другое.  


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
// Функция Task2code: мигает светодиодом каждые 700 мс:
// Функция Task2code: мигает светодиодом каждые 700 мс:
void Task2code( void * pvParameters ){
void Task2code( void * pvParameters ){
Строка 316: Строка 313:
Наконец, в блоке [[loop()]] не пишем ничего.
Наконец, в блоке [[loop()]] не пишем ничего.


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
void loop() {  
void loop() {  


Строка 322: Строка 319:
</syntaxhighlight>
</syntaxhighlight>


'''Примечание:''' Ранее уже говорилось, но напомним – код в блоке [[loop()]] выполняется на ядре 1. Поэтому вместо создания задачи для выполнения на ядре 1 вы можете просто вписать этот код в блок [[loop()]].
{{Примечание1|Ранее уже говорилось, но напомним – код в блоке [[loop()]] выполняется на ядре 1. Поэтому вместо создания задачи для выполнения на ядре 1 вы можете просто вписать этот код в блок [[loop()]].}}


=== Демонстрация ===
=== Демонстрация ===
Строка 352: Строка 349:


<references />
<references />
{{Навигационная таблица/Портал/ESP32}}


[[Категория:ESP32]]
[[Категория:ESP32]]

Текущая версия от 09:26, 18 июня 2023

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Создание задач для использования обоих ядер ESP32

Плата ESP32 оснащена двумя 32-битными микропроцессорами Xtensa LX6 – ядром 0 и ядром 1. То есть, это 2-ядерная плата. По умолчанию код IDE Arduino запускается на ядре 1. В этом руководстве мы расскажем, как запустить код на втором ядре ESP32 при помощи создания заданий. Благодаря этому вы сможете запускать код одновременно на обоих ядрах, что сделает ESP32 многозадачной.

Примечание

Для того, чтобы добиться многозадачности, необязательно запускать оба ядра.

Необходимые компоненты

Введение

Плата ESP32 оснащена двумя 32-битными микропроцессорами Xtensa LX6. То есть это 2-ядерная плата:

  • Ядро 0;
  • Ядро 1;

По умолчанию код IDE Arduino запускается на ядре 1. В этом руководстве мы расскажем, как использовать второе ядро ESP32 при помощи создания задач. В результате вы сможете запускать код на обоих ядрах одновременно, тем самым сделав ESP32 многозадачной (впрочем, ESP32 можно сделать многозадачной и по-другому – не запуская оба ядра ESP32).

Когда мы загружаем код на ESP32 через IDE Arduino, он просто запускается и все – нам не нужно беспокоиться о том, на каком ядре выполняется этот код.

Но есть функция, с помощью которой можно узнать, на каком именно ядре запущен код:

xPortGetCoreID()

Воспользовавшись этой функцией в скетче IDE Arduino, вы увидите, что оба главных блока скетча – setup() и loop() – выполняются на ядре 1. Можете проверить это, загрузив на ESP32 код ниже.

/*********
  Руи Сантос
  Более подробно о проекте на: http://randomnerdtutorials.com  
*********/

void setup() {
  Serial.begin(115200);
  Serial.print("setup() running on core ");
           //  "Блок setup() выполняется на ядре "
  Serial.println(xPortGetCoreID());
}

void loop() {
  Serial.print("loop() running on core ");
           //  "Блок loop() выполняется на ядре "
  Serial.println(xPortGetCoreID());
}

Создаем задачи

IDE Arduino поддерживает использование для ESP32 операционной системы FreeRTOS. Она позволяет паралельно выполнять на микропроцессорах платы несколько независимых друг от друга задач.

Задачи – это фрагменты кода, выполняющие что-либо. К примеру, это может быть мигание светодиодом, выполнение сетевого запроса, считывание данных от датчика, публикация данных от датчика и т.д.

И мы можем создавать задачи, привязывая определенные фрагменты кода к определенным ядрам. Создавая задачу, вы можете выбрать, на каком ядре его нужно запустить и с каким приоритетом. Самый слабый приоритет – это «0». Процессор в первую очередь выполняет задачи со высоким приоритетом.

Итак, чтобы создать задачу, нужно сделать следующее:

  • Создаем идентификатор задачи. Например, «Task1»:
TaskHandle_t Task1;
  • В блоке setup() при помощи функции xTaskCreatePinnedToCore() создаем задачу и привязываем ее к ядру 0 (последний параметр). Кроме того, в параметрах этой функции задаются код самой задачи, приоритет и т.д. Выглядит она вот так:
xTaskCreatePinnedToCore(
 Task1code, /* Функция, содержащая код задачи */
 "Task1", /* Название задачи */
 10000, /* Размер стека в словах */
 NULL, /* Параметр создаваемой задачи */
 0, /* Приоритет задачи */
 &Task1, /* Идентификатор задачи */
 0); /* Ядро, на котором будет выполняться задача */
  • После создания задачи нам нужно создать функцию, содержащую код для созданной задачи. В нашем случае нужно создать функцию Task1code(). Выглядит она вот так:
Void Task1code( void * parameter) {
  for(;;){
    Код для задачи «Task1»  бесконечный цикл
    (...)
  }
}

Оператор for(;;) создает бесконечный цикл. Следовательно, эта функция будет работать так же, как и loop(). Она может пригодиться, например, если вам в коде нужен второй цикл loop().

Если вы во время выполнения кода хотите удалить созданную задачу, это можно сделать при помощи функции vTaskDelete(), указав в ее параметре идентификатор задачи (Task1):

vTaskDelete(Task1);

Теперь давайте посмотрим, как все это работает на практике.

Создание задач для разных ядер – пример

Чтобы продемонстрировать создание разных задач для разных ядер, давайте создадим две задачи для мигания двумя светодиодами с разной задержкой. Подключите два светодиода к ESP32 согласно схеме ниже:

Примечание

На этой схеме изображена 36-контактная версия платы ESP32 DEVKIT DOIT V1. Если у вас какая-то другая модель, обязательно сверьтесь с ее распиновкой.

Мы создадим две задачи, каждая из которых будет выполняться на собственном ядре.

  • Задача Task1 – на ядре 0
  • Задача Task2 – на ядре 1

Загрузите скетч ниже на ESP32.

/*********
  Руи Сантос
  Более подробно о проекте на: http://randomnerdtutorials.com  
*********/

TaskHandle_t Task1;
TaskHandle_t Task2;

// Контакты для светодиодов:
const int led1 = 2;
const int led2 = 4;

void setup() {
  Serial.begin(115200); 
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);

  // Создаем задачу с кодом из функции Task1code(),
  // с приоритетом 1 и выполняемую на ядре 0:
  xTaskCreatePinnedToCore(
                    Task1code,   /* Функция задачи */
                    "Task1",     /* Название задачи */
                    10000,       /* Размер стека задачи */
                    NULL,        /* Параметр задачи */
                    1,           /* Приоритет задачи */
                    &Task1,      /* Идентификатор задачи,
                                    чтобы ее можно было отслеживать */
                    0);          /* Ядро для выполнения задачи (0) */                  
  delay(500); 

  // Создаем задачу с кодом из функции Task2code(),
  // с приоритетом 1 и выполняемую на ядре 1:
  xTaskCreatePinnedToCore(
                    Task2code,   /* Функция задачи */
                    "Task2",     /* Название задачи */
                    10000,       /* Размер стека задачи */
                    NULL,        /* Параметр задачи */
                    1,           /* Приоритет задачи */
                    &Task2,      /* Идентификатор задачи,
                                    чтобы ее можно было отслеживать */
                    1);          /* Ядро для выполнения задачи (1) */
    delay(500); 
}

// Функция Task1code: мигает светодиодом каждые 1000 мс:
void Task1code( void * pvParameters ){
  Serial.print("Task1 running on core ");
           //  "Задача Task1 выполняется на ядре "
  Serial.println(xPortGetCoreID());

  for(;;){
    digitalWrite(led1, HIGH);
    delay(1000);
    digitalWrite(led1, LOW);
    delay(1000);
  } 
}

// Функция Task2code: мигает светодиодом каждые 700 мс:
void Task2code( void * pvParameters ){
  Serial.print("Task2 running on core ");
           //  "Задача Task2 выполняется на ядре "
  Serial.println(xPortGetCoreID());

  for(;;){
    digitalWrite(led2, HIGH);
    delay(700);
    digitalWrite(led2, LOW);
    delay(700);
  }
}

void loop() {
  
}

Как работает этот код

Примечание

В этом коде создается две задачи. Выполнение одной из них привязывается к ядру 0, а другое – к ядру 1. По умолчанию Arduino-скетчи выполняются на ядре 1, поэтому код для задачи Task2 можно просто написать в блок loop(), не создавая для него отдельной задачи. Но в нашем случае мы создадим обе задачи – в обучающих целях. Впрочем, такой подход – организация кода по задачам – порой более практичен, но это зависит от особенностей проекта.

Код ниже начинается с создания идентификаторов для обеих задач – Task1 и Task2.

TaskHandle_t Task1;
TaskHandle_t Task2;

Задаем контакты GPIO2 и GPIO4 как контакты для подключения светодиодов.

const int led1 = 2;
const int led2 = 4;

В блоке setup() инициализируем монитор порта на скорости 115200 бод.

Serial.begin(115200);

Переключаем контакты светодиодов в режим «OUTPUT» (т.е. в режим вывода данных).

pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);

Затем создаем задачу Task1 при помощи функции xTaskCreatePinnedToCore().

xTaskCreatePinnedToCore(
                    Task1code,   /* Функция задачи */
                    "Task1",     /* Название задачи */
                    10000,       /* Размер стека задачи */
                    NULL,        /* Параметр задачи */
                    1,           /* Приоритет задачи */
                    &Task1,      /* Идентификатор задачи,
                                    чтобы ее можно было отслеживать */
                    0);          /* Ядро для выполнения задачи (0) */

Эта задача будет выполнять код из функции Task1code(). Значит далее в коде нам нужно будет создать эту функцию. Мы даем ей приоритет 1 и привязываем к ядру 0.

Далее аналогичным образом создаем задачу Task2.

xTaskCreatePinnedToCore(
                    Task2code,   /* Функция задачи */
                    "Task2",     /* Название задачи */
                    10000,       /* Размер стека задачи */
                    NULL,        /* Параметр задачи */
                    1,           /* Приоритет задачи */
                    &Task2,      /* Идентификатор задачи,
                                    чтобы ее можно было отслеживать */
                    1);          /* Ядро для выполнения задачи (1) */

Создав задачи, создаем функции с кодом для этих задач.

// Функция Task1code: мигает светодиодом каждые 1000 мс:
void Task1code( void * pvParameters ){
  Serial.print("Task1 running on core ");
           //  "Задача Task1 выполняется на ядре "
  Serial.println(xPortGetCoreID());

  for(;;){
    digitalWrite(led1, HIGH);
    delay(1000);
    digitalWrite(led1, LOW);
    delay(1000);
  } 
}

Функция в задаче Task1 называется Task1code(), но вы можете назвать ее как заблагорассудится. В целях отладки сначала она напечатает в мониторе порта ядро, на котором выполняется задача:

Serial.print("Task1 running on core ");
         //  "Задача Task1 выполняется на ядре "
Serial.println(xPortGetCoreID());

Затем создаем бесконечный цикл, который работает аналогично loop() в скетче IDE Arduino. В этом цикле мигаем светодиодом «LED1» каждую секунду.

То же самое происходит и в функции для задачи Task2, но время мигания светодиода – другое.

// Функция Task2code: мигает светодиодом каждые 700 мс:
void Task2code( void * pvParameters ){
  Serial.print("Task2 running on core ");
           //  "Задача Task2 выполняется на ядре "
  Serial.println(xPortGetCoreID());

  for(;;){
    digitalWrite(led2, HIGH);
    delay(700);
    digitalWrite(led2, LOW);
    delay(700);
  }
}

Наконец, в блоке loop() не пишем ничего.

void loop() { 

}
Примечание

Ранее уже говорилось, но напомним – код в блоке loop() выполняется на ядре 1. Поэтому вместо создания задачи для выполнения на ядре 1 вы можете просто вписать этот код в блок loop().

Демонстрация

Загрузите этот код на ESP32, но перед этим убедитесь, что выбрали в IDE Arduino правильные плату и COM-порт.

Откройте монитор порта на скорости 115200 бод. Там должна появиться примерно такая информация:

Как и ожидалось, задача Task1 выполняется на ядре 0, а Task2 – на ядре 1.

В результате один из светодиодов, подключенных к ESP32, должен мигать раз в секунду, а другой – с периодичностью в 700 мс.

Итого

Итак, в этой статье мы узнали следующее:

  • Плата ESP32 имеет два ядра;
  • Скетчи IDE Arduino по умолчанию запускаются на ядре 1;
  • Чтобы использовать ядро 0, необходимо создать задачи;
  • Привязка задач к ядрам осуществляется с помощью функции xTaskCreatePinnedToCore();
  • С помощью этой функции можно запустить две разные задачи на двух разных ядрах одновременно и независимо друг от друга;

В этом руководстве мы продемонстрировали создание задач на скетче-примере с двумя светодиодами. Но вы можете использовать этот метод и для более продвинутых проектов. К примеру, одно ядро можно использовать для чтения данных с датчиков, а другое – для публикации этих данных в веб-сервер, подключенный к системе домашней автоматизации.

См.также

Внешние ссылки