标题：rtx51tny
简介
RTX51 Tiny是一种实时操作系统（RTOS），可以用它来建立多个任务（函数）同时执行的应用。嵌入式应用系统经常有这种需求。RTOS可以提供调度、维护、同步等功能。
实时操作系统能灵活的调度系统资源，像CPU和存储器，并且提供任务间的通信。RTX51 Tiny是一个功能强大的RTOS，且易于使用，它用于8051系列的微控制器。
RTX51 Tiny的程序用标准的C语言构造，由Keil C51 C编译器编译。用户可以很容易的定义任务函数，而不需要进行复杂的栈和变量结构配置，只需包含一个指定的头文件。
产品规格
参   数
范    围
最大任务数
16
最大活动任务
16
代码空间需求
900字节最大
数据空间需求
7字节
栈空间需求
3字节/任务
外部RAM需求
0字节
定时器
0
系统时钟因子
1000～65535
中断等待
20个周期或更少
上下文切换时间
100～700个周期


工具需求
以下为使用RTX51 Tiny需要的应用软件：
C51编译器
A51宏汇编器
BL51连接器或LX51连接器
RTX51TNY.LIB和RTX51BT.LIB库文件必须保存于库路径下,通常,该路径是"KEIL"C51"LIB文件夹。RTX51TNY.H必须保存在包含路径下,通常是"KEIL"C51"INC文件夹。

目标需求
RTX51 Tiny运行于大多数8051兼容的器件及其变种上。RTX51 Tiny应用程序可以访问外部数据存储器，但内核无此需求。
RTX51 Tiny支持Keil C51编译器全部的存储模式。存储模式的选择只影响应用程序对象的位置，RTX51 Tiny系统变量和应用程序栈空间总是位于8051的内部存储区（DATA或IDATA区），一般情况下，应用程序应使用小（SMALL）模式。
RTX51 Tiny执行协作式任务切换（每个任务调用一个操作系统例程）
和时间片轮转任务切换（每个任务在操作系统切换到下一个任务前运行一个固定的时间段），不支持抢先式任务切换以及任务优先级。RTX51 Full支持抢先式任务切换。1、中断RTX51 Tiny与中断函数并行运作，中断服务程序可以通过发送信号（用isr_send_signal函数）或设置任务的就序标志（用isr_set_redy函数）与RTX51 Tiny的任务进行通信。
如同在一个标准的，没有RTX51 Tiny的应用中一样，中断例程必须在RTX51Tiny应用中实现并允许，RTX51 Tinyim 没有中断服务程序的管理。
RTX51 Tiny使用定时器0、定时器0中断，和寄存器组1。如果在程序中使用了定时器0，则RTX51 Tiny将不能正常运转。你可以在RTX51 Tiny定时器0的中断服务程序后追加自己的定时器0中断服务程序代码（参见硬件定时器）。
RTX51 Tiny假设总中断总是允许（EA=1）。RTX51 Tiny库例程在需要时改变中断系统（EA）的状态，以确保RTX51 Tiny的内部结构不被中断破坏。当允许或禁止总中断时，RTX51 Tiny只是简单的改变EA的状态，不保存并重装EA，EA只是简单的被置位或清除。因此，如果你的程序在调用RTX51例程前某止了中断，RTX51可能会失去响应。
在程序的临界区，可能需要在短时间内禁止中断。但是，在中断禁止后，不能调用任何RTX51 Tiny的例程。如果程序确实需要禁止中断，应该持续很短的时间。2、再入函数C51编译器提供对再入函数的支持，再入函数在再入堆栈中存储参数和局部变量，从而保护递归调用或并行调用。RTX51 Tiny不支持对C51再入栈的任何管理。因此，如果在程序中使用再入函数，必须确保这此函数不调用任何RTX51 Tiny系统函数，且不被循环任务切掉所打断。
仅用寄存器传递参数和保存自动变量的C函数具有内在的再入性，可以无限制的调用RTX51 Tiny。
非可再入C函数不能被超过一个以上的任务或中断过程调用。非再入C51函数在静态存储区段保存参数和自动变量（局部数据），该区域在函数被多个任务同时调用或递归调用时可能会被修改。
如果确定多个任务不会递归（或同时）调用，则多个任务可以调用非再入函数。通常，这意味着必须禁止循环任务调度，且该非再入函数不能调用任何RTX51 Tiny系统函数。
附注：
l        如果希望在多个任务或中断中调用再入或非再入函数，应当禁止循
环任务调度。3、C库例程可再入C51库函数可在任何任务中无限制的使用。对于非再入的C51库函数，同样有非可再入C函数的限制。4、多数据指针Keil C51编译器允许使用多数据指针(存在于许多8051的派生芯片中), RTX51 Tiny不提供对它们的支持.因此,在RTX51 Tiny的应用程序中应小心使用多数据指针。
从本质上说，必须确保循环任务切换不会在执行改变数据指针选择器的代码时发生。
附注：
l        如果要使用多数据指针，应该禁止循环任务切换。5、运算单元Keil C51编译器允许使用运算单元（存在于许多8051的派生芯片中）。RTX51 Tiny不提供对它们的支持。
因此，在RTX51 Tiny的应用程序中须小心使用运算单元。
从本质上说，必须确保循环任务切换不会在执行用运算单元的代码时发生。
附注：
l            如果希望使用运算单元，应禁止循环任务切换。6、寄存器组RTX51 Tiny分配所有的任务到寄存器0，因此，所有的函数必须用C51的默认设置进行编译，REGISTERBANK（0）。
中断函数可以使用剩余的寄存器组。然而，RTX51 Tiny需要寄存器组区域中的6个永久性的字节，用于这些字节的寄存器组在配置文件中指定。

实时程序
实时程序必须对实时发生的事件快速响应。事件很少的程序不用实时操作系统也很容易实现。随着事件的增加，编程的复杂程度和难度也随之增大，这正是RTOS的用武之地。一、单任务程序嵌入式程序和标准C程序都是从main函数开始执行的，在嵌入式应用中，main通常是一个无限循环，可以认为是一个持续执行的单个任务，例如：
void main (void)
﹛while(1)           /*永远重复*/
﹛
do_something();  /*执行 do_something“任务”*/
﹜
﹜
在这个例子里，do_something函数可以认为是一个单任务，由于仅有一个任务在执行，所以没有必要进行多任务处理或使用多任务操作系统。二、多任务程序许多C程序通过在一个循环里调用服务函数（或任务）来实现伪多任务调度。如：
void main(void)
﹛
int counter="0";
while(1)                         /*一直重复执行*/
﹛
check_serial_io();              /*检查串行输入*/
process_serial_cmds() ;         /*处理串行输入*/
check_kbd_io();              /*检查键盘输入*/
process_kbd_cmds();           /*处理键盘输入*/
adjust|ctrlr_parms()；          /*调整控制器*/
counter++;                   /*增加计数器*/
﹜
﹜
该例中，每个函数执行一个单独的操作或任务，函数（或任务）按次序依次执行。
当任务越来越多，调度问题就被自然而然的提出来了。例如，如果process_kbd_cmds函数执行时间较长，主循环就可能需要较长的时间才能返回来执行check_sericd_io函数，导致串行数据可能被丢失。当然，可以在主循环中更
频繁的调用check_serial_io函数以纠正这个问题，但最终这个方法还是会失效三、RTX51 Tiny 程序当使用Rtx51Tiny时，为每个任务建立独立的任务函数，例如：
void check_serial_io_task(void) _task_ 1
﹛/*该任务检测串行I/0*/﹜
void process_serial_cmds_task(void) _task_ 2
﹛/*该任务处理串行命令*/﹜
void check_kbd_io_task(void) _task_ 3
﹛/*该任务检测键盘I/O*/﹜
void process_kbd_cmds_task(void) _task_ 4
﹛/*处理键盘命令*/﹜ 
void startup-_task(void) _task_ 0
﹛
os_create_task(1);                 /*建立串行I/O任务*/
os_create_task(2);                 /*建立串行命令任务*/
os_create_task(3)；               /*建立键盘I/O任务*/
os_create_task(4);                /*建立键盘命令任务*/
os_delete_task(0);                 /*删除启动任务*/
﹜
该例中，每个函数定义为一个RTX51 Tiny任务。RTX51 Tiny程序不需要main函数，取而代之，RTX51 Tiny从任务0开始执行。在典型的应用中，任务0简单的建立所有其他的任务。
   
原理

RTX51 Tiny 用于管理目标系统的资源，本章讨论RTX51 Tiny如何使用这些资源。一、定时器滴答中断RTX51 Tiny 用标准8051的定时器0（模式1）生产一个周期性的中断。该中断就是RTX51 Tiny的定时滴答（Timer Tick）。库函数中的超时和时间间隔就是基于该定时滴答来测量的。
默认情况下，RTX51每10000个机器周期产生一个滴答中断，因此，对于运行于12MHZ的标准8051来说，滴答的周期是0.01秒，也即频率是100HZ(12MHz/12/10000)。该值可以在CONF_TNY.A51配置文件中修改。
附注：
l可以在RTX51的定时滴答中断里追加自己的代码。参见CONF_TNY.A51 配置文件。
l关于RTX51 Tiny如何使用中断可以参考概述中中断一节的叙述。二、任务RTX51 Tiny本质上是一个任务切换器，建立一个RTX51 Tiny程序，就
是建立一个或多个任务函数的应用程序。下面的信息可以帮助你快速的理解
RTX51 。
l任务用新的关键字由C语言定义，该关键字是Keic C51 所支持的。
lRTX51 Tiny维护每个任务的正确状态（运行、就绪、等待、删除、超时）。
l某个时刻只有一个任务处于运行态。
l任务可能处于就绪态、等待态、删除态或超时态。
l空闲任务（Idle_Task）总是处于就绪态，当定义的所有任务处于阻 塞状态时，运行该任务。三、任务管理每个RTX51 Tiny 任务总是处于下述状态中的一种状态中。
状态
描    述
运行
正在运行的任务处于运行态。某个时刻只能有一个任务处于该状态。
os_running_task_id 函数返回当前正在运行的任务编号。
就绪
准备运行的任务处于就绪态。一旦运行的任务完成了处理，RTX51    Tiny选择一个就绪的任务执行。一个任务可以通过用os_set_ready或os_set_ready函数设置就绪标志来使其立即就绪（即便该任务正在等待超时或信号）。
等待
正在等待一个事件的任务处于等待态。一旦事件发生，任务切换到就绪态。Os_wait函数用于将一个任务置为等待态。
删除
没有被启动或已被删除的任务处于删除态。Os-delete-task函数将一个已经启动（用os_create_task）的任务置为删除态。
超时
被超时循环中断的任务处于超时态，在循环任务程序中，该状态相当于就绪态。
四、事件在实时操作系统中，事件可用于控制任务的执行，一个任务可能等待一个事件，也可能向其他任务发送任务标志。
os_wait函数可以使一个任务等待一个或多个事件。
l超时是一个任务可以等待的公共事件。超时就是一些时钟滴答数，   当一个任务等待超时时，其他任务可以执行。一旦到达指定数量的滴答数，任务就可以继续执行。
l时间间隔（Interval）是一个超时（Timeout）的变种。时间间隔与超
时类似，不同的是时间间隔是相对于任务上次调用os_wait函数的指定数量的时钟滴答数。
l信号是任务间通信的方式。一个任务可以等待其他任务给它发信号（用os_send_signal和isr_send_signal函数）。
l每个任务都有一个可被其它任务设置的就绪标志（用os_set_ready和
isr_set_ready函数)。一个个等待超时、时间间隔或信号的任务可以通过设置它的就绪标志来启动。
lisr_set_ready函数）。一个等待超时、时间间隔或信号的任务可以通 过设置它的就绪标志来启动。
下表是os_wait函数等待的事件：
K_IVL
等待制定的时间
隔K_SIG
等待一个信号
K_TMO
等待指定的超时
os-wait返回时，返回值表明发生了的事件：
返回值
意 义
RDY_EVENT
任务的就绪标志被置位
SIG_EVENT
收到一个信号
TMO_EVENT
超时完成或时间间隔到达。
os_wait可以等待下面的事件组合：
lK_SIG︱K_TMO:任务延迟直到有信号发给它或者指定数量的时钟滴答
到达。
lK_SIG︱K_IVL:任务延迟直到有信号到来或者指定的时间间隔到达。
附注：
lK_IVL和K_TMO事件不能组合
五、任务调度程序：任务调度程序给任务分配处理器，RTX51 Tiny调度程序用下列规则确定
哪个任务要被运行：
当前任务被中断如果：
1、任务调用了os_switch_task且另一个任务正准备运行。
2、任务调用了os_wait且指定的事件没有发生。
3、任务执行了比轮转时间片更长的时间。
另一个任务启动如果：
1、无其它任务运行。
2、要启动的任务处于就绪态或超时态。六、循环任务切换RTX51 Tiny可以配置为用循环法进行多任务处理（任务切换）。循环法允许
并行的执行若干任务。任务并非真的同时执行，而是分时间片执行的（CPU时间分
成时间片，RTX51 Tiny给每个任务分配一个时间片）。由于时间片很短（几毫秒），
看起来好象任务在同时执行。
任务在它的时间片内持续执行（除非任务的时间片用完）。然后，RTX51 Tin
g切换到下一个就绪的任务运运行。时间片的持续时间可以通过RTX51 Ting配置
定义。
下面是一个RTX51 Tiny程序的例子，用循环法多任务处理，程序中的两个任务
是计数器循环。RTX51 Tiny在启动时执行函数名为job0的任务0，该函数建立了另
一个任务job1，在job0执行完它的时间片后， RTX51 Tiny切换到job1。在job1执
行完它的时间片后，RTX51 Ting又切换到job0，该过程无限重复。
#include 
int counter0;
int counter1;
void job0(void)  _task_  0
﹛
os_create(1);              /*标记任务1为就绪*/
while(1) 
﹛                         /*无限循环*/
counter0++;             /*更新记数器*/
｝
｝
void job1(void) _task_1
﹛
while(1) 
﹛                       /*无限循环*/
counter++;                /*更新记数器*/
｝
｝
附注:
l可以用os_wait 或os_switch_task让RTX51 Tiny切换到另一个任务而不是
等待任务的时间片用完。 os_wait函数挂起当前的任务（使之变为等待态）直
到指定的事件发生（接着任务变为就绪态）。在此期间，任意数量的其他任务
可以运行。七、协作任务切换如果禁止了循环任务处理，就必须让任务以协作的方式运作，在每个任务
里调用os_wait或os_switch_task，以通知RTX51 Tingy切换到另一个任务。
os_wait与os_switch_task的不同是，os_wait是让任务等待一个事件，而
os_switch_task是立即切换到另一个就绪的任务。八、空闲任务没有任务准备运行时，RTX51 Ting执行一个空闲任务。空闲任务就是一个
无限循环。如：
SJMP＄
有些8051兼容的芯片提供一种降低功耗的空闲模式，该模式停止程序的执
行，直到有中断产生。在该模式下，所有的外设包括中断系统仍在运行。
RTX51 Tiny允许在空闲任务中启动空闲模式（在没有任务准备执行时）。当
RTX51 Tiny的定时滴答中断（或其它中断）产生时，微控制器恢复程序的执行。
空闲任务执行的代码在CONF_TNY.A51配置文件中允许和配置。九、栈管理RTX51 Tiny为每个任务在8051的内部RAM区（IDATA）维护一个栈。任务
运行时，将得到可能得到的最大数量的栈空间。任务切换时，先前的任务栈被
压缩并重置，当前任务的栈被扩展和重置。
下图表明一个三任务应用的内部存储器的布局。
图略…… :-)
？STACK表示栈的起始地址。该例中，位于栈下方的对象包括全局变量、寄存器和位寻址存储器，剩余的存储器用于任务栈。存储器的顶部可在配置中指定
使用RTX51 Tiny
一般地，下面三步是使用RTX51 Tiny要实现的
l        编写RTX51程序
l        编译并连接程序
l        测试和调试程序
一、编写程序
写RTX51 Tiny程序时，必须用关键字对任务进行定义，并使用在RTX51TNY.H中声明的RTX51 Tiny核心例程。
1、包含文件
RTX51 Tiny仅需要包含一个文件：RTX51TNY.H。所有的库函数和常数都在该头文件中定义。你可以在你的源文件中包含它：
＃include<rtx51tny.h>
2、编程原则
以下是建立RTX51 Tiny程序时必须遵守的原则：
①、确保包含了RTX51TNY.H头文件。
②、不要建立main函数，RTX51 Tiny有自己的mian函数。
③、程序必须至少包含一个任务函数。
④、中断必须有效（EA=1），在临界区如果要禁止中断时一定要小心。参见概述中的中断一节。
⑤、程序必须至少调用一个RTX51 Tiny库函数（象os_wait）。否则，连接起将不包含RTX51 Tiny库。
⑥、Task 0是程序中首先要执行的函数，必须在任务0中调用os_create_task 函数以运行其余任务。
⑦、任务函数必须是从不退出或返回的。任务必须用一个while(1)或类似的结构重复。用os_delete_task函数停止运行的任务。
⑧、必须在uvison中指定RTX51 Tiny，或者在连接器命令行中指定。更多技术文档参见keil软件知识库。
3、定义任务
实时或多任务应用是由一个或多个执行具体操作的任务组成的，RTX51 Tiny支持最多16个任务。
任务就是一个简单的C函数，返回类型为void，参数列表为void，并且用_task_声明函数属性。例如：
void func (void)_task_task_id
这里，func是任务函数的名字，task_id是从0到15的一个任务ID号。
下面的例子定义函数job0编号为0的任务。该任务使一个计数器递增并不断重复。
void job0(void)_task_0
{
while(1)
{
Counter0++;
}
}
附注：
l        所有的任务都应该是无限循环，任务一定不能返回。
l        任务不能返回一个函数值，它们的返回类型必须是void。
l        不能对一个任务传递参数，任务的形参必须是void。
l        每个任务必须赋予一个唯一的，不重复的ID。
l        为了最小化RTX51 Tiny的存储器需求，从0开始对任务进行顺序编号。
二、编译和连接
有两种方法编译和连接RTX51 Tiny应用程序。
l        用uvison集成开发环境
l        用命令行工具
1、命令行工具
RTX51 Tiny已经完全集成到了C51编译语言中，这使得生成RTX51 Tiny应
用非常容易。建立RTX51 Tiny程序只需编写C函数，无需使用汇编。
从命令行编译RTX51 Tiny程序…
按常规方式调用编译器，无需特别的编译指示。例如：
C51 RTXPROG.C DEBUG OBJECTEXTEND
产生的RTXPROG.OBJ文件中包含C代码和定义的RTX51 Tiny任务。
从命令行连接RTX51 Tiny程序：
l        在连接器命令行内指定RTX51TNY指示
l        在目标文件列表中包含RTX_CONF.OBJ文件（如果改变了配置）
例如：BL51 RTPROG.OBJ, RTX_CONF.OBJ RTX51TNY
RTX51TNY指示命令连接器连接RTPROG.OBJ和TX_CONF.OBJ并且包含RTX51 Tiny库。这样就建立了RTX51 Tiny程序。
附注：
l        不要在RTX51 Tiny程序中建立mian函数，只建立任务函数就可以。main函数包含在RTX51 Tiny库中，它启动操作系统和任务0。如果在程序中包含了main函数，将产生一个连接错误指示有多个main被定义。
l        程序中至少建立一个任务函数。
l        必须至少调用一个RTX51 Tiny函数（象os_wait或os_create_task），这样，连接器才会包含RTX51 Tiny库。
2、uvison集成开发环境
用uvison建立RTX51 Tiny程序。
1)      打开目标对话框选项（从project菜单选择Options for Target）。
2)      选择目标标签。
3)      从操作系统选项列表选择RTX51 Tiny。
三、调试
uvison模拟器允许运行和测试RTX51 Tiny应用程序。RTX51 Tiny程序的载入和无RTX51 Tiny程序的载入是一样的。无需指定特别的命令和选项。
一个核心的对话框显示RTX51 Tiny核心和程序中任务的所有特征。从Peripherals菜单选择RTX51 Tiny Tasklist显示该对话框。
该对话框中：
l        TID是在任务定义中指定的任务ID。
l        Task Name是任务函数的名字。
l        State是任务当前的状态。
l        Wait for Event指出任务正在等待什么事件。
l        Sig显示任务信号标志的状态（1为置位）。
l        Timer指示任务距超时的滴答数，这是一个自由运行的定时器，仅在任务等待超时和时间间隔时使用。
l        Stack指示任务栈的起始地址