一种裸奔多任务嵌入式操作系统模型Word文档格式.docx
《一种裸奔多任务嵌入式操作系统模型Word文档格式.docx》由会员分享,可在线阅读,更多相关《一种裸奔多任务嵌入式操作系统模型Word文档格式.docx(20页珍藏版)》请在冰点文库上搜索。
![一种裸奔多任务嵌入式操作系统模型Word文档格式.docx](https://file1.bingdoc.com/fileroot1/2023-4/30/24afb16c-85dc-489d-baa7-5223c1740512/24afb16c-85dc-489d-baa7-5223c17405121.gif)
例如后面讲的如何在裸奔环境实现taskDelay()。
消息队列既适用于OS环境,也适用于裸奔环境。
在OS环境下,消息队列机制由OS提供。
在裸奔环境下,消息队列要自己来实现。
如果对队列的概念不清楚,可参考《数据结构》教材。
这个队列机制,可做成通用模块,在不同的程序中复用。
消息队列用于缓冲事件。
事件不知道什么时候会到来,也不能保证来了就能迅速得到处理。
使用消息队列,可以保证每个事件都被处理到,以及处理顺序。
一般在两种情况下会用到消息队列:
存储外部事件:
外部事件由中断收集,然后存储到队列。
串口接收程序中的接收循环缓冲区,可理解为消息队列。
任务间通讯:
一个任务给其它任务发送消息。
timerTick,就是系统中的时钟基准。
OS中总是有一个这样的基准。
在裸奔时,我们要用一个定时器(或RTC或watchdog)来建立这个时间基准。
一个tick间隔可以设置为10ms(典型RTOS的缺省设置)。
让定时器10ms中断一次,中断发生时给tickNum++。
以前,我在定时器中断中设置1S标志、200ms标志等等。
时间相关的任务根据这些标志判断是否要执行。
近来,一般让任务直接去察看tickNum。
两次相减来判断定时是否到达。
也可以在系统中建立一个通用定时器任务,管理与不同任务相关的多个定时器;
在定时到达时,由定时器任务去调用相应的callback。
系统时钟基准是所谓“零耗时裸奔”的基础。
timerTick的分辨率,决定了只适于于较大的时间延时。
在做时序时的小延时,用传统方法好了。
OS中的taskDelay()在裸奔环境下的一种实现:
OS环境:
void
xxxTask(void)
//waitEvent
//do
step_1
taskDelay(TIME_OUT_TICK_NUM);
step_2
}
裸奔环境:
static
unsigned
int
taskStat
=
STAT_GENERAL;
//任务状态变量
timer_t
startTick;
currTick;
if
(taskStat
==
STAT_GENERAL)
//check
event
//if
no
return;
startTick
sysGetTick();
//sysGetTick()就是察看系统时间
STAT_WAIT;
else
STAT_WAIT)
currTick
((currTick
-
startTick)
>
TIME_OUT_TICK_NUM)
step_2
else
老生常谈---一种裸奔多任务模型ourdev_629752P0O6JH.txt(文件大小:
4K)
(原文件名:
老生常谈---一种裸奔多任务模型.txt)
C51多任务编程思想ourdev_629753EWA0LM.pdf(文件大小:
143K)
C51多任务编程思想.pdf)
基于51单片机的C语言多任务操作
完美版ourdev_629754PETS4B.rar(文件大小:
3K)
完美版.rar)
Easy51RTOS的原理
//Easy51RTOS操作系统头文件
#include
"
os_cfg.h"
functns.h"
//常用一些功能函数
char
TempBuffer[6];
//显示温度字符串
str2[12]={'
'
'
0,0,0,0,0,0,0xdf,0x43,0};
//任务0:
测温度送显
task0(void)
{
temp=ReadTemperature();
IntToStr(temp,TempBuffer);
str2[3]=TempBuffer[0];
str2[4]=TempBuffer[1];
str2[5]=TempBuffer[2];
str2[6]=TempBuffer[3];
str2[7]=TempBuffer[4];
str2[8]=TempBuffer[5];
GotoXY(0,1);
Print(str2);
delay_nms(300);
//任务1:
键盘扫描,LCD显示
task1(void)
if(CHANGE==0)
//判断change温度键是否按下
set_temp=key_set();
//设定需要更改的温度值
if(set_temp<
temp)
fengshan();
//设定的温度<
实际温度,则打开电机风扇
if(set_temp>
dianlu();
//若大于,则打开电炉(这里用LED模拟一下)
//任务2
task2()
//任务3
task3()
//任务4
task4(void)
//任务5
task5(void)
//任务6
task6()
//任务7
task7()
//main主函数
main(void)
OS_InitTimer0();
EA=1;
LCD_Init();
LCD_w_data(1,1,Temp_Str);
LCD_w_data(2,1,Key_Str);
while
(1)
(OS_Delay[0]==0){task0();
OS_Delay[0]=100;
}
//温度测量,每秒1次
(OS_Delay[1]==0){task1();
OS_Delay[1]=10;
//键盘扫描,键值存储
(OS_Delay[2]==0){task2();
OS_Delay[2]=100;
//读出存储的键值,LCD显示
(OS_Delay[3]==0){task3();
OS_Delay[3]=50;
(OS_Delay[4]==0){task4();
OS_Delay[4]=100;
(OS_Delay[5]==0){task5();
OS_Delay[5]=60;
(OS_Delay[6]==0){task6();
OS_Delay[6]=70;
(OS_Delay[7]==0){task7();
OS_Delay[7]=80;
Delay(50);
//Taskturn;
//定时中断服务
OS_Timer0(void)
interrupt
1
using
2
uchar
i;
//CRY_OSC,TIME_PER_SEC在easycfg.h中配置
TH0
255-CRY_OSC/TIME_PER_SEC/12/256;
TL0
255-CRY_OSC/TIME_PER_SEC/12%256;
//每节拍对任务延时变量减1
,减至
0
后,任务就绪。
for(i=0;
i<
MAX_TASK;
i++)
if(OS_Delay[i]!
=0)
OS_Delay[i]--;
//Runing(On);
//和传统的前后感觉基本上是一样的…
//唯一的优点呢,是感觉OS_Delay[n]数组起到了分配各
Easy51RTOS的原理ourdev_629755MEIQGP.txt(文件大小:
Easy51RTOS的原理.txt)
完美版
/*
1.本程序不使用任何汇编指令
2.由定时器T0产生中断,切换进程
3.由于中断或调用子程序,要把PC堆栈,故可以以SP为基址的地方找到PC
4.中断或子程序返回,要把SP出栈给PC,故可以操作SP改变程序入口
5.本程序经调试运行
电路图已上传
6.程序编译是会有一个警告提示,为正常现象,因为保存R0-R7时,重新定义地址,
出现地址覆盖的警告提示。
7.用户以此模板写程序只需写用户的进程子程序和用户初始化子程序,并把各进程参数
放在规定地方,各程序放在规定地方就可以;
所有的任务调度已处理好。
*/
//头文件
#include<
reg52.h>
//#include<
absacc.h>
intrins.h>
//宏定义
#defineuchar
char
#define
uint
int
TN
65436
//进程1,2,3执行时间之比为
T1:
T2:
T3
(时间单位us)
TN1
55536
//1个进程循环周期内进程1执行的时间T1us
TN1=(65536-T1)
TN2
//1个进程循环周期内进程2执行的时间T2us
TN2=(65536-T1)
TN3
//1个进程循环周期内进程3执行的时间T3us
TN3=(65536-T1)
//
N1
4
//
进程1的延时参数
N2
进程2的延时参数
N3
进程3的延时参数
idata
temp[8]
_at_
0x00;
//R0--R7
tempbf1[8];
//用于保存R0--R7
进程1
tempbf2[8];
进程2
tempbf3[8];
进程3
//定义全局变量
address1,address2,address3;
test1_1=0,test2_1=0,test3_1=0,PID=1;
//各进程的标志位,是否为第一次执行,0第一次,非0非第一次;
PID进程号;
ac1,ac2,ac3;
//,
PC_Next;
各进程的初始地址寄存器.
//test1
的参数
由于进程切换时
没有保存普通变量,
//所以各进程的普通参数需要定义成全局变量.
m1,i1,j1,k1;
table1[4];
//在此加入
用户进程1参数
//test2
m2,i2,j2,k2;
table2[4];
用户进程2参数
//test3
的参数
m3,i3,j3,k3;
table3[4];
//声明
//unsigned
Get_Next_PC(void);
//调用子程序,获取PC
chushihua(void);
//初始化函数
yonghuchushihua(void);
//用户初始化函数
test1(void);
//进程一
test2(void);
test3(void);
//main函数
PC_Next=Get_Next_PC();
chushihua();
ac1=(unsigned
int)(test1);
//获取进程1的入口地址
ac2=(unsigned
int)(test2);
//获取进程2的入口地址
ac3=(unsigned
int)(test3);
//获取进程3的入口地址
yonghuchushihua();
TR0=1;
while
(1);
//初始化时钟
chushihua(void)
TMOD=0x01;
//
ET0=1;
TH0=TN/256;
TL0=TN%256;
//中断处理,进程调度
time0()
1
ib;
TR0=0;
//进程顺序分配
PID++;
if(PID==4)
{PID=1;
//进程调度
switch(PID)
case
1:
if(test3_1!
=0)//第一次否?
//保存现场,还回地址
address3=*((unsigned
*)(SP-4));
//PC的高字节
address3
<
8;
address3+=*((unsigned
*)(SP-5));
//PC的低字节
table3[0]=*((unsigned
*)(SP));
//现场保护
table3[1]=*((unsigned
*)(SP-1));
table3[2]=*((unsigned
*)(SP-2));
table3[3]=*((unsigned
*)(SP-3));
for(ib=0;
ib<
=7;
ib++)
//保护R0--R7
tempbf3[ib]=temp[ib];
if(test1_1==0)
//第一次执行
//执行新进程,恢复现场
test1_1=1;
*((unsigned
*)(SP-4))=ac1>
//PC的高字节
*)(SP-5))=ac1
&
0x00ff;
else//非第一次执行
*)(SP-4))=address1>
*)(SP-5))=address1
*)(SP))=table1[0];
//现场恢复
*)(SP-1))=table1[1];
*)(SP-2))=table1[2];
*)(SP-3))=table1[3];
//恢复R0--R7
temp[ib]=tempbf1[ib];
TH0=TN1/256;
TL0=TN1%256;
}break;
2:
if(test1_1!
=0)
//第一次否?
//保存现场,还回地址,否
address1=*((unsigned
address1
address1+=*((unsigned
table1[0]=*((unsigned
table1[1]=*((unsigned
//现场保护
table1[2]=*((unsigned
table1[3]=*((unsigned
tempbf1[ib]=temp[ib];
if(test2_1==0)//第一次
//执行进程2,恢复现场
test2_1=1;
*)(SP-4))=ac2>
*)(SP-5))=ac2
else
//非第一次
*)(SP-4))=address2>
*)(SP-5))=address2
*)(SP))=table2[0];
*)(SP-1))=table2[1];
*)(SP-2))=table2[2];
*)(SP-3))=table2[3];
ib++)//恢复R0--R7
temp[ib]=tempbf2[ib];
TH0=TN2/256;
TL0=TN2%256;
3:
if(test2_1!
=0)
address2=*((unsigned
address2
address2+=*((unsigned
table2[0]=*(