上一节我们利用cubemx自动生成了一个实例,它建立了两个用户任务和一个默认任务,并成功运行起来了。本节我们将分析一下FreeRTOS任务创建的过程,并结合创建时需要的参数讲解一下FreeRTOS任务的一些特性。
使用的工程文件仍然是上一节生成的代码。
1)FreeRTOS创建任务的API
FreeRTOS提供了一套API函数,我们调用它的函数即可创建任务。
基本的创建任务函数有两个:xTaskCreate 和 xTaskCreateStatic。
xTaskCreate函数是用动态的方法创建一个任务,而xTaskCreateStatic函数用静态的方法创建任务;使用xTaskCreate创建任务时,系统自动为任务分配内存;而使用xTaskCreateStatic创建任务时,需要用户指定内存地址。
我们看一下它们的函数原型,首先是xTaskCreate
各参数的含义解释如下:
pxTaskCode:任务函数的入口,是一个函数指针,指向要执行的任务;
pcName:任务名称,是一个字符串;
usStackDepth:任务堆栈大小,注意实际申请的堆栈是usStackDepth个4字节空间;
pvParameters:传递给任务函数的参数;
uxPriotiry:任务优先级,范围0~configMAX_PRIORITIES-1;
pxCreatedTask:任务句柄,任务创建成功以后会返回此任务的句柄,这个句柄其实就是任务的任务堆栈地址;
xTaskCreateStatic函数的参数与xTaskCreate差不多,区别是最后两个参数:
最后两个参数的含义是:
puxStackBuffer:用户指定的这块内存地址,作为任务堆栈地址;
pxTaskBuffer:用户指定的这块内存地址,作为任务控制块。
2)cubemx生成的代码分析
了解了FreeRTOS创建任务的API函数,我们再来看看cubemx生成的代码是如何使用的。我们从main函数开始看。
打开工程文件的main函数,我们发现,与无操作系统的不同之处,是在硬件初始化之后、while(1)之前多了MX_FREERTOS_Init();和osKernelStart();两个函数。
MX_FREERTOS_Init就是操作系统初始化函数,我们跳转到它内部,创建了三个任务:
osThreadDef是一个宏,它的定义如下:
例如第114行的osThreadDef(myTask02, StartTask02, osPriorityHigh, 0, 128);这句,宏展开之后就是:
(还记得以前讲过的C语言宏定义#和##的用法吗?)
实际上,这一句就是定义了一个结构体变量os_thread_def_myTask02,并给它赋了初值。
然后,用osThreadCreate这个函数进行了任务创建。
它的第一个参数也是个宏,找到宏定义:
例如第115行的myTask02Handle = osThreadCreate(osThread(myTask02), NULL);将宏展开后,就是:
myTask02Handle = osThreadCreate(&os_thread_def_myTask02, NULL);
可以看到,第一个参数实际上就是我们前面定义的那个结构体os_thread_def_myTask02的指针。
继续深入,看看osThreadCreate这个函数到底干了啥。
osThreadCreate的代码比冗长,但是实际上,它只干了一件事,依据configSUPPORT_STATIC_ALLOCATION和configSUPPORT_DYNAMIC_ALLOCATION这两个宏定义的值,来选择调用freeRTOS的API函数 xTaskCreate 或 xTaskCreateStatic 创建任务。
结合传入的参数,由于结构体os_thread_def_myTask02的最后两个参数初始化为NULL(没有指定内存地址),osThreadCreate内部调用了动态创建函数。任务的堆栈大小都是128*4字节。
至此,我们的三个任务创建成功。
这里说明一下这两个宏:
configSUPPORT_STATIC_ALLOCATION定义为1可以使用静态内存分配;
configSUPPORT_DYNAMIC_ALLOCATION定义为1可以使用动态内存分配。
这里两个宏在FreeRTOSConfig.h文件中(实际上freeRTOS的还有很多其他配置宏在这个文件中,以后涉及到了会详细讲解)。
在MX_FREERTOS_Init();函数中创建完三个任务,就进入函数osKernelStart();这个也是调用了一个freeROTS的API函数vTaskStartScheduler,用于启动任务调度。这里如果我们只关注应用,暂时不研究操作系统内部怎么执行任务的,不必深究。
3)创建任务测试
上一节中Cubemx生成的工程代码,是我们在图形界面下创建的几个任务;经过上面的讲解,我们掌握了freeROTS的API函数后,完全可以自己直接编写创建任务的函数。
这里,我们在原工程中添加一个任务,用直接用API函数创建任务。
我们使用静态方式,创建一个低优先级的任务,代码如下,先添加几个定义:
然后定义任务函数,就是一个无限循环,里面只是延时一下,其他什么也不干:
最后在MX_FREERTOS_Init函数中添加代码,创建Task04:
注意,这里我们使用的是静态创建的API函数xTaskCreateStatic:
这样,我们就定义好了一个自己的任务,可以测试了。
编译,下载到开发板,我们在调试模式下测试,执行时分别在各个任务内设置断点,可以看到均能执行到断点处:
说明我们新添加的任务可以成功执行,也不影响其他任务的执行。
4)任务优先级测试
接下来我们修改一下代码,测试任务的优先级,由于我们新添加的Task04设置了优先级为(1)。如果有其他较高优先级的任务一直占用CPU,就不会执行到Task04。
查看宏定义,defaultTask的优先级osPriorityNormal定义是0,注意osThreadCreate在执行时做了一次变换,优先级变成了0-(-3)=3,比Task04要高。为此,我们把defaultTask任务里的延时去掉,让他一直占用CPU,另外添加一个变量自加语句,以方便设置断点观察。
再次编译,下载,进入调试模式运行,可以发现defaultTask的任务可以进入:
而Task04任务再也进入不了了,而且Task04任务里的j也不会增加。
而此时,如果在高优先级的Task02和Task03中设置断点,也是可以进入的:
这个实例说明,freeRTOS会执行优先级高的任务。只有高优先级的任务主动放弃执行(如执行到代码里的osDelay(xx),就会释放CPU x x毫秒),才会轮到低优先级的任务执行。
好了,本节的内容就到这里了。
如果觉得有用可以关注作者微信号“小白白学电子”,在公众号可以找到代码和资料下载地址: