物联网开发 14 ESP8266 使用FreeRTOS函数创建任务

 
简 介
本文主要介绍如何使用FreeRTOS函数创建任务。因为涉及到一些比较复杂的概念,我们将从一个非常简单的示例开始。在这个例子中,我们会先创建两个打印“Hello World”消息的任务,然后将它们删除。
 
 
 
 
 
 
setup函数和循环代码
在setup函数中,首先要打开一个串行连接,用于输出测试程序的运行结果。这是常规的Arduino功能。
 
 
[AppleScript]  纯文本查看  复制代码
?
1
2
3
Serial.begin ( 112500 ) ;
 
delay ( 1000 ) ;
 
 
然后,调用 xTaskCreate函数以创建任务。该函数的参数如下所述[1]:
 
TaskCode:我们需要把一个函数指针(该函数负责任务的实现)传递给这个参数。我们将创建两个函数(TaskOne和TaskTwo),它们的定义在后面的代码中,我们将把这两个函数传递给这个参数。
 
 
TaskName:任务的名称,以字符串表示。我们要创建的两个任务名称分别为“TaskOne”和“TaskTwo”。
 
 
StackDepth:任务堆栈大小,以字节数表示。尽管可以进行一些计算,但是并没有一种简单的方式能够确定任务的大小[2]。在简单的示例中,我们通常会使用一个足够大的数值。
 
 
Update:在最初的帖子里,提到StackDepth是以字(word)数表示的, FreeRTOS xTaskCreate文档也是如此。但是,IDF版的具体实现有所不同,堆栈深度实际上是以字节数表示的,如IDF文档所述: https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/freertos-smp.html#tasks-and-task-creation 。我们在IDF的 FreeRTOS task.h文件里也能对此予以验证。
 
 
Parameter:指针,指向任务函数所接收的参数。其类型必须为(void *) [2]。在本教程中,为简单起见,我们将不会使用这个参数(参数值为NULL)。
 
 
Priority:任务的优先级。我们将创建两个具有相同优先级的任务。
 
 
TaskHandle:返回一个句柄,用于以后进行函数调用(比如要删除某个任务或者修改其优先级)时对任务的引用[2]。同样地,在这个简单示例中,我们将不使用这个参数(参数值为NULL)。
 
 
该函数会返回pdPass(成功时)或错误代码[1] ( http://esp32.info/docs/esp_idf/html/db/d67/projdefs_8h.html)。我们假设任务创建时不会有任何问题,所以代码中不会进行任何错误校验。当然,在实际的应用场景中,应该确认任务是否创建成功。
 
 
 
完整的setup函数如下所示,其中包括了创建两个不同任务的函数调用。
 
 
[AppleScript]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
[ / font ][ / align][ font = 微软雅黑]void setup ( ) {
 
  
 
   Serial.begin ( 112500 ) ;
 
   delay ( 1000 ) ;
 
  
 
   xTaskCreate (
 
                     taskOne ,          / * Task function. * /
 
                     "TaskOne" ,        / * String with name of task. * /
 
                     10000 ,            / * Stack size in bytes. * /
 
                     NULL ,             / * Parameter passed as input of the task * /
 
                     1 ,                / * Priority of the task. * /
 
                     NULL ) ;            / * Task handle. * /
 
  
 
   xTaskCreate (
 
                     taskTwo ,          / * Task function. * /
 
                     "TaskTwo" ,        / * String with name of task. * /
 
                     10000 ,            / * Stack size in bytes. * /
 
                     NULL ,             / * Parameter passed as input of the task * /
 
                     1 ,                / * Priority of the task. * /
 
                     NULL ) ;            / * Task handle. * /
 
  
 
}
 
 
因为所创建的两个任务已经实现了所有功能,所以主循环什么也不用做。我们在主循环中只放了一个延时。
 
[AppleScript]  纯文本查看  复制代码
?
1
2
3
4
5
void loop ( ) {
 
    delay ( 1000 ) ;
 
}
 
 
 
任务函数
现在,我们唯一需要做的就是指定任务函数。不要忘了,我们要创建两个任务,分别由函数TaskOne和TaskTwo实现。
 
请注意,这些任务都将使用正则函数实现,因此它们需要遵循预先定义的函数原型[3]。它们必须返回空(void),而且必须接收(void *)类型的输入参数[3],如下例所示。
 
[AppleScript]  纯文本查看  复制代码
?
1
void taskOne ( void * parameter )
 
 
有一点非常重要,那就是这个函数不应该返回任何数值。因此,它们不能包含return语句或者执行到代码结尾[3]。相反,应该显式地将其删除[3]。
 
要在某个任务的代码内部将其自身删除,我们只需要调用它的 TaskDelete函数即可。该函数接收的输入参数是要删除的任务句柄[4](就是前面提到本教程不使用的xTaskCreate函数的参数)。尽管如此,如果我们将输入参数值设为NULL,那么主调任务仍然能被删除[4],这正是我们想要的效果(我们会从任务自身代码内部进行调用)。
 
[AppleScript]  纯文本查看  复制代码
?
1
vTaskDelete ( NULL ) ;
 
 
除了这几点不同之外,这两个函数的实现非常简单。简而言之,我们将使用一个简单的循环,让每个任务打印一条“Hello World”消息,循环结束后再打印一条表示任务已结束的消息。
 
 
本教程的完整代码如下所示,其中包括两个任务的实现代码。
 
 
[AppleScript]  纯文本查看  复制代码
?
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
[ / font ][ / align][ font = 微软雅黑]void setup ( ) {
 
  
 
   Serial.begin ( 112500 ) ;
 
   delay ( 1000 ) ;
 
  
 
   xTaskCreate (
 
                     taskOne ,          / * Task function. * /
 
                     "TaskOne" ,        / * String with name of task. * /
 
                     10000 ,            / * Stack size in bytes. * /
 
                     NULL ,             / * Parameter passed as input of the task * /
 
                     1 ,                / * Priority of the task. * /
 
                     NULL ) ;            / * Task handle. * /
 
  
 
   xTaskCreate (
 
                     taskTwo ,          / * Task function. * /
 
                     "TaskTwo" ,        / * String with name of task. * /
 
                     10000 ,            / * Stack size in bytes. * /
 
                     NULL ,             / * Parameter passed as input of the task * /
 
                     1 ,                / * Priority of the task. * /
 
                     NULL ) ;            / * Task handle. * /
 
  
 
}
 
  
 
void loop ( ) {
 
   delay ( 1000 ) ;
 
}
 
  
 
void taskOne ( void * parameter )
 
{
 
  
 
     for ( int i = 0 ;i < 10 ;i + + ) {
 
  
 
         Serial.println ( "Hello from task 1" ) ;
 
         delay ( 1000 ) ;
 
     }
 
  
 
     Serial.println ( "Ending task 1" ) ;
 
     vTaskDelete ( NULL ) ;
 
  
 
}
 
  
 
void taskTwo ( void * parameter )
 
{
 
  
 
     for ( int i = 0 ;i < 10 ;i + + ) {
 
  
 
         Serial.println ( "Hello from task 2" ) ;
 
         delay ( 1000 ) ;
 
     }
 
     Serial.println ( "Ending task 2" ) ;
 
     vTaskDelete ( NULL ) ;
 
  
 
}


 
 
 
运行代码
使用Arduino IDE对代码进行编译并上传到ESP32开发板,即可运行代码。运行结果如图1所示,串行控制台会打印出两条“Hello World”消息。最后,两条表示任务结束的消息也会打印到控制台上。
 
 
 
图1 - 程序运行结果。
 
 
 
请注意,两个任务是并行运行的,所以两个任务打印的消息是混在一起的。每个任务的执行时间由RTOS调度器决定。
发布了115 篇原创文章 · 获赞 49 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/aa120515692/article/details/103768372