0°

(十四)ESP32:在特定的核心上运行代码

这篇文章的目的是解释如何使用Arduino环境支持在ESP32的特定核心上运行代码。

介绍

这篇文章的目的是解释如何使用Arduino环境支持在ESP32的特定核心上运行程序。更确切地说,我们将分析如何创建固定到特定核心的FreeRTOS任务。

ESP32的有趣特性之一是它有两个 Tensilica LX6内核 ,我们可以利用它来执行我们的代码,具有更高的性能和更多功能。

在此之前的文章中,我们已经知道其中的核心任务是执行,使用的方式xPortGetCoreID 功能。我们还看到,当我们使用xTaskCreate 函数创建任务时,它可能被分配给两个ESP32核心中的任何一个。

因此,为了将任务的执行固定到特定的ESP32内核,我们需要执行xTaskCreatePinnedToCore 函数,该函数接收核心ID执行位置作为其参数之一。

现在,我们将给出一个非常简单的示例,说明如何使用前面提到的函数在ESP32的两个不同内核上启动一个非常基本的任务。

确认多核执行

由于我们将开始使用ESP32多核代码执行,我们需要有一种方法来确认API正确工作,我们正在获得所需的结果。

因此,从我们分析如何获得任务优先级的帖子中,我们得到设置和主循环函数都以1的优先级执行。我们没有进一步分析它,所以我们还不知道函数是否是两个独立的FreeRTOS任务,但现在这是无关紧要的。

从关于获取任务执行核心的帖子中,我们还看到初始化函数和主循环函数都在核心1上执行。此外,我们可以在此处确定执行是固定的,因此在执行程序期间核心不会发生变化。

其他重要方面是,在FreeRTOS上,任务具有指定的优先级,调整程序使用该优先级来决定将运行哪个任务。准备运行的高优先级任务将优先于优先级较低的任务,这意味着只要优先级较高的任务可以运行,优先级较低的任务就不会拥有CPU。

因此,如果我们将这种情况发生在高优先级任务永远不会离开CPU的极端情况下,就会出现极端情况,这意味着优先级较低的任务永远不会获得对CPU的访问权限以及执行代码的进度 。

但是,如果有两个可用核心,则两个任务都应该能够运行,即使优先级较高的任务永远不会阻止其执行,只要它们被分配给不同的核心即可。

出于实现目的,我们需要考虑FreeRTOS优先级从0到N分配,其中较低的数字对应较低的优先级。因此,最低优先级为0。

考虑到这一点,我们将设计的应用程序将允许我们在设置功能中在核心0或1上启动FreeRTOS任务。此任务的优先级为0,低于设置和主循环功能。这意味着我们将创建的此FreeRTOS任务将具有较低的优先级。

然后,在Arduino主循环函数上,我们将进行无限循环,没有任何类型的延迟或让步机制,以便它维持CPU。请记住,Arduino主循环在核心1上运行。

最后,我们将分析在ESP32核心1上执行时任务是否会极端,并且能够在核心0上执行时运行,这是预期的行为。

初始化设置

首先,我们将声明一个全局变量,该变量将包含将固定我们将启动的FreeRTOS任务的核心数。这确保了我们可以在测试代码时轻松更改它。

请注意,我们将运行代码两次,将值0和1分配给此变量。我们本可以创建一种更复杂的方法,作为串行输入接收应该固定任务的核心,但我希望保持代码尽可能简单,以专注于多核功能:

static int taskCore = 0;

然后,我们将进入初始化函数。开始时,我们打开一个波特率为112500的串行连接,以便向Arduino IDE串行监视器发送一些消息。

之后我们将打印我们正在测试的核心数,它存储在先前声明的全局变量中:

Serial.begin(112500);
delay(1000);
 
Serial.print("Starting to create task on core ");
Serial.println(taskCore);

现在我们将启动FreeRTOS任务,将其分配给ESP32的特定核心。如前所述,我们将使用 xTaskCreatePinnedToCore函数。此函数使用与xTaskCreate完全相同的参数,并在末尾使用另一个参数来指定任务应运行的核心。如果您在创建任务和所需输入的过程中需要帮助,请查看以前的帖子,其中包含有关如何使用xTaskCreate函数的详细说明。

我们将在一个名为coreTask的函数中实现该任务。我们将检查后者的代码。另外,如上一节所述,我们应该将优先级0归于任务,因此它低于设置和主循环。我们不需要担心传递任何输入参数或存储任务句柄。

在此调用之后,我们将向串行端口打印另一条消息,指示已创建任务:

xTaskCreatePinnedToCore(
                    coreTask,   /* Function to implement the task */
                    "coreTask", /* Name of the task */
                    10000,      /* Stack size in words */
                    NULL,       /* Task input parameter */
                    0,          /* Priority of the task */
                    NULL,       /* Task handle. */
                    taskCore);  /* Core where the task should run */
 
Serial.println("Task created...");

有了这个,我们完成了设置功能。

主循环

现在我们将设计主循环,这将非常简单。我们首先在串口打印一条消息,指示我们正在启动主循环,所以我们现在处理代码的执行点。

之后我们将进行无限循环,其中没有任何代码。至关重要的是我们不要在其中放置任何类型的原型或延迟函数,因此最好的方法是将其留空。这样,我们现在调整程序将维护分配给它的CPU执行:

void loop() {
 
  Serial.println("Starting main loop...");
  while (true){}
 
}

任务功能

任务功能也很简单。我们将只打印一条消息,指示分配给它的核心,使用xPortGetCoreID获得。当然,这必须对应于全局变量中指定的核心。

我们将首先使用此信息构建字符串:

String taskMessage = "Task running on core ";
taskMessage = taskMessage + xPortGetCoreID();

然后我们做一个无限循环,我们打印这个消息,每次迭代都有一个小延迟。在这种情况下,由于它具有较低的优先级,因此在该任务中延迟没有问题。因此,它不会影响我们的应用程序和演示的目的。

while(true){
        Serial.println(taskMessage);
        delay(1000);
}

完整的代码

为了便于测试,下面显示了完整的代码。您可以复制并粘贴它,它应该编译并执行正常。我们从分配给核心1的全局变量开始:

当前内容已被隐藏,您需要登录才能查看

测试结果

要测试程序结果,只需使用Arduino IDE将代码上传到ESP32板。我们将首先测试核心1上任务的创建。您可以得到类似于图1的结果:

ESP32:在特定的核心上运行代码

图1 – 将任务分配给ESP32的核心1时程序的输出

可以看出,在安装功能上启动的FreeRTOS任务没有输出。由于我们将其固定到核心1并为其分配优先级0,因此Arduino主循环将始终执行,因为它具有更高的优先级(等于1)并且还在核心1上运行。

现在,将全局变量更改为0并重新上载代码。您现在可以得到不同的结果,如图2所示:

ESP32:在特定的核心上运行代码

图2 – 将任务分配给ESP32的核心0时程序的输出

在这种情况下,我们从setup函数中启动的任务中获取打印件,该函数在核心0上运行。这一事实与使用xPortGetCoreID函数获得的信息一致并打印到串行监视器。

在这种情况下,虽然主循环正在执行并锁定核1的资源并具有更高的优先级,但新任务可以在核0上执行,因为它是空闲的。所以,没有极端,一切正常。

最后的笔记

正如我们从本教程的结果中看到的那样,ESP32的两个内核中的代码执行都在起作用。然而,在编写本文时,我们需要使用FreeRTOS和ESP32 IDF原语才能使其工作,尽管我们使用的是Arduino环境。

在两个内核中执行代码的可能性开启了各种各样的可能性,包括许多代码执行的优化。希望我们将开始看到更多应用程序探索ESP32双核执行的全部潜力。

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论