通过51单片机定时中断实现的非抢占式实时多任务

发布于:2022-12-14 ⋅ 阅读:(371) ⋅ 点赞:(0)

以本人开发的NerveOS为例,介绍通过51单片机定时中断实现的非抢占式实时多任务操作系统。

注意:本次只写非抢占式实时多任务操作系统的核心,并非完整的系统。

多任务大概运行流程,如下图:

/******************开发环境***************/

[IDE]:Keil μVisio5                                                                        

[主控芯片]:STC8A8K64S4A12@22.1184MHz

/******************声明定义***************/

数据类型定义:

typedef unsigned int u16;//无符号整型
typedef unsigned int uint;//无符号整型
typedef signed int s16;//有符号整型
typedef unsigned char u8;//无符号字符型
typedef unsigned char uchar;//无符号字符型
typedef signed char s8;//有符号字符型
typedef bit u2;//位型
typedef unsigned long int u32;//无符号长整型
typedef signed long int s32;//有符号长整型

第一步:启动单片机内设定时器

源码:

/********************************
函数名称:timer0Init
函数功能:定时器0初始化
输入参数:
输入:
输出参数:
输出:
********************************/
void timer0Init()//任务调度使用
{
	TMOD |= 0x01;//定时器0工作方式2,TH0是重装值,TL0是初值
	TH0 = T0VH;//初值(高8位)
    TL0 = T0VL;//初值(低8位)
	ET0 = 1;//开启定时器0中断
	EA = 1;//开启总中断
	TR0 = 1;//开启定时器0
}

第二步:完善定时器0中断响应程序

在写这部分代码,我们先明白两个概念:

1.任务队列

 2.任务系统

源码:

/******************声明定义***************/
#define TaskQueueMax 32//任务队列长度
#define IAIMax 32//条件任务信息最大数量
#define TAIMax 32//定时任务信息最大量
#define TaskSwitchMax 32//任务调度等待时间
u8 xdata AQSP = 0;//多任务队列指针
u8 xdata TaskSwitch_Flag = 0;//定时器0关闭标志位
/*任务队列*/
typedef struct
{
 	u8 ID;//应用ID
	u8 Py;//优先级
}xdata taskqueue;
taskqueue TaskQueue[TaskQueueMax + 2] = {0};

/********************************
函数名称:MAIN
函数功能:多任务程序入口
输入参数:TaskSn(任务ID)
输入:
输出参数:
输出:
********************************/
void MAIN(u8 TaskSn)
{
 	switch(TaskSn)
	{
	 	case 1: break;//ID:1
		case 2:break;//ID:2
		case 3:break;//ID:3
		case 4:break;//ID:4
		case 5:break;//ID:5
		case 6:break;//ID:6
		case 7:break;//ID:7
		case 8:break;//ID:8
		case 9:break;//ID:9
		case 10:break;//ID:10
		case 11:break;//ID:11
		case 12:break;//ID:12
		case 13:break;//ID:13
		case 14:break;//ID:14
		case 15:break;//ID:15
		case 16:break;//ID:16
		case 17:break;//ID:17
		case 18:break;//ID:18
		case 19:break;//ID:19
		case 20:break;//ID:20
		case 21:break;//ID:21
		case 22:break;//ID:22
		case 23:break;//ID:23
		case 24:break;//ID:24
		case 25:break;//ID:25
		case 26:break;//ID:26
		case 27:break;//ID:27
		case 28:break;//ID:28
		case 29:break;//ID:29
		case 30:break;//ID:30
		case 31:break;//ID:31
		case 32:break;//ID:32
		default:break;//ID:x
	}
}
/********************************
函数名称:TaskQueue_read
函数功能:读多任务队列
输入参数:
输入:
输出参数:
输出:
********************************/
u8 TaskQueue_read()
{
 	u8 Aqw_F0,Aqw_temp;
	if(AQSP != 0xFF)//在任务指针在0~254之间,可以读取
		Aqw_temp = TaskQueue[0].ID;
	else
	{
		AQSP = 0;
		return 0;
	}
	for(Aqw_F0 = 0;Aqw_F0 < AQSP;Aqw_F0++)//整体向前移一位
		TaskQueue[Aqw_F0] = TaskQueue[Aqw_F0 + 1];
	TaskQueue[Aqw_F0].ID = 0;
	if(AQSP > 0)//下限
		AQSP--;//任务指针减一
	return Aqw_temp;//返回任务ID
}
/********************************
函数名称:TaskSwitch
函数功能:任务调度
输入参数:
输入:
输出参数:
输出:
********************************/
void TaskSwitch()
{
	u8 TaskId;
	TaskSwitch_Flag++;
	if(Sf.TaskSwitch)
		TaskId = TaskQueue_read();//读取ID
	else
		TR0 = 0;
	if(TaskId > 0)
		TaskSwitch_Flag = 0;
	MAIN(TaskId);//启动用户应用
	if(TaskSwitch_Flag > TaskSwitchMax)//关闭定时器0,降低CPU占用量
	{
		TR0 = 0;
		TaskSwitch_Flag = 0;
	}
}

/********************************
函数名称:timer0Interrupt
函数功能:定时器0中断服务
输入参数:
输入:
输出参数:
输出:
********************************/
void timer0Interrupt () interrupt 1 
{
	TH0 = T0VH;//初值(高8位)
    TL0 = T0VL;//初值(低8位)
	TaskSwitch();
}

第三步:完善队列系统

源码:

/********************************
函数名称:TaskQueue_write
函数功能:写多任务队列
输入参数:TaskID(任务ID)Py(任务优先级)
输入:
输出参数:
输出:
********************************/
void TaskQueue_write(u8 TaskID,u8 Py) reentrant
{
 	u8 AQSP1,AQSP2;//定义变量
	ET0 = 0;//关闭定时器0中断
	if(TR0 == 0)//检测定时器3未开启,开启定时器3
		TR0 = 1;//开启定时器0
	if(AQSP < TaskQueueMax && TaskID > 0 && Sf.TaskSwitch == 1)//在任务指针小于任务队列的最大长度时,才可以写入
	{
		if(Py)//优先级大零
		{
			 AQSP1 = 0;
			 while(AQSP1 < TaskQueueMax)//在任务指针大于任务队列的最大长度时,停止写入
			 {
			  	 if(TaskQueue[AQSP1].Py >= Py)
				 	AQSP1++;
				 else
				 {
				  	 for(AQSP2 = AQSP;AQSP2 > AQSP1;AQSP2--)//整体向后移一位
					  	 TaskQueue[AQSP2] = TaskQueue[AQSP2 - 1];
					 TaskQueue[AQSP1].ID = TaskID;//写入任务ID
					 TaskQueue[AQSP1].Py = Py;//写入任务优先级
					 AQSP++;//任务指针加一
					 break;//退出循环
				 }
			 }
		}
		else
		{
			while(AQSP < TaskQueueMax)
			{
			 	if(TaskQueue[AQSP].ID == 0)
				{
					TaskQueue[AQSP].ID = TaskID;//写入任务ID
					TaskQueue[AQSP].Py = Py;//写入任务优先级
					break;
				}
				else
				    AQSP++;//任务指针加一
			}
			AQSP++;//任务指针加一
		}
	}
	ET0 = 1;//开启定时器0中断
}

第四步:完善条件任务

源码:

/********************************
函数名称:TaskTypeIf
函数功能:条件任务启动
输入参数:TYPE(类型)
输入:
输出参数:
输出:
********************************/
void TaskTypeIf(u8 TYPE) reentrant 
{
 	u8 TTI_F0;
	if(Sf.TaskSwitch)
	{
		for(TTI_F0 = 0;TTI_F0 <= DIAIMax; TTI_F0++)//使用for,进行遍历
			if(IAI[TTI_F0].type == TYPE && IAI[TTI_F0].Py != 0xFF)//与当前输入事件ID相同
				TaskQueue_write(IAI[TTI_F0].ID,IAI[TTI_F0].Py);//注入ID
	}
}

第五步:写任务创建方法

源码:

/******************声明定义***************/
/*定时型任务信息*/
u8 xdata DTAIMax = 0,DIAIMax = 0;//开放任务最大值
typedef struct 
{
 	u8 ID;//应用ID
	u16 freq;//时间常量
	u16 timer;//剩余时间
	u8 Py;//优先级
}xdata TimerAppInfo;
TimerAppInfo TAI[TAIMax] = {0};
/*条件型任务信息*/
typedef struct
{
	u8 ID;//应用ID
	u8 type;//类型
	u8 Py;//优先级
}xdata IfAppInfo;
IfAppInfo IAI[IAIMax] = {0};


/********************************
函数名称:TaskLoad
函数功能:注册注销任务
输入参数:TaskId(任务ID)TaskType(任务类型)PY(任务优先级)TaskPm(任务参数)
输入:
输出参数:
输出:
********************************/
void TaskLoad(u8 TaskId,u8 TaskType,u8 PY,u16 TaskM) reentrant
{
	u8 TL_F0;
	if(TaskType)//条件型任务信息
	{
		if(TaskM > 0)//注册
		{
			for(TL_F0 = 0;TL_F0 < IAIMax;TL_F0++) 
			{
				if(IAI[TL_F0].ID == 0 || IAI[TL_F0].ID == TaskId)
				{
					IAI[TL_F0].type = TaskType;//输入类型
					IAI[TL_F0].Py = PY;//输入优先级
					IAI[TL_F0].ID = TaskId;//输入ID
					if(TL_F0 >= DIAIMax)//获取最大值
						DIAIMax = TL_F0;
					break;
				}
			}
		}
		else//注销
		{
			for(TL_F0 = 0;TL_F0 < IAIMax;TL_F0++) 
			{
				if(IAI[TL_F0].ID == TaskId)
				{
					IAI[TL_F0].type = 0;//输入类型
					IAI[TL_F0].Py = 0;//输入优先级
					IAI[TL_F0].ID = 0;//输入ID
					for(TL_F0 = 0;TL_F0 < IAIMax;TL_F0++)//重新计算最大值 
					{
					 	if(IAI[TL_F0].ID == 0)
							DIAIMax = TL_F0 - 1;
					}
					break;
				}
			}
		}
	}
	else//定时型任务信息
	{
		if(TaskM > 0)//注册
		{
			if(TR1 == 0)//如果检测到未开启定时器1,则开启定时器1
				timer1Init();
			for(TL_F0 = 0;TL_F0 < TAIMax;TL_F0++) 
			{
				if(TAI[TL_F0].ID == 0|| TAI[TL_F0].ID == TaskId)
				{
					TAI[TL_F0].freq = TaskM;//输入数值
					TAI[TL_F0].Py = PY;//输入优先级
					TAI[TL_F0].ID = TaskId;//输入ID
					TAI[TL_F0].timer = TaskM;//输入ID
					if(TL_F0 >= DTAIMax)//获取最大值
						DTAIMax = TL_F0;
					break;
				}
			}
		}
		else//注销
		{
			for(TL_F0 = 0;TL_F0 < TAIMax;TL_F0++) 
			{
				if(TAI[TL_F0].ID == TaskId)
				{
					TAI[TL_F0].timer = 0;//输入
					TAI[TL_F0].freq = 0;//输入数值
					TAI[TL_F0].Py = 0;//输入优先级
					TAI[TL_F0].ID = 0;//输入ID
					for(TL_F0 = 0;TL_F0 < TAIMax;TL_F0++)//重新计算最大值 
					{
					 	if(TAI[TL_F0].ID == 0)
							DTAIMax = TL_F0 - 1;
					}
					break;
				}
			}	
		}	
	}
}

第六步:完善定时任务系统

源码:

/********************************
函数名称:TaskTimeMs
函数功能:定时任务计时
输入参数:
输入:
输出参数:
输出:
********************************/
void TaskTimeMs()
{
	u8 ttms_F0;
	for(ttms_F0 = 0; ttms_F0 <= DTAIMax; ttms_F0++)//使用for循环,进行扫描处理
	{
		if(TAI[ttms_F0].timer > 0 &&TAI[ttms_F0].freq > 0 && TAI[ttms_F0].Py != 0xFF)//定时任务计时
		{
			TAI[ttms_F0].timer--;//数值减一
			if(TAI[ttms_F0].timer == 0)//运行
			{
				TaskQueue_write(TAI[ttms_F0].ID,TAI[ttms_F0].Py);//注入ID
				TAI[ttms_F0].timer = TAI[ttms_F0].freq;//重装值
			}
		}
	}
}
/********************************
函数名称:timer1Init
函数功能:定时器1初始化
输入参数:
输入:
输出参数:
输出:
********************************/
void timer1Init()//系统毫秒定时使用
{
    TMOD |= 0x10;//定时器运行模式
    TH1 = T1VH;//初值(高8位)
    TL1 = T1VL; //初值(低8位)
    ET1 = 1;//开启定时器1中断
	EA = 1;//开启总中断
    TR1 = 1;//开启定时器1
}
/********************************
函数名称:timer1Interrupt
函数功能:定时器1初始化
输入参数:
输入:
输出参数:
输出:
********************************/
void timer1Interrupt() interrupt 3
{
	TH1 = T1VH;//初值(高8位)
    TL1 = T1VL; //初值(低8位)
	TaskTimeMs();
}

完成上述的步骤,基本可以实现一个简单的非抢占式实时多任务操作系统,严格意义并非真正的操作系统。

这是一个完整的系统内核:

 以后有机会的话,再为大家讲述其他部分,再见。


网站公告

今日签到

点亮在社区的每一天
去签到