uCOS/FreeRTOS任务创建的两种模式

来源:互联网 发布:java cs的前景 编辑:程序博客网 时间:2024/06/07 03:24

在我们使用uCOS/FreeRTOS编写代码时,首先要面临的一个问题是怎样创建任务并启动整个系统。一般来说,我们会有两种不同的方式。这两种方式不仅适用于uCOS/FreeRTOS,同时也适用于其它RTOS。

创建任务的两种方式

这两种方式各有优缺点。如果用一幅图来表示的话,见下方。

RTOS启动流程

下面将分别说明这两种方式的具体应用方法。

方式一:先创建启动任务,启动任务初始化软硬件并创建其它任务

这种方式是这样的:在启动启动前创建一个初始任务,然后在该任务跑起来后,再完成其它软硬件初始化、创建启动任务。

int main () {    OSInit();    CreateFirstTask();      // 创建初始任务    OSStart();}void FirstTask () {    DataInit();         // 初始化应用相关的数据    HardwareInit();     // 初始化硬件模块    CreateTask1();      // 创建任务1    CreateTask2();      // 创建任务2}

相于接下来讲到的方式二而言,这种方式的一大优点是:所有的初始化及任务创建都是在整个跑起来之后进行。如果这些是在系统跑起来之前进行的话,则有可能存在潜在的隐患。具体可以看下面的方式二。

在完成所有初始化操作之后,该初始任务可以继续完成一些其它的功能。当然,也有的人直接让任务自行删除。不过我认为自行删除并不必要,因为在任务任务时,我们可能是通过全局变量定义任务的Task结构和堆栈空间。删除之后,这些空间肯定还要用的吧?如果是分配给别的任务,那么还得再创建一个任务。既然如此,那么为什么不直接不删除呢?

要特别注意一点:初始任务的优先级应尽可能的高。因为有些RTOS的任务创建接口中,创建任务之后可能会立即启动该任务,如果新任务的优先级比初始任务的优先级更高,则会切换至新任务运行。而这与我们所期望的流程就不一致。

方式二:在main中完成所有软硬初始化及所有任务创建

这是最简单也是最常用的方式。简单来说是这样:在main()中调用RTOS的初始化函数,中间初始化软硬件、创建其它任务,然后再调用启动os的API。以uCOS为例,如果采用这种方式,其初始化流程可能是这样的。

int main() {    OSInit();    DataInit();         // 初始化应用相关的数据    HardwareInit();     // 初始化硬件模块    CreateTask1();      // 创建任务1    CreateTask2();      // 创建任务2    OSStart();        return 0;}

可以看到采用这种方式,在整个系统跑起来之前已经完成了所有任务的初始化,系统跑起来之后,各个任务就可以专注实现其要实现的功能。

但是这种方式有一个隐藏的缺陷:如果在初始化硬件模块时产生中断,且在中断服务函数中又调用了RTOS相关的API,则有可能导致系统崩溃。

原因是什么呢?

这是因为当前RTOS还没有跑起来,甚至于有些内核数据还没初始化好。此时如果发生中断,在中断中调用的RTOS的API可能访问这些内核数据,或者尝试执行只能在RTOS跑起来后执行的操作。

举个实际的例子,假设当前正在配置GPIO外部上沿升输入中断,刚刚开启中断配置使能后恰恰外部输入了一个上升沿信号(外部往往不可控),则立即进入GPIO中断处理函数中。在中断处理函数中,简单快速处理后向Task 1发送消息。发送完消息后,退出中断时会执行任务切换,尝试切换至Task 1,而此时当前还没有任务运行,那么此时应该怎样完成切换呢?

当然,不排除RTOS在设计时有考虑这种情况。但是你能确定所用的RTOS一定能正确处理这种异常问题吗?

所以,如果你的应用中可能会遇到这种问题,与其花时间研究是否能正确处理这种问题,倒不如直接采用方式一,这是最稳妥也最简单的方式。

总结

总结以上内容,可以看出方式一相对于方式二有着明显的优点:即系统启动之前不会受外部事件干扰的影响。

正是因为这样,我个人比较推荐第一种。目前,在有些RTOS的实现中,默认采用了方式一。其内部已经自动帮我们创建了一个初始任务,我们只需要额外实现一个其定义好的接口函数,然后在函数中添加软硬件初始化及其它任务的创建即可。

当然,实际在写代码时,采用的具体方式可能并不完全同上述两种方式,但基本流程差不太多。

该文章同时发布在:http://01ketang.cc/rtos/rtos-init.html

原创粉丝点击