Oggi voglio mostrare quali "Risorse performance" bloccate nell'SP32. È molto meno noto che il nostro microcontrollore ESP32 non è un singolo processore, ma un multiprocessore con 2 nuclei. Nell'esp, 2 cpus LX6 a 32 XTensa a 32 bit, che condividono la RAM e ROM. Questo si distingue dal suo predecessore, l'ESP8266. I due nuclei hanno nomi diversi. La CPU 0 è anche chiamata Protocollo CPU (Pro_CPU) e CPU 1 Application Application CPU (App_CPU). La CPU 0 controlla Wi-Fi, Bluetooth e altre periferiche interne come SPI, I2C, ADC, ecc., Mentre la CPU 1 è disponibile per il nostro programma utente. Schizzi che scriviamo nel ciclo principale e il caricamento in ESP verranno eseguiti senza eccezioni sulla CPU 1, mentre APP_CPU (CPU 0) viene omessa per impostazione predefinita per il codice dell'applicazione. Il seguente diagramma mostra la distribuzione predefinita delle attività sulle CPU:
Si può vedere che 2 core quasi raddoppiano le prestazioni dell'ESP32 non sono direttamente disponibili per uso gratuito.
Tuttavia, il framework ESP fornisce anche funzioni con l'IDE Arduino, che consente di distribuire singole attività sulle CPU ESP32 e quindi alla CPU.
Taskhandle_t namedamesteskhadle; |
a disposizione. Per creare un nuovo compito, utilizziamo la funzionalità XTASKCreatePinnedtocore utilizzando le seguenti opzioni:
Xtaskcreatepinnedtocore ( |
Il nostro obiettivo è quello di eseguire il codice personalizzato come compito sulla CPU1. Pertanto, il nostro codice viene eseguito come compito sulla CPU1 indipendentemente dal CPU0, come mostrato nella figura seguente:
Ora entriamo nel seguente codice di esempio nel nostro IDE e lo invitiamo a ESP32:
Taskhandle_t. Core0teekhnd. ; Taskhandle_t. Core1taskhnd. ; vuoto impostare() { Seriale.Inizio(9600); Xtaskcreatepinnedtocore.(Coretask0.,"CPU_0",1000,ZERO,1,&Core0teekhnd.,0); Xtaskcreatepinnedtocore.(Coretask1.,"CPU_1",1000,ZERO,1,&Core0teekhnd.,1); } vuoto ciclo continuo() { Seriale.Stampa ("La CPU dell'applicazione è su Core:"); Seriale.Println. (XportgetCoreID.()); ritardo (500); } vuoto Coretask0.( vuoto * parametro ) { per (;;) { Seriale.Stampa("CoreTask0 funziona con il nucleo:"); Seriale.Println.(XportgetCoreID.()); cedimento(); ritardo (600); } } vuoto Coretask1.( vuoto * parametro ) { per (;;) { Seriale.Stampa("CoreTask1 funziona al nucleo:"); Seriale.Println.(XportgetCoreID.()); ritardo (700); } }
.
Con la funzione interna ESP XPortgetCoreID () possiamo spendere il numero di base su cui è in esecuzione la nostra sezione del codice. Questo numero di base può accettare il valore 0 o 1. Utilizziamo questa funzione per fornire informazioni seriali su quale base l'attività è in esecuzione in questo momento:
Ora vediamo i 3 compiti eseguiti nel problema. Un compito denominato "CoreTask 0" su CPU 0, un'attività denominata "CoreTask1" sulla CPU 1 e la nostra commissione Slop principale (loop) su Core 1.
Finora, tutto sembra troppo bello per essere vero. Infatti, con l'uso della CPU 0, abbiamo un problema che dobbiamo prestare attenzione: come mostrato nell'immagine in alto, l'attività del protocollo del kernel viene eseguita sulla CPU 0. Questo compito si occupa della pila WiFi e TCP / IP tra le altre cose. Se questo tempo più lungo non è in esecuzione, perché, ad esempio, la nostra attività richiede troppo tempo di CPU, il sistema può diventare instabile nel suo complesso e crash. Quindi dobbiamo assicurarci che il nostro compito non riceve alcuna o solo le massime dichiarazioni di ritardo molto su piccola scala, in modo che l'attività del protocollo del kernel abbia un tempo di calcolo sufficiente.
I lettori attentati em avranno notato un altro problema di codice: il programma genera 3 attività che sono indipendentemente l'una dall'altra, ad esempio, su diverse CPU, ma condividono ancora una risorsa (il porto COM degli ESP). In linea di principio, i compiti non sanno nulla di "conoscere" e quindi, quando una risorsa è occupata o cambiata da un altro compito., Può venire qui alle collisioni. Questi causano un risultato non prevedibile, poiché non può essere determinato con precisione in base a quale attività utilizza la risorsa. Tali costellazioni possono quindi al meglio sia in un programmatico Condizione di gara o anche in uno deadlock finire. Che cosa è esattamente un deadlock, lo spiega Problema filosofico, dove 5 filosofi sono un tavolo spaghetti, molto chiaro. Voglio evitare problemi reciproci con l'esclusione reciproca (mutex) e le collisioni quando si accede alla ricerca delle risorse condivise come variabili o interfacce.
Questo è dove siamo nel mezzo del tema della comunicazione di interprocesso. Abbiamo imparato molto su compiti e multitasking.
Maggiori informazioni sulla generazione di attività e il sistema operativo in tempo reale (RTOS) può essere trovato nella seconda parte di questa serie o su:
https://exploreembedded.com/wiki/index.php?title=Hello%20World%20with%20ESP32%20Explained
E ora divertiti a sperimentare.
5 commenti
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
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
{
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
CPU 1 ist für das Anwenderprogramm verantwortlich.
Der Tippfehler wird bestimmt zeitnah korrigiert.
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