C51编程:多任务程序设计的结构,纯属个人观点,希望大家借签一下,提出更好的意见。 [小师⊕] [156次] 01-7-31 下午
08:48:27
C51的一些特征技巧可供利用:
1.时间的模糊性.
在大多数情况下,时间是具有模糊性的.象秒,分钟,小时..,从长的时间角度,即使你计秒的时
间被退后0.5秒,
在大多数情况下都是允许的,包括一些显示.还有象扫描键盘,你可在20MS去抖,也可在30,30MS时
间去抖,这个时间范围是有一定弹性的.又如闪烁要求400MS,你可在410MS去刷新,下次在
2*400MS,只要保证长的周期定时是准确的,个别时间是可推迟的。这样的情形会在许多地方发
生,这就给设计多任务程序提供了一个基础.
2.消息的周期循环性.
消息指系统函数(定时类的),模块之间有状态变化,模块内部有状态请求而相应产生的标志数
据或变量数据,它的特点是它的遍历整个模块,直到有模块接收它后让它消失,没有模块接收时,循
环一周被自身消失.
举个例,有T0计数器0.1MS产生一个中断,让其他所有模块都知道,模块不能消灭它,它只能被自
己消灭:
void timer0(void) interrupt 1 /*T0中断*/
{
fSYS_100us=1;
}
bit fSYS_TimeNow;
#define Timer0_MainLoop() {fSYS_TimeNow=0;if(fSYS_100us)
{fSYS_TimeNow=1;fSYS_100us=0;}}
unsigned char uCount;
main()
{
init();
uCount="100";
while(1){
Time0_MainLoop();
Task0();
if(fSYS_TimeNow)Task1();
Task2();
if(fSYS_TimeNow){
uCount--;
if(uCount==0){
uCount=100;
Task3();
}
}
}
}
这样消息具有自我生成消失发布的能力,而且使模块具有独立性(Time0_MainLoop();可放在
WHILE中的任何地方而不影响它的作用).
而象键盘之类产生的消息,常常是每个模块接收到它后,就使它消失,避免其他模块也接收.
消息在多任务程序中的作用:相当与桥梁,使模块间既相互独立又相互连接。比如说,有个按键
产生的消息,打开设置画面显示:nSetScreenOn,让其它的模块中相应程序运行,这是连接。如
果你的程序没写到设置画面显示部分,完全不影响整个程序,照常运行正确,这是独立性。
消息通常用队列存储,一如按键缓冲队列,一般包括函数:NewsPush(unsigned char nData)压
入消息,unsigned char NewsPop()弹出消息,NewsRead()宏定义的读队列中最前端的消息,
fNewsEnable表示有消息需要各个模块接收,gNewsNum消息个数。一个模块接收后执行NewsPop
();fNewsEnable=0消灭消息。
现在举个例,P1.0键盘扫描模块作为多任务模块,并产生消息nKeyPush按键按下,nKeyPop松
开。nKeyPush时P1.1=1,nKeyPop时P1.1=0.
/**********************************/
void timer0(void) interrupt 1 /*T0中断*/{
fSYS_1ms=1;
}
bit fSYS_TimeNow; /*1MS时间到消息*/
#define Timer0_MainLoop() {fSYS_TimeNow=0;if(fSYS_1ms)
{fSYS_TimeNow=1;fSYS_1ms=0;}}
main(){
init();
while(1){
Timer0_MainLoop(); /*系统时间循环*/
/*----------------------------------*/
if(fSYS_TimeNow) /*如果有1MS到的消息产生*/
Key_MainLoop(); /*按键检查循环*/
}
/*----------------------------------*/
Work_MainLoop(); /*按键的任务循环*/
/*----------------------------------*/
/*消息处理中心,可写成宏较直观*/
fNewsEnable=0;
if(gNewsNum)fNewsEnable=1; /*有消息,通知*/
}
}
/*按键检查循环模块*/
unsigned char mKeyTask; /*多任务中模块的任务号,代表模块的执行点*/
sbit fKeyIn="P1"^0;
unsigned char mKeyTime;
void Key_MainLoop(){
switch(mKeyTask){
case 0: /*现在是常规状态*/
if(fKeyIn==0){
mKeyTime=30; /*大概30MS的去抖时间*/
mKeyTask++;
}
break;
case 1: /*按下去抖29-30MS*/
mKeyTime--;
if(mKeyTime==0)mKeyTask++;
break;
case 2: /*判断按键是否保持按下*/
if(fKeyIn==0){
NewsPush(nKeyPush); /*压入按键按下消息*/
mKeyTask++;
}
else mKeyTask="0";
break;
case 3: /*判断按键是否松开*/
if(fKeyIn){
mKeyTime=30; /*大概30MS的去抖时间*/
mKeyTask++;
}
break;
case 4: /*松开去抖29-30MS*/
mKeyTime--;
if(mKeyTime==0)mKeyTask++;
break;
case 5: /*判断按键是否保持按下*/
if(fKeyIn){
NewsPush(nKeyPop); /*压入按键按下消息*/
mKeyTask=0;
}
else mKeyTask="3"; /*继续检查*/
break;
}
}
/*按键的任务循环的模块*/
sbit oKeyTest="P1"^1;
void Work_MainLoop(){
if(fNewsEnable){ /*如果有消息*/
if(NewsRead()==nKeyPush){
oKeyTest=1;
NewsPop(); /*弹掉消息*/
fNewsEnable=0; /*清除消息广播标志,禁止其它模块使用,可提高效率*/
}
}
if(fNewsEnable){ /*如果有消息*/
if(NewsRead()==nKeyPop){
oKeyTest=0;
NewsPop(); /*弹掉消息*/
fNewsEnable=0; /*清除消息广播标志,禁止其它模块使用,可提高效率*/
}
}
}
/*********************************************************/
消息在多任务程序中占很重的分量,好处多多:可轻而易举锝实现演示功能(认为压入各种消
息),可使模块结构清晰明了,编写程序及调试极为方便(不用等其它功能模块).....
现在再说明一下多任务程序结构组成:
大致分为驱动函数,系统函数,功能模块和实时模块.
<一>系统函数指定时器的定时部分.他产生其它模块需要的定时消息,做为定时器的复用.
<二>驱动函数指多各功能模块中共同使用的硬件设备的函数,因为牵涉到公用硬件,被多各模块或
中断使用,故必须将之写为专用函数.如LCD显示中的画点函数(画线或其它可列为也可不列为专用
函数.我还没想清楚),读AD,送DA,读串行存储IC等等,但也有方法根据情况将这些也写为专用的多
任务结构,这些先不讨论.
<三>功能模块在程序中可独立运行,表现在他的结构上:
1.有提供给主循环的入口,也可带有一定入口条件的宏和函数.如上面的XXX_MainLoop().这各入
口可放在主循环的任何地方,和模块的循序无关.
2.具有任务号寄存器(或标志),使内部模块是流水结构(不做停留),每次进入模块只执行极短的部
分.可以说,模块内部不存在延时,至少不存在几十us级的延时.象上面的Key_MainLoop()就是一个
典型.
3.模块内部也可存在多任务模块,是可嵌套的.
(还有一些东东现在说不上来....)
<四>实时模块是的是执行实时任务功能的模块,他主要将实时任务转为非实时的消息和数据.比如
串口通信,中断中接收到数据,可只处理必须的数据,再将数据压入串口接收队列后发出消息,让通
信模块处理相互的协议.要知道每个模块在飞快的以极短的处理在运行,一般一个循环在几us-
>100或200us左右循环,用时间模糊概念可处理大多数的问题.如果是处理时间不够块,可将串口中
断也按多任务方式处理协议.总之方法还是很多的,有脉络可寻.又比如A/D,D/A之类也是如此.
此资料来源于C51BBS潜入式开发论坛,感觉不错给大家看看!~不做商业用途!~呵呵!~
文章评论(0条评论)
登录后参与讨论