[牛人杂谈]

RTX51 Tiny 实时内核理解

[复制链接]
954|8
手机看帖
扫描二维码
随时随地手机跟帖
734774645|  楼主 | 2017-6-20 18:03 | 显示全部楼层 |阅读模式
RTX51 Tiny中容易混淆的问题



RTX51 Tiny是 Keil uVision中自带的一个小型嵌入式RTOS,具有小巧、速度快、系统开销小、使用方便等优点。使用RTX51 Tiny能够提高系统的稳定性,优化程序的性能;而且它是为51单片机专门定制的,所以在51单片机上的运行效率比其它一些通用的RTOS性能也要好一些。

  但是,由于RTX51 Tiny的相关资料和书籍比较少,大部分只是对程序自带帮助文件的简单翻译,很少进行深入探讨。下面就RTX51 Tiny使用中经常遇到的一些问题进行探讨。

1  关于时间片的问题

  RTX51 Tiny使用的是无优先级时间片轮询法,每个任务使用相同大小的时间片,但是时间片是怎样确定的呢?

  RTX51 Tiny的配置参数(Conf_tny.a51文件中)中有INT_CLOCK和TIMESHARING两个参数。这两个参数决定了每个任务使用时间片的大小:INT_CLOCK是时钟中断使用的周期数,也就是基本时间片;TIMESHARING是每个任务一次使用的时间片数目。两者决定了一个任务一次使用的最大时间片。如假设一个系统中INT_CLOCK设置为10000,即10ms,那么TIMESHARING=1时,一个任务使用的最大时间片是 10ms;TIMESHARING=2时,任务使用最大的时间片是20ms;TIMESHARING=5时,任务使用最大的时间片是50ms;当 TIMESHARING设置为0时,系统就不会进行自动任务切换了,这时需要用os_switch_task函数进行任务切换。这部分功能是RTX51 Tiny 2.0中新增加的。


734774645|  楼主 | 2017-6-20 18:03 | 显示全部楼层
2  关于os_wait延时的问题

  os_wait 是RTX51 Tiny中的基本函数之一。它的功能是将当前任务挂起来,等待一个启动信号(K_SIG)或超时信号(K_TMO)或周期信号(K_IVL)或者是它们之间的组合。虽然os_wait很简单,但是因为涉及到多任务的操作方式,很容易产生误解。

2.1  关于K_TMO的延时时间

  在RTX51 Tiny中,如果一个任务中使用了os_wait(K_TMO,1,0),那么它的延时时间是多少呢?
很多人都会认为是一个时间片,其实这不完全对。正确的理解是,延时时间与正在运行的任务相关。因为RTX51 Tiny是一个非占先或多优先级的实时操作系统,是一个平级的时间片轮询实时操作系统,所有的任务平等运行。K_TMO是等待产生超时信号,当信号产生后,只是将相应的任务置上就绪标志位,任务并不是立即就能够运行。任务需要等到其它任务轮流执行,到自己的时间片后才会执行。

  这就是说,最后的效果是延时时间加上正在运行的任务执行时间,而这个时间是与任务数和任务运行情况相关的。如果其它任务执行的时间短,那么延时可能只是一个时间片;如果其它任务执行的时间长,那么就需要多个时间片了。用os_wait做时钟是不准确的。

  关于延时时间还有一个很容易理解错的地方,那就是os_wait中无论使用K_TMO还是K_IVL参数,延时的时间都只与INT_CLOCK有关,而与TIMESHARING无关。或者说,os_wait函数一次只使用一个基本时间片,而不是任务的时间片。

2.2  关于K_TMO和K_IVL参数的区别

  在os_wait中有三个参数:K_TMO、K_IVL和K_SIG。其中,K_TMO与K_IVL是最容易让人混淆的,特别是搞不清楚 K_IVL到底是什么含义,好像使用起来与K_TMO效果差不多。一般的书上和Keil自带的RTX51 Tiny的帮助中,也没有清楚解释K_IVL的含义。

  K_IVL与K_TMO有很大区别,但是在一定环境下最终产生的效果却差不多。

  K_TMO是指等待一个超时信号,只有时间到了,才会产生一个信号。它产生的信号是不会累计的。产生信号后,任务进入就绪状态。K_IVL是指周期信号,每隔一个指定的周期,就会产生一次信号,产生的信号是可以累计的。这里累计的意思是,如果在指定的时间内没有对信号进行响应,信号的次数会迭加,以后进行信号处理时就不会漏掉信号。比如说,在系统中有几个任务,其中一个任务使用K_TMO方式延时,另外一个任务使用K_IVL延时,延时的时间相同。如果系统的任务很轻,两个任务都可以及时响应,那么这两种延时的效果是一样的。如果系统的负担比较重,任务响应比较慢,不能及时响应所有的信号,那么使用K_TMO方式的任务就有可能丢失一部分没有及时响应的信号,而使用K_IVL方式的任务就不会丢失信号。只是信号的响应方式会变成这样:在一段时间内不响应信号,然后一次把所有累计的信号都处理完。

  下面的一个例子可以将上面两个关于os_wait的问题解释清楚。

  在x1++和x2++这两个地方加上断点,进行仿真,观察执行到断点的时间。然后,去掉任务job4中的语句“//os_wait(K_TMO,1,0);”这一行前面的注释符号,再次仿真。比较一下运行的结果,就可以清楚地知道它们的细微差别了。

使用特权

评论回复
734774645|  楼主 | 2017-6-20 18:04 | 显示全部楼层
软件环境:Keil uVision 7.08
  仿真CPU:AT89C52 12MHz
  RTX51 Tiny:使用Keil自带的RTX51 Tiny操作系统,v2.02。
  RTX51 Tiny的参数:INT_CLOCK=10000,TIMESHARING=5

  其它参数使用默认设置。(需要自己建立一个工程文件,再将下面的文件添加到工程文件中。)
#include
unsigned long int x1,x2,x3;
void job0(void) _task_ 0{
  x1=x2=x3=0;
  os_create_task(1);
  os_create_task(2);
  os_create_task(3);
  os_create_task(4);
  while(1) {
    os_wait(K_TMO,1,0);
    x1++;
  }
}
void job1(void) _task_ 1{
  while(1) {
    os_wait(K_IVL,1,0);
    x2++;
  }
}
void job2(void) _task_ 2{
  while(1) {
    os_wait(K_IVL,1,0);
    x3++;
  }
}
void job3(void) _task_ 3{
  while(1) {
    os_wait(K_TMO,1,0);
  }
}
void job4(void) _task_ 4{
  while(1) {
    //注释下面一句使系统的负担变得很重,不能及时响应job0和job1的延时信号
    //取消注释后,系统负担变轻,可以及时响应
    //os_wait(K_TMO,1,0);
  }
}

  当job4中os_wait(K_TMO,1,0)的注释不取消时,job0每执行一次,job1就连续执行5次,x2是x1的5倍。因为 job1中的os_wait(K_IVL,1,0)产生了5次信号,并累计下来;而job0中的os_wait(K_TMO,1,0)虽然也产生了5次信号,但是没有累计,只有最后一次是真正有效的。

  当job4中os_wait(K_TMO,1,0)的注释取消时,job0和job1的执行次数是一样的,x1=x2。

使用特权

评论回复
734774645|  楼主 | 2017-6-20 18:04 | 显示全部楼层
关于RTX51 TINY的分析与探讨



1 概述

  RTX51 TINY是一种应用于MCS5l系列单片机的小型多任务实时操作系统。它完全集成在Keil C5l编译器中,具有运行速度快、对硬件要求不高、使用方便灵活等优点,因此越来越广泛地应用到单片机的软件开发中。它可以在单个CPU上管理几个作业(任务),同时可以在没有扩展外部存储器的单片机系统上运行。

   RTX51 TINY允许同时“准并行”地执行多个任务:各个任务并非持续运行,而是在预先设定的时间片(time slice)内执行。CPU执行时间被划分为若干时间片,RTX51 TINY为每个任务分配一个时间片,在一个时间片内允许执行某个任务,然后RTX51 TINY切换到另一个就绪的任务并允许它在其规定的时间片内执行。由于各个时间片非常短,通常只有几ms,因此各个任务看起来似乎就是被同时执行了。

   RTX51 TINY利用单片机内部定时器0的中断功能实现定时,用周期性定时中断驱动RTX51 TINY的时钟。它最多可以定义16个任务,所有的任务可以同时被激活,允许循环任务切换,仅支持非抢占式的任务切换,操作系统为每一个任务分配一个独立的堆栈区,在任务切换的同时改变堆栈的指针,并保存和恢复寄存器的值。RTX51 TINY没有专门的时间服务函数和任务挂起函数,而是通过os_wait()中的参数设定实现的。使用RTX51 TINY时用户程序中不需要包含main()函数,它会自动地从任务0开始运行。如果用户程序中包含有main()函数,则需要利用 os_create_task函数来启动RTX51实时操作系统。

使用特权

评论回复
734774645|  楼主 | 2017-6-20 18:04 | 显示全部楼层
2 任务切换

2.1 RTX51 TINY任务状态

  RTX51 TINY的用户任务具有以下几个状态:

  ① 运行(RUNNING)——任务正处于运行中。同一时刻只有一个任务可以处于“RUNNING”状态。
  ② 就绪(READY)——等待运行的任务处于“READY”状态。在当前运行的任务退出运行状态后,就绪队列中的任务根据调度策略被调度执行,进入到运行状态。
  ③ 阻塞(BLOCKED)——等待一个事件的任务处于“BLOCKED”状态。如果等待的事件发生,则此任务进入“READY”状态,等待被调度。
  ④ 休眠(SLEEPING)——被声明过但没有开始运行的任务处于休眠状态。运行过但已经被删除的任务也处在休眠状态中。
  ⑤ 超时(TIMEOUT)——任务由于时间片用完而处于“TIMEOUT”状态,并等待再次运行。该状态与“READY”状态相似,但由于是内部操作过程使一个循环任务被切换,因而单独算作一个状态。

  处于“READY/TIMEOUT”、“RUNNING”和“BLOCKED”状态的任务被认为是激活的状态,三者之间可以进行切换。“SLEEPING”状态的任务是非激活的,不能被执行或认为已经终止。

2.2 RTX51 TINY任务切换

   任务切换是RTX51 TINY提供的基本服务。RTX51 TINY是基于时间片调度算法的操作系统,它支持的是非抢占式的任务切换。所以在一个任务被执行时不能对其进行中断,除非该任务主动放弃CPU的资源,中断才可以打断当前的任务,中断完成后把CPU的控制权再交还该被中断的任务。任务切换有两种情况,一种是当前任务主动让出CPU资源;另一种情况是在当前任务的时间片已经用完的情况下,进行任务切换。CPU执行时间被分成若干个时间片,RTX51 TINY为每个任务分配一个时间片。时间片是通过对变量TIMESHARING的设置来确定的,即用“TIMESHARING EQU 5;”设置多少个系统时钟周期为一个时间片。系统默认5个系统时钟为一个时间片,如果晶振频率为11.059 2 MHz,则时间片为10.850 7×5=54.253 5 ms。

  RTX51 TINY的任务切换共有TASKSWITCHING 和SWITCHINGNOW两个入口,前者供定时器T0的中断服务程序调用,后者供系统函数os_delete和os_wait调用。相应地也有两个不同的出口,分别是恢复保护现场和清除状态标志位。系统首先将当前任务置为“TIMEOUT”状态,等待下一次时间片循环,然后找到下一个处于“READY” 状态的任务,通过堆栈管理,将自由堆栈空间分配给该任务,使其成为当前任务。清除使该任务进入“READY”或“TIMEOUT”状态的相关位后,执行该任务。任务切换的流程如图1所示。(图没有下载下来)
 

使用特权

评论回复
734774645|  楼主 | 2017-6-20 18:04 | 显示全部楼层
3 共享资源实现[1]

  RTX51 TINY由于是一个多任务的操作系统,那么就不免会有几个任务使用同一个资源,这些资源可能是一个变量,也可能是输入/输出设备。这就要求一个任务在使用共享资源时必须独占该资源,否则可能会造成数据被破坏。

   在RTX51 TINY中实现共享资源独占的方法比较多。比如,可以通过TIMESHARING这个变量来禁止时间片轮转,使其值为0,就可以实现禁止任务切换,从而当前任务就可以独占共享资源。还可以关闭中断来实现,使EA=0,定时器T0的中断被关闭,不能再为时间片轮转提供基准,从而禁止了任务切换。但这两种方法都带有一定的局限性,前一种方法只能适用于实时性要求不高的场合,后一种方法由于T0中断关闭时间不能太长,只能适用于一些简单变量操作的场合。基于以上情况,下面通过另一种方法来实现共享资源的使用。

  在RTX51 full中可以利用信号量很好地实现对共享资源的操作,也可以把这种思想应用到RTX51 TINY中;而在RTX51 TINY中不支持信号量,这就要求用户自己定义信号量及其操作过程。以下是部分代码:

  struct signal {//定义信号量结构体
    uchar count;//该信号量的当前计数值
    uint list_tasks;//等待该信号量任务表
  } signal_list[3];
  
  void init_signal(uchar task_id,uchar count) {
    signal_list[task_id].count=count;
    signal_list[task_id].list_tasks=0;
  }
  
  char wait_signal(uchar task_id) {
    if(signal_list[task_id].count>0) {
      signal_list[task_id].count;//获取信号量
      return(-1);
    }
    signal_list[task_id].list_tasks|=(1os_running_task_id());//标记为等待状态
    return(0);
  }
  void wait_sem(uchar task_id) {
    if(wait_signal(task_id==0)
      while(os_wait(K_TMO,255,0)!=RDY_EVENT);//等待,直到该任务就绪
  }
  
  char release_signal(uchar task_id) {
    uchar i:
    uint temp=1;
    if((signal_list[task_id].count>0)||( signal_list[task_id].list_tasks==0)) {
      signal_list[task_id].count++; //释放信号量
      return(-1);
    }
    for(i=0;i<16;i++) {
      if((signal_list[task_id].list_tasks&(temp))!=0){//查找任务表
        signal_list[task_id].list_tasks&= ~(1<<i);return(i); //返回等待信号量的任务号
      }
      temp<<=1:
    }
  }
  void release_sem(uchar task_id) {
    char task_temp;
    task_temp=release_signal(task_id);
    if(task_temp!=-1) {
      os_set_ready(task_temp); //任务task_id进入就绪状态
      os_switch_task();
    }
  }

  有了以上几个函数的定义和实现,就可以应用等待信号量和释放信号量来完成对共享资源的独占。例如:

  void job()_task_ id {
      ――――用户代码――――
      wait_sem(task_id);//等待任务task_id的信号量
      ――――对共享资源使用代码――――
      release_sem(task_id);//释放任务task_id的信号量
      ――――用户代码――――
  }

  应用信号量来实现共享资源的使用,不用禁止时间片轮转和关闭T0中断,可以有效地实现对共享资源的独占;但增加了代码,等待和释放信号量花费了一定的时间,在具体应用中要视情况而定。

使用特权

评论回复
734774645|  楼主 | 2017-6-20 18:05 | 显示全部楼层
4 需要注意的问题

  在应用RTX51 TINY时应注意以下几点:

   ① 尽可能不使用循环任务切换。使用循环任务切换时要求有13个字节的堆栈区来保存任务内容(工作寄存器等)。如果由os_wait()函数来进行任务触发,则不需要保存任务内容。由于正处于等待运行的任务并不需要等待全部循环切换时间结束,因此os_wait()函数可以产生一种改进的系统响应时间。
  ② 不要将时钟节拍中断速率设置得太高,设定为一个较低的数值可以增加每秒的时钟节拍个数。每次时钟节拍中断大约需要100~200个CPU周期,因此应将时钟节拍率设定得足够高,以便使中断响应时间达到最小化。
   ③ 在os_wait()函数中有3个参数: K_TMO、K_IVL和K_SIG。其中对于K_TMO和K_IVL的使用要加以区别。在使用时,两者似乎差别不是很大。其实不然,两者存在很大的区别:K_TMO是指等待一个超时信号,只有时间到了,才会产生一个信号。它产生的信号是不会累计的,产生信号后,任务进入就绪状态。而K_IVL是指周期信号,每隔一个指定的周期,就会产生一次信号,产生的信号是可以累计的。这样就使得在指定事件内没有响应的信号,通过信号次数的叠加,在以后信号处理时,重新得以响应,从而保证了信号不会被丢失。而通过K_TMO方式进行延时的任务,由于某种原因信号没有得到及时的响应,那么这样就可能会丢失一部分没有响应的信号。不过两者都是有效的任务切换方式,在使用时要根据应用场合来确定对两者的使用。

结语

  RTX51 TINY实时操作系统既能保证对外界的信息以足够快的速度进行相应处理,又能并行运行多个任务,具有实时性和并行性的特点,因此能很好地完成对多个信息的实时测量、处理,并进行相应的多个实时控制。任务切换是RTX51 TINY的一个基本服务。本文对任务切换做了详细的分析,在实际应用中还要对任务切换时的堆栈管理有一定了解,这样才能更好地掌握任务切换的机制。共享资源的使用在多任务操作系统中是不可避免的,RTX51 TINY中没有专门的处理共享资源函数,所以在实际应用中要视情况来应用文中提到的几种方法。

使用特权

评论回复
zhuotuzi| | 2017-6-21 17:59 | 显示全部楼层
貌似最小的RTOS。专门跑51的?

使用特权

评论回复
天灵灵地灵灵| | 2017-6-22 11:35 | 显示全部楼层
做OS的好多啊,都不知道用哪个。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

183

主题

3399

帖子

14

粉丝