ESP32 содержит в себе два 32-битных микропроцессора Xtensa LX6, поэтому он двухъядерный. Когда мы запускаем код в Arduino IDE, по умолчанию он запускается на ядре 1. В этой статье мы будем использовать второе ядро ESP32, создавая задачи. Вы сможете запускать фрагменты кода одновременно на обоих ядрах и сделать свой ESP32 многозадачным.
Когда мы загружаем код в ESP32 с помощью Arduino IDE, он просто запускается - нам не нужно беспокоиться, какое ядро выполняет код.
Есть функция, которую вы можете использовать, чтобы определить, на каком ядре выполняется код:
xPortGetCoreID ()
Если вы используете эту функцию в скетче Arduino, вы увидите, что и setup (), и loop () работают на ядре 1. Протестируйте его сами, загрузив следующий скетч в плату ESP32.
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
Serial.print("loop() running on core ");
Serial.println(xPortGetCoreID());
}
Мигание встроенным светодиодом происходит на ядре 1.
Создание задачи:
IDE Arduino поддерживает FreeRTOS для ESP32, которая является операционной системой реального времени. Это позволяет нам обрабатывать несколько задач параллельно, которые выполняются независимо. Задачи - это фрагменты кода, которые что-то выполняют. Например, это может быть мигание светодиода, выполнение сетевого запроса, измерение показаний датчика, публикация показаний датчика и.т.д. Чтобы назначить определенные части кода конкретному ядру, вам необходимо создать задачи. При создании задачи вы можете выбрать, в каком ядре она будет запускаться, а также ее приоритет. Значения приоритета начинаются с 0, который является самым низким приоритетом. Процессор сначала выполнит задачи с более высоким приоритетом.
Для создания задач вам необходимо выполнить следующие шаги:
1. Создайте дескриптор задачи. Пример для Task1:
TaskHandle_t Task1;
2. В setup () создайте задачу, назначенную конкретному ядру, с помощью функции xTaskCreatePinnedToCore . Эта функция принимает несколько аргументов, включая приоритет и ядро, на котором должна выполняться задача (последний параметр).
xTaskCreatePinnedToCore(
Task1code, /* Функция для реализации задачи */
"Task1", /* Название задачи*/
10000, /* Размер стека */
NULL, /* Входной параметр задачи */
0, /* Приоритет задачи*/
&Task1, /* Дескриптор задачи */
0); /* Ядро, в котором должна выполняться задача */
3. После создания задачи необходимо создать функцию, содержащую код созданной задачи. В этом примере вам нужно создать функцию Task1code () . Вот как выглядит функция задачи:
Void Task1code( void * parameter) {
for(;;){
Код для задачи 1 - бесконечный цикл
(...)
}
}
For (;;) создает бесконечный цикл. Итак, эта функция работает аналогично функции loop () . Вы можете использовать его, например, как второй цикл в вашем коде.
Если во время выполнения кода вы хотите удалить созданную задачу, вы можете использовать функцию vTaskDelete () , которая принимает дескриптор задачи ( Task1 ) в качестве аргумента:
vTaskDelete(Task1);
Давайте посмотрим, как эти концепции работают, на простом примере.
Подключим еще один светодиод на PIN 13 и протестируем 2-е ядро
Прошейте ESP32 следующим скетчем
Цикл void loop() пустой - весь код выполняется в двух задачах которые работают полностью независимо друг от друга.
В коде мы создаем две задачи и назначаем одну задачу ядру 0, а другую - ядру 1. По умолчанию эскизы Arduino выполняются на ядре 1. Итак, вы можете написать код для Task2 в цикле () (не нужно было создавать еще одну задачу). В этом случае мы создаем две разные задачи.
Однако, в зависимости от требований вашего проекта, может быть более практичным организовать код по задачам, как показано в этом примере.
Код начинается с создания дескриптора задачи для Task1 и Task2, называемого Task1 и Task2.
Затем создайте Task1 с помощью функции xTaskCreatePinnedToCore () :
Task1 будет реализован с помощью функции Task1code () . Итак, нам нужно создать эту функцию позже в коде. Мы присвоили задаче приоритет 1 и закрепили ее за ядром 0.
Таким же методом создаем Task2:
После создания задач нам нужно создать функции, которые будут выполнять эти задачи.
Функция Task1 называется Task1code () (вы можете называть ее как хотите). В целях отладки сначала печатаем ядро, в котором выполняется задача
Затем у нас есть бесконечный цикл, аналогичный loop () в скетче Arduino. В этом цикле мы мигаем LED1 каждую секунду.
То же самое происходит и с Task2, но мы мигаем светодиодом с разной задержкой.
Функция loop () пуста.
В итоге:
- ESP32 - двухъядерный;
- По умолчанию скетчи Arduino запускаются на ядре 1;
- Для использования ядра 0 необходимо создавать задачи;
- Вы можете использовать функцию xTaskCreatePinnedToCore (), чтобы привязать конкретную задачу к определенному ядру;
- Используя этот метод, вы можете запускать две разные задачи независимо и одновременно, используя два ядра.
В этом статье был приведен простой пример со светодиодами. Идея состоит в том, чтобы использовать этот метод в более сложных проектах с реальными приложениями. Например, может быть полезно использовать одно ядро для снятия показаний датчиков, а другое - для публикации этих показаний в системе домашней автоматизации.
Код примеров качайте по этой ссылке: Всем Удачи!
Вы можете ознакомиться с полным списком статей на моем канале.