首页 > 代码库 > 51系列小型操作系统精髓 简单实现

51系列小型操作系统精髓 简单实现

源码地址(专业定制程序:MCU,Windows,Android ,VC串口,Android蓝牙等不限。)

1.切换任务并记录位置,保证在时间到后能切换回来。(在任务中切换出去,在定时器中切换回来。)(时间片轮转)




;********************************************
;RTOS		用定时器0的多任务处理程序
;项目增加方式使用。
;程序不影响其它数据,目前程序的前后段不能进行参数传递 。 最多可以同时运行8个任务,
;执行时使用定时器0,定时时间根据初始化时的参数确定,一般为10mS。
;当指定优先级被占用时自动向高优先级调整,在R7中返回实际优先级,当所有高优先级都被占用时返回 0FFH,任务将被忽略。
;占用24H个内存单元。
;如果程序不可再入,需要设定 RWZT 的标志位。调用前先检查相应位。
;如果定时数设置过大,会引起一直在中断中,不能执行外部程序。如果任务执行时间过长,会引起堆栈溢出。
;18.432MHz,单时钟周期模式。不用任务单取反,输出频率 1.3MHz ,指令周期 384 nS 。速度是带系统的 100 倍。
;8个任务,只对管脚取反,T0=FEF0H,输出 2.8K 频率,为最高频率。执行周期不受任务影响。18.432MHz,单时钟周期模式。
;1个任务,只对管脚取反,T0=FFE1H,输出 12K 频率,为最高频率。执行周期受任务影响。每次调度时间为42US。
;*********************************************
;-------------------------------------------------------------	示例程序

;***********************************************
	NAME	RTOS_C    ;程序段名
	?PR?RTOS?RTOS_C		SEGMENT	CODE    ;代码段 ; 段名 SEGMENT 段类型 
	?DT?RTOS?RTOS_C		SEGMENT DATA
	?ID?RTOS?RTOS_C		SEGMENT IDATA
	
	PUBLIC	RTOS
	PUBLIC	_RTOS_WAIT
	PUBLIC	_RTOS_INIT
;---------------------------------------------
;BANBH	EQU	0A3H	;版本号 101221
;定义数据地址
;----------------------------------------------
RSEG	?DT?RTOS?RTOS_C
;RWZT		EQU	29H	;任务状态,0为停止,1为正在执行
TL:	DS 1	;EQU	30H	;RTOS定时器值,C=FFFFH-ft
TH:	DS 1	;EQU	31H	;18.432MHz(12周期)=C400H
RWB:	DS 1	;EQU	33H	;任务表


RSEG	?ID?RTOS?RTOS_C
RWDZ:	DS 16	        ;任务堆栈
;	EQU	80H	;任务地址
;	EQU	8FH
RWS:	DS 8		;任务数
;	EQU	90H	;任务定时数
;	EQU	97H
;*********************************************
        CSEG  AT   0BH         ;TO入口
        JMP     RTOS	
;*********************************************
RSEG	?PR?RTOS?RTOS_C

_RTOS_INIT:			;任务初始化
;-------------------------------------------- 在 R6 R7 中,设置定时值
;RTOS定时器值,C=FFFFH-ft,18.432MHz(12周期)=C400H,40MHz(12周期)=7DC9H
;定时器0定时程序,18.432MHz,10MS,方式1
	  ORL    TMOD,#01H   ;定时10MS  ;定时器0初始化,设置TMOD          MOV    TL0,R7
          MOV	 TL,R7	     ;设置TL
          MOV    TH0,R6
          MOV	 TH,R6       ;设置TH
          MOV	 RWB,#0	     ;任务表清0
          SETB   TR0   	     ;启动定时器
          SETB   ET0	     ;打开中断
          SETB   EA	     ;打开总中断
          RET                ;等待中断
;********************************************** 等待函数,优先级为最低 0 级,等待时间放入 R7 中
;	目前函数不能传递参数
_RTOS_WAIT:	MOV	A,R7 	;接收参数
	MOV	R5,A		;接收的数存在R5
	MOV	R7,#0		;R7清零
	CALL	YXJSZ		;优先级设置
	CJNE	R7,#8,RWSZ	;R7如果不等 于8,则任务设置
	MOV	R7,#0FFH	;返回值0xff  当指定优先级被占用时自动向高优先级调整,在R7中返回实际优先级,当所有高优先级都被占用时返回 0FFH,任务将被忽略。
	RET			;返回
;---------------------------------------------	
RWSZ:	MOV	A,R7		;送任务数
	ADD	A,#RWS		;
	MOV	R0,A		;任务数保存到R0
	MOV	A,R5		;R5上是什么  定时时间
	MOV	@R0,A   	;保存定时时间数到【R7+RWS】位置
	
	MOV	A,R7		;送
	RL	A		;
	ADD	A,#RWDZ
	MOV	R0,A
	POP	ACC		;跳转地址,在堆栈中
	POP	B
	MOV	@R0,B		;低地址在低字节
	INC	R0
	MOV	@R0,A
	
	MOV	B,R7		;设置任务列表 
	CLR	A		; 当前正在执行的程序如果被中断,并且中断中使用了此任务位,则此次任务将会丢失。
	INC	B		;可以关中断来解决此问题。
	SETB	C
RWSZ2:	RLC	A
	DJNZ	B,RWSZ2
	ORL	RWB,A
	RET
;--------------------------
YXJSZ:	MOV	B,R7	;0	 ;优先级设置
	INC	B	;B++ 1
	MOV	A,RWB	;0
	CLR	C	;0
YXJ2:	RRC	A	;RRC是带进位的循环右移指令
	DJNZ	B,YXJ2 	;如果B不等于0,  b=0
YXJ4:	JNC	YXJ3	;C=0 ,则 
	RRC	A	;
	INC	R7	;
	JMP	YXJ4    ;
YXJ3:	RET 		;返回 

;------------------------------- 中断程序   把所有相关寄存器内容压入堆栈  任务调度完后 再弹出来
RTOS:	MOV    TH0,TH		;重载定时数
	MOV    TL0,TL		;
	JB	PSW.3,RTOS1	;RS0==1 用于选择当前工作寄存器区。8051有8个8位寄存器R0~R7
	JB	PSW.4,RTOS2	;RS1==1 
	PUSH	0		;RS0=0,RS1=0;  第一组寄存器区
	PUSH	1
	PUSH	2
	PUSH	3
	PUSH	4
	PUSH	5
	PUSH	6
	PUSH	7		;存入椎栈第一组寄存器上的内容
	JMP	RTOS0		;都跳到RTOS0
RTOS1:	JB	PSW.4,RTOS3	;
	PUSH	8		;RS0=1,RS1=0;  第三组寄存器区
	PUSH	9
	PUSH	10
	PUSH	11
	PUSH	12
	PUSH	13
	PUSH	14
	PUSH	15
	JMP	RTOS0		;
RTOS2:	
	PUSH	10H		;RS0=0,RS1=1;  	第二组寄存器区
	PUSH	11H
	PUSH	12H
	PUSH	13H
	PUSH	14H
	PUSH	15H
	PUSH	16H
	PUSH	17H
	JMP	RTOS0
RTOS3:	PUSH	18H		;RS0=1,RS1=1;	第四组寄存器区
	PUSH	19H
	PUSH	1AH
	PUSH	1BH
	PUSH	1CH
	PUSH	1DH
	PUSH	1EH
	PUSH	1FH

RTOS0:	PUSH	PSW 		;入栈
	PUSH	ACC		;
	PUSH	B
	PUSH	DPL
	PUSH	DPH
	CALL	RWDD0		;任务调度
	POP	DPH		;出栈
	POP	DPL
	POP	B		
	POP	ACC
	POP	PSW
	JB	PSW.3,RTOS01
	JB	PSW.4,RTOS02
	POP	7
	POP	6
	POP	5
	POP	4
	POP	3
	POP	2
	POP	1
	POP	0		;弹出第一组寄存器区上的变量
	RETI			;中断返回
RTOS01:	JB	PSW.4,RTOS3
	POP	15
	POP	14
	POP	13
	POP	12
	POP	11
	POP	10
	POP	9
	POP	8
	RETI
RTOS02:	
	POP	17H
	POP	16H
	POP	15H
	POP	14H
	POP	13H
	POP	12H
	POP	11H
	POP	10H
	RETI
RTOS03:	POP	1FH
	POP	1EH
	POP	1DH
	POP	1CH
	POP	1BH
	POP	1AH
	POP	19H
	POP	18H
	RETI
;-----------------------------	中断任务调度
RWDD0:	MOV	B,#7		;任务号
	MOV	R0,#RWS+7	;
	MOV	R1,#0FFH	;执行任务号
	MOV	A,RWB
	JNZ	RWDD3
	RET
RWDD3:	CLR	C
	RLC	A
	JNC	RWDD6
	CJNE	@R0,#0,RWDD2
RWDD4:	CJNE	R1,#0FFH,RWDD6
	MOV	R1,B
RWDD6:	DEC	R0
	DEC	B
	JNZ	RWDD3
	CJNE	R1,#0FFH,RWCL
	RET
RWDD2:	DEC	@R0
	CJNE	@R0,#0,RWDD6
	JMP	RWDD4
;---------------------------------
RWDD:	MOV	R1,#7
	MOV	R0,#RWS+7
	MOV	A,RWB
	JNZ	RWDD03
	RET
RWDD03:	CLR	C
	RLC	A
	JNC	RWDD06
	CJNE	@R0,#0,RWDD06
	JMP	RWCL
RWDD06:	DEC	R0
	DEC	R1
	JNZ	RWDD03
	RET
;--------------------------------------
RWCL:	MOV	DPTR,#RWDD   ;任务处理?
	PUSH	DPL		
	PUSH	DPH
	MOV	A,R1
	RL	A
	ADD	A,#RWDZ
	MOV	R0,A
	MOV	A,@R0
	PUSH	ACC		;低地址在低字节
	INC	R0
	MOV	ACC,@R0
	PUSH	ACC

	MOV	A,#0FFH		;清除任务列表  当前正在执行的程序如果在下一个中断到来时还未执行完, 它的优先级将变为最低优先级,
	INC	R1		;可以加入一个执行标志 RWZT 来解决此问题。
	CLR	C
RWCL22:	RLC	A
	DJNZ	R1,RWCL22
	ANL	RWB,A
	RETI
;***********************************
	END

C语言版切换示例(网摘)

#include <REG52.H>

#define MAX_TASKS 2 //任务槽个数.必须和实际任务数一至
#define MAX_TASK_DEP 12 //最大栈深.最低不得少于2 个,保守值为12.
unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP];//任务堆栈.
unsigned char idata task_sp[MAX_TASKS];
unsigned char task_id; //当前活动任务号

//任务切换函数(任务调度器)
void task_switch(){
	task_sp[task_id] = SP;
	if(++task_id == MAX_TASKS)
		task_id = 0;
	SP = task_sp[task_id];
}
//任务装入函数.将指定的函数(参数1)装入指定(参数2)的任务槽中.如果该槽中原来就有任
//务,则原任务丢失,但系统本身不会发生错误.
void task_load(unsigned int fn, unsigned char tid){
	task_sp[tid] = task_stack[tid] + 1;
	task_stack[tid][0] = (unsigned int)fn & 0xff;	 //低字节
	task_stack[tid][1] = (unsigned int)fn >> 8;	     //高字节
}
//从指定的任务开始运行任务调度.调用该宏后,将永不返回.
#define os_start(tid) {task_id = tid,SP = task_sp[tid];return;}
/*============================以下为测试代码==========================*/
void task1(){
	static unsigned char i;
	while(1){
		i++;
		task_switch();//编译后在这里打上断点
	}
}
void task2(){
	static unsigned char j;
	while(1){
		j+=2;
		task_switch();//编译后在这里打上断点
	}
}
void main(){
	//这里装载了两个任务,因此在定义MAX_TASKS 时也必须定义为2
	task_load(task1, 0);//将task1 函数装入0 号槽
	task_load(task2, 1);//将task2 函数装入1 号槽
	os_start(0);
}


源码地址(专业定制程序MCU,Windows,Android ,VC串口,Android蓝牙等不限。)