RTX51学习心得


       RTX51是一个用于8051系列处理器多任务实时操作系统,由Keil自带。有二个版本, Full版和Tiny版。 RTX51 Tiny版是RTX51的一个子集,占用资源极低,采用时间片轮转任务切换和使用信号进行任务切换,和full版相比,不支持抢先式的任务切换,不包括消息历程没有存储器池分配程序。下面结合一些实例来分析RTX51Tiny版。编译器Keil3,所有程序在51单片机上测试通过,并采用proteus仿真。那么51单片机上跑操作系统和不跑操作系统有什么区别呢?请看下问

   .多任务

1单任务程序 
嵌入式系统或者是标准 C程序设计,程序都是从 main函数开始。在嵌入式系统程序中 main函数
通常是一个死循环,也可以被看作是一个连续运行的单一任务,下面就是一个例子。 
 
void main (void) 

 Initial();//初始化

while(1)              /* repeat forever */  
  { 
  do_something ();      /* execute thedo_something 'task' */ 
  } 

 
在这个例子中,函数 do_something可以被看作是一个单任务,因为这里只有一个需要执行的任务,
因此不需要多任务能力或者一个多任务操作系统。

2 多任务程序

如果你采用 RTX51 Tiny,那么在你的应用中就可以为每一个任务创建一个独立的任务函数,以下
就是一个例子。 

 #include<rtx51tny.h> 
 
int counter0;  
int counter1;  
 
void job0 (void) _task_ 0 { 
  os_create_task (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 */ 
  } 
}

时间片轮转

关于时间片的问题
RTX51 Tiny使用的是无优先级时间片轮询法,每个任务使用相同大小的时间片,但是时间片是怎样确定的呢?
RTX51 Tiny的配置参数(Conf_tny.a51文件中)中有INT_CLOCKTIMESHARING两个参数。
这两个参数决定了每个任务使用时间片的大小:INT_CLOCK是时钟中断使用的周期数,也就是基本时间片;
TIMESHARING
是每个任务一次使用的时间片数目。两者决定了一个任务一次使用的最大时间片。
如假设一个系统中INT_CLOCK设置为10000,10ms,那么TIMESHARING=1,一个任务使用的最大时间片是10ms;
TIMESHARING=2
,任务使用最大的时间片是20ms;TIMESHARING=5,任务使用最大的时间片是50ms;
TIMESHARING设置为0,系统就不会进行自动任务切换了,这时需要用os_switch_task函数进行任务切换。
这部分功能是RTX51 Tiny 2.0中新增加的。
关于os_wait延时的问题
os_wait RTX51 Tiny中的基本函数之一。它的功能是将当前任务挂起来,等待一个启动信号(K_SIG)
超时信号(K_TMO)或周期信号(K_IVL)或者是它们之间的组合。虽然os_wait很简单,但是因为涉及到多任务的
操作方式,很容易产生误解。
2.1 
关于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函数一次只使用一个基本时间片,而不是任务的时间片。

2.2 
关于K_TMOK_IVL参数的区别

  在os_wait中有三个参数:K_TMOK_IVLK_SIG。其中,K_TMOK_IVL是最容易让人混淆的,特别是搞不清楚K_IVL到底是什么含义,好像使用起来与K_TMO效果差不多。一般的书上和Keil自带的RTX51 Tiny的帮助中,也没有清楚解释K_IVL的含义。

K_IVLK_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
  仿真CPUAT89C52 12MHz
RTX51 Tiny:使用Keil自带的RTX51 Tiny操作系统,v2.02
RTX51 Tiny的参数:INT_CLOCK=10000,TIMESHARING5

  其它参数使用默认设置。(需要自己建立一个工程文件,再将下面的文件添加到工程文件中。)    */
#include <rtx51tny.h>
#include <reg52.h>
#include <stdio.h>
#include "com.h"
uint x0,x1,x2;
void job0(void) _task_ 0 {
  x0=x1=x2=0;
  com_init(9600);
  sendstr("The different between K_VIL with K_TMO\x0a\x0d");//
结尾的两个转义字符起换行作用
  os_create_task(1);
  os_create_task(2);
  os_create_task(3);
  os_create_task(4);
  while(1){
   os_wait(K_TMO,1,0);
   x0++;
  }
}
 void job1(void) _task_ 1{
  while(1) {
  os_wait2(K_IVL,1);//os_wait(K_IVL,1,0);
使用RTX51tiny中使用os_wait2效率更高
  x1++;
  }
}
 void job2(void) _task_ 2{
   while(1) { 
   os_wait2(K_IVL,1); //os_wait(K_IVL,1,0);
   x2++;
    }
}
void job3(void) _task_ 3{
  while(1) { //
取消注释后,系统负担变轻,可以及时响应
 //
注释下面一句使系统的负担变得很重,不能及时响应job0job1的延时信号
  os_wait2(K_TMO,1);
 }

void job4(void) _task_ 4{
 uchar StrTmp[18]="";
 while(1)
 {
  os_wait2(K_IVL,50);
  sprintf(StrTmp,"%d %d %d%c%c",x0,x1,x2,10,13);
  sendstr(StrTmp);
 
 }
}
/*
 编译之后进入dubug状态,点击watch and callstack window,添加要监视的三个变量x0 x1 x2 ,然后点run
 
或者先点视图,选择serial window#1,在点run
  
job3os_wait(K_TMO,1,0)的注释不取消时,job0每执行一次,job1就连续执行5,x1x05倍。
  
因为job1中的os_wait(K_IVL,1,0)产生了5次信号,并累计下来;job0中的os_wait(K_TMO,1,0)虽然
  
也产生了5次信号,但是没有累计,只有最后一次是真正有效的。
  当job3os_wait(K_TMO,1,0)的注释取消时,job0job1的执行次数是一样的,x0=x1
*/

 

    三、进程通信

  RTX51的进程间通信机制很简单,仅能发送一个信号,os_send_signal为任务向任务发送信号, isr_send _signal为中断服务程序向任务发送信号。

采用os_wait接受。

下面为一个简单实例:

     /*
1
rtx51默认的时钟滴答为10000个时钟周期,若晶振12M,则为10ms。最大时间片5个滴答,可修改conf_tny.a51中的宏
INT_CLOCK    EQU    10000    ;default is 10000 cycles   1
个时钟滴答的时钟周期
TIMESHARING    EQU     5    ;default is 5 Hardware-Timer ticks.    
时间片占的滴答数
*/
/*********************************
*** RTX
51的移植
*** 
移植到AT89S52
*** 
此程序是分秒程序
*** 
有六个数码管来显示毫秒,秒,分钟                                                         
*********************************/
#include <reg51.h>
#include <rtx51tny.h>
#define uchar unsigned char
#define uint unsigned int 

uchar min;
uchar sec;
uchar micsec;
uchar minh;
uchar minl;
uchar sech;
uchar secl;
uchar micsech;
uchar micsecl;

 #define P_out P0    //
输出引脚
 #define P_sel P2     //
位选信号引脚
void led_7seg_disp(uchar num0,uchar num1,uchar num2,uchar num3,uchar num4,ucharnum5)
 {
        const unsigned chartable[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x00};
        P_sel = 0x20;
        P_out = table[num0];
        os_wait(K_TMO,1,0); 
        P_sel = 0x10;
        P_out = table[num1];
        os_wait(K_TMO,1,0); 
        P_sel = 0x08;
        P_out = table[num2];
        os_wait(K_TMO,1,0); 
        P_sel = 0x04;
        P_out = table[num3];
        os_wait(K_TMO,1,0); 
        P_sel = 0x02;
        P_out = table[num4];
        os_wait(K_TMO,1,0); 
        P_sel = 0x01;
        P_out = table[num5];
        os_wait(K_TMO,1,0); 
 }
void job0 (void) _task_ 0

{
   
   micsec = 0x00;
   sec = 0x00;
   min =0x00;
   micsech = micsec /10;
   micsecl = micsec %10;
   sech = sec /10;
   secl = sec %10;
   minh = min /10;
   minl = min %10;
   os_create_task(1);//
创建进程1
   os_create_task(2);//
创建进程1
   os_create_task(3);//
创建进程1
   os_delete_task(0);//
删除任务0
   
}

void job1 (void) _task_ 1

{
   while(1)
     {         
          micsec ++;
        micsech = micsec /10;
           micsecl = micsec %10;
        if(micsec==50)
        {    
            micsec =0;    
           os_send_signal(2);    //
发送Signal信号,激活进程2
        }

        os_wait(K_TMO,2,0);      //
时钟滴答为1,大约等待10m
         // os_wait(K_IVL,1,0); //
K_TMO功能不同,会累加,
     }
}

void job2 (void) _task_ 2

{
    while(1)
    {
        os_wait(K_SIG,0,0);
        sec ++;
        if(sec == 60)
        {
            sec = 0;
            min ++;
        }
        sech = sec /10;
           secl = sec %10;   
        minh = min /10;
        minl = min %10;    
    }
}
void job3 (void) _task_ 3

{
    while(1)
    {
    led_7seg_disp(micsecl,micsech,secl,
               sech,minl,minh);

    }
}          

    .扩展信号量

RTX51Tiny版不提供信号量机制,同时不支持任务优先级,如果多个任务同时使用使用同一个资源的话,会出现竞争情况,例如:

#include<rtx51tny.h>
#include <stdio.h>
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
#define fosc 11059200

void com_init(uint baud)
{
  SCON = 0x50;    //
串口工作方式为1,串行允许接受              
  TMOD&=0x0f;
  TMOD|=0x20;     //
定时器1工作在方式2     
  if(baud<=9600) TH1=256-fosc/32/12/baud;
  else
   {PCON = 0x80;   //SMOD = 1;
波特率加倍
   TH1=256-fosc/32/12/(baud/2);
    }
    TR1  =1;            //
允许定时器1工作
 // ES   = 1; //
开串口中断,必须注释掉该句puts才能输出
  EA   =1;             //
开总中断
  TI=1;//
要加上此句,才能用puts输出
}
//
注意,在使用系统的库函数putsprintf前首先要关串口中断,其次TI=1
void task0(void) _task_ 0{
com_init(9600); /*
初始化串行口 */
init_semaphore(0, 1, 1); /*
初始化信号量,最大值为1 */
puts("Using semaphore in RTX51tiny"); //puts
语句自动在结尾加上换行
os_create_task(1);
os_create_task(2);
os_delete_task(0);
}
//
以下为两个任务,通过信号量防止同时使用串口发生冲突
void task1(void) _task_ 1{
while (1) {
puts("task1 is using uart!");
}
}
void task2(void) _task_ 2{
while(1) {

printf("TASK2 IS USING UART!\n");//puts("TASK2 IS USINGUART!");

}
}

串口输出结果:
task1 is using uart!
task1 is using uart!
TASK2 IS USING UART!
TASK2 IS USING UART!
TASK2 IStask1 is using uart!
task1 is using uart!
task1 i USING UART!
TASK2 IS USING UART!
TASK2 IS USING Us using uart!
task1 is using uart!
task1 is using ART!
TASK2 IS USING UART!
TASK2 IS USING UART!
TAuart!
task1 is using uart!
task1 is using uart!
tSK2 IS USING UART!
TASK2 IS USING UART!
TASK2 IS Uask1 is using uart!
task1 is using uart!
task1 is SING UART!
TASK2 IS USING UART!
TASK2 IS USING UARusing uart!
task1 is using uart!
task1 is using uaT!
TASK2 IS USING UART!
由于两个任务同时使用串口,发生竞争,导致输出有问题,下面采用信号量改进如下:

semophere.c

/**************RTX51 Tiny中信号量操作的实现 *******************************
 
信号量实际上是一种约定机制,在多任务操作系统内核中普遍使用。信号量可分为二值信号量和计数式信号量。
每一个信号量都有一个计数值,它表示某种资源的可用数目。二值信号量的值只能是01;计数式信号量的取
值范围则由所使用的嵌入式操作系统内核决定。内核根据信号量的值,跟踪那些等待信号量的任务。
    
对信号量的操作一般有初始化、等待和释放三种,下面简要介绍一下这三种操作过程。
初始化信号量:信号量初始化时要给信号量赋初值,并清空等待信号量的任务表。
等待信号量:需要获取信号量的任务执行等待操作。如果该信号量值大于0,则信号量值减1
任务得以继续运行;如果信号量值为0,等待信号量的任务就被列入等待该信号量的任务表。
释放信号量:已经获取信号量的任务在使用完某种资源后释放其信号量。如果没有任务等待
该信号量,信号量值仅仅是简单的加1;如果有任务正在等待该信号量,那么就会有一个任务进
入就绪态,信号量的值也就不加1。至于哪个任务进入就绪态,要看内核是如何调度的。
***************************************************************************************/
#include <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();
}
}
改进后的主程序main.c


#include <rtx51tny.h>
#include <stdio.h>
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
#define fosc 11059200
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 com_init(uint baud)
{
  SCON = 0x50;    //
串口工作方式为1,串行允许接受              
  TMOD&=0x0f;
  TMOD|=0x20;     //
定时器1工作在方式2     
  if(baud<=9600) TH1=256-fosc/32/12/baud;
  else
   {PCON = 0x80;   //SMOD = 1;
波特率加倍
   TH1=256-fosc/32/12/(baud/2);
    }
    TR1  =1;            //
允许定时器1工作
 // ES   = 1; //
开串口中断,必须注释掉该句puts才能输出
  EA   =1;             //
开总中断
  TI=1;//
要加上此句,才能用puts输出
}
//
注意,在使用系统的库函数putsprintf前首先要关串口中断,其次TI=1
void task0(void) _task_ 0{
com_init(9600); /*
初始化串行口 */
init_semaphore(0, 1, 1); /*
初始化信号量,最大值为1 */
puts("Using semaphore in RTX51tiny"); //puts
语句自动在结尾加上换行
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); //
等待信号量
printf("TASK2 IS USING UART!\n");//puts("TASK2 IS USINGUART!");
post_semaphore(0); //
释放信号量
}
}


进入调试状态,打开serail window #1,run按钮,也可采用proteus仿真
该程序中的task1task2轮流使用串口输出数据。程序执行后循环输出:
    Task1 is using UART!
    Task2 is using UART!

 

 


 

 

 

 

 

 

 

 

 

 

 

  • 9
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: rtx51tiny源码是一种为51系列单片机开发的迷你实时操作系统(Real-Time Operating System,简称RTOS)。该源码适用于51系列单片机,具有小巧、高效、简洁的特点。 rtx51tiny源码提供了一组简单、易用的任务管理器、时间管理器和信号量等功能。通过任务管理器,我们可以方便地创建和管理多个任务,每个任务都可以运行在不同的优先级下。时间管理器可以提供精确的定时功能,在实时控制系统中非常有用。信号量可以用来实现任务之间的同步和互斥操作。 在rtx51tiny源码中,主要包含以下几个关键部分: 1. 内核:rtx51tiny源码中的内核负责任务的创建和调度,以及中断的处理。它实现了任务的状态管理、任务切换和中断处理等核心功能。 2. 任务管理:rtx51tiny源码提供了一个简单的任务管理器,可以方便地创建、删除和切换任务。每个任务接收一个任务函数,并可以分配任务的优先级。 3. 时间管理:rtx51tiny源码中的时间管理器提供了准确的定时功能,可以设置任务的延时和周期性执行。这样可以使任务按照预定的时间间隔运行,提高系统的实时性。 4. 信号量:rtx51tiny源码提供了信号量的实现,可以用来实现任务之间的同步和互斥操作。通过信号量,任务可以等待某个事件的发生,并在事件发生后继续执行。 总之,rtx51tiny源码是一种小巧高效的迷你实时操作系统,适用于51系列单片机。它提供了任务管理、时间管理和信号量等功能,可以方便地实现任务的创建、调度和同步。 ### 回答2: rtx51tiny是一种嵌入式操作系统,专门设计用于Intel 8051系列单片机。源码是指rtx51tiny操作系统的程序代码。 rtx51tiny源码提供了一个完整的嵌入式操作系统环境,其中包含一系列的函数和指令,用于实现任务调度、内存管理、输入输出等功能。开发人员可以根据自己的需求,使用源码进行定制和扩展。 使用rtx51tiny源码,可以轻松创建多个任务,并通过任务调度器进行任务切换。这使得开发人员能够更好地管理系统资源,提高系统的响应能力和效率。 此外,rtx51tiny源码还提供了丰富的输入输出功能,包括串口通信、中断处理等。开发人员可以根据自己的需求,使用源码进行自定义的输入输出操作。 通过使用rtx51tiny源码,开发人员可以更加方便地开发嵌入式应用程序。源码的开放性使得开发人员可以了解和控制系统的内部运行机制,以及对系统进行优化。这有助于提高系统的稳定性和性能。 总结而言,rtx51tiny源码是一种重要的资源,可用于构建嵌入式操作系统。它提供了任务调度、内存管理和输入输出等功能。通过使用源码,开发人员可以根据自己的需求进行定制和扩展,提高系统性能和稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值