ESP32 – Nutzung beider CPU Kerne für eigene Projekte

Aujourd'hui, je veux montrer quelles «ressources de performance» bloquées dans l'ESP32. Il est beaucoup moins connu que notre microcontrôleur ESP32 n'est pas un processeur unique, mais un multiprocesseur avec 2 cœurs. Dans l'ESP, 2 XTENSA 32 bits LX6 CPU fonctionnent, qui partagent la RAM et la ROM. Cela se distingue de son prédécesseur, l'ESP8266. Les deux noyaux ont des noms différents. CPU 0 est également appelé processeur CPU (PRO_CPU) et CPU 1 Application CPU (App_CPU). La CPU 0 contrôle Wi-Fi, Bluetooth et d'autres périphériques internes tels que SPI, I2C, ADC, etc., tandis que la CPU 1 est disponible pour notre programme utilisateur. Les croquis que nous écrivons dans la boucle principale et que nous avons téléchargé sur ESP seront exécutés sans exception sur la CPU 1, tandis que l'app_cpu (CPU 0) est omis par défaut pour le code de l'application. Le diagramme suivant montre la distribution par défaut des tâches sur les CPU:

On peut voir que 2 cœurs presque doubler les performances d'ESP32 ne sont pas directement disponibles pour une utilisation gratuite.

Toutefois, le framework ESP fournit également des fonctions avec l'IDE Arduino, qui permet de distribuer des tâches individuelles sur les processeurs ESP32 et donc de la CPU.

TâcheHandle_t namedestaskhadle;

élimination. Pour créer une nouvelle tâche, nous utilisons la fonction XTASKCreatepInnedtoCore à l'aide des options suivantes:

 

Xtaskcreepinnedtocore (
Coretask0, / - fonction de tâche à appeler - /
"Task1", ​​/ - Nom de la tâche n'importe quel nom /
1000, / - Mémoire de tas de la tâche disponible - /
Zéro, / - paramètres possibles de la tâche - /
1, / - Priorité de la tâche - /
& CORE0TEEKHND, / - Poignée de tâche d'occasion - /

 

Notre objectif est d'exécuter du code personnalisé comme une tâche sur la CPU1. Par conséquent, notre code est exécuté comme une tâche sur la CPU1 indépendamment de la CPU0, comme indiqué dans la figure suivante:

 

 

Nous entrons maintenant le code d'échantillon suivant dans notre IDE et nous l'invitons à l'ESP32:

 

 

Taskhandle_T  Core0teekhnd ;  
Taskhandle_T  Core1taSaskhnd ; 

vide d'installation() 
{
  En série.Commencer(9600);  
  Xtaskcreepinnedtocore(Corétask0,"CPU_0",1000,ZÉRO,1,&Core0teekhnd,0);
  Xtaskcreepinnedtocore(Corétask1,"CPU_1",1000,ZÉRO,1,&Core0teekhnd,1);
}

vide boucle() 
{
  En série.imprimer ("L'application de l'application est sur le noyau:");
  En série.Imprimeur (Xporttcoreid());
  retard (500);
}  

vide Corétask0( vide * paramètre ) 
{ 
  pour (;;) 
  { 
    En série.imprimer("Coreask0 fonctionne sur noyau:"); 
    En série.Imprimeur(Xporttcoreid()); 
    cédant();
    retard (600);
  } 
} 

vide Corétask1( vide * paramètre ) 
{ 
  pour (;;) 
  { 
    En série.imprimer("Coreask1 fonctionne sur noyau:"); 
    En série.Imprimeur(Xporttcoreid()); 
    retard (700);
  } 
}

 

 

.

Avec la fonction interne ESP XPORTCOREID (), nous pouvons dépenser le numéro de base sur lequel notre section de code est en cours d'exécution. Ce numéro de base peut soit accepter la valeur 0 ou 1. Nous utilisons cette fonctionnalité pour donner des informations série sur lesquelles la tâche principale est en cours d'exécution:

La sortie série montre quelle tâche fonctionne sur quel noyau

Nous voyons maintenant les 3 tâches courues dans la question. Une tâche nommée "CORETASK 0" sur la CPU 0, une tâche nommée "CORETASK1" sur la CPU 1 ainsi que notre tâche principale de slop (boucle) sur le noyau 1.

Jusqu'à présent, tout semble trop agréable d'être vrai. En fait, avec l'utilisation de la CPU 0, nous avons un problème que nous devons faire attention: comme indiqué sur la photo supérieure, la tâche du protocole du noyau s'exécute sur la CPU 0. Cette tâche s'occupe de la pile WiFi et TCP / IP entre autres. Si cela ne fonctionne pas plus longtemps, car, par exemple, notre tâche exige trop de temps processeur, le système peut devenir instable dans son ensemble et un crash. Nous devons donc nous assurer que notre propre tâche ne reçoit aucune ou seule des énoncés maximaux de retard de très petite taille, de sorte que la tâche du protocole du noyau obtient une durée de calcul suffisante.

 

Les lecteurs attentionnés des EM auront remarqué un autre problème de code: le programme génère 3 tâches indépendamment les unes des autres, par exemple, sur différents processeurs, mais partage toujours une ressource (le port COM de l'ESP). En principe, les tâches ne savent rien de "savoir" et donc, lorsqu'une ressource est occupée ou modifiée par une autre tâche., Il peut venir ici aux collisions. Celles-ci provoquent un résultat non prévisible, car il ne peut pas être déterminé avec précision au cours de laquelle la tâche utilise la ressource. De telles constellations peuvent alors au mieux soit dans une programmation Condition de course ou même dans un impasse finir. Quelle est exactement une impasse, explique que Problème de philosophe, où 5 philosophes sont une table de spaghetti, très claire. Je veux éviter les problèmes mutuels avec l'exclusion mutuelle (mutex) et les collisions lors de l'accès aux ressources partagées, recherchez comme variables ou interfaces.

C'est là que nous sommes au milieu du sujet de la communication interprocessée. Nous avons beaucoup appris sur les tâches et le multitâche.

Plus sur la génération de tâches et le système d'exploitation en temps réel (RTO) se trouvent dans la deuxième partie de cette série ou sur:

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

Et maintenant amusez-vous à expérimenter.


 

Esp-32Grundlagen software

5 commentaires

PiffPoff

PiffPoff

“Wir müssen also dafür Sorge trage, dass unser eigener Task keine oder nur maximal sehr klein bemessene delay-Anweisungen erhält, damit der Kernel Protokoll Task genügend Rechenzeit zugewiesen bekommt.”

Wenn der andere Task mehr Rechenzeit bekommen soll, dann ist es doch gut wenn der eigene task möglichst lange suspended ist.
Also ist es doch gut wenn der eigene task viele/lange delays hat, oder?

Siggi

Siggi

Hallo und guten Tag,
herzlichen Dank für die Erklärung. Hat mir sehr dabei geholfen, ein flackerndes Display in den Griff zu bekommen. Unabhängig von der Berechnung wird jetzt die Anzeige über CPU0 ausgegeben.
Kleine Anmerkung zur Ressourcenaufteilung:
Wird eine Funktion aus dem Core Task aufgerufen, wird diese Funktion auch in der zugehörigen CPU ausgeführt.

Liebe Grüße Siggi

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

Laisser un commentaire

Tous les commentaires sont modérés avant d'être publiés