基于51内核的多任务系统(原创)

基于51内核的多任务系统(原创)

以前写基于定时中断的顺序执行架构程序,实时性是没问题,但是逻辑过于复杂,于是想实现延时,超时切换的操作系统,利用多任务,可以编写更简洁的程序,本来想做无限任务的系统,但是调试发现任务切换开销很大,占用资源也多,于是将任务定在8任务上。

系统概况:
此系统最多八个任务,任务越多,任务开支越大,一个任务栈深度初定25字节,栈放在高128-255的RAM中,任务越多,占用越多,深度能调节,任务切换检测大概需要25us(STC 24Mhz),所有任务在10ms内完成执行,也就是说,在10ms内,如果不是任务忙,总有一段时间能执行。

任务优先级为ID优先和轮流优先,10ms内,优先级根据ID切换,如果当前任务ID准备就绪,当前ID优先级最高,如果当前任务ID未就绪,优先级按ID越小越高。

这段时间没精力去继续研究,所以将思路和程序贴出来,希望对大家有帮助。

#include <INTRINS.H>
#include "BASE.h"


#define uchar	unsigned char
#define uint	unsigned int



void Task0(void)
{
	while(1)
	{
		Deply(100);//100us为单位
		P30 = !P30;
	}
}

void Task1(void)
{
	while(1)
	{
		Deply(100);
		P31 = !P31;
	}
}

void Task2(void)
{
	while(1)
	{
		Deply(100);
		P32 = !P32;
	}
}

void Task3(void)
{
	while(1)
	{
		Deply(10);
		P33 = !P33;
	}
}

void Task4(void)
{
	while(1)
	{
		Deply(100);
		P34 = !P34;
	}
}


void main(void)
{
	P35 = 0;
	Task_inti();
	
	Task_Load(Task0, 0);
	Task_Load(Task1, 1);
	Task_Load(Task2, 2);
	Task_Load(Task3, 3);
	Task_Load(Task4, 4);
	
	Task_Start();
	
	while(1);
}

为了减少开销,我将空闲任务直接定在main函数里面的while(1);,系统进入无任务或者全任务延时,会进入main函数里面的while(1);进行空循环。

Task_inti();

#define Task_inti()		SP = &SP_Main+1;Task_Id = 0;\
						Task_Pro = 0;Task_Stack = 0;\
						Task_Deply = 0;Task_Ready = 0;\
						Task_Sum = 0;Task_Swh_Time = 0

这个其实是一个宏定义,包括任务的入口,还有变量的初始化

Task_Load(Task0, 0);

//------------------任务装载--------------------
unsigned char Task_Load(unsigned int Fn, unsigned char T_id)
{ 
	unsigned char i;
	
	if(Task_Stack & Get_1bit[T_id])					//创建任务失败
		return TASK_FAIL;
	
	Task_Stack |= Get_1bit[T_id];
	
	Task_Deply &= ~(Get_1bit[T_id]);
	Task_Ready |= Get_1bit[T_id];
	
	for(i=0;i<MAX_SFA;i++)
	{
		SP_STACK[T_id][i] = 0;
	}
	
	SP_STACK[T_id][0] = (unsigned int)Fn & 0xff; 
	SP_STACK[T_id][1] = (unsigned int)Fn >> 8;
	SP_Add[T_id] = &SP_STACK[T_id]+MAX_PUSP_DEEP;
	Deply_ID[T_id] = 0;
	if(Task_Sum < MAX_TASK)Task_Sum++;
	Task_Swh_Time = T_10MS / Task_Sum;
	return TASK_SUCCESS;	
}

装载任务,根据任务的ID加载到对应的数组里面,然后初始化任务数据,给任务加载任务的地址,还有计算任务数量

Task_Start();

void Task_Start(void)
{
	intiTimer1();
	Timer0_K = 0;
	Task_Deply = 0;
	Deply_Ready_Sum = 0;
	
	if(Task_Sum == 0)
	{
		Task_Swh_Time = T_10MS;
	}
	EA = 1;
}

开启任务,先初始化定时器,任务占用一个定时器作为延时和任务切换。

intiTimer1();

void intiTimer1(void)
{
	TMOD |= 0x02;					//设置定时器为模式2(8位自动重装载)
    // TL0 = 0x00;                     //初始化计时值
    // TH0 = 0x00;
	TL0 = T0SET;                     //初始化计时值
    TH0 = T0SET;
    TR0 = 1;                        //定时器0开始计时
    ET0 = 1;                        //使能定时器0中断
}

到这里,任务的创建,初始化,装载,运行已经开始,等第一个定时器中断来临,就进入任务执行。

任务的进入,为了不让编译器自动加载入栈出栈,用一个A51的文件设置了中断加载地址。

$NOMOD51

EXTRN CODE (timer0)

NAME	TIMER
CSEG	AT	0BH
LJMP	timer0
; CSEG	AT	0124H
; RETI
END

下面这个是中断处理函数:

/*
定时器0,延时100us
每次延时查看延时数组是否不为零
如果不为零,将不为零数组减一
根据任务数分配10ms时间进行倒计时
倒计时结束,更改任务优先级
如果需要切换任务,调用任务切换函数
*/
void timer0(void)
{
	P36 = 1;/
	if(Timer0_K == 0)				//初始化
	{
		Timer0_K = 1;
		Timer0_T = 0;
		
#pragma asm			 
		POP		SP_Main+1;
		POP		SP_Main;	
			
		MOV  	SP,#LOW (SP_STACK+01H);
		MOV		Task_Id,#0;
		MOV		Task_Pro,#0;
		MOV		Time_10ms,#T_10MS;	

		SETB	Timer0_D;
		SETB	Main_W;
		// RETI;						//退出
#pragma endasm 		
	}
	
/*
建立两个数组
一个数组存储延时变成1的ID,另外设立一个变量记录这样延时的数量
一个数组存储被激活ID,Task_pro提供获取,Task_Sum记录激活任务数量


延时变成1的最先优先级是当前Task_Pro,

*/	
	
	
#pragma asm 
		XCH		A,Task_Temp;
		MOV		A,Task_Deply;			//延时数组存在数据
		JZ		DEPEND1;					//不为0,跳转		(不等跳转)
DEPST1:
		PUSH	AR0;
		PUSH	AR1;
		MOV		Deply_Ready_Sum,#00H;
		MOV		Task_Temp1,#00H;
		MOV		Switch_i,#MAX_TASK;
		
		MOV		A,Task_Pro;					//Task_Temp1 = Task_Pro + 1;  ???未启动的任务?????
		INC		A;
		CJNE	A,#MAX_TASK,DEPST2;			//不等转移 if(A == MAX_TASK)
		MOV		A,#00H;		
DEPST2:	
		MOV		Task_Temp1,A;
		ADD		A,ACC;
		ADD		A,#LOW(Deply_ID);
		MOV		R1,A;
		INC		A;
		MOV		R0,A;

DEPST:	
		CJNE	@R1,#00H,DEP1;		//高位不为0,跳转		(不等跳转)
		CJNE	@R0,#00H,DEP2;		//低位不为0,跳转		(不等跳转)
		SJMP	DEPEND;				
DEP1:								//高位不为0的情况
		DEC		@R0;
		CJNE	@R0,#0FFH,DEP1;		//低位不为0XFF,跳转	(不等跳转)
		DEC		@R1;
		SJMP	DEPEND;	
DEP2:								//这里高位为0,低位不为0				
		CJNE	@R0,#01H,DEP3;		//低位不为1,跳转		(不等跳转)
									//Deply_Ready[Deply_Ready_Sum] = Task_Temp1;
		PUSH	AR0;
		MOV		A,#LOW(Deply_Ready);
		ADD		A,Deply_Ready_Sum;
		MOV		R0,A;
		MOV		A,Task_Temp1;
		XCH		A,@R0;
		POP		AR0;
		INC		Deply_Ready_Sum;
		SETB	Timer0_D;
		SJMP	DEPEND;
DEP3:
		DEC		@R0;
DEPEND:
		INC		R0;
		INC		R0;
		INC		R1;
		INC		R1;
		
		INC		Task_Temp1;
		MOV		A,Task_Temp1;
		CJNE	A,#MAX_TASK,DEPEND2;			//不等转移 if(A == MAX_TASK)
		MOV		Task_Temp1,#00H;
		MOV		R0,#LOW(Deply_ID+1);
		MOV		R1,#LOW(Deply_ID);
DEPEND2:
		DJNZ	Switch_i,DEPST;

		POP		AR1;
		POP		AR0;	
DEPEND1:
		XCH		A,Task_Temp;
#pragma endasm 
	
#pragma asm 		
	DJNZ	Time_10ms,TIMEST1;		//切换任务延时,总时间为10ms,根据任务多少划分时间
	XCH		A,Time_10ms; 
	MOV		A,Task_Swh_Time;
	XCH		A,Time_10ms; 
	SETB	Timer0_T;
TIMEST1:;
#pragma endasm
P36 = 0;/
	if(Timer0_D == 1 || Timer0_T == 1)
	{
		Task_Switch();
	}
	
	__asm RETI;	
}

我用P3.6查看任务的开销,基本上,任务切换开销大概在25us以内(单片机STC,频率24Mhz)
任务切换开销其实和任务数量有关,任务越少,开销越少。
任务优先级有两种,一种是当前优先任务,有个Task_Pro,这个变量每隔一段时间在改变,划分10ms完成一个任务改变周期,譬如任务有5个,那Task_Pro在10ms内改变5次,Task_Pro的值如果跟ID一致,那此时此ID任务优先级最高;还有一种优先级,如果Task_Pro在忙状态(延时或者挂起),根据ID越小,优先级越高。
执行过程大概如此:
1.第一次进入,也就是在main函数进入,需要做地址调整,先记录while(1);的入口地址,再调整任务0的地址;
2.进入延时检测,验收检测设定为1000us,也可以在H文件更改,任务如果延时未结束,将延时数据减一,如果任务延时结束,先登记任务;
3.任务优先级切换计数,之后根据标志位确定是否要启动任务切换。

任务切换函数:

//----------------------切换任务---------------------
/*
功能:1.直接调用 2.定时器调用 3.延时调用
调用改变任务
任务优先级: 1.当前默认任务 2.延时时间达到任务 3.ID越小任务

*/
void Task_Switch(void)
{
	OS_SW_ENTER();

	P35 = 1;//
	
	if(Timer0_D == 1 || Timer0_T == 1 || Deply_D == 1)	//定时器或者延时调用,减去
	{
		SP--;
		SP--;
	}
		
	if(Main_W == 0)
	{		
#pragma asm
		PUSH	ACC;
		PUSH	B;
		PUSH	PSW;
		PUSH	DPL;
		PUSH	DPH;
		
		PUSH	AR0;
		PUSH	AR1;
		PUSH	AR2;
		PUSH	AR3;

		PUSH	AR4;
		PUSH	AR5;
		PUSH	AR6;
		PUSH	AR7;
#pragma endasm	

	SP_Add[Task_Id] = SP;	
	}

	if(Timer0_T == 1 || (Timer0_D == 0 && Deply_D == 0))		//任务切换时间已到 或者 任务主动切换
	{
		for(Switch_i = 0;Switch_i < MAX_TASK;Switch_i++)		//查找下一个可执行任务
		{
			Task_Pro++;
			if(Task_Pro >= MAX_TASK)
			{
				Task_Pro = 0;
			}
			if(Task_Stack & (Get_1bit[Task_Pro])) 
			{
				break;
			}
		}		
	}
	
	//切换任务,当前Task_Pro优先级最高
	//Task_Pro、Task_Id、Task_Ready、Task_Deply
	
	if(Task_Ready == 0 && Deply_Ready_Sum == 0)												//所有任务在忙
	{
		Main_W = 1;
		SP = &SP_Main+1;
	}
	else
	{	
		if(Deply_Ready_Sum > 0)										//有延时结束任务
		{
			Deply_Ready_Sum--;
			Task_Id = Deply_Ready[Deply_Ready_Sum];
			Deply_ID[Task_Id] = 0;
			Task_Deply &= ~(Get_1bit[Task_Id]);
			Task_Ready |= Get_1bit[Task_Id];
		}
		else														//一般任务
		{
			//条件:最优先Task_Pro任务,然后是其它任务
			//Task_Ready == 1;
			Task_Id = Task_Pro;
			
			for(Switch_i = 0;Switch_i < MAX_TASK;Switch_i++)		//查找下一个可执行任务
			{
				Task_Temp = Get_1bit[Task_Id];
				if (Task_Ready & Task_Temp)
				{
					break;
				}

				Task_Id++;
				if (Task_Id >= MAX_TASK)
				{
					Task_Id = 0;
				}
			}
		}
		
	Main_W = 0;	
	Deply_D = 0;
	SP = SP_Add[Task_Id];	
	
#pragma asm
	POP		AR7;
	POP		AR6;
	POP		AR5;
	POP		AR4;
	
	POP		AR3;
	POP		AR2;
	POP		AR1;
	POP		AR0;
	
	POP		DPH;
	POP		DPL;
	POP		PSW;
	POP		B;
	POP		ACC;
#pragma endasm		
	}
	
	if(Timer0_T == 1 || Timer0_D == 1)
	{
		Timer0_T = 0;
		Timer0_D = 0;
		OS_SW_EXIT();
		__asm RETI;
	}
	P35= 0;/
	OS_SW_EXIT();
}

还有一个任务延时函数

//----------------------任务延时---------------------
void Deply(unsigned int i)
{
	OS_SW_ENTER();
	
	if(TL0>T0SET_PART)
	{
		if(i<0xffff)
		{
			i++;
		}			
	}	
	
	if(i > 0)
	{
		Deply_ID[Task_Id] = i;
		Task_Deply  |= Get_1bit[Task_Id];
		// Deply_Ready |= (1<<Task_Id);
		Task_Ready  &= ~(Get_1bit[Task_Id]);
	}
	else
	{
		OS_SW_EXIT();
		return;
	}
	Deply_D = 1;
	Task_Switch();
}

执行此函数,一个功能是登记延时,另外一个功能是切换任务

源文件我放在下载资源,提供参考

此系统参考了51_00_OS

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值