单片机MCU51系列RTOS多任务超微操作系统精髓 简单实现 汇编及C语言混合 keil9.0工程源码

31 篇文章 0 订阅
15 篇文章 0 订阅

系统设计核心意图:使用定时器,在延时过程中运行其它的任务。

工程源码:链接: https://pan.baidu.com/s/1LEV9qYmUn6SdemGz7TH6dw 提取码: iua5 

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

可以支持同时8个任务。

工程就两个文件:汇编操作系统rtos_c.asm 及 C语言示例 Test.c

Test.c

/*
开发工具:keil c51 V9.0 及注册机
芯片:标准C8051
晶振:12M
源码任务数:加主任务共4个
2019-05-07
*/

//-------------------------------------------	包含文件
#include <REG51.H>
//#include <stdio.h>
//#include <string.h>
//----------------------------------------------------多任务处理  以rtos_开头
extern	unsigned char rtos_wait(unsigned char time);  //返回实际优先级 ,当所有高优先级都被占用时返回 0FFH,任务将被忽略。time为延时时间
extern	rtos_init(unsigned int count);	 //依据时间片轮转时间后,确定设置定时器count
void task0()
{
	P0=0x0FF;
	rtos_wait(100); 	
	P0=0x0;	
}
void task1()
{
	while(1)
	{
		P1=~P1; //按位取反
		rtos_wait(100); 	
	}
}
void task2()
{
	while(1)
	{
		P2=0x0FF;
		rtos_wait(200); 
		P2=0x0;	
		rtos_wait(200); 	
	}
}
void	main()
{
	P0=0;	//初始化端口
	P1=0;
	P2=0;
	rtos_init(0xD8F0); //操作系统初始化,根据单片机晶振,设置定时参数,比如10ms
	task0(); //第一个任务。没有循环,任务结束就销毁。相当于200*10即2000ms即2s,在延时过程中运行其它的任务。
	task1();  //第二个任务 在循环中任务就一直存在。不要以为这是死循环,进入这里后会自动切换到其它任务,时间到后又回来继续执行后面的。	
	task2();  //第三个任务,类似第二个任务
	while(1);	//主任务		
}

 

rtos_c.asm

;********************************************
;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。
;*********************************************
;-------------------------------------------------------------	示例程序
;extern	RTOS(unsigned char priority,time,unsigned int *add);
;extern	RTOS_INI(unsigned int count);
;extern	RTOS();
;sbit	P1_0=P1^0;
;void	deng(void)
;{
;	P1_0=!P1_0;
;	JMP(0,100,deng);
;}
;void main()
;{
;	JMP(0,100,deng);
;	while(1)
;	{
;	RTOS();
;	}
;}
;***********************************************
	NAME	RTOS_C
	?PR?RTOS?RTOS_C		SEGMENT	CODE
	?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:			;任务初始化		1.定时器启动,2.RWB任务表置0
;-------------------------------------------- 在 R6 R7 中,设置定时值
;RTOS定时器值,C=FFFFH-ft,18.432MHz(12周期)=C400H,40MHz(12周期)=7DC9H
;定时器0定时程序,18.432MHz,10MS,方式1
	  ORL    TMOD,#01H   ;定时10MS		 
          MOV    TL0,R7
          MOV	 TL,R7
          MOV    TH0,R6
          MOV	 TH,R6
          MOV	 RWB,#0
          SETB   TR0
          SETB   ET0
          SETB   EA
          RET                ;等待中断
;********************************************** 等待函数,优先级为最低 0 级,等待时间放入 R7 中
;	目前函数不能传递参数
_RTOS_WAIT:	MOV	A,R7
	MOV	R5,A
	MOV	R7,#0
	CALL	YXJSZ ;优先级设置
	CJNE	R7,#8,RWSZ	  ;任务设置
	MOV	R7,#0FFH
	RET
;---------------------------------------------任务设置	
RWSZ:	MOV	A,R7		;送任务数
	ADD	A,#RWS
	MOV	R0,A
	MOV	A,R5
	MOV	@R0,A
	
	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
	INC	B
	MOV	A,RWB
	CLR	C
YXJ2:	RRC	A
	DJNZ	B,YXJ2
YXJ4:	JNC	YXJ3
	RRC	A
	INC	R7
	JMP	YXJ4
YXJ3:	RET

;------------------------------- 中断程序
RTOS:	MOV    TH0,TH
	MOV    TL0,TL
	JB	PSW.3,RTOS1
	JB	PSW.4,RTOS2
	PUSH	0
	PUSH	1
	PUSH	2
	PUSH	3
	PUSH	4
	PUSH	5
	PUSH	6
	PUSH	7
	JMP	RTOS0
RTOS1:	JB	PSW.4,RTOS3
	PUSH	8
	PUSH	9
	PUSH	10
	PUSH	11
	PUSH	12
	PUSH	13
	PUSH	14
	PUSH	15
	JMP	RTOS0
RTOS2:	
	PUSH	10H
	PUSH	11H
	PUSH	12H
	PUSH	13H
	PUSH	14H
	PUSH	15H
	PUSH	16H
	PUSH	17H
	JMP	RTOS0
RTOS3:	PUSH	18H
	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);
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小黄人软件

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值