RTX51tiny摘要
RTX实时多任务概要
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 用标准8051的定时器0(模式1)生产一个周期性的中断。该中断就是RTX51 Tiny的定时滴答(Timer Tick)。库函数中的超时和时间间隔就是基于该定时滴答来测量的。
默认情况下,RTX51每10000个机器周期产生一个滴答中断,因此,对于运行于12MHZ的标准8051来说,滴答的周期是0.01秒,也即频率是100HZ(12MHz/12/10000)。该值可以在CONF_TNY.A51配置文件中修改。
任务调度程序:
任务调度程序给任务分配处理器,RTX51 Tiny调度程序用下列规则确定
哪个任务要被运行:
当前任务被中断如果:
1、任务调用了os_switch_task且另一个任务正准备运行。
2、任务调用了os_wait且指定的事件没有发生。
3、任务执行了比轮转时间片更长的时间。另一个任务启动如果:
1、无其它任务运行。
2、要启动的任务处于就绪态或超时态。
信号是任务间通信的方式。一个任务可以等待其他任务给它发信号(用
os_send_signal和isr_send_signal函数)。每个任务都有一个可被其它任务设置的就绪标志(用os_set_ready和isr_set_ready函数)。一个个等待超时、时间间隔或信号的任务可以通过设置它的就绪标志来启动。
?超时TIMEOUT:主要用于os_wait函数开始的时间延时,由RTX-51 Tiny 的定时器脉冲来确定持续时间。如果一个任务调用os_wait函数函数时带有TIMEOUT参数,则其将被挂起,当延时结束时将返回到READY状态并可以被再次运行。
?时间间隔事件INTERVAL:主要用于os_wait函数开始的时间间隔,由RTX-51 Tiny的定时器脉冲来确定间隔延时时间。RTX-51系统的定时器是不复位的,定时器一直处于运行状态,因此事件INTERVAL将同样一直工作,这与TIMEOUT不同。如果某个任务需要在同步间隔内执行,则可以使用时间间隔事件INTERVAL来完成。
?信号SIGNAL:信号SIGNAL为位变量,主要用于多任务之间的通信。在RTX-51 Tiny系统中,可以使用系统函数置位或者清除信号SIGNAL位。
一个任务在运行前可以使用os_wait函数来等待信号SIGNAL置位,如果信号SIGNAL未置位,则该任务将不执行;如果信号SIGNAL置位,该任务返回到READY状态,并可以被RTX-51 Tiny再次执行。
优化 RTX51 Tiny程序
当建立rtx51应用程序时下列项目应该注意
1 如果可能的话禁止时间片轮转多重任务那些使用时间片轮转多任务处理的任务需要13字节的堆栈空间用于存储任务环境寄存器等等.) 如果任务切换是由 os_wai t函数触发的则不需要环境存储器 os_wait函数还会改善系统反应时间因为一个等待执行的任务无须等待全部的时间片轮转超时持续时间
2 不要将报时信号中断速率设得太快将报时信号发生率设到低一些的数值
增加每秒发出报
时信号的数目每个时钟报时中断约有100到200个CPU周期所以应该把时间报时信号速率设的足够高以使中断等待时间减到最少
编程原则
以下是建立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软件知识库。
附注:
●所有的任务都应该是无限循环,任务一定不能返回。
●任务不能返回一个函数值,它们的返回类型必须是void。
●不能对一个任务传递参数,任务的形参必须是void。
●每个任务必须赋予一个唯一的,不重复的ID。
●为了最小化RTX51 Tiny的存储器需求,从0开始对任务进行顺序编号。
从Peripherals菜单选择RTX51 Tiny Tasklist显示该对话框。
该对话框中:
●TID是在任务定义中指定的任务ID。
●Task Name是任务函数的名字。
●State是任务当前的状态。
●Wait for Event指出任务正在等待什么事件。
●Sig显示任务信号标志的状态(1为置位)。
●Timer指示任务距超时的滴答数,这是一个自由运行的定时器,仅在任务等待超时和时间间隔时使用。
●Stack指示任务栈的起始地址
参数调整
方法是对\ keil\c51\rtx_tiny目录下的系统配置文件conf_tny.a51的相
应部分作出调整。
INT_ CLOCKEQU 10000用于决定定时器0多少时间产生一次中断由它决定操作系统的时钟节拍. 系统默认为10000个机器周期对于采用11. 0592MHz晶振的单片机而言系统的时钟周期为
1.08507×10000=10850s=10.8507 ms.
TIME SHARING EQU 5 定义任务切换的时间片的长度即多少个系统时钟周期为一个时间片.系统默认为5个系统时钟如果晶振为11. 0592MHz 则时间片为
10.8507×5=54.2535 ms .注意该值不能设定为0 否则系统不会进行任务切换. 函数参考
●以os_开头的函数可以由任务调用,但不能由中断服务程序调用。
●以isr_开头的函数可以由中断服务程序调用,但不能由任务调用。
1、irs_send_signal
概要: #include
char isr_send_signal(unsigned char task_id); /*信号发往的任务*/
描述: isr_send_signal函数给任务task_id发送一个信号。如果指定的任务正在等待一个信号,则该函数使该任务就绪,但不启动它,信号存储在任务的信号标志中。
附注:
●该函数是RTX51 Tiny实时操作系统的一部分,仅包含于PK51中。
●该函数仅被中断函数调用。
返回值成功调用后返回0,如果指定任务不存在,则返回-1。
参阅 os_clear_signal,os_send_signal,os_wait
例子
#include
void tst_isr_send_signal(void) interrupt 2
{
isr_send_signal(8); /*给任务8发信号*/
}
2、irs_set_ready
概要 #include< rtx51tny.h>
char isr_set_ready{ unsigned char task_id};/*使就绪的任务*/
描述将由task_id指定的任务置为就绪态。
附注
●该函数是RTX51 Tiny的一部分,包含在PK51中。
●该函数仅用于中断函数。
返回值无
例子 #include< rtx51tny.h>
void tst_isr_set_ready(void)interrupt 2
{ isr_set_ready(1);/*置位任务1的就绪标志*/
}
3、os_clear_signal
概要 #include< rtx51tny.h>
char os_clesr_signal(unsigned cahr task_id);/*清除信号的任务*/
描述清除由task_id指定的任务信号标志。
附注:该函数是RTX51 Tiny的一部分,包含在PK51中。
返回值信号成功清除后返回0,指定的任务不存在时返回-1。
参阅 isr_send_signal,os_send_signal,os_wait
例子 #include< rtx51tny.h>
void tst_os_clsar_siganl(void)_task_8
{
…
os_clear_signal(5); /*清除任务5的信号标志*/ …
}
4、os_create_task
概要 #include
char os_create_task(unsigned char task_id);/*要启动的任务ID*/
描述启动任务task_id,该任务被标记为就绪,并在下一个时间点开始执行。
附注:该函数是包含在PK51中的RTX51 Tiny的组成部分。
返回值任务成功启动后返回0,如果任务不能启动或任务已在运行,或没有以task_id定义的任务,返回-1。
参阅 os_delete_task
例子 #include< rtx51tny.h>
#include
void new_task(void)_task_2
{…}
void tst_os_create_task(void)_task_0
{
…
if(os_create_task(2))
{
printf(“couldn’t start task2\n”);
}
…
}
5、os_delete_task
概要 #include
char os_delete_task(unsigned char task_id);/*要删除的任务*/
描述函数将以task_id指定的任务停止,并从任务列表中将其删除。
附注该函数是包含在PK51中的RTX51 Tiny的组成部分。
返回值任务成功停止并删除后返回0。指定任务不存在或未启动时返回-1。
附注如果任务删除自己,将立即发生任务切换。
参阅 os_create_task
例子 #include
#include
void tst_os_delete_task(void)_task_0
{
…
if(os_delete_task(2))
{
printf(“couldn’t stop task2\n”);
}
…
}
6、os_reset_interval
概要 #include
void os_reset_interval(unsigned char ticks); /*滴答数*/
描述用于纠正由于os_wait函数同时等待K_IVL和K_SIG事件而产生的时间问题,在这种情况下,如果一个信号事件(K_SIG)引起os_wait退出,时间间隔定时器并不调整,这样,会导致后续的os_wait调用(等待一个时间间隔)延迟的不是预期的时间周期。允许你将时间间隔定时器复位,这样,后续对os_wait的调用就会按预期的操作进行。
附注:该函数是包含在PK51中的RTX51 Tiny的组成部分。
返回值无
例子 #include
void task_func(void)_task_4
{
…
switch(os_wait2(KSIG|K_IVL,100))
{
case TMO_EVENT:
/*发生了超时,不需要Os_reset_interval*/
break;
case SIG_EVCENT:
/*收到信号,需要Os_reset_interval*/
os_reset_interval(100);
/*依信号执行的其它操作*/
break;
}
…
}
7、os_running_task_id
概要 #include
char os_running_task_id(void);
描述函数确认当前正在执行的任务的任务ID。
附注:该函数是包含在PK51中的RTX51 Tiny的组成部分。
返回值返回当前正在执行的任务的任务号,该值为0~15之间的一个数。
例子 #include
void tst_os_running_task(void)_task_3
{
unsigned char tid;
tid=os_running_task_id( ); /*tid=3*/
}
8、os_send_signal
概要 #include
char os_send_signal(char task_id);/*信号发往的任务*/
描述函数向任务task_id发送一个信号。如果指定的任务已经在等待一个信号,则该函数使任务准备执行但不启动它。信号存储在任务的信号
标志中。
附注该函数是包含在PK51中的RTX51 Tiny的组成部分。
返回值成功调用后返回0,指定任务不存在时返回-1。
参阅 isr_send_signal,os_clear_signal,os_wait
#include
void signal_func(void)_task_2
{
…
os_send_signal(8); /*向8号任务发信号*/
…
}
void tst_os_send_signal(void)_task_8
{
…
os_send_signal(2); /*向2号任务发信号*/
…
}
9、os_set_ready
概要 #include
char os_set_ready(unsigned char task_id);/*使就绪的任务*/ 描述将以task_id指定的任务置为就绪状态。
附注:该函数是包含在PK51中的RTX51 Tiny的组成部分。
返回值无
例子 #include
void ready_func(void)_task_2
{
…
os_set_ready(1); /*置位任务1的就绪标志*/
…
}
10、os_switch_task
概要 #include
char os_switch_task(void);
描述该函数允许一个任务停止执行,并运行另一个任务。如果调用
os_switch_task的任务是唯一的就绪任务,它将立即恢复运
行。
附注:该函数是包含在PK51中的RTX51 Tiny的组成部分。
返回值无
例子 #include
#include
void long_job(void)_task_1
{
float f1,f2;
f1=0.0;
while(1)
{
f2=log(f1);
f1+=0.0001;
os_switch_task(); /*运行其它任务*/
}
}
11、os_wait
概要 #include
char os_wait(
NOT_OK 参数的值无效
参阅
isr_send_signal,isr_set_ready,os_clear_signal,os_reset_interval,
os_send_signal,os_set_ready,os_wait1,os_wait2
例子 #include
#include
void tst_os_wait(void)_task_9
{
while(1)
{
char event;
event=os_wait(K_SIG|K_TMO,50.0);
switch(event)
{
default: /*从不发生,该情况*/
break;
case TMO_EVENT;/*超时*/
break; /*50次滴答超时*/
case SIG_EVENT; /*收到信号*/
break;
}
}
}
12、os_wait1
概要 #include
char os_wait1(unsigned char event_sel);/*要等待的事件*/ 描述该函数挂起当前的任务等待一个事件发生。os_wait1是os_wait的一个子集,它不支持os_wait提供的全部事件。参数event_sel指定要等
待的事件,该函数只能是K_SIG。
附注:
●该函数是包含于PK51中的RTX51Tiny的组成部分。
●参见事件一节获得K_IVL,K_SIG和K_TMO的更多信息。
返回值当指定的事件发生,任务进入就绪态。任务恢复运行时,os_wait1 返回的值表明启动任务的事件,返回值见下面的常数列表:
范例:
RTX51 TINY为任务管理、任务通信以及其他服务提供函数,TRX51 TINY的系统函数可以直接被C51调用,这些函数的说明以及所有常数说明都放在头文件“RTX51TY.H”中,该头文件必须在用户应用程序的开始处用“#include”包含进来。
在该单片机程序中,首先为了与上位机保持一致,将8051初始化为:9600b/s 波特率,8位数据位,1位停止位,无奇偶校验,串行口工作在方式1,然后单片机根据上位机发送来的命令切换高频开关到所需要的天线。该程序中需要同时执行的任务有:闪烁指示任务、当前通道显示任务、开关转换以及结果输出任务。
主程序进行系统初始化以后,顺序建立3个任务,进入CPU休眠状态。各个任务运行后,首先进入任务Ready状态,等待相应任务的唤醒。任务被唤醒以后,进行相应处理,再次进入休眠状态。这样,可以减少任务切换,减轻系统负担。
单片机主要程序如下:
#include
#include
void serial_init (void); //串口初始化函数说明
void init (void) _task_ INIT{ //任务初始化
while(1){
serial_init ();……
os_create_task (BLINKING);
os_create_task (DISPLAY);
os_create_task (SWITCH); //标志任务进入就绪状态
os_delete_task (INIT); //删除初始化任务 }
}
void blinking (void) _task_ BLINKING{ //闪烁程序
…… }
void disp (void) _task_ DISPLAY{ //当前通道显示程序
……}
void swit (void) _task_ SWITCH{ //开关切换程序
……}
void sendback (void) _task_ SENDOK{ //输出结果程序
……}
serial () interrupt 4 using 2 { //中断函数
……
if (RI) {……
isr_send_signal(SWITCH);}
if (TI) {……
isr_send_signal (SENDOK);}
}
void serial_init (void) { //初始化串口
SCON = 0X50; //波特率9600,方式1,T1,
TMOD |= 0X20; //允许接收,允许中断,晶振11.0592
TH1 = 0XFD;
TR1 = 1;
ES = 1;
RI = 0; }
其中,串口命令到来时,引发串口中断serial()处理程序,串口中断处理程序唤醒开关转换任务或者结果输出任务。
程序编写完毕,经RTX51 TINY实时操作系统的编译之后即可生成相应的执行文件,然后通过编程器写入89C2051单片机。
RTX_EX2: 一个简单的RTX51应用程序
|程序RTX_EX2示范了RTX51 Tiny应用程序如何使用os_wait函数和信号传递这程序仅由一个源文件RTX_EX2.C组成位于\C51V4\RTX_TINY\RTX_EX2或\CDEMO\51\RTX_TINY\RTX_EX2目录
RTX_EX2.C的内容列在下面
/******************************************************************** **********/
/* RTX_EX2.C: A RTX51 Application */
/******************************************************************** **********/
#pragma CODE DEBUG OBJECTEXTEND
#include
int counter0; /* counter for task 0 */
int counter1; /* counter for task 1 */
int counter2; /* counter for task 2 */
int counter3; /* counter for task 2 */
/******************************************************************** **********/
/* Task 0 'job0': RTX51 tiny starts execution with task 0 */
/******************************************************************** **********/
job0 () _task_ 0 {
os_create_task (1); /* start task 1 */
os_create_task (2); /* start task 2 */
os_create_task (3); /* start task 3 */
while (1) { /* endless loop */
counter0++; /* increment counter 0 */
os_wait (K_TMO, 5, 0); /* wait for timeout: 5 ticks */
}
}
/******************************************************************** **********/
/* Task 1 'job1': RTX51 tiny starts this task with os_create_task (1) */
/******************************************************************** **********/
job1 () _task_ 1 {
while (1) { /* endless loop */
counter1++; /* increment counter 1 */
os_wait (K_TMO, 10, 0); /* wait for timeout: 10 ticks */
} }
/******************************************************************** **********/
/* Task 2 'job2': RTX51 tiny starts this task with os_create_task (2) */
/******************************************************************** **********/
job2 () _task_ 2 {
while (1) { /* endless loop */
counter2++; /* increment counter 2 */
if (counter2 == 0) { /* signal overflow of counter 2 */
os_send_signal (3); /* to task 3 */
}
}
}
/******************************************************************** **********/
/* Task 3 'job3': RTX51 tiny starts this task with os_create_task (3) */
/******************************************************************** **********/
job3 () _task_ 3 {
while (1) { /* endless loop */
os_wait (K_SIG, 0, 0); /* wait for signal */
counter3++; /* process overflow from counter 2 */
}
}
This example program shows how to use RTX51 Tiny in Code Banking Applications. The system contains 4 tasks and is very similar
to the RTX51 Tiny Example EX2.
However, the system avoids Round-Robin task switching by using the
os_switch_task function in Task2. Therefore the round-robin timeout
is disabled with the setting TIMESHARING 0 in the configuration file
Conf_tny.A51. Task 2 also uses the new os_set_run function to pass
the counter overrun to task 3.
/********************************************************************/ /* Task 0: Create other Tasks and Count up Every 5 ticks */ /********************************************************************/ #include
os_create_task (1); /* start task 1 */ os_create_task (2); /* start task 2 */ os_create_task (3); /* start task 3 */ while (1) { /* endless loop */ counter0++; /* increment counter 0 */ os_wait (K_TMO, 5, 0); /* wait for timeout: 5 ticks */
}
}
/********************************************************************/ /* Task 1: Count up Every 2 ticks
/********************************************************************/ #include
/********************************************************************/ void job1 (void) _task_ 1 {
while (1) { /* endless loop */ counter1++; /* increment counter 1 */ os_wait (K_TMO, 2, 0); /* wait for timeout: 2 ticks */
}
}
/********************************************************************/ /* Task 2: Count as fast as possible and Signal Overflow to Task 3 */ /********************************************************************/ #include
/********************************************************************/ void job2 (void) _task_ 2 {
while (1) { /* endless loop */ counter2++; /* increment counter 1 */ if ((counter2 & 0x0F) == 0) { /* every 16 counts: */ os_switch_task (); /* give other tasks CPU time */ }
if ((counter2 & 0xFFFF) == 0) { /* if counter overflows, */ os_set_ready (3); /* set run flag of task 3 */ }
}
}
/********************************************************************/ /* Task 3: Process Overflow from Task 2
/********************************************************************/ #include
/********************************************************************/ void job3 (void) _task_ 3 {
while (1) { /* endless loop */ os_wait1 (0); /* wait for RDY */ counter3++; /* process overflow from counter 2 */
}
}
下面通过一个例子来说明在RTX51 Tiny环境下是如何使多个任务共享串口的。#include
#include
#include
#define uchar unsigned char
#define uint unsigned int
extern void pend_semaphore(uchar sem_id);
extern void post_semaphore(uchar sem_id);
extern void init_semaphore(uchar sem_id, uchar max_count, uchar count);
void task0(void) _task_ 0{
SCON = 0x50;
TMOD |= 0x20;
TH1 = 221;
TR1 = 1;
TI = 1; /* 初始化串行口*/
init_semaphore(0, 1, 1); /* 初始化信号量,最大值为1 */
os_create_task(1);
os_create_task(2);
os_delete_task(0);
}
void task1(void) _task_ 1{
while (1) {
pend_semaphore(0);
puts(“Task1 is using UART!”);
post_semaphore(0);
}
}
void task2(void) _task_ 2{
while (1) {
pend_semaphore(0);
puts(“Task2 is using UART!”);
post_semaphore(0);
}
}
该程序中的task1和task2轮番使用串口输出数据。程序执行后在串口循环输出如下内容。
Task1 is using UART!
Task2 is using UART!