全新论坛MCU智学网上线,欢迎访问新论坛!稀缺资源、技术干货、参考设计、原厂资料尽在MCU智学网
更新自动建库工具PCB Footprint Expert 2024.04 Pro / Library Expert 破解版

C51上跑的操作系统.

[复制链接]
3912 3

本文包含原理图、PCB、源代码、封装库、中英文PDF等资源

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
KEIL RTX51 TINY内核的分析与应用

1 RTX51简介
1.1 RTX51 TINY特性
RTX51是KEIL公司开发的用于8051系列单片机的多任务实时操作系统。它有两个版本,RTX51 FULL和RTX51 TINY。
RTX51 TINY是RTX51 FULL的子集,仅支持按时间片循环任务调度,支持任务间信号传递,最大16个任务,可以并行地利用中断。具有以下等待操作:超时、另一个任务或中断的信号。但它不能进行信息处理,不支持存储区的分配和释放,不支持占先式调度。RTX51 TINY一个很小的内核,完全集成在KEIL C51编译器中。更重要的是,它仅占用800字节左右的程序存储空间,可以在没有外放数据存储器的8051系统中运行,但应用程序仍然可以访问外部存储器。RTX51 TINY下文简称为内核。
1.2 RTX51 TINY的使用
内核完全集成在KEIL C51编译器中,以系统函数调用的方式运行,因此可以很容易地使用KEIL C51语言编写和编译一个多任务程序,并嵌入到实际应用系统中。内核提供以下函数供应用程序引用:
①char os_create_task(task_id);
②char os_delete_task(task_id);
③char os_send_signal(task_id);
④char isr_send_signal(task_id);
⑤char os_clear_signal(task_id);
⑥char os_running_task_id(void);
⑦char os_wait(event_sel,ticks,dummy)。
各函数的函数原型和具体意义。
2 RTX51 TINY内核分析
2.1 任务状态
RTX51 TINY的用户任务具有以下几个状态。
*RUNNING:任务处于运行中,同一时间只有一个任务可以处于“RUNNING”状态。
*READY:任务正在等待运行,在当前运行的任务时间片完成之后,RTX51 TINY运行下一个处于“READY”状态的任务。
*WAITING:任务等待一个事件。如果所等待的事件发生的话,任务进入“READY”状态。
*DELETED:任务不处于执行队列。
*TIME OUT:任务由于时间片用完而处于“TIME OUT”状态,并等待再次运行。该状态写“READY”状态相似,但由于是内部操作过程使一个循环任务被切换而被冠以标记。
图1所示为任务状态转换图。

2.2 同步机制
为了能保证任务在执行次序上的协调,必须采用同步机制。内核用以下事件进行任务间的通信和同步。
①SIGNAL:用于任务之间通信的位,可以用系统函数置位或清除。如果一个任务调用os_wait函数等待SIGNAL而SIGNAL未置位,则该任务被挂起直到SIGNAL置位,才返回到READY状态,并可被再次执行。
②TIMEOUT:由os_wait函数开始的时间延时,其持续时间可由定时节拍数确定。带 有TIMEOUT值调用os_wait函数的任务将被挂起,直到延时结束,才返回到READY状态,并可被再次执行。
③INTERVAL:由os_wait函数开始的时间间隔,其间隔时间可由定时节拍数确定。带有INTERVAL值调用os_wait函数的任务将被挂起,直到间隔时间结束,然后返回到READY状态,并可被再次执行。与TIMEOUT不同的是,任务的节拍计数器不复位。
2.3 调度规则
RTX51 TINY使用8051内部定时器T0来产生定时节拍,各任务只在各自分配的定时节拍数(时间片)内执行。当时间片用完后,切换至下一任务运行,因此,各任务是并发执行的。
调度规则如下:如果
,且特定事件还没有发生,②任务执行比循环切换所规定的时间长,则运行任务被中断;如果①没有其它任务正在运行,②任务处于“READY”或“TIMEOUT”状态下等待运行,则另一个任务开始。
2.4 任务控制块
为了能描述和控制任务的运行,内核为每个任务定义了称作任务控制块的数据结构,主要包括三项内容:
①ENTRY[task_id]:task_id任务的代码入口地址,位于CODE空间,2字节为一个单位。
②STKP[taskid]:taskid任务所使用堆栈栈底位置,位于IDATA空间,1字节为一个单位。
③STATE[taskid].time和STATE[tasked].state:前者表示任务的定时节折计数器,在每一次定时节拍中断后都自减一次;后者表示任务状态寄存器,用其各个位来表示任务所处的状态。位于IDATA空间,以2字节为一单位。
2.5 存储器管理
内核使用了KEIL C51编译器的对全局变量和局部变量采取静态分配存储空间的策略,因此存储器管理简化为堆栈管理。内核为每个任务都保留一个单独的堆栈区,全部堆栈管理都在IDATA空间进行。为了给当前正在运行的任务分配尽可能大的栈区,所以各个任务所用的堆栈位置是动态的,并用STKP[taskid]来记录各任务所用的堆栈位置是动态的,并用STKP[taskid]来记录和任务堆栈栈底位置。当堆栈自由空间小于FREESTACK(默认为20)个字节时,就会调用宏STACK_ERROR,进行堆栈出错处理。
在以下情况会进行堆栈管理:
*任务切换,将全部自由堆栈空间分配正在运行的任务;
*任务创建,将自由堆栈空间的2个字节,分配给新创新的任务task_id,并将ENTRY[task_id],放入其堆栈;
*任务删除,回收被删除的任务task_id的堆栈空间,并转换为自由堆栈空间。
堆栈管理如图2所示。
3 代码分析
内核代码用汇编语言写成,可读性差,但代码效率较高,主要由两个源程序文件conf_tny.a51和rtxtny.a51组成。前者是一个配置文件,用来定义系统运行所需要的全局变量和堆栈出错的宏STACK_ERROR,这些全变量和宏,用户都可以根据自己的系统配置灵活修改;后者是系统内核,完成系统调用的所有函数。
3.1 主程序main
主程序main的主要任务是初始化各任务堆栈栈底指针STKP、状态字STATE和定时器T0,创建任务0并将其导入运行队列。这个过程加上KEIL C51的启动代码CSTARTUP正是一般嵌入式系统中BSP所作的工作。
3.2 定时器T0中断服务程序
内核使用定时器T0作为定时节拍发生器,是任务切换、时间片轮转的依据。中断服务程序有三个任务。
①更新各个任务节拍数:将STATE[taskid].timer减1,如果某任务超时(STATE[taskid].timer=0),并且该任务正在等待超时事件,则将该任务置为“READY”状态,使其返回任务队列。
②检查自由堆栈空间:若自由堆栈空间范围小于FREESTACK(默认为20字节)时,可以调用宏STACK_ERROR,进行堆栈出错处理。
③检查当前任务(处于RUNNING状态)的时间片是否到时。若当前任务的时间片到时,将程序转到任务切换程序段(taskswitching)切换下一任务运行。
程序流程如图3所示。
3.3 任务切换程序段
这个程序段是整个内核中最核心的一们,主要功能是完成任务切换。它共有两个入口TASKSWITCHING和SWITCHINGNOW。前者供定时器T0的中断服务程序调用,后能供系统函数os_delete和os_wait调用。相应也有两个不同的出口。
其基本工作流程是首先将当前任务置为“TIME OUT”状态,等待下一次时间片循环,其次找到下一个处于“READY”状态的任务并使其成为当前任务。然后进行堆栈管理,将自由堆栈空间分配给该任务。清除使该任务进入“READY”或“TIMEOUT”状态的相关位后,执行该任务。流程框图如图4所示。
3.4 os_wait程序段
主要完成os_wait函数。任务调用os_wait函数,挂起当前任务,等待一个或几个间隔(K_IVL)、超时(K_TMO)、信号(K_SIG)事件。如果所等待的事件已经发生,继续执行当前任务;如果所等待的事件没有发生,则置相应的等待标志后,挂起该任务,转任务切换程序段(switchingnow)切换到下一任务。
3.5 其它程序段
其它程序段主要完成os_create_task、os_delete_task函数和有关信号处理的os_send_signal、isr_send_signal、os_clear_signal函数。这些函数功能相对比较简单,主要是根据上述存储器管理策略进行堆栈的分配和删除,并改变任务字STATE[tasked].state,使任务处于不同的状态。
以上所有程序段,若涉及到任务状态字操作,必须关中断,以防止和定时器T0同时操作任务状态字。
结语
以上分析可以看到这个内核简洁高效,非常适合于运行在资源较少的单片机上。根据其设计思想,我们也很容易把它移植到其它单片机上。但是它也有缺陷,例如:不支持外部任务切换;不支持用户使用定时器T0等。这些缺陷的存在,限制了任务切换的灵活性。

RTX51 tiny(1)
特性:
最大任务数:16
最大激活的任务数:16
所需的CODE空间:最大900Byte
DATA空间:7字节
STACK空间:3字节
XDATA空间:0字节
时钟数:0个
系统时钟分频:1000-----65535
中断嵌套:小于20层
任务切换时间:100-700时钟周期

工具要求:
以下软件应用需要用 RTX51 Tiny
C51纺编译器
A51 Marco Assembler
BL51 Linker or Lx51 Linker
RTX51TNY.LIB 和 RTX51BT.LIB必需存储在库路径上,一般是文件夹\KEIL\C51\LIB
RTX51TNY.H包含文件必须存储在包含文件路径上,一般是文件夹\KEIL\C51\INC

目标要求:
应用程序可能需要外部分的数据存储空间,但内核并不需要外部存储空间
KEIL C51支持各种存储模式:选择存储模式只影响应用程序的存的数据存储,操作系统的变量和堆栈只存在于51的内存中,即DATA和IDATA,一般情况下,RTX51 Tiny的应用只需要小存储模式
在RTX51 Tiny中每个应用程序运行的时间是固定的,支持cooperative任务切换和round-robin任务切换,不支持preemptive任务切换和任务优先级。如果应用程序需要preemptive任务切换则必须使用RTX51 FULL-Time Executive版本。

中断:
RTX51 Tiny的工作与中断函数是并行工作的。中断服务程序可以向RTX51 Tiny的任务发信号(使用函数isr_send_signal),也可以置位作任务的Ready标志(使用函数isr_set _ready)
在RTX51 Tiny中必须使能中断,但是在RTX51 Tiny没有中断服务管理
RTX51 Tiny使用Timer0和Timer0中断。全局中断禁能和Timer0中断禁能会使RTX51 Tiny停止工作,所以只能在短时间内禁能中断。

可重入函数
非可重入的C函数不能被多个任务或中断过程调用。非可重入的函数的参数和变量保存在静态存储空间中,当它们同时被多个任务调用时,这些参数和变量就可能被覆盖了。
你可以在多任务系统中调用非可重入函数如果你能保证他们不会同进行。不过,这就意味着必须能 round-robin任务调度,并且非可重入函数不可以调用RTX51 Tiny的功能函数。
只使用寄存器就量和自动变量的函数自然地就是可重入函数,可以毫无限制地在RTX51 Tiny中调用。

举报

回复

3 个评论

ad***  管理员  发表于 2012-9-3 02:21:39  | 显示全部楼层
C51编译器支可重入函数。可重入函把参数和局部变量保存在堆栈中。这样就可以保证他们被多个任务同时使用。RTX51 Tiny并不管理可重入函数的堆栈,因些,如果你在应用程序中使用了可重入函数,你必须确保这些功能函数不调用任务RTX51 Tiny的系统函数,并且可重入函数不会被round-robin任务切换所中断。
注意:如果你打自在多个任务或中断中调用可重入或非可重入函数必须禁止round-robin任务切换。

运行时库:
所有的C51可重入运行时库可能毫无限制地在所用任务中使用。非可重入时库的要与可重入函数的要求相同。

多个数据指针
C51编译器允许使用多个数据指针。RTX51 Tiny对他们不进行管理,所以在应用中你必须小心
Essentially,你必须确保在改变数据指针时不会发生round-robin切换。
注意:当你打算使用多数据指针时你最好禁止round-robin切换。

算术单元
(说法与多个数据指针相同)

工作寄存器组
RTX51 Tiny的所有任务都使用工作组0。 因些,所有的任务都必须使用C51默认的编译选项:REGISTERBANK (0)
中断函数可能会使用其他的工作寄存器组。然而RTX51 Tiny在工作寄存器组中永远需要6个字节。RTX51 Tiny在工作寄存器组中使用的这些字节可以参考配置文件

单个任务程序
嵌入式和标准C程序都mian()函数开始。在入式应用中, main函数一般都是一个死循环,我们也可以把它看作是一个连续执行的任务。如:
void main (void)
{
while (1) /* repeat forever */
{
do_something (); /* execute the do_something ’task’ */
}
}
在这个例子中,do_something ()函数可以认为是一个单任务,既然只有一个任务在执行,就不需要具有多任务能力或多任务操作系统。

多任务程序
许多成熟的C程序使用一种叫做pseudo多任务的策略,把多个作协做为一循环,如:
void main (void)
{
int counter = 0;

while (1) /* repeat forever */
{
check_serial_io (); /* check for serial input */
process_serial_cmds (); /* process serial input */

check_kbd_io (); /* check for keyboard input */
process_kbd_cmds (); /* process keyboard input */

adjust_ctrlr_parms (); /* adjust the controller */

counter++; /* increment counter */
}
}
在这个例子中,每个函数完成一个独立的操作或任务。这些函数是一个一个顺序执行的
当添加更多的任务时调度就行了一个问题。比方说,如果函数process_kbd_cmds执行的时间比较长,主循环就需要很长时间才能再执行到函数check_serial_io ,这时候串口的数据可能会丢失。当然check_serial_io 可以在主循环中多调用几次来解决这个问题,但最终这种方法并不是最有效的。

RTX51 Tiny的程序
当使用RTX51 Tiny时,你可以为每一个任务生成一个独立的函数,如:
void check_serial_io_task (void) _task_ 1
{
/* This task checks for serial I/O */
}

void process_serial_cmds_task (void) _task_ 2
{
/* This task processes serial commands */
}

void check_kbd_io_task (void) _task_ 3
{
/* This task checks for keyboard I/O */
}

void process_kbd_cmds_task (void) _task_ 4
{
/* This task processes keyboard commands */
}

void startup_task (void) _task_ 0
{
os_create_task (1); /* Create serial_io Task */
os_create_task (2); /* Create serial_cmds Task */
os_create_task (3); /* Create kbd_io Task */
os_create_task (4); /* Create kbd_cmds Task */

os_delete_task (0); /* Delete the Startup Task */
}
在这个例子中,每个函数定义了一个RTX51 Tiny的任务。RTX51 Tiny程序没有主C函数,RTX51 Tiny首先执行任务0。作为一个典型的应用,任务0只是简单地用来生成其他的所有任务。

工作原理
RTX51 Tiny使用并管理你的目标资源,这一部分讲述RTX51 Tiny如何使用这些资源。RTX51 Tiny的许多方面都可以根据工程需要进行配置。

时间片中断
RTX51 Tiny使用标准的8051的定时器0来产生定时中断。这个中断就是RTX51 Tiny的时钟片。RTX51 Tiny运行时库中用的时等待时间都是以这个时间片为单位的
RTX51 Tiny的默认的时间片是10000个机器周期。因此,标准的8051运行在12MHz的时钟下的时候,时间片有0.01S或着说是100Hz。这个值可以在CONF_TNY.A51 配置文件中更改。
注意:你在以RTX51 Tiny时间片中断中编辑你自己的代码,参考:CONF_TNY.A51


任务:
RTX51 Tiny可以看作是一个任务切换器。要生成RTX51 Tiny程序,你必须生成一个具有一个或多个任务的应用。以下细节可以帮你更快地理解RTX51
任务必须用C语言编写,并且用Keil C51支持的关键词声明
RTX51 Tiny使用准确的状态维护任务。
同时只有一个任务可以处于运行状态
可能会有多个任务处于就绪,等待,删除或超时状态
空闲任务总是处于就绪状态,即使使用你的所有任务都处于block状态

任务管理
RTX51 Tiny的任务总是处于以下状态中一确定的状态
RUNNING:任务处于运行状态,os_running_task_id返回正在运行的任务的编号
READY:任务处于就绪状态。当一个正在运行的任务运行完毕,RTX51 Tiny就会启动下一个就绪状态的任务。一个任务可以通设置他的READY标志位os_set_ready or isr_set_ready使它立即进入就绪状态(即使它可能正在等待时间到或等一个信号)
WAITTING:处于等待一个事件的任务就处于等待状态。当所等待的事件发生后,任务就转换到就绪状态,函数os_wait用来使一个作任务进行等待状态
DELETED:没有开始运行的任务或被删除的任务处于DELETED状态。函数os_delete_task使一个已经启动(使用函数os_create_task)的任务进入DELETED状态
TIME-OUT:被round-robin Time-Out中断的任务处于TIME-OUT状态。这个状态在round-robin程序中等效于READY状态

事件(Events)
在实时操作系统中事件可以用来控制任务的执行。一个可能会等待一个事件,也可能给其他任务设置事件标志。
函数os_wait允许任务等待一个或多个事件。
任务等待的是最普通的事件就是Timeout,一个简单的Timeout就是一定数量的clock ticks,当一个任务等待的时间耗尽时,它就可以继续执行了,当它等待时,其他的任务就可以运行
variant of the Timeout is the Interval. An interval is like a timeout except that the specified number of clock ticks is relative to the last time the os_wait function was invoked by the task. The Interval may be used to generate a task which is run on a regular, synchronous schedule (like once every second) regardless of how long the task takes between calls to the os_wait function.(以上内容是说,interval类似于timeout,但与timeout不同的是interval参考的不是上一次任务执行后的时间,而是某个特定的时间点,从而是一个规律性的、周期性的运行的任务:比如说每秒一次)
信号是任务间通信的一种简单的形式,一个任务可以等待其他作任务给他发一个信号(使用 os_send_signal and isr_send_signal 函数)。
每个任务的READY标志都可能被其他任务置位(使用 os_set_ready and isr_set_ready 函数)。一个等timeout, interval, 或信号的任务都可以通过置位READY标志重新启动。
RTX51 Tiny会为每个事件维护一个标志。以下事件选择项可以被用来表明等待什么:
事件选项 描述
K_IVL 等待特定数目的Interval
K_SIG 等待一个信号
K_TMO 等待Timeout
当函数os_wait返回,发生的事件被返回值标志
返回值 描述
RDY_EVENT 任务的就绪标志被置位
SIG_EVENT 信号已收到
TMO_EVENT 一个Timeout已经结束或一个interval已经完毕
函数os_wait可能会等待以下事件组合
K_SIG | K_TMO:os_wait延迟任务直到一个信号已经发给他,或直到设定的clocktick耗尽
K_SIG | K_IVL:os_wait延迟任务直到一个信号已经发给他,或直到设定的interval耗尽
注意:上面的两个事件也可能不组合。

任务调度器
任务调度器是RTX51 Tiny的一部分,用来将处理器交给任务。任务调度器根据以下规则决定具体执行哪一个任务。
当出现以下情况将中断当前任务
任务调用函数os_wait,并且等待的任务还没有发生
任务执行的时间超过了设定的round-robin时间片
其他的任务在出现以下条件时开始运行
没有其他任务正在运行
将启动的任务正处于就绪状态或TIME-OUT状态

RTX51 tiny(2)
Round-robin 任务切换
RTX51 Tiny可以配置成使用round-robin多任务。Round-robinp容许quasi-parallel执行多任务。任务并不是连续执行的,而是分时间片执行的(可用的CPU时间被分成时间片,RTX51 Tiny把时间片分配给各个任务)。时间片的时间很短(以毫秒为单位),所以任务看起来像连续执行一样
任务在分配给他的时间片内执行(除非放弃)。然后切换到下一个就绪的任务。这个时间片在RTX51 Tiny Configuration.配置文件中定义.
下面的例子是一个使用round-robin多任务的RTX51 Tiny的程序。这个程序中的两个任务都是循环计数器。RTX51 Tiny执行称为job0的任务0。这个函数创建了另一个任务job1。Job0执行完它的时间片后,RTX51 Tiny开始执行job1。Job1执行完它的时间片后,RTX51 Tiny又返回到job0开始执行。然后再切换到job1,如此循环。
#include
int counter0;
int counter1;
void job0 (void) _task_ 0 {
os_create (1); /* mark task 1 as ready */
while (1) { /* loop forever */
counter0++; /* update the counter */
}
}

void job1 (void) _task_ 1 {
while (1) { /* loop forever */
counter1++; /* update the counter */
}
}
注意:除了一个任务的时片到时,也可以使用函数os_wait 或函数os_switch_task通知RTX51 Tiny可以切换到另一个任务。函数os_wait挂起当前任务直到特定的事件发生。在这期间任何其他的任务都可以执行。

Cooperative
ad***  管理员  发表于 2012-9-3 02:21:41  | 显示全部楼层
任务切换
如果你禁止了round-robin多任务,你必须设计并执行你的任务从而让他你们以cooperativ方式工作。特别地,你必须在每个任务的某个地方调用。这些函数告知RTX51 Tiny切换到另一任务。
函数os_wait 和函数os_switch_task的不同之处在于os_wait可以让你的任务等待某一事件的发生,而函数os_switch_task直接切换到另一个准务就绪的任务。

空闲任务
当没有任务需要运行时,RTX51 Tiny执行空闲任务。空闲任务只是一个简单的无限循环,比如:
SJMP $
有些8051器件提供了空闲模式,通过持起任务的执行以降低功耗,直到出现中断。在这种模式下,所有外围设备包括中断系统仍然在继续工作。
RTX51 Tiny允许你在空闲任务中初始化空闲模式(没有其他任务需要执行)。当RTX51 Tiny时钟节拍中断(或任何其他中断)出现,微控制器恢复执行程序。空闲任务执行的代码可以通过配置文件CONF_TNY.A51进行配置并使能

堆栈管理
RTX51 Tiny在8051的IDATA内存区为每个任务维持一个堆栈。当一个任务执行时,给他准备了最大可能需要地堆栈。当任务切换时,前一个任务的堆栈被压栈并重定位,而把当前任务的堆栈被重定位并弹栈
下图显示了三个任务的堆栈在内存中的分布
?STACK是堆栈的起始地址。在这个例子中,位于堆栈中的对像包括全局变量,寄存器和位地止内存,剩下的内存用作任务堆栈。内存的顶端在配置文件中定义。

配置RTX51 Tiny
RTX51 Tiny可以由根据用户的应用来定制。

配置
RTX51 Tiny必须根据你的嵌入式应用来配置。所有地配置参数都在椟置文件CONF_TNY.A51中,这个文件位于\KEIL\C51\RTXTINY2\文件夹中,文件中的椟置选可以你做如下事情。
指定时钟节拍中断寄存器组
指定时钟节拍间隔(多个8051机器周期)
指定在时钟节拍中断中执用的用户代码
指定round-robin溢出时间
使能禁能round-robin任务切换
指定你的应用程序包含了长时间的中断
指定是否使用了code banking
定义RTX51 Tiny的栈顶
指令需要最小的堆栈空间
指定堆栈错误时执行代码
定义空闲任务操作
CONF_TNY.A51的默认配置包含在RTX51 Tiny的库中。然而,要在就用中使用配置文件,必须把配置文件拷贝到你的工程文件夹并添加到工程中。
要定制RTX51 Tiny的配置,必须改变CONF_TNY.A51的设置
注意:
如果在工程中不包括配置文件,默认的配置文件将会自动地包含到工程中。后事存在库中的配置文件可能会对你的应用起到相反效果

硬件时钟
以下参数指定了如何配置制RTX51 Tiny的配件时钟
INT_REGBANK 指定制RTX51 Tiny时钟中断使用的寄存器组,默认的是寄存器组1
INT_CLOCK 指定时钟产生中断前的周期数据。这个值的范围是1000~65535。较小的值产生中断较快。这个值用来计算时钟的重新装载值(65536-INT_CLOCK)。缺省值是10000。
HW_TIMER_CODE 是一个宏定义,它用来指定在制RTX51 Tiny时钟节拍中断中执的代码。这个宏缺省的设置是从中断中返回(RETI)如:
HW_TIMER_CODE MACRO
; Empty Macro by default
RETI
ENDM


Round-robin
Round-robin切换是默认使能的,以下参数用来设定Round-robin切换的时间或禁能Round-robin切换
TIMESHARING 指定任务在进行Round-robin切换前执行的RTX51 Tiny时钟节拍数。当这个值为0时禁止Round-robin切换。缺省值是5个时钟节拍。

长时间中断
一般情况下,中断服务程序(ISRs)都要求很快执行完毕。有时候,中断服务程序可能需执行很长一段时间。如果一个高优先级的中断执的时间超过了节拍间隔,RTX51的时钟中断就可能被这个更高优先级的中断中断了,并且被以后的RTX51 时钟中断重入了。
如果使用了需要运行很长时间的高优先级中断,就应该考虑减少中断服务程序的工作量,改变RTX51 时钟节拍的速率,或考使用以下配置。
LONG_USR_ISR 指定是否使用执行时间超过时钟节拍的中断。当这个值置为1时,RTX51 Tiny就会把保护RTX51 Tiny时钟节拍中断重入的代码包含进去。这个值缺省为0

Code banking
以下设置指定RTX51 Tiny应用中是否使用code banking
CODE_BANKING 置1使用code banking,清0不使用code banking。缺省值为0
注意:L51_BANK.A51的2.2或更高版本需要RTX51 Tiny 程序使用code banking

堆栈
堆栈的配置选项有几个。以下参指定了用于堆栈的内存空间的大小和堆栈的最小空间。当CPU的堆栈空间不够时用一个宏指定去执行哪一段代码。
RAMTOP指定栈顶地址。最好不要修改这个地址除非你在这个堆栈的上面使用了IDATA变量。
FREE_STACK指定堆栈上的最少可用空间。当切换到一个任务是时,如果RTX51 Tiny检测到可用的堆栈空间小于这个值,STACK_ERROR宏就会被执行。置为0将禁止对堆栈的检查,缺省值为20字节
STACK_ERROR是一个宏,它代表了一段在堆栈出现错误时执行的代码。这个宏的缺省代码为禁能中断并进入一个无限循环。如
STACK_ERROR MACRO
CLR EA ; disable interrupts
SJMP $ ; endless loop if stack space is exhausted
ENDM

空闲任务
没有任务需要执行时,RTX51 Tiny就执行空闲任务。空闲任务什么都不做,只是等待RTX51 Tiny的时钟节拍中断并切换到另一个就绪的任务。以下参数用来配置RTX51 Tiny空闲任务的不同方面
CPU_IDLE 这是一个宏,代表了在空闲任务中执行的代码。缺省指令是设置寄存器PCON的空闲模式位。这样就可以通过挂起程序节省能耗。如
CPU_IDLE MACRO
ORL PCON,#1 ; set 8051 CPU to IDLE
ENDM

CPU_IDLE_CODE 指定CPU_IDLE宏在空闲任务中是否执行。缺省值为0,这样空闲任务将不包含CPU_IDLE宏

库文件
RTX51 Tiny包括两个库文件
RTX51TNY.LIB is used for non-banking RTX51 Tiny programs
RTX51BT.LIB is used for code-banking RTX51 Tiny programs.
The RTXTINY2.PRJ project found in the \KEIL\C51\RTXTINY2\SOURCECODE\ folder is used to build these two libraries.
注意:不需要在应用显式地包含这两个文件。如果你使用µVision2 IDE或command-line linker时这两个文件会自动地包含进去。
如果要建立RTX51 Tiny的库,缺省的配置文件会加入进去。如果不显式地声明包含配置文件,默认的配置文件会包含进去。以后对配置文件做的修改会对就用产生意想不到的效果。

优化
可以通过以下方式优化RTX51 Tiny程序
如果可能,禁能round-robin任务切换。Round-robin需要13字节堆栈空间来存储任务地址和所有寄存器。如果任务切换是通过RTX51 Tiny运行时库(比如os_wait或 os_switch_task)来触发的就不需要这些空间了。
使用os_wait而不是使用round-robin的时间耗尽来切换任务。这些会提高系统的反应时间和任务的反应速度
避免把系统时钟节拍的中断速率设的太高。把时钟溢出的时间设的比较短增加了每秒的时钟节拍数但是减少了每个任的可用时间,因为每个时钟节拍中断会使用100-200个周期来运行。所以要把湔出时间设的足够的小从而减小时钟节拍中断带来的副作用。

使用RTX51 Tiny
要使用RTX51 Tiny,必须成功地生成RTX51程序并编译连接他们

编写程序
定义RTX51程序使用关键字 _task_
以RTX51TNY.H的模板使用RTX51 Tiny的内核

包含文件
RTX51 Tiny只需包含一个文件RTX51TNY.H。所有的运行时库来常量都在这个头文件中定义。可以采用以下方式包含它:
#include

编程指南
使用RTX51 Tiny编程时须遵循下规则:
包含文件rtx51tny.h
不要创建C主函数,RTX51 Tiny有它自已的主要函数
程序至少创建一个任务
程序必须至少调用一次RTX51 Tiny运行时库(如os_wait)。否则,连接器将不会把RTX51 Tiny库包含进去
任务0是你的程序执行的第一任务。必须调用在任务0中调用os_create_task函数来运行其他任务
任务永不返回或退出。任务必须使用while(1)或类似的结构。使用os_delete_task函数可以挂起一个任务
必须在µVision2中或在连接编译命令行中指定RTX51 Tiny

定义任务
实时或多任务应用由一个或多个完成特定功能的任务组成。RTX51 Tiny支持最多16个任务
任务必须是用_task_声明的C函数,返回值和参数都必须是void类型的,如
void func (void) _task_ task_id
这里
func is the name of the task function.
task_id is a task ID number from 0 to 15
以下例子定义了函数job0作为一个任务,任务编号为0。
void job0 (void) _task_ 0 {
while (1) {
counter0++; /* increment counter */
}
}
注意:所有的任务都必须通过循环实现,而且永不返回

RTX51 tiny(3)
编译连接
有两种方法编译和连接RTX51 Tiny应用程序。
使用 µVision2 IDE.
使用 Command-Line Tools.
命令行工具
RTX51 Tiny完全集成在C51编程语言中。这样就可能很容易地生成RTX51 Tiny应用。要成RTX51 Tiny程序,你只需要写C函数,不要需汇编编程
使用命令行编译RTX51 Tiny程序
以一般形式调用编译器,不需要特别的参数,如
C51 RTXPROG.C DEBUG OBJECTEXTEND
RTXPROG.OBJ文件包含了C语言代码和你定义的TX51 Tiny任务
使用命令行连接RTX51 Tiny程序
在连接器命令行使用ZTX51TINY指示
在目标文件列表中包含RTX_CONF.OBJ 文件(如果你更改了配置)例如:
BL51 RTXPROG.OBJ, RTX_CONF.OBJ RTX51TINY
RTX51TINY命令参数指示连接器连接RTXPROG.OBJ到RTX_CONF.OBJ,并且包含RTX51 Tiny库。RTX51 Tiny程序调用C文件生成的任务
注意:
在RTX51 Tiny不创建C主函数。只创建任务函数。C主函数包含在RTX51 Tiny库中用来起动操作系统和任务0。如果你在应用中包含了主C函数,连接时就会出现有多个全局主函数的错误提示
在程序中至少要创建一个任务
应用中至少调用一次RTX51 Tiny的函数(如os_wait 或 os_create_task)以使连接器包含RTX51 Tiny库

µVision2 IDE
打开目标选项对话框
在operating system一栏中选择RTX-51 Tiny

函数参考
以下介绍RTX51 Tiny的系统函数。这些函数以字母顺序排列,并且被分成几部分;
ad***  管理员  发表于 2012-9-3 02:21:44  | 显示全部楼层
概要:简要介绍程序的功能,列出包含其声明和原型的文件,说明其定义,解释其参数
描述:提供程序的详细描述和使用方法
返回值:描述程序的返回值
参见:相关程序的名字
举例:用一个函数或程序段来显示如何正确使用该函数。
注意:
以os_开头的函数可以被任务调用,但不能被中断服程序调用
以isr_开头的函数可以被中断调用,但不能被任务调用

isr_send_signal
概述
#include

char isr_send_signal (
unsigned char task_id); /* ID of task to signal */
描述
该函给一个任务(用task_id标识)发送一个信号。如果这个任务正在等待该信号,该函数唤醒这个任务并使其进入就绪状态;否则,这个信号就存放在这个任务的信号标志里。isr_send_signal只能在中断程序里调用。
返回值
成功发送返回0,如果任务不存在返回-1
参见
os_clear_signal, os_send_signal, os_wait

#include

void tst_isr_send_signal (void) interrupt 2
{
isr_send_signal (8); /* signal task #8 */
}


isr_set_ready
Summary:
include

char isr_set_ready (
unsigned char task_id); /* Task to make ready */
Description:
isr_set_ready使编号为task_id的任务进入就绪状态,只能在中断程序中调用
Return Value:
See Also: os_set_ready, os_wait
Example: None.
#include

void tst_isr_set_ready (void) interrupt 2
{
isr_set_ready (1); /* Set ready flag for task 1 */
}

os_clear_signal
Summary:
#include
char os_clear_signal (
unsigned char task_id); /* signal to clear */
Description:
os_clear_signal清除编号为task_id的信号标志
Return Value:成功清除则返回0,如果任不存在则返回-1
See Also: isr_send_signal, os_send_signal
Example:
#include

void tst_os_clear_signal (void) _task_ 8
{
.
.
.
os_clear_signal (5); /* clear signal flag in task 5 */
.
.
.
}

os_create_task
Summary:
#include

char os_create_task (
unsigned char task_id); /* ID of task to start */
Description:
该函数启动编号为task_id的任务,并且这个任务被标识为就绪并按照RTX51 Tiny的规则开始执行。
Return Value:如果任务被成功启动则返加0,如果任务不存在或任务不能被启动则返回-1
See Also: os_delete_task
Example:
#include
#include /* for printf */

void new_task (void) _task_ 2
{
.
.
.
}


void tst_os_create_task (void) _task_ 0
{
.
.
.
if (os_create_task (2))
{
printf ("Couldn’t start task 2\n");
}
.
.
.
}


os_delete_task
Summary:
#include

char os_delete_task (
unsigned char task_id); /* task to delete */
Description:
停止编号为task_id的执行,这个任务在任务列表中删除
Return Value:
如果任务被删除成功则返回0,如果任务不存在或任务还没有开始执行则返回-1;

os_running_task_id
Summary:
#include

char os_running_task_id (void);
Description:该函数返回当前运行的任务编号
Return Value:当前执行的任务编号,范围为0-15
See Also: os_create_task, os_delete_task
Example:
#include

void tst_os_running_task (void) _task_ 3
{
unsigned char tid;

tid = os_running_task_id (); /* tid = 3 */
}

os_send_signal
Summary:
#include

char os_send_signal (
unsigned char task_id); /* ID of task to signal */
Description:该函数给编号为task_id的任务发一个信号。如果任务在等待该信信号,该函数使这个任务进行就绪状态。否则信号就存储在任务的信号标志里
Return Value:发送成功则返回0,如果任务不存在返回-1
See Also: isr_send_signal, os_clear_signal, os_wait
Example:
#include

void signal_func (void) _task_ 2
{
.
.
.
os_send_signal (8); /* signal task #8 */
.
.
.
}

void tst_os_send_signal (void) _task_ 8
{
.
.
.
os_send_signal (2); /* signal task #2 */
.
.
.
}


os_reset_interval
Summary:
#include

void os_reset_interval (
unsigned char ticks); /* Number of Timer Ticks */
Description:该函数用来纠正调用os_wait同时等待K_IVL和K_SIG事件时带来的定时问题。在这种情况下,如果是信号(K_SIG)导致os_wait退出,间隔定时器不进行调整,随后调用的os_wait要等待的时间间隔就不会延迟要求的时间。在这种情况下使用os_reset_interval复位时间间隔时钟。
Return Value:None.
See Also: os_wait
Example:
#include

void task_func (void) _task_ 4
{
.
.
.
switch (os_wait2 (K_SIG | K_IVL, 100))
{
case TMO_EVENT:
/* Timeout occurred - os_reset_interval not required */
break;

case SIG_EVENT:
/* Signal received - os_reset_interval required */
os_reset_interval (100);
/* do something with the signal */
break;
}
.
.
.
}

RTX51 Tiny(4)
os_set_ready
Summary:
#include

char os_set_ready (
unsigned char task_id); /* Task to make ready */
Description:
使编号为task_id的任务就入就绪状态,可以任务中调用该函数
Return Value: None.
See Also: isr_set_ready, os_wait
Example:
#include

void ready_func (void) _task_ 2
{
.
.
.
os_set_ready (1); /* Set ready flag for task 1 */

.
}


os_switch_task
Summary:
#include

char os_create_task (void);
Description:该函数使用任务放弃CPU的使用权以使其他的任务可以执行。如果只有这一个任务,那么它就马上恢复执行
Return Value: None.
See Also: os_wait
Example:
#include
#include /* for printf */

void long_job (void) _task_ 1
{
float f1, f2;

f1 = 0.0;

while (1)
{
f2 = log (f1);
f1 += 0.0001;
os_switch_task (); // run other tasks
}
}

os_wait
Summary:
#include

char os_wait (
unsigned char event_sel, /* events to wait for */
unsigned char ticks, /* timer ticks to wait */
unsigned int dummy); /* unused argument */
Description:该函数挂起当前任务等待一个或几个事件如:时间间隔,时钟溢出或者信号或中断。参数event_sel用来表示等待哪一个事件或等待哪一种组合事件发生
事件 描述
K_IVL 等待特定数目时钟节拍的间隔
K_SIG 等待一个信号
K_TMO 等待时钟节拍确认的时钟溢出
上面的任务可以用(’|’)进行或,K_TMO | K_SIG表示任务等待时钟溢出或者一个信号
参数ticks用来指明要等待的时钟节拍数,对于事件K_IVL来说是等待的时间间隔数对于事件K_TMO来说时钟溢出的次数
参数dummy用于与RTX51 Full兼容,这个参数在RTX51 Tiny中是无用的。
Return Value
当以下事件之一发生时,任务运行使能。函数os_wait返回使任务恢复运行的常量, 任务然后开始执行。可能的返回值有:
RDY_EVENT: 任务的就绪标志被os_set_ready 或isr_set_ready置位,
SIG_EVENT: 收到了一个信号
TMO_EVENT: 时钟已经耗尽或一个时间间隔计数已满
NOT_OK: 参数event_sel无效
See Also: os_wait1, os_wait2
Example:
#include
#include /* for printf */

void tst_os_wait (void) _task_ 9
{
while (1)
{
char event;

event = os_wait (K_SIG + K_TMO, 50, 0);

switch (event)
{
default: /* this never happens */
break;

case TMO_EVENT: /* time-out */
/* 50 tick time-out */
break;

case SIG_EVENT: /* signal received */
break;
}
}
}
os_wait1
Summary:
#include
char os_wait1 (
unsigned char event_sel); /* events to wait for */
Description: 该函数挂起当前任务并等待一个事件发生,该函数是函数os_wait的一个子集,并不支持os_wait的所有参数。参数event_sel只能指明是等待一个事件K_SIG的发生
Return Value:当信号事件发生时,任务的运行使能。使能任务的参数由os_wait1返回。可能的返回值有:
RDY_EVENT: 任务的就绪标志被os_set_ready 或isr_set_ready置位,
SIG_EVENT: 收到了一个信号
NOT_OK: 参数event_sel无效
See Also: os_wait, os_wait2
Example: See os_wait.
os_wait2
Summary:
#include
char os_wait2 (
unsigned char event_sel, /* events to wait for */
unsigned char ticks); /* timer ticks to wait */
Description:该函数挂起当前的任务等待一个事件的发生,事件可以是时间间隔,时钟溢出,或者是来自其他任务或中断的信号。参数event_sel指明要等待的事件的类型或几个事件的组合。可能的事件是
K_IVL 等待特定数目时钟节拍的间隔
K_SIG 等待一个信号
K_TMO 等待时钟节拍确认的时钟溢出
上面的任务可以用(’|’)进行或,K_TMO | K_SIG表示任务等待时钟溢出或者一个信号
参数ticks用来指明要等待的时钟节拍数,对于事件K_IVL来说是等待的时间间隔数对于事件K_TMO来说时钟溢出的次数
Return Value:
当以下事件之一发生时,任务运行使能。函数os_wait返回使任务恢复运行的常量, 任务然后开始执行。可能的返回值有:
RDY_EVENT: 任务的就绪标志被os_set_ready 或isr_set_ready置位,
SIG_EVENT: 收到了一个信号
TMO_EVENT: 时钟已经耗尽或一个时间间隔计数已满
NOT_OK: 参数event_sel无效
See Also: os_wait, os_wait1
Example: See os_wait.
*滑块验证:
您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

打开支付宝扫一扫,最高立得1212元红包
搜索

图文热点

更多

社区学堂

更多

客服中心

QQ:187196467 服务时间:周一至周日 8:30-20:30

关注我们

关于我们
关于我们
友情链接
联系我们
帮助中心
网友中心
购买须知
支付方式
服务支持
资源下载
售后服务
定制流程
关注我们
官方微博
官方空间
官方微信
快速回复 返回顶部 返回列表