RTX51 Tiny用户指南(二)

实时程序

实时程序必须快速响应实时发生的事件。没有实时操作系统(RTOS),事件的创建就不会那么轻松。随着更多事件被添加,复杂性和程序复杂程度增加,就凸显了RTOS的好处。

 

单任务程序

嵌入式和标准C程序都是从main C函数开始执行。在嵌入式应用中,main通常按死循环执行,可以看做是一个连续执行的单任务。例如:

void main (void)
{
while (1)               /* repeat forever */
  {
  do_something ();      /* execute the do_something 'task' */
  }
}

在这个示例中,do_something函数可以看做是一个单任务。此处只有一个任务执行,不需要多任务性能或多任务此处系统。

 

多任务程序

很多复杂的C程序可以执行一个伪多任务,多个函数(或任务)在一个循环中执行。例如:

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函数执行很长时间,main循环需要很长时间才能返回到check_serial_io函数,串口数据将会丢失。当然,check_serial_io函数可以在main循环中多次调用来解决这个问题,但是最终这种方式是行不通的。

 

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程序没有main C函数。而是从task 0开始执行。典型应用中,task 0轻松的创建其它的任务。

 

 

操作原理

RTX51 Tiny使用和管理目标系统的资源。RTX51 Tiny的很多方面可以通过project basis在project中配置。

  1. 定时器滴答(Timer Tick)中断

主要论述驱动RTOS的周期性时钟中断。

  1. 任务

论述RTX51 Tiny任务的概念。

  1. 任务管理

论述RTX51 Tiny如何管理任务及各种任务的状态。

  1. 事件

论述如何用事件触发任务。

  1. 任务调度程序

提供调度管理任务的详细信息。

  1. Round-Robin(循环)任务切换

描述如何使用round-robin任务切换。

  1. Cooperative(联合)任务切换

描述如何使用cooperative任务切换。

  1. Idle(空闲)任务

描述RTX51 Tiny的idle任务

  1. 堆栈管理

提供RTX51 Tiny管理8051硬件堆栈的详细信息。

 

定时器滴答(Timer Tick)中断

RTX51 Tiny使用标准8051定时器0(模式1)创建周期中断。这个中断是RTX51 Tiny的定时器滴答。为RTX51 Tiny库程序指定的超时和区间值需要使用RTX51 Tiny时钟滴答测试。

默认的,RTX51定时器滴答中断每10000个机器周期出现一次。因此,对于运行在12MHz的标准的8051,定时器滴答周期中是0.01秒或100Hz的频率(12MHz/12/10000)。只可以在CONF_TNY.A51配置文件中修改。

注意:

  1. 可以在RTX51 Tiny滴答中断中添加自己的代码。更多信息请参考“CONF_TNY.A51配置文件”。
  2. RTX51 Tiny如何使用中断系统的详细信息请参考“综述”中的“中断”章节。

 

任务

RTX51 Tiny主要的任务切换开关。要创建一个RTX51 Tiny程序,必须创建一个有一个或多个任务函数的应用。以下的详细信息可以帮助你快速理解RTX51。

  1. 在C程序设计语言中定义任务,使用Keil C51编译器支持的新关键字。
  2. RTX51 Tiny将每一个任务维持一种状态(Running(运行),Ready(就绪),Waiting(等待),Deleted(删除)或Time-Out(超时))。
  3. 同一时间只有一个任务是Running状态。
  4. 多个任务可以是Ready,Waiting,Deleted或Time-Out状态。
  5. Idle任务永远是Ready状态,所有自定义的任务都是阻塞的。

 

任务管理

每一个RTX51 Tiny任务都有一个确定的状态表明任务处理方式。

 

状态

描述

RUNNING

当前执行的任务,状态为RUNNING状态。同时只能有一个任务是这个状态。os_running_task_id 返回当前执行任务的任务号

READY

就绪的任务是READY状态。一旦运行的任务处理完成,RTX51 Tiny选择并开始执行下一个ready的任务。任务可以使用os_set_readyisr_set_ready函数通过设置其ready标志直接置为ready状态(即使任务在等待超时或信号)。

WAITING

正在等待时间的任务,其状态是WAITING。一旦事件发生,任务将切换到READY状态。os_wait函数用于将任务置为WAITING状态。

DELETED

那些从未启动的任务或已被删除的任务为DELETED状态。os_delete_task程序将已启动的任务(使用os_create_task)置为DELETED状态。

TIME-OUT

被Round-Robin超时中断的任务为TIME-OUT状态。这个状态相当于Round-Robin 程序的READY 状态。

 

事件

在实时操作系统中,事件可以用来控制程序中任务的执行。一个任务可以等待一个事件或为其它任务设置事件。

Os_wait函数允许一个任务等待一个或多个事件。

  1. Timeout 对于一个可等待的任务,Timeout是一个普通的事件。超时仅仅是一些时钟周期。当任务等待超时的时候,其它任务可以执行。一旦指定数量的时钟周期过去,任务可以接着继续执行。
  2. Interval 是一个超时的变种。Interval像一个超时,除非指定数量的时钟周期与最后一次被任务调用的os_wait函数相关。Interval可以用于创建一个按周期执行的任务,同步调度(如:每秒一次),不管调用os_wait函数之间需要多长时间。如果指定数量的时钟周期已经过(从os_wait函数最后一次执行),任务立即重新执行——没有其它任务执行。
  3. Signal 是任务间通信的一种简单形式。一个任务可以等待另一个任务发送一个信号(os_send_signal和isr_send_signal)。
  4. 每个任务都有一个Ready标志,可以被其它任务(os_set_ready和isr_set_ready)设置。等待timeout、interval或signal的任务可以通过设置ready标志开始执行。

每一个任务有一个由RTX51 Tiny维持的关联的事件标志。以下事件选择器可以被os_wait函数使用来指定等待的是哪个:

事件选择器

描述

K_IVL

等待指定的interval

K_SIG

等待signal

K_TMO

等待指定的timeout

当os_wait函数返回,发送的时间由返回值说明:

返回值

说明

RDY_EVENT

任务的ready标志被设置

SIG_EVENT

接收到一个signal

TMO_EVENT

超时已过或interval已消逝

Os_wait函数可以等待以下事件的组合:

  1. K_SIG | K_TMO: os_wait延迟任务,直到signal发送给它或指定数量的时钟周期已经消逝。
  2. K_SIG | K_IVL: os_wait延时任务,直到signal发送给它或指定的interval已消逝。这种情况下,必须使用os_reset_interval函数消除定时器延时问题。

注意:

  1. K_IVL和K_TMO事件选择器不能联合使用。

 

任务调度

任务调度为任务分配处理器。RTX51 Tiny调度程序使用以下规则决定运行那个任务:

以下情况会使当前任务中断:

  1. 任务调用os_switch_task函数或其它ready状态的任务。
  2. 任务调用os_wait函数,并且指定的事件没有发生。
  3. 任务执行的事件超过定义的round-robin时间片。

以下情况会启动另一个任务:

  1. 没有任务在运行。
  2. READY状态或TIME-OUT状态的任务被启动。

 

Round-Robin任务切换

RTX51 Tiny可以配置使用Round-Robin多任务(或任务切换)。Round-Robin允许多个任务伪并行执行。任务不是正真的同时执行,而是按时间片(可用的CPU时间被分成时间片,RTX51 Tiny为每一个任务分配一个时间片)执行。如果时间片足够短(仅几个毫秒)任务看起来就像同时执行。

任务在其连续的时间片执行(除非任务的时间片已放弃)。此外,RTX51 Tiny切换到下一个ready状态的任务。连续的时间片可以由RTX51 Tiny “配置”进行定义。

以下示例演示一个简单的使用Round-Robin多任务的RTX51 Tiny程序。这个程序中的两个任务是计数循环。RTX51 Tiny开始执行任务0,是一个名为job0的函数。这个函数创建一个任务名为job1。Job0执行完它的时间片后,RTX51 Tiny切换至job1。Job1的时间片执行完后,RTX51 Tiny切换至job0。程序无限期循环。

#include <rtx51tny.h>

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 */
  }
}

注意:

  1. 可以使用os_wait函数或os_switch_task函数让RTX51 Tiny切换至其它任务而不需要等待任务的时间片执行完。os_wait函数暂停当前任务(更改为WAITING状态),直到指定的事件发生(任务更改为READY状态)。这期间,其它的任务可以执行。

 

Cooperative任务切换

如果禁用Round-Robin多任务,则必须设计并执行自己的任务,保证它们协作进行。通常,必须在每个任务中调用os_wait函数或os_switch_task函数。这些函数指示RTX51 Tiny切换至其它任务。

os_wait函数和os_switch_task函数的不同点在于,os_wait函数允许任务等待事件,os_switch_task函数直接切换到其它ready的任务。

 

Idle任务

当没有任务是ready状态时,RTX51 Tiny执行idle任务。Idle任务是一个简单的死循环。例如:

SJMP $

一些8051兼容设备提供一个idle模式通过停止程序执行来降低功耗,直到一个中断发生。在这种模式下,所有外设,包括中断系统依然继续操作。

RTX51 Tiny允许在Idle任务中发起idle模式(当没有任务处于ready状态)。当RTX51 Tiny时钟滴答中断(或其它中断)发生,微控制器恢复程序执行。被Idle任务执行的代码可以在CONF_TNY.A51配置文件中使能或配置。

 

堆栈管理

RTX51 Tiny为每个任务维持一个堆栈,仅使用8051的内部数据存储器(IDATA)。当任务运行时,它可以获得最大数量的堆栈空间。当任务切换出现时,前面的任务堆栈会被压缩和重新定位,当前任务的堆栈会被扩展和重新定位。

下图说明一个使用3个分离堆栈的示例程应用的内部存储器的分布。

?STACK符号表示堆栈的起始地址。在示例中,处于堆栈下面的对象包含全局变量、寄存器和可位寻址存储器。剩余的存储器用于任务堆栈。存储器的顶部可以在“配置”中指定。

 

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值