smset 发表于 2012-11-26 11:41:35

再出个调度器,极小资源单片机值得一用

自认为有如下特点:

1)超级可以移植性,与CPU无关,几乎任何支持C语言编程的CPU都可以用!(本文仅仅以51单片机为例而已,但实际上可以任意移植)
2)小之又小, 原理很简单,一看就懂。
3)省之又省, 可以说对RAM和ROM省到极致。
4)取protothread之精华,将定时器与状态机和伪线程语法融合到一个框架,任务函数可以有两种写法。
5)基于定时器触发,调度效率高,最大化减少无效的代码运行时间。
***********************************************************/
#include <stc89c51.h>
#include <stdio.h>

/*****************小小调度器部分开始********************************************/
#define_SS   static char lc=0; switch(lc){   case 0: lc=0;
#define_EE   }; lc=0;
#defineWaitX(a,b)settimer(&lc,__LINE__,a,b); return ; case __LINE__:
struct TASK {
char td;
void (*fp)();
};
#define MAXTASKS 5
struct TASK tasks;

//设置定时器
void settimer(char *lc,charline,chartmrid,int d){
*lc=line;
tasks.td=d;
}
//逻辑定时器处理,在定时器中断里调用
void dectimers() {
unsigned char i;   
for (i=0;i<MAXTASKS;i++){
   if (tasks.td>0)tasks.td--;
}
}
//任务调度函数,在main里面运行
void runtasks() {
   unsigned char i;   
   for(i=0;i<MAXTASKS;i++)      
   {   
   if (tasks.fp!=0){   
         if (tasks.td==0){
             tasks.td=-1;
             tasks.fp();
                }
         }         
      }
}
/****************小小调度器部分结束*******************************************************/


sbit KEY = P3^2;
unsigned char code numtab={0x24,0x6F,0xE0,0x62,0x2B,0x32,0x30,0x67,0x20,0x22,0x21,0x38,0xB4,0x68,0xB0,0xB1};


sfr IAP_CONTR = 0xC7;
sfr WDT_CONTR = 0xC1;

//清除看门狗
void clr_wdt()
{
WDT_CONTR =0x3C;
}

//初始化定时器
void InitT0()
{
      TMOD = 0x21;
      IE |= 0x82;// 12t
      TL0=0Xff;
      TH0=0Xb7;
      TR0 = 1;
}
//定时器中断
void INTT0(void) interrupt 1 using 1
{
      TL0=0Xff;    //10ms 重装
      TH0=0Xb7;
      dectimers();
}

sbit LED1= P2^4;

//任务一,状态机写法
void ontimer0(){
LED1=!LED1;// LED1引脚接在发光管负极,LED1=0 为亮,LED1=1为灭。

//重装定时器
if (LED1) tasks.td=45;//450mS 灭
else tasks.td=5;//50ms亮
}

//任务二,状态机写法
char keycount=0;
void task1(){
if(KEY==0) {
   keycount++;
   if (keycount>20) IAP_CONTR = 0x60;//持续按下键1秒,将重启并进入固件升级
}
else{
    keycount=0;
}
//重装定时器
tasks.td=5;
}


//任务三,伪线程写法
voidtask2()
{
static char i;
_SS

while(1){

for(i=0;i<=9;i++){   //从0--9快速显示,间隔200mS
   WaitX(2,20);         //    等待200mS,实际是设置定时器2为200mS
   P1=numtab;
}
for(i=0;i<=9;i++){ //从0--9慢速显示,间隔500mS
   WaitX(2,50);       //    等待500mS,实际是设置定时器2为500mS
   P1=numtab;
}
}

_EE
}



void main()
{
      unsigned char         KeyNum;
      P3M0 = 0x00;
      P3M1 =0x00;
      //WDT_CONTR= 0x00;   //关闭看门狗
      P1 = 0xff;         //关显示

          clr_wdt();

      InitT0();

      KEY =1;                              //按键IO口
      KeyNum=0;                        //按下次数

    //装载任务:
      tasks.fp=ontimer0;
      tasks.fp=task1;
      tasks.fp=task2;

    //循环调度
      while(1){
         runtasks();
         clr_wdt();
      }
}

smset 发表于 2012-12-18 18:29:53

本帖最后由 smset 于 2012-12-19 13:00 编辑

优化无止境!呵呵,330楼看似不能再优化了,但我再尝试做一次优化:

敬请评测该版本,看是否还能优化:
/****小小调度器开始**********************************************/
#define MAXTASKS 2
static unsigned char timers;
unsigned char currdt;
#define _SS static unsigned char _lc; switch(_lc){default:
#define _EE ;}; _lc=0; return 255;
#define WaitX(tickets)do {_lc=__LINE__+((__LINE__%256)==0); return tickets ;} while(0); case __LINE__+((__LINE__%256)==0):
#define RunTask(TaskName,TaskID) do { if (timers==0) timers=TaskName(); }while(0);

#define CallSub(SubTaskName) do { _lc=__LINE__+((__LINE__%256)==0); return 0; case __LINE__+((__LINE__%256)==0):currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
#define UpdateTimers() unsigned char i; for(i=MAXTASKS;i>0 ;i--){if((timers!=0)&&(timers!=255)) timers--;}

#define SEM unsigned int
//初始化信号量
#define InitSem(sem) sem=0;
//等待信号量
#define WaitSem(sem) do{ sem=1; WaitX(0); if (sem>0) return 1;} while(0);
//等待信号量或定时器溢出, 定时器tickets 最大为0xFFFE
#define WaitSemX(sem,tickets)do { sem=tickets+1; WaitX(0); if(sem>1){ sem--;return 1;} } while(0);
//发送信号量
#define SendSem(sem)do {sem=0;} while(0);

/*****小小调度器结束*******************************************************/

sbit LED1 = P2^1;
sbit LED2 = P2^2;

void InitT0()
{
      TMOD = 0x21;
      IE |= 0x82;// 12t
      TL0=0Xff;
      TH0=0XDB;//22M---b7;
      TR0 = 1;
}

void INTT0(void) interrupt 1 using 1
{
    UpdateTimers();

    TL0=0Xff;    //10ms 重装
    TH0=0XDB;//b7;   
}


voidtask1(){
_SS
while(1){
   WaitX(50);
   LED1=!LED1;   
}
_EE
}

voidtask2(){
_SS
while(1){
   WaitX(100);
   LED2=!LED2;   
}
_EE
}


void main()
{
      InitT0();
      while(1){
         RunTask(task1,0);
         RunTask(task2,1);
    }
}

在keil下编译,又减少了18字节的ROM(超过10%了)。应该运行效率会更高。

------------------以下为说明-----------------------------------

小小调度器任务函数的写法主要注意的,主要有三点:

1) 任务函数内部变量,建议都用静态局部变量来定义。
2) 任务函数内不能用switch语句。
3) 任务函数内,不能用return语句。 因为return已经被赋予任务延时的特定意义。(这是返回型任务函数版本的一个强制要求)

这三点,并不会明显造成写程序的不方便。
---------------------------
从裸奔到使用OS操作系统或调度系统的代价主要有:

硬件资源代价(对RAM和ROM的消耗),学习代价(学会其原理,并掌握其用法),移植代价(往不同cpu上移植的工作量),效率代价(使用调度系统后带来的额外cpu负担),商业代价(版权费用),稳定性代价(是否引入潜在不稳定因素,或者增大bug跟踪调试工作量)。

从这几方面来讲,应用小小调度器的代价,都是非常小的。
1) 硬件资源代价: 前面的优化版本已经说明问题。keil下,本例程ram消耗 : 22字节,rom消耗126字节.
2) 学习代价: 小小调度器总共只有十多行代码,如果我们做一个简单的解释说明,理解起来其实是很快的。我相信学习时间比其他调度系统要短。
3) 移植代价: 几乎没有什么移植工作量,对于各种cpu,几乎是通吃。
4) 效率代价: 我们一直在努力优化,相信调度效率已经不低了。比如任务切换时间,应该是可以做到uS级别,甚至亚uS级别。
5) 商业代价: 小小本调度器为免费使用,无需支付任何费用。
6) 稳定性代价:小小调度器本质上仅仅是几个宏而已,未涉及任何对内部寄存器或堆栈的操作,避免了引入不稳定风险因素,所有操作都在可预见,可把控的前提下进行。
--------------------------------------------------------------------------------

smset 发表于 2012-11-26 11:42:25

本帖最后由 smset 于 2012-11-26 11:44 编辑

自己写的,抛砖引玉,希望大家指正哈。本例用于说明调度器的核心思想,当然也可以用,但实际上还可以做得更完善。

tim 发表于 2012-11-26 11:44:29

先收藏再学习{:loveliness:}

linucos 发表于 2012-11-26 11:57:54

多谢lz分享!

tianxian 发表于 2012-11-26 12:16:37

收藏先,谢谢分享

zqy517 发表于 2012-11-26 12:28:07

多谢lz分享,学习下思想!

zhonggp 发表于 2012-11-26 13:05:31

裸机中的战斗机

smset 发表于 2012-11-26 13:15:42

本调度器的宗旨是:以最小的代价,实现基于自然语法的多任务并行处理机制。并具备代码的高度可以移植性。
每个任务占用3个字节RAM。任务数量没有限制。

其工作原理很简单,大家可在此基础上任意DIY自己的调度方法。

powermeter 发表于 2012-11-26 13:16:50

xiexie mark

soosqt 发表于 2012-11-26 13:22:05

把工程也上传上来观摩下

smset 发表于 2012-11-26 13:25:30

本帖最后由 smset 于 2012-11-26 13:30 编辑

soosqt 发表于 2012-11-26 13:22 static/image/common/back.gif
把工程也上传上来观摩下

整个工程只有一个文件 main.c,上面的就是main.c的完整代码。


呵呵,也许是调度器太小了,连单独的.h文件和c文件都没有做。

dory_m 发表于 2012-11-26 13:26:25

学习,谢谢!!!{:shocked:}

chhh 发表于 2012-11-26 13:27:38

记号,学习一下

whatcanitbe 发表于 2012-11-26 13:30:11

#defineWaitX(a,b)settimer(&lc,__LINE__,a,b); return ; case __LINE__:

请问这句话如何理解啊?
看不明白

zhonggp 发表于 2012-11-26 13:34:45

替代就明白了,经典啊!!!

xad74 发表于 2012-11-26 13:41:02

好了,等会看看

dreampet 发表于 2012-11-26 14:14:38

非常不错,WaitX的替换很巧妙

lee345 发表于 2012-11-26 14:32:36

学习参考下

huangdongle 发表于 2012-11-26 14:34:31

mark!!!

sdyzxue 发表于 2012-11-27 09:13:09

虽然不是很懂,也是要谢谢lz的无私分享。

duanll 发表于 2012-11-27 09:18:30

再改改,去掉函数指针,这样在51下能工作得更好。

yijiangshan 发表于 2012-11-27 09:20:58

什么牛人都有啊。{:shocked:}

wmm20031015 发表于 2012-11-27 09:46:09

不错{:lol:}

smset 发表于 2012-11-27 10:07:36

本帖最后由 smset 于 2012-11-27 10:41 编辑

duanll 发表于 2012-11-27 09:18 static/image/common/back.gif
再改改,去掉函数指针,这样在51下能工作得更好。

嗯,是的,可以自己改,无需函数指针也完全可以,这样每个任务只需1到2个字节。

我已经在此基础上,继续优化下一个版本:
已经实现的新增特点:
1) 整个调度器总共占用1个字节RAM。 (是的!只占1个字节的RAM! 实在想不出还有没有比这个更省资源的调度器),调度器本身占用的ROM在200字节以内。
2) 去掉函数指针,一个任务只占用1到2个字节,支持任务内延时函数。顶层任务数量最大为255个。
3) 支持子任务调用。 一个子任务只占用1个字节RAM,子任务内也支持延时函数,并支持子任务函数传递参数。支持多级嵌套子任务调用,子任务数量没有限制。
4) 任务函数语法更精炼,如WaitX(2,20)简化为WaitX(20),无需指定定时器编号。

我的目标是: 打造一个极小单片机均可用的,与CPU硬件型号无关的调度系统,让256字节RAM都显得宽敞!
调度器代码将全公开,全免费使用,让天下“贫民”的单片机都用起,而且用得好! 比裸奔都更省资源!让“贫民”单片机不再裸奔,也无需裸奔!
(该版本将在第100楼放出,希望大家捧场哈)

llysc 发表于 2012-11-27 10:36:13

收藏学习~~~~~~~

end2000 发表于 2012-11-27 10:42:13

学习参考

formatme 发表于 2012-11-27 10:59:31

试试看,不要汇编的移植起来容易多了

xiaoziwen 发表于 2012-11-27 11:05:03

基于时间片的 轮训调度器

kuki0702 发表于 2012-11-27 12:10:20

非常不错,学习学习!

elecfun 发表于 2012-11-27 12:14:46

真够小的,我看我以后我的程序都可以用上了   

顶到100楼哈

shotstar 发表于 2012-11-27 12:20:08

楼主的好样的,我帮顶一下。

ihuotui 发表于 2012-11-27 12:46:12

有点意思,时间分片任务调度。晚上回去试试。

guzhongqi 发表于 2012-11-27 12:50:52

跟我写的差不多,但有个缺陷,如果某一个任务超时或死循环了,其他任务就没机会运行了,这是跟实时内核最本质的区别。

oktek 发表于 2012-11-27 12:52:34

顶到100楼

up101 发表于 2012-11-27 12:54:27

捧场了,好牛逼的样子

soosqt 发表于 2012-11-27 12:56:22

smset 发表于 2012-11-27 10:07 static/image/common/back.gif
嗯,是的,可以自己改,无需函数指针也完全可以,这样每个任务只需1到2个字节。

我已经在此基础上,继续 ...

{:lol:} 顶100楼,最好把工程设成可以软件仿真的,上面那个工程软件仿真不成

ustbzm 发表于 2012-11-27 12:56:28

学习一下!

shdjdq 发表于 2012-11-27 13:09:14

系统调度任务时间太长,个头也太大。如果小而精,那太好了。

makesoft 发表于 2012-11-27 13:12:31

一般来说大一点的系统才需要OS,像我们一般是超过1W行源程序的才会考虑OS,小系统做不好的人即使使用OS一样做不好,所以这种小资源的OS基本也就是写个什么论文用用罢了

aworker 发表于 2012-11-27 13:15:43

不错的,支持一下!

smset 发表于 2012-11-27 13:27:45

soosqt 发表于 2012-11-27 12:56 static/image/common/back.gif
顶100楼,最好把工程设成可以软件仿真的,上面那个工程软件仿真不成 ...

{:lol:} 这个光荣的任务就交给你吧。最好是基于AVR防真,我对proteus又有段时间没弄,生疏了。

lihaolongli 发表于 2012-11-27 13:28:21

前边的宏定义看的不大明白{:mad:}

metalmadman 发表于 2012-11-27 13:30:59

      mark

zhwm3064 发表于 2012-11-27 13:32:16

看不懂啊,

hailing 发表于 2012-11-27 13:35:38

学习了。{:smile:}

eedesign 发表于 2012-11-27 13:58:10

高级呀,学习一下!

akwkevin2011 发表于 2012-11-27 14:22:02

ding ,qi dai 100

akwkevin2011 发表于 2012-11-27 15:23:47

再仔细看了看,其实就是所有任务共用同一个定时器的进行计数延时.前面有评论说一个任务死循环了,其它任务就没办法运行了.呵呵,希望楼主能做的更加完善.

smset 发表于 2012-11-27 15:35:55

akwkevin2011 发表于 2012-11-27 15:23 static/image/common/back.gif
再仔细看了看,其实就是所有任务共用同一个定时器的进行计数延时.前面有评论说一个任务死循环了,其它任务就 ...

{:smile:}写程序本来就是精细活,写成死循环本来就是程序员的问题啊,谁让你去写个死循环呢?

哪怕是抢占式多任务系统,也不允许乱写代码啊,即便是其他100个任务不会死,其中有一个任务会不时死掉,产品敢发布吗?

单片机程序又不是windows系统,死掉一个进程,把它杀掉就可以继续用啊。

Bicycle 发表于 2012-11-27 16:04:07

进来学习下

netawater 发表于 2012-11-27 16:29:03

mark,楼主用心了!

Excellence 发表于 2012-11-27 16:38:36

建议:最好有硬件方面的东西。
或者说加入外设控制,比如按键,小灯,数码管,液晶,PWM等等。
希望不仅仅是内核。

个人看法,不喜勿喷。

bosia仔 发表于 2012-11-27 16:42:56

100楼呢?
顶啊顶

liuchangyin 发表于 2012-11-27 16:44:51

{:smile:}   

athena_min 发表于 2012-11-27 16:53:40

顶,喜欢这些精妙的东西{:victory:}

cmdrainsy 发表于 2012-11-27 16:57:03

时间触发嵌入式系统设计模式.pdf

苦行僧 发表于 2012-11-27 17:28:12

这个要学习一下,每次都有收获,哈哈

shotstar 发表于 2012-11-27 17:58:50

下班回来还没顶到100楼啊! 加油

akwkevin2011 发表于 2012-11-27 18:11:35

本帖最后由 akwkevin2011 于 2012-11-27 18:12 编辑

smset 发表于 2012-11-27 15:35 static/image/common/back.gif
写程序本来就是精细活,写成死循环本来就是程序员的问题啊,谁让你去写个死循环呢?

哪怕是抢 ...

ucos里面是有这个处理的.不一定是死循环,可能是执行时间超长.系统会强行切换任务.抢占型优先级.

smset 发表于 2012-11-27 18:43:36

akwkevin2011 发表于 2012-11-27 18:11 static/image/common/back.gif
ucos里面是有这个处理的.不一定是死循环,可能是执行时间超长.系统会强行切换任务.抢占型优先级. ...

嗯,我可以设置个监控机制,如果某个任务占用时间过长,可以暂停掉这个任务一段时间。

而死循环调试程序时很容易发现,一旦某个任务死循环,那么所有任务都死掉,更容易暴露出程序代码的问题。

xy-mcu 发表于 2012-11-27 19:04:12

不错,向您学习了,正好在一个小产品上试试。

cnxh 发表于 2012-11-27 19:12:55

继续顶起了

superaspen 发表于 2012-11-27 19:24:59

才67楼,赶紧顶

leijiayou 发表于 2012-11-27 19:52:03

{:lol:}必须顶!!!!!!!!!!!!!!!!

tdh03z 发表于 2012-11-27 19:54:52

对于小的应用来说,有个小的调度器足够了,也不需要做的那么大。稳定第一啊,且程序易于阅读,几年后重新读此代码时不需花太多时间。

411412 发表于 2012-11-27 19:58:51

学习下,《时间触发嵌入式系统设计模式》中LCD1602驱动没看懂,希望楼主提供个LCD1602用调度器的例子.

whatcanitbe 发表于 2012-11-27 20:46:30

100楼应该不是个问题

zenghl 发表于 2012-11-27 21:57:03

这么强,应该顶到1W楼,不过还是希望楼主能够说明一下以下宏定义,为的就是能简单理解清楚。
#define_SS   static char lc=0; switch(lc){   case 0: lc=0;
#define_EE   }; lc=0;
#defineWaitX(a,b)settimer(&lc,__LINE__,a,b); return ; case __LINE__:

smset 发表于 2012-11-27 22:45:47

zenghl 发表于 2012-11-27 21:57 static/image/common/back.gif
这么强,应该顶到1W楼,不过还是希望楼主能够说明一下以下宏定义,为的就是能简单理解清楚。
#define_SS...

你可参考我以前的帖子:《protothread就是状态机》

第一个_SS是switch散转,第二个_EE只是一个括弧而已。WaitX就是记住当前运行行号,然后设置下次任务函数被调用的时刻。

sange 发表于 2012-11-27 22:52:32

学习了。谢谢楼主分享。。。

cooleaf 发表于 2012-11-28 00:06:29

我也支持一下楼主。

flor 发表于 2012-11-28 00:24:49

时间触发的调度器容易实现,但实现一个task比较麻烦,要搞一堆状态机,真心不好用。

fuliaokai 发表于 2012-11-28 00:43:27

我来顶一顶!

meirenai 发表于 2012-11-28 00:44:48

楼主还是不明白这个宏什么意思:

#defineWaitX(a,b)settimer(&lc,__LINE__,a,b); return ; case __LINE__:


holts2 发表于 2012-11-28 08:04:56

这个可以有

hbzxx 发表于 2012-11-28 08:33:58

好东西,向LZ学习了。{:smile:}

liumaojun_cn 发表于 2012-11-28 08:42:12

支持,快速顶到100楼。

pursuits 发表于 2012-11-28 08:47:17

学习了,顶到100楼

qd118118 发表于 2012-11-28 08:54:15

这个要顶

ihuotui 发表于 2012-11-28 09:16:14

用处不大,只是状态机,而且加了定时也没有用。

gzfslt 发表于 2012-11-28 09:21:36

86楼,我也顶顶

Name_006 发表于 2012-11-28 09:44:26

必须要 学习一下                                                                     

shotstar 发表于 2012-11-28 11:37:17

100还没到啊~~ 如果楼主是女的估计1000楼也到了!

holts2 发表于 2012-11-28 11:52:38

这个很有用,最好LZ能出个详细说明, 普及下

holts2 发表于 2012-11-28 11:59:34

这个很有用,最好LZ能出个详细说明, 普及下

holts2 发表于 2012-11-28 12:01:47

不管男女, 只要能写出好结构, 我都顶,只是功力不够, 看不懂几个宏, 能否标注上行号模拟运行展开说明

holts2 发表于 2012-11-28 12:06:37

我就是看了楼主介绍的protothread,然后用在我的一个小项目中, 确实很爽,只是在我的项目中,任务内延时没有找到好的封装方式.

liumaojun_cn 发表于 2012-11-28 12:34:58

还没有到100楼呢。

maxims 发表于 2012-11-28 12:55:18

我也来添砖加瓦~

希望楼主能用bascom写一个avr的列子,或者谁给移植到bascom。。。。

谈起学C,我就头疼。。。一堆的乱七八糟符号也是一段代码。。。不像bascom那么简洁明了

zhonggp 发表于 2012-11-28 12:57:00

重点是应用的时候怎么添加到实际中,很多写代码的时候都处理不好delay

holts2 发表于 2012-11-28 13:01:02

重点是看懂程序, 运用自己发挥了

holts2 发表于 2012-11-28 13:01:56

delay 处理好了, 多任务效率就上去了

holts2 发表于 2012-11-28 13:05:50

LZ100楼到了, 等你的程序

holts2 发表于 2012-11-28 13:15:17

有时候顶贴也是一种美得

r166 发表于 2012-11-28 13:15:19

本帖最后由 r166 于 2012-11-28 13:16 编辑

随然不懂,还是要顶

增加100楼标志

holts2 发表于 2012-11-28 13:15:35

一鼓作气, 拿下100楼

smset 发表于 2012-11-28 13:55:28

谢谢大家的捧场,中午出门办事,回来就发贴。

holts2 发表于 2012-11-28 14:12:25

你终于出现了, 最好是现场解说下

smset 发表于 2012-11-28 15:32:48

#include <STC89C51.h>

/**************小小调度器开始 *********************************/
#define MAXTASKS 3          //顶层任务数量,可按需增加,最大为255个。
unsigned chartimers;
char currid; //当前运行的任务号
#define _SS   static char lc=0; switch(lc){ case 0:        //跳转开始
#define _EE   }; lc=0;//跳转结束
#define DelayX(b)settimer(&lc,__LINE__,b); return ; case __LINE__: //任务内延时等待“函数”
#define RunTask(a,b) currid=b; if (timers==0){timers[ b]=-1; a();} //运行顶层任务
#define CallSub(x)DelayX(0);x(); if (timers!=-1) return;       //调用子任务
void settimer(char *lc,charline,unsigned char d){//设置定时器
*lc=line;timers=d;
}
/****************小小调度器结束*** (我很短,但用起来很爽)******************************/

sbit KEY = P3^2;
unsigned char code numtab={0x24,0x6F,0xE0,0x62,0x2B,0x32,0x30,0x67,0x20,0x22,0x21,0x38,0xB4,0x68,0xB0,0xB1};

sfr IAP_CONTR = 0xC7;
sfr WDT_CONTR = 0xC1;
//清除看门狗
void clr_wdt()
{
WDT_CONTR =0x3C;
}
//初始化定时器,产生10ms中断
void InitT0()
{
        TMOD = 0x21;
        IE |= 0x82;// 12t
        TL0=0Xff;
        TH0=0Xb7;
        TR0 = 1;
}
//定时器中断处理函数
void INTT0(void) interrupt 1 using 1
{
    unsigned char i;
        TL0=0Xff;    //10ms 重装
        TH0=0Xb7;
        //逻辑定时器处理
    for (i=0;i<MAXTASKS;i++){
   if (timers>0) {
           timers--;
       }
    }       
}

sbit LED1= P2^4;

//任务一,状态机写法
void ontimer0(){
LED1=!LED1;        // LED1引脚接在发光管负极,LED1=0 为亮,LED1=1为灭
if (LED1) timers=45;        //450mS 灭
else timers=5;        //50ms亮
}

//任务二,状态机写法
char keycount=0;
void task1(){
if(KEY==0) {
   keycount++;
   if (keycount>20) IAP_CONTR = 0x60;
}
else{
    keycount=0;
}
timers=5;       //重装定时器
}

//这是一个子任务函数
voidsubtask()
{
static char i;
_SS
for(i=0;i<=5;i++){
DelayX(20);
P1=numtab;
}
_EE
}

//任务三,“线程”写法,复杂任务请用这种方式来写,比传统状态机写法更简单,更自然,而且更省ROM!
voidtask2()
{
static char i;       //变量请申明为静态的。
_SS

while(1){
for(i=0;i<=9;i++){          //先从0--9快速显示,间隔200mS
DelayX(20);
P1=numtab;
}
for(i=0;i<=9;i++){//然后从0--9慢速显示,间隔500mS
DelayX(50);
P1=numtab;
CallSub(subtask); //顶层任务的任何地方都可以调用子任务
}
//CallSub(subtask); //顶层任务的任何地方都可以调用子任务
}

_EE
}

void main()
{
        P3M0 = 0x00;
        P3M1 =0x00;
        P1 = 0xff;         //关LED数码管显示

        InitT0();          //初始化定时器,以便产生10ms中断
        KEY =1;                  //按键IO口       
   
        while(1){
           clr_wdt(); //清除看门狗

           //这里,只需运行顶层任务即可
           RunTask(ontimer0,0);        //ontimer0任务放在第0个任务槽
           RunTask(task1,1);        //task1任务放在第1个任务槽
           RunTask(task2,2);    //task2任务放在第2个任务槽
           //...还可以增加更多任务
        }
}

holts2 发表于 2012-11-28 15:37:53

本帖最后由 holts2 于 2012-11-28 15:39 编辑

抓个板登听课,是不是每个任务只能有一个计时器 ?
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 再出个调度器,极小资源单片机值得一用