一、简介
RTX51是Keil公司提供的一个用于8051系列处理器多任务实时操作系统,而RTX51 Tiny 是一个 RTX51的子集,支持许多RTX51中的基本特征,可以很容易地在没有任何外部存储器的单片8051系统上运转。相对于RTX51,RTX51 Tiny仅支持时间片轮转任务切换和使用信号进行任务切换,不支持抢先式的任务切换,不包括消息历程,没有存储器池分配程序。
RTX51 Full对硬件要求比较高,不但需要至少650byte的片外RAM,编译出来的代码还会增大6~8KB,而RTX51 Tiny仅需要7byte的片内RAM即可运行,编译后的代码也只是增加大约900个Byte,现有的绝大部分51芯片都很容易满足这个要求。RTX51 full跟tiny之间的主要区别是tiny不支持任务抢占式运行。
二、帮助说明
RTX51在Keil 的安装路径下(C:\Keil_v5\C51\RtxTiny2),有例程以及源码,在keil中也有相关的帮助文档。
三、创建第一个RTX51工程
RTX51的使用非常简单,在keil已经有了集成,需要在工程选项中设置 "operating system "属性,并添加相关头文件和汇编文件即可。
设置工程属性
添加相关文件(可以从keil中自带的例程复制)
最后添加头文件
#include <rtx51tny.h>
四、第一个RTX51程序
在RTX51中,并没有main函数,取而代之的是 "任务0(_task_ 0)",在任务0开始执行,并且创建其他任务。
在这个程序中创建了0~3是个任务,任务0是初始任务,在创建其他任务后,退出任务链表。
1 #include <rtx51tny.h> 2 #include "stc15.h" 3 #include "iic.h" 4 #define u8 unsigned char 5 #define u16 unsigned int 6 u8 code smg_du[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x40,0x00}; 7 u8 Smg_Buf[8] = {11,0,5,0,0,0,5,0}; 8 u8 Trg; 9 u8 Cont; 10 11 bit bitflag; 12 u16 cost; 13 u16 water; 14 15 void LED_Switch (void); 16 u8 read_adc (u8 add); 17 void Key_Read( void ); 18 19 void job0 () _task_ 0//创建初始任务 20 { 21 P0=0X00;P2=0XA0;P2=0X00; 22 P0=0Xff;P2=0X80;P2=0X00; 23 os_create_task(1); 24 os_create_task(2); 25 os_create_task(3); 26 os_delete_task(0);//创建任务后删除自身 27 } 28 29 void Smg_Show () _task_ 1 //任务1 30 { 31 static unsigned char i ; 32 while(1) 33 { 34 P0=1<<i;P2=0xc0;P2=0x00; 35 if(i==1||i==5) 36 P0=~(smg_du[Smg_Buf[i]]|0x80);//点号 37 else 38 P0=~smg_du[Smg_Buf[i]]; 39 P2=0xe0;P2=0x00; 40 if(++i>7) i=0; 41 os_wait(K_TMO, 3, 0); 42 } 43 } 44 45 void job2 () _task_ 2 //任务2 46 { 47 while(1) 48 { 49 Key_Read(); 50 if(Trg&0x01)//s7 51 { 52 if(!bitflag) water = 0; 53 bitflag = 1; 54 } 55 else if(Trg&0x02)//s6 56 { 57 bitflag = 0; 58 } 59 os_wait(K_TMO, 5, 0); 60 } 61 } 62 63 void job3 () _task_ 3 //任务3 64 { 65 while(1) 66 { 67 if(bitflag) 68 { 69 P0=0x10;P2=0XA0;P2=0X00; 70 Smg_Buf[4]=water/1000; 71 Smg_Buf[5]=water/100%10; 72 Smg_Buf[6]=water/10%10; 73 Smg_Buf[7]=water%10; 74 if(++water >= 1000) 75 { 76 water=0; 77 bitflag = 0; 78 } 79 } 80 else 81 { 82 P0=0x00;P2=0XA0;P2=0X00; 83 cost=water/2; 84 Smg_Buf[4]=cost/1000; 85 Smg_Buf[5]=cost/100%10; 86 Smg_Buf[6]=cost/10%10; 87 Smg_Buf[7]=cost%10; 88 } 89 os_wait(K_TMO, 100 , 0); 90 LED_Switch(); 91 } 92 } 93 94 void Key_Read( void )//按键扫描 95 { 96 u8 ReadData = P3^0xff; 97 Trg = ReadData & (ReadData ^ Cont); 98 Cont = ReadData; 99 } 100 101 u8 read_adc (u8 add)//读取ad 102 { 103 u8 dat; 104 IIC_Start(); 105 IIC_SendByte(0x90); 106 IIC_WaitAck(); 107 IIC_SendByte(add); 108 IIC_WaitAck(); 109 IIC_Start(); 110 IIC_SendByte(0x91); 111 IIC_WaitAck(); 112 dat=IIC_RecByte(); 113 IIC_WaitAck(); 114 IIC_Stop(); 115 return dat; 116 } 117 118 void LED_Switch (void) 119 { 120 float adc_val = 0; 121 adc_val=read_adc(0x03); 122 adc_val=adc_val*5/256; 123 if(adc_val>1.25) 124 { 125 P0=0Xff;P2=0X80;P2=0X00; 126 } 127 else 128 { 129 P0=0Xfe;P2=0X80;P2=0X00; 130 } 131 }
五、RTX51函数说明
RTX51的函数说明
os开头的只能在主函数里使用,中断只能用isr开头的
os_create_task(task_id);
1,创建task_id号任务,未被创建的任务无法执行
2,被os_delete_task(task_id);删除的任务如果要再次运行,需要被os_create_task(task_id);重新创建
3,被创建的任务会从头运行,重复创建同一个任务无影响
os_delete_task(task_id);
1,删除task_id号任务,被删除的任务无法执行
2,对静态变量无影响,静态变量的数据仍在。不是静态局部变量会消失
os_wait(typ,ticks,dummy)
1,dummy无用,填0即可。os_wait1(typ)只能填K_SIG,os_wait2(typ,ticks)没有dummy
2,typ可以写成 K_SIG|K_TMO ,K_SIG|K_IVL,如果延时未到,被K_SIG提前结束需使用os_reset_interval(task_id);置位延时寄存器,否则下次延时将加上剩余部分
3,ticks是延时的滴答次数
4,单个任务里,多个os_wait(K_SIG,0,0);连用,需要被多次触发
4,K_TMO和K_IVL的区别在于:K_TMO等待指定滴答数后,下次执行到这个地方就可以运行,延时时间是滴答数完成+滴答数完成后执行其他语句到这个K_TMO处。
而K_IVL是每间隔一定的滴答数后,次数+1,这个次数可以被累加,如果其他语句太长或延时滴答数太少导致次数累加过多,比如加到了3,那么当再次轮询到这个地方时不是像K_TMO执行一遍就跳出任务,而是每判断过一次该语句,次数-1,接着执行下面的语句,直至判断次数为0,跳过任务
os_send_signal(task_id);isr_send_signal(task_id);
1,向task_id号任务发送信号。只能作用于K_SIG
os_clear_signal(task_id);
1,清除task_id号任务的信号。只能作用于K_SIG
2,如果os_send_signal(task_id);向task_id号任务发送信号后,又被os_clear_signal(task_id);把task_id号任务得到的信号清除了,那么相当于task_id号任务没有收到信号
os_set_ready(task_id);isr_set_ready(task_id);
1,使task_id号任务里的os_wait(),os_wait1(),os_wait2()认为延时完成或者信号到达
os_running_task_id();
1,返回当前正在执行的x号任务
os_switch_task();
1,跳出当前任务
六、注意事项
RTX51里中断程序的编写。RTX51系统本身占用了定时器0中断作为时间片计时调度,也就是说EA必须置1,应用代码里不能有对定时器0的中断或EA的操作,也不要另行编写定时器0的中断代码(如果必须要利用定时器0中断来执行一些额外代码,请修改conf_tny.a51里HW_TIMER_CODE段,把额外代码插入到这个段里),否则会影响整个rtx核心的正常运行。
Rtx51里编写中断服务程序可以有两种方法,一种是按常规无操作系统时的C写法,譬如:
void int1(void) interrupt 1
使用这种方法时要注意中断服务代码要尽量优化,整个中断服务的执行时间不能超过时间片的时间。因为缺省状态下,所有的中断是同一优先级别,万一在执行其他中断服务程序的过程中又发生了时间片任务调度中断,那么时间片调度将由于优先级不高的原因而不执行,导致任务无法切换。因此使用这种方法的话,最好在应用程序里把定时器0的中断优先级设置成高,保证时间片调度中断为最优先执行。另外中断服务里不能使用os_wait函数,否则也会导致任务切换失败。
另一种方法是在中断服务里使用 isr_send_signal() ,把信号发给另外一个任务,让另外一个任务来处理中断服务。这样内核在执行isr_send_signal()时会自动做好相应的设置以保证时间片的调度工作正常进行。这种方法是Keil公司推荐的,稳定性高。譬如:
void fast_int (void) interrupt 1 { isr_send_signed (5); } void int_handle (void) _task_ 5 { os_wait(K_SIG,0,0); //中断服务代码 }
RTX51 Tiny的配置参数(Conf_tny.a51文件中)中有INT_CLOCK和TIMESHARING两个参数。
这两个参数决定了每个任务使用时间片的大小:
INT_CLOCK是时钟中断使用的周期数,也就是基本时间片;
TIMESHARING是每个任务一次使用的时间片数目。
两者决定了一个任务一次使用的最大时间片。
如假设一个系统中INT_CLOCK设置为10000,即10ms,那么TIMESHARING=1时,一个任务使用的最大时间片是 10ms;TIMESHARING=2时,任务使用最大的时间片是20ms;TIMESHARING=5时,任务使用最大的时间片是50ms。