Step10.编译下载运行。LED0和LED1分别闪烁,LED0闪烁周期是1秒,LED1的周期是2秒。
程序分析:
1.分析语句:osThreadDef(Task_LED0, Func_LED0, osPriorityNormal, 0, 128); osThreadDef(…)并不是一个函数,而是一个宏。 其定义在cmsis_os.h文件中,作用是定义一个osThreadDef_t结构体。
在cmsis_os.h文件中,osThreadDef_t结构体的定义如下:
因此,将osThreadDef(Task_LED0, Func_LED0, osPriorityNormal, 0, 128);展开 结果就是 const osThreadDef_t os_thread_def_Task_LED0 = { Task_LED0, (Func_LED0), (osPriorityNormal), (0), (128) }; 即,定义了一个名为os_thread_def_Task_LED0的osThreadDef_t类型结构体,并赋值给各个成员变量。
2.分析语句:Task_LED0Handle = osThreadCreate(osThread(Task_LED0), NULL); 同样的,osThread(…)也是一个宏定义,在cmsis_os.h文件中可查到。
osThread(Task_LED0)展开的结果就是 &os_thread_def_ Task_LED0。 因此,将Task_LED0Handle = osThreadCreate(osThread(Task_LED0), NULL);展开 结果就是 Task_LED0Handle = osThreadCreate(&os_thread_def_Task_LED0, NULL); 所以上面分析的两句话,其过程就是定义一个结构体变量,然后将结构体作为参数传递给osThreadCreate()函数,创建一个任务。
3.分析osThreadCreate()函数。
查看其源码,可以发现,这个函数实际上调用了xTaskCreate()函数,这才是原生FreeRTOS的API函数。
STM32CubeMX的工程师根据CMSIC接口标准对FreeRTOS的API函数进行了二次封装,使用户开发更加容易。封装后的函数接口都放在cmsis_os.h文件中。 其实,在开发过程中,不需要像上面的分析过程那样,将函数或者宏定义展开进行详细分析。我们知道每个接口参数的意义,并会使用该接口就行了。
附加内容:FreeRTOS任务调度策略探讨
本例中的两个任务函数Func_LED0和Func_LED1,他们实际占用CPU的时间很少,在调用osDelay()函数之后,它们就进入阻塞状态了,它们在等待“定时时间到”事件。在用户任务都进入阻塞状态时,运行的是空闲任务。空闲任务是启动调度器时自动创建的。 本例中,两个任务的优先级是一样的,都是osPriorityNormal。但是由于调用了osDelay()函数,它们进入阻塞状态时就让出了CPU的使用权。因此,两个任务看上去就像并行执行的一样。 如果把其中的一任务的优先级设置成osPriorityLow或者osPriorityHigh,让两个任务的优先级不同,会怎样呢?结果是,运行起来还是像并行执行的一样。 我们都知道,如果两个就绪的任务优先级不同,那么优先级高的任务得到运行。那么,如果两个就绪任务优先级相同,且一直处于就绪状态,那么在FreeRTOS中将如何运行呢?下面将通过实验检验其运行过程。 写一个用for循环实现的延时函数:
然后在任务中替代原来的osDelay()函数函数。这样,两个任务会一直处于可运行的状态,因为他们“总是有事情要做”。
a).如果两个任务的优先级不同,则运行结果是:只有优先级高的任务得到运行。 b).如果两个任务的优先级相同,则运行结果是:两个任务都得到运行,但是LED的闪烁频率比单独运行时的低。按照上述代码,两个任务调用的都是my_delay(500);,结果就是LED闪烁频率减半,相当于单个任务运行时的my_delay(1000);。 从b)的情况可知,FreeRTOS在任务优先级相同时,会分配给各任务相同的CPU时间,即任务会轮流执行相等的时间片。
S.D.Lu 于 深圳 2016年8月