原创 RTX51 tiny(1、2、3、4)

2008-3-29 23:48 3298 0 分类: MCU/ 嵌入式

RTX51 tiny(1、2、3、4)





特性:


最大任务数: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中调用。


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(3)



编译连接


有两种方法编译和连接RTX51 Tiny应用程序



RTX51 Tiny(4)



os_set_ready


Summary:


#i nclude <rtx51tny.h>



char os_set_ready (


  unsigned char task_id);     /* Task to make ready */


Description:


使编号为task_id的任务就入就绪状态,可以任务中调用该函数


Return Value: None.




RTX51 Tiny中信号量操作的实现


引 言


  RTX51 Tiny是一种应用于MCS51系列单片机的小型多任务实时操作系统。它完全集成在Keil C51编译器中,具有运行速度快、对硬件要求不高、使用方便灵活等优点,因此越来越广泛地应用到单片机的软件开发中。但是RTX51 Tiny自身并不支持信号量的操作,这就给设计开发中共享资源的使用带来了诸多不便。本文介绍一种在RTX51 Tiny环境中添加信号量支持的方案。


1 信号量


  信号量实际上是一种约定机制,在多任务操作系统内核中普遍使用。信号量可分为二值信号量和计数式信号量。每一个信号量都有一个计数值,它表示某种资源的可用数目。二值信号量的值只能是0和1;计数式信号量的取值范围则由所使用的嵌入式操作系统内核决定。内核根据信号量的值,跟踪那些等待信号量的任务。


  对信号量的操作一般有初始化、等待和释放三种,下面简要介绍一下这三种操作过程。


① 初始化信号量:信号量初始化时要给信号量赋初值,并清空等待信号量的任务表。


② 等待信号量:需要获取信号量的任务执行等待操作。如果该信号量值大于0,则信号量值减1,任务得以继续运行;如果信号量值为0,等待信号量的任务就被列入等待该信号量的任务表。


③ 释放信号量:已经获取信号量的任务在使用完某种资源后释放其信号量。如果没有任务等待该信号量,信号量值仅仅是简单的加1;如果有任务正在等待该信号量,那么就会有一个任务进入就绪态,信号量的值也就不加1。至于哪个任务进入就绪态,要看内核是如何调度的。


2 在RTX51 Tiny中添加信号量支持


  RTX51 Tiny采用时间片轮转的办法来调度任务,不支持任务优先级,也不支持信号量。为了在RTX51 Tiny环境中使用信号量,必须另外添加信号量的定义及其操作过程,可以在应用程序中加入以下代码。


#i nclude <rtx51tny.h>


#define uchar unsigned char


#define uint unsigned int


#define MAX_SEMAPHORES 3 /* 使用信号量的最大数目 */


/* 定义信号量 */


struct sem_set{


uchar max_count; /* 该信号量的最大计数值 */


uchar count; /* 该信号量的当前计数值 */


uint pending_tasks; /* 等待该信号量任务表 */


} sem_tab[MAX_SEMAPHORES];


/* 初始化信号量 */


#pragma disable


void init_semaphore(uchar sem_id, uchar max_count, uchar count){


sem_tab[sem_id].max_count = max_count;


sem_tab[sem_id].count = count;


sem_tab[sem_id].pending_tasks = 0;


}


/* 等待信号量 */


#pragma disable


char pend_sem(uchar sem_id){


if (sem_tab[sem_id].count > 0) {


sem_tab[sem_id].count? /* 获取信号量 */


return (-1);


}


sem_tab[sem_id].pending_tasks


|=(1 << os_running_task_id()); /* 标记为等待状态 */


return (0);


}


void pend_semaphore(sem_id){


if (pend_sem(sem_id) == 0) {


while (os_wait(K_TMO, 255, 0) != RDY_EVENT);


/*等待,直到该任务就绪*/


}


}


/* 释放信号量 */


#pragma disable


char post_sem(uchar sem_id){


uchar i;


uint temp = 1;


if ((sem_tab[sem_id].count > 0)


||(sem_tab[sem_id].pending_tasks == 0)) {


sem_tab[sem_id].count++; /* 释放信号量 */


return (-1);


}


for (i=0; i<16; i++) {


if ((sem_tab[sem_id].pending_tasks & (temp)) != 0){


/* 查找任务表 */


sem_tab[sem_id].pending_tasks &= ~(1 << i);


return (i); /* 返回等待信号量的任务号 */


}


temp <<= 1;


}


}


void post_semaphore(uchar sem_id){


char task_id;


task_id = post_sem(sem_id);


if (task_id != -1) {


os_set_ready(task_id); /* 任务task_id进入就绪状态 */


os_switch_task();


}


}


  其中函数init_semaphore用于初始化信号量,函数pend_semaphore 和post_semaphore用于等待和释放信号量。


  MAX_SEMAPHORES为应用程序中需要用到信号量的最大数目,根据设计需要做相应的修改。结构体sem_set记录信号量的相关信息,包括该信号量的最大值、当前值以及等待该信号量的任务表。其中,sem_tab[sem_id].pending_tasks中的bit0~bit15分别与任务0~任务15一一对应,如果某一位置位,则表示与之相应的任务正在等待该信号量。函数post_sem总是让等待信号量任务表中任务号最小的那个任务最先得到信号量。


  编译器伪指令#pragma disable保证程序在对信号量进行操作期间不被中断,避免发生错误。


3 应用举例


  下面通过一个例子来说明在RTX51 Tiny环境下是如何使多个任务共享串口的。


#i nclude <rtx51tny.h>


#i nclude <stdio.h>


#i nclude <reg51.h>


#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!


结 语


  与其它实时多任务内核相比,RTX51 Tiny显得非常小巧,它最大仅占用900字节的程序存储空间,可以在没有任何外部存储器的8051系统上运行。对RTX51 Tiny添加信号量支持后,能够简化程序设计,提高开发效率,降低开发成本。




RTX51TINY中容易混淆的问题



介绍


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


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


问题


关于时间片的问题


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


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


关于os_wait延时的问题


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


关于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函数一次只使用一个基本时间片,而不是任务的时间片。


关于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 12M


RTX51 Tiny:使用Keil自带的RTX51 Tiny操作系统,v2.02。


RTX51 Tiny的参数:INT_CLOCK=10000,TIMESHARING=5


其他参数使用默认设置。(需要自己建立一个工程文件,再将下面的文件添加到工程文件中)


#i nclude <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次(因为job1中的os_wait(K_IVL,1,0)产生了5次信号,并累计下来,而job0中的os_wait(K_TMO,1,0)虽然也产生了5次信号,但是没有累计,只有最后一次是真正有效的),x2是x1的5倍。


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





实时操作系统RTX51 Tiny


实时操作系统RTX51 Tiny是RTX51的简板,它不支持优先级和CAN总线的相关定义函数。RTX51使用一个8051硬件计时器中断作为定时程序,产生的周期性中断用于驱动 RTX51时钟


    该系统是通过给各个任务一定的时间片进行轮回完成调度的。系统给要执行的N个任务按Os_create_task(task_id)创建的先后顺序排成一个队列,系统首先执行任务0,然后按队列中的任务一个一个的执行。


1、我们可以通过修改其配置文档CONF_TNY.A51(keil\c51\ RTX_TINY)来修改系统的相关配置;经常修改的部分如下:


;             ; define the register bank used for the timer interrupt.


INT_REGBANK     EQU       1     ; default is Registerbank 1


;


;             ; define Hardware-Timer Overflow in 8051 machine cycles.


INT_CLOCK  EQU       10000     ; default is 10000 cycles


;


;             ; define Round-Robin Timeout in Hardware-Timer Ticks.


TIMESHARING     EQU       5     ; default is 5 ticks.


;             ;


;             ; note: Round-Robin can be disabled by using value 0.


;


;  Note:   Round-Robin Task Switching can be disabled by using '0' as


;          value for the TIMESHARING equate.


;------------------------------------------------------------------------------


;


;  'RTX-51 tiny' Stack Space


;  =========================


;


;  The following EQU statements defines the size of the internal RAM used


;  for stack area and the minimum free space on the stack.  A macro defines


;  the code executed when the stack space is exhausted.


;


;             ; define the highest RAM address used for CPU stack


RAMTOP              EQU       0FFH      ; default is address (256-1)


;


FREE_STACK       EQU       20    ; default is 20 bytes free space on stack


;


STACK_ERROR    MACRO


              CLR EA   ; disable interrupts


              SJMP      $     ; endless loop if stack space is exhausted


              ENDM


;


;------------------------------------------------------------------------------


2、RTX51事件:


(1)       使用RTX51的时间溢出事件:


使用 os_wait函数等待的最简单的事件是事件溢出 RTX51时钟报时信号周期,这类事件可被


用于任务中需要延迟一段时间的地方,这可用于查询一个开关状态的代码中。 在这样的条件下,


只须每隔 50ms左右查询一次开关。使用 os_wait函数等待RTX51时钟报时信号周期的任务停止,下一个处于READY状态的任务开始执行。


(2)       使用RTX51的信号事件


你可以使用 os_wait功能暂停一个任务并等待从另一个任务发出的信号或旗标。这可以用于


协调两个或更多的任务。如果一任务在等待一个信号并且信号标志是 0,在这个信号被发送之前这个任务将一直处于挂起状态,下一个处于READY状态的任务需要等到上一个任务时间片完成以后才开始执行;如果信号标志已经是 1,当任务查询信号时,信号标志会被清除并且继续执行任务。Os_send_signal(task_id)发送信号后,需等到下次执行因等待信号而终止的任务查询信号时,该任务才开始继续向下执行,在此期间如果该信号被清除,因等待而挂起的任务则无法执行os_wait后面的指令。


3、下面,我们看RTX51 Tiny提供的系统函数;



系统函数名称



功能说明



运行时间(时钟周期)



Os_create_task(task_id)



将一任务加入到执行队列



302



Os_delete_task(task_id);



将一任务从执行队列删除



172



Os_send_signal(task_id);



给一任务发送信号



408/316/71



Os_clear_signal(task_id);



删除一个发送的信号



57



Isr_send_signal(task_id)



在中断中给一任务发送信号



46



Os_wait()



等待



68/160



Os_wait1()



等待





Os_wait2()



等待



 


4、任务之间的通信:


(1)char os_wait (


unsigned char event_sel, /* events to wait for */


unsigned char tick s, /* timer ticks to wait等待信号的数目*/


unsigned int dummy); /* unused argument */


参数event_sel可以是下面一个或几个间的逻辑或:


K_IVL:等待一个时钟报时间隔


K_SIG:等待一个信号


K_TMO:等待一个超时(time-out)


(2)char os_wait1 (


unsigned char event_sel); /* events to wait for */


它是os_wait的一个子集,只等待一个信号。


(3)char os_wait2 (


unsigned char event_sel, /* events to wait for */


unsigned char tick s); /* timer ticks to wait */


参数event_sel同os_wait参数 ticks规定等待一个间隔事件k_ivl 或一个超时事件k_tmo 的报时信号数目

文章评论0条评论)

登录后参与讨论
我要评论
0
0
关闭 站长推荐上一条 /2 下一条