以本人开发的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();
}
完成上述的步骤,基本可以实现一个简单的非抢占式实时多任务操作系统,严格意义并非真正的操作系统。
这是一个完整的系统内核:
以后有机会的话,再为大家讲述其他部分,再见。