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两个参数。这两个参数决定了每个任务使用时间片的大小:
l INT_CLOCK是时钟中断使用的周期数,也就是基本时间片;
l TIMESHARING是每个任务一次使用的时间片数目。
两者决定了一个任务一次使用的最大时间片。如假设一个系统中INT_CLOCK设置为10000,即10ms,那么TIMESHARING=1时,一个任务使用的最大时间片是10ms;TIMESHARING=2时,任务使用最大的时间片是20ms;TIMESHARING=5时,任务使用最大的时间片是50ms;当TIMESHARING设置为0时,系统就不会进行自动任务切换了,这时需要用os_switch_task函数进行任务切换。这部分功能是RTX51 Tiny 2.0中新增加的。
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);”这一行前面的注释符号,再次仿真。比较一下运行的结果,就可以清楚地知道它们的细微差别了。
软件环境:Keil uVision 7.08
仿真CPU:AT89C52 12MHz
RTX51 Tiny:使用Keil自带的RTX51 Tiny操作系统,v2.02。
RTX51 Tiny的参数:INT_CLOCK=10000,TIMESHARING=5
其它参数使用默认设置。(需要自己建立一个工程文件,再将下面的文件添加到工程文件中。)
#include <rtx51tny.h>
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。