从裸奔到RTX的使用提示

RTX是实时微内核操作系统,本文涉及的部分内容同样适合于μcos等RTOS。同时,某些内容可能是RTX特有的。
1. 跟循环实现的Delay说拜拜
形如这样的Delay函数应该从代码中消失了:

void Delay(int n)
{
  int i = 0;
  int j = 0;
  int k = 0;
  int temp =0;

  for (i = 0; i < n; i++)
    for (j = 0; j < 2450; j++)
      for (k = 0; k < 65535; k++)
        temp++;
}


调用这样的Delay,有以下坏处:
(1)延时时间无法保证。在某个CPU的某种配置下是这个时间,换个CPU或更改配置,延时的时间就变了。
(2)如果在任务中调用,会使较低优先级的任务阻塞,而如果Round-Robin没打开的话,相同优先级任务也被阻塞,完全发挥不出多任务的优势。
使用os_dly_wait或os_itv_wait等函数吧。
或许有人要说,我在os_sys_init之前就要用到延时,怎么办?那么我要说的是,这种情况并不多见,即使有,也多半可以移到os_sys_init后面去运行。
假如实在没有办法,确实要在os_sys_init之前调用Delay,那么请确保它不在os_sys_init之后的任务中被调用。
2. 任务永远不要返回
我没有试过用os_tsk_create去创建一个不带__task关键字的任务会有什么后果。
但是,我试过让带有__task关键字的函数返回,结果是死机。
不知道μcos是不是这样的。
__task关键字的定义在\Keil\ARM\RV31\INC\RTL.h

#define __task          __declspec(noreturn)


关于__declspec(noreturn),这里有描述:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0348bc/CJADJEII.html
不过遗憾的是,当我显式地返回时,编译器并没有给出警告(我用的警告级别是All Warnings)。
当一个任务所要做的事都做完了,要么用os_tsk_delete_self将自己删除,要么无限循环。而且我建议使用os_tsk_delete_self,因为多余的循环是有害的,坏处在第1点中的Delay中已经说明。
在使用RTX的时候,这一点要特别注意。
3. 任务栈
裸奔的时候,我们都习惯于在启动文件中为用户模式和ISR模式分配足够的调用栈,然后就基本上不用管它了。而且Keil会生成一个最终映像的htm文件,提供静态调用栈的最大值,可以参考。
然而RTX的每个任务栈是独立分配的。我碰到的90%的死机问题,都是由于任务栈的不足引起的。我的一位同事对此深有同感,他以前所在的公司曾经用过μcos。
可以在RTX_Conf_xxx.c文件中修改系统自动分配的任务栈大小,而os_tsk_create_user和os_tsk_create_user_ex则可以指定单个任务的栈大小。
暂时还没有发现有什么方法可以查看一个任务的调用栈大小情况,目前只能手工估算了。
4. ISR中断的处理
正如《RL-ARM User's Guide》所建议,中断函数应尽量短小,用isr_evt_set等函数将中断事件告诉一个任务就可以了。提高这个任务的优先级,那么这个中断就可以优先处理了。

此外,还要注意FIFO Queue Buffer的大小。按照《RL-ARM User's Guide》的说法,似乎每个isr_函数占用4 entries。对于不调用isr_的中断函数,不占用该缓存。而FIFO Queue Buffer一旦溢出,内核会调用system error,因此需要注意。
5. 查询等待的问题
与硬件通讯时,经常会需要查询等待某个状态。很多时候,我们可以使用中断,比如串口的收发。然而并不是所有状态都能通过中断来触发。对于裸奔系统,只能以牺牲代码结构为代价使得系统的其它部分不因这个状态的等待而阻塞。
用了操作系统之后,由于有多任务,这个问题就简单了许多。我们可以在一个任务里面简单地使用while循环来等待,哪怕这个状态的条件永远无法达到,系统中比它高优先级的任务依然畅通运行。
那么相同优先级和低优先级任务呢?任务中的循环正如第1点的Delay函数,对相同优先级任务和低优先级任务会有影响。
RTX提供了Round-Robin功能和os_tsk_pass函数,可以将CPU的控制权交出。比如使用os_tsk_pass,查询等待变成这样:

while (!IsStateReady())
  os_tsk_pass();


遗憾的是,无论是Round-Robin还是os_tsk_pass,都只能将控制权交给相同优先级的任务,低优先级的任务依然被阻塞。
看来解决的办法只有一个,那就是将查询等待的任务设为最低优先级,并考虑将Round-Robin打开或使用os_tsk_pass。

事实上,许多任务本身是一个无限循环。比如一个键盘任务:

__task void task_key(void) {
  while (1) {
    //等待、判断、查询等待

    //处理键盘事件
  }
}


那么,这个无限循环内部不管采取什么方式去处理,都需要考虑:这个任务会不会阻塞其它任务。

RL-ARM Getting Started上有个典型设计,可参考一下:

IRQ任务和高优先级的后台任务完成一定功能后,必须挂起(MUST BLOCK);低优先级的用户任务采用round robin方式运行;空闲任务运行多余的循环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值