ESP32 - Use of both CPU cores for your own projects

Today I want to show which "power resources" are in at ESP32. It is much less known that our ESP32 microcontroller is not a single processor but rather a multi-processor with 2 cores. In the ESP are working 2 Xtensa 32-bit LX6 CPU sharing RAM and ROM. Thus it differs from its predecessor, the ESP8266. The two cores have different names. CPU 0 is also called Protocol CPU (PRO_CPU) and CPU 1 Application CPU (APP_CPU). The CPU 0 controls the WLAN, Bluetooth and other internal peripherals such as SPI, I2C, ADC, etc., while the CPU 1 is available to our user program. Skits that we write in the main loop and upload to the ESP are always executed on the CPU 1 without exception.The APP_CPU (CPU 0) is omitted by default for the application code. The following diagram shows the standard distribution of tasks between the CPUs:

It can be seen that 2 core, which almost doubles the performance of ESP32, is not directly available for free use.

However, the ESP Framework also provides functions with the Arduino IDE that allow to distribute individual tasks to the ESP32 CPUs and thus to the CPU. 

TaskHandle_t name of the taskhadle;

 to disposal. To create a new task, we use the function xTaskCreatePinnedToCore with the following options:

 

xTaskCreatePinnedToCore (
CoreTask0, / - Task function to be called - /
"Task1", / - Name of the task Any Name - /
1000, / - available heap memory of the task - /
NULL, / - possibly parameter of the task - /
1, / - task priority - /
& Core0TaskHnd, / - used task handle - /

 

Our goal is to execute custom code as a task on the CPU1, so as shown below, our code runs as a task on the CPU1 independent of the CPU0, as illustrated in the following image:

 

 

We now enter the following example code in our IDE and upload it to the ESP32:

 

 

TaskHandle_t  Core0TaskHnd ;  
TaskHandle_t  Core1TaskHnd ; 

Void Setup() 
{   Serial.Begin(9600);     xTaskCreatePinnedToCore(CoreTask0,"CPU_0",1000,Null,1,&Core0TaskHnd,0);   xTaskCreatePinnedToCore(CoreTask1,"CPU_1",1000,Null,1,&Core0TaskHnd,1);
}

Void Loop() 
{   Serial.Print ("Application CPU is on core:");   Serial.println (xPortGetCoreID());   Delay (500);
}  

Void CoreTask0( Void * Parameter ) 
{    for (;;)    {      Serial.Print("CoreTask0 runs on Core: ");      Serial.println(xPortGetCoreID());      Yield();     Delay (600);   } 
} 

Void CoreTask1( Void * Parameter ) 
{    for (;;)    {      Serial.Print("CoreTask1 runs on Core: ");      Serial.println(xPortGetCoreID());      Delay (700);   } 
}

 

 

.

With the ESP internal function xPortGetCoreID() we can get the core number on which our code section is currently running. This core number can be either 0 or 1. We use this feature to output serial information about which core the task is currently running on:

Serial output shows which task is running on which core

We now see in the output that a total of 3 tasks are running. A task named CoreTask 0 on CPU 0, a task named CoreTask1 on CPU 1, and our main log task (loop) on Core 1.

So far, everything sounds too good to be true. In fact, we have a problem with the use of CPU 0 that we have to pay attention to: As shown on the upper image, the kernel protocol task is also running on the CPU 0. This task also takes care of the WiFi and TCP/IP stack. If this longer time is not running because, for example, our task requires too much CPU time, the system can become unstable and crash overall. So we have to make sure that our own task receives no delay statements or only very small delay statements, so that the kernel protocol task is allocated enough computation time.

 

em attentive reader will have noticed another problem of the code: The program generates 3 tasks, which run independently on different CPU's, but still share a resource (the COM port of the ESP). Since the tasks do not "know" anything from each other and therefore do not know when a resource is occupied or modified by another task, collisions can occur here. These cause an unpredictable result because it is not possible to determine exactly when the resource is using which task. Such constellations can then, at best, be either in a programmatic Race Condition or even in a Deadlock Ends. What exactly is a deadlock explains the Philosopher's problem, where 5 philosophers sit around a spaghetti table, very vivid. I want to avoid mutually exclusion mutual issues through mutual exclusion ( Mutex ) and collisions when accessing shared resources search as variables or interfaces avoid,

That puts us in the middle of the topic of interprocess communication . We've learned a lot about tasks and multitasking.

More about task generation and the Real Time Operation System (RTOS) is available in the second part of this series or on ::

https://exploreembedded.com/wiki/index.php?title=Hello%20World%20with%20ESP32%20Explained

And now have fun experimenting.


 

 

Esp-32Grundlagen software

3 comments

doob

doob

{
Serial.begin(9600);
xTaskCreatePinnedToCore(CoreTask0,“CPU_0”,1000,NULL,1,&Core0TaskHnd,0);
xTaskCreatePinnedToCore(CoreTask1,“CPU_1”,1000,NULL,1,&Core0TaskHnd,1);
}
noch ein Tippfehler? sollte es beim zweiten pinning nicht Core1TaskHnd heißen?

Sven

Sven

CPU 1 ist für das Anwenderprogramm verantwortlich.

Der Tippfehler wird bestimmt zeitnah korrigiert.

veit

veit

Diese Namentliche Unterscheidung wird getroffen, um zu verdeutlichen, dass die CPU 0 das WLAN, Bluetooth und andere interne Peripheriegeräte wie SPI, I2C, ADC usw. steuert, während die CPU 0 für unser Anwenderprogramm zur Verfügung steht.

bitte korrigieren …. irgendwas müsste von cpu 1 gemacht werden

Leave a comment

All comments are moderated before being published