smset 发表于 2012-11-26 11:36:21

适用于小资源的小小调度器程序(原创)

发布这个小小调度器主要起个抛砖引玉的作用。

自认为有如下特点:

1)超级可以移植性,与CPU无关,几乎任何支持C语言编程的CPU都可以用!
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();
        }
}

and001 发表于 2012-11-26 12:03:09

真舍得拿出来呀!楼主牛

and001 发表于 2012-11-26 12:03:28

好人!!!士大夫十分

smset 发表于 2012-11-26 13:45:47

话不多说,上工程!请验证哈。

tim 发表于 2012-11-26 13:48:07

{:sweat:}楼主一帖两发是违规的哦

smset 发表于 2012-11-26 13:50:21

哦,抱歉,我以为两个单片机都需要呢,呵呵。不好意思啦。请版主高抬贵手。

duanll 发表于 2012-11-26 23:36:27


  想法不错,可是不太适合在51下用,ARM平台应该不错。

liber798A 发表于 2012-11-27 10:05:05

MARK一下,谢谢啦!~

whatcanitbe 发表于 2012-11-27 10:44:07

替换之后还是不怎么明白,请明白的帮忙解释解释。

//任务三,伪线程写法
voidtask2()
{
static char i;
static char lc=0;
switch(lc){   
    case 0: lc=0;

while(1){

for(i=0;i<=9;i++){ //从0--9快速显示,间隔200mS
//WaitX(2,20);
settimer(&lc,__LINE__,2,20); return ; case __LINE__:
P1=numtab;
}
for(i=0;i<=9;i++){ //从0--9慢速显示,间隔500mS
//WaitX(2,50);
settimer(&lc,__LINE__,2,50); return ; case __LINE__:
P1=numtab;
}
}

};
lc=0;
}

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

这个就是一个状态机,只是以行号__LINE__作为状态变量而已。
在切换时,先保存当前行号__LINE__作为状态变量,并设置下次调用本状态机函数的时刻。
下次调用本函数时,直接跳至改行号__LINE__处,再往下运行。

aworker 发表于 2012-11-27 11:19:02

没太看懂,这个和protothread有关系么?

smset 发表于 2012-11-27 13:21:51

aworker 发表于 2012-11-27 11:19 static/image/common/back.gif
没太看懂,这个和protothread有关系么?

取其精髓而已,看来你还没有真正理解protothread

aworker 发表于 2012-11-27 13:23:31

本帖最后由 aworker 于 2012-11-27 13:41 编辑

smset 发表于 2012-11-27 13:21 static/image/common/back.gif
取其精髓而已,看来你还没有真正理解protothread

__LINE__+switch,似乎就是protothread的骨髓。你写的似乎不够“简明”,还不如直接用protothread好些。

smset 发表于 2012-11-27 13:58:09

{:lol:}你可以再对比下,看看那种用法更简明,不过我一直很崇拜protothread的作者,是他创造了这种写法,我只是应用其思想而已。

电子小生 发表于 2012-11-27 14:06:32

mark!学习了!

aworker 发表于 2012-11-27 14:10:41


PT_INIT(pt)   初始化任务变量,只在初始化函数中执行一次就行
PT_BEGIN(pt)   启动任务处理,放在函数开始处
PT_END(pt)   结束任务,放在函数的最后
PT_WAIT_UNTIL(pt, condition) 等待某个条件(条件可以为时钟或其它变量,IO等)成立,否则直接退出本函数,下一次进入本   函数就直接跳到这个地方判断
PT_WAIT_WHILE(pt, cond)和上面一个一样,只是条件取反了
PT_WAIT_THREAD(pt, thread) 等待一个子任务执行完成
PT_SPAWN(pt, child, thread) 新建一个子任务,并等待其执行完退出
PT_RESTART(pt)   重新启动某个任务执行
PT_EXIT(pt)   任务后面的部分不执行,直接退出重新执行
PT_YIELD(pt)   锁死任务
PT_YIELD_UNTIL(pt, cond) 锁死任务并在等待条件成立,恢复执行

protothread有这些东西做基础,似乎更好理解,更易用。

cqfeiyuxmj 发表于 2012-11-27 14:29:10

不错,能再讲透彻点更好了

gaoyichuan000 发表于 2012-11-27 20:55:30

和这个有什么不一样?
《给51 DIY超轻量级多任务操作系统》
http://www.amobbs.com/forum.php?mod=viewthread&tid=1398508

代码如下:

#include <reg51.h>

#define MAX_TASKS 2       //任务槽个数.必须和实际任务数一至
#define MAX_TASK_DEP 12   //最大栈深.最低不得少于2个,保守值为12.
unsigned char idata task_stack;//任务堆栈.
unsigned char task_id;    //当前活动任务号


//任务切换函数(任务调度器)
void task_switch(){
      task_sp = SP;

      if(++task_id == MAX_TASKS)
                task_id = 0;

      SP = task_sp;
}

//任务装入函数.将指定的函数(参数1)装入指定(参数2)的任务槽中.如果该槽中原来就有任务,则原任务丢失,但系统本身不会发生错误.
void task_load(unsigned int fn, unsigned char tid){
      task_sp = task_stack + 1;
      task_stack = (unsigned int)fn & 0xff;
      task_stack = (unsigned int)fn >> 8;
}

//从指定的任务开始运行任务调度.调用该宏后,将永不返回.
#define os_start(tid) {task_id = tid,SP = task_sp;return;}




/*============================以下为测试代码============================*/

void task1(){
      static unsigned char i;
      while(1){
                i++;
                task_switch();//编译后在这里打上断点
      }
}

void task2(){
      static unsigned char j;
      while(1){
                j+=2;
                task_switch();//编译后在这里打上断点
      }
}

void main(){
      //这里装载了两个任务,因此在定义MAX_TASKS时也必须定义为2
      task_load(task1, 0);//将task1函数装入0号槽
      task_load(task2, 1);//将task2函数装入1号槽
      os_start(0);
}

smset 发表于 2012-11-27 22:00:28

gaoyichuan000 发表于 2012-11-27 20:55 static/image/common/back.gif
和这个有什么不一样?
《给51 DIY超轻量级多任务操作系统》
http://www.amobbs.com/forum.php?mod=viewthre ...

表面上很相似哈,实际上有很多不同,

1)本调度系统与单片机无关。并非51专用。
2)本调度系统每个任务占3个字节,后续将有新版本,在lgt那边会放出。新版本是每个任务1-2字节ram.
3)本调度系统不涉及堆栈操作。所有任务公用一个堆栈空间。

fiddly 发表于 2012-11-27 22:21:34

好贴,慢慢看!

ShawnLinson 发表于 2012-11-27 22:40:40

收藏啦~~

wxlcj 发表于 2012-11-27 22:42:46

本帖最后由 wxlcj 于 2012-11-27 22:44 编辑

能不能加上休眠
改到stm32要注意什么?

smset 发表于 2012-11-27 22:50:22

{:smile:}WatiX()就是休眠啊, 改stm32没啥特殊的,只要能产生定时中断就行。

371278638 发表于 2012-11-27 22:59:32

先收藏,学习一下!

gaoyichuan000 发表于 2012-11-28 11:57:55

smset 发表于 2012-11-27 22:50 static/image/common/back.gif
WatiX()就是休眠啊, 改stm32没啥特殊的,只要能产生定时中断就行。

你是说STM32的SysTick定时器吗?

dark_ness 发表于 2012-11-28 12:13:26

留个足印,以后学习学习。。。{:smile:}{:smile:}

maimaige 发表于 2012-11-28 12:35:10

MARk 一下

eddia2012 发表于 2012-12-3 11:21:33

{:smile:}不错哦,呵呵。

skylilin 发表于 2012-12-3 12:25:02

标记下!慢慢看!

renmin 发表于 2012-12-3 13:10:50

mark!学习了!

PaulDE 发表于 2012-12-4 20:55:35

Mark 调度器
这个是不是发俩贴了啊

lixiao7892998 发表于 2012-12-4 21:11:41

强悍,学习了!

fnems 发表于 2012-12-6 20:51:42

本帖最后由 fnems 于 2012-12-6 21:01 编辑

牛!

Wait配合伪线程这个结构LZ怎么想出来的……

能不能再加入事件等待呢?这样就更完美了,因为线程block有时候是等待时间,有时候是等待事件

另外感觉可以在任务中加入this指针全局变量,切换任务的时候顺便切换this,this指向的结构体可以包含任务号的信息,这样wait函数中不用指定任务号了。


还有,以前我用STM8状态机的时候,主循环喜欢加上WFI,类似这样的结构:#define WFI   _asm("wfi")

main(void) {
init();
while(1) {
    switch(state) {
    case STATE_INIT:
    case STATE_A:
    case STATE_B:
    } /* end switch */
    WFI;
} /* end while */
}

smset 发表于 2012-12-7 00:08:56

本帖最后由 smset 于 2012-12-7 09:55 编辑

新版本已无需指定定时器的。
/**********************************************************/
#include "STC89C51.h"

/****小小调度器开始**********************************************/
#define MAXTASKS 3
unsigned char currid;
unsigned char timers;
#define _SS   static unsigned char lc=0; switch(lc){   case 0:
#define _EE   ;}; lc=0;
#define WaitX(b) {lc=__LINE__; timers=b;} return ; case __LINE__:
#define RunTask(a,b) {currid=b; if (timers==0){timers=255; a();}}
#define CallSub(x)   WaitX(0); x(); if (timers!=255) return;
/*****小小调度器结束*******************************************************/

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
{
    unsigned char i;
        TL0=0Xff;    //10ms 重装
        TH0=0XDB;//b7;

    for (i=0;i<MAXTASKS;i++){
   if ((timers!=0)&&(timers!=255)) {
           timers--;
       }
    }       
}


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

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


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

zj_871112 发表于 2012-12-7 12:25:40

好贴,慢慢学习~~

zhizhiyu 发表于 2012-12-7 13:18:06

不错,慢慢学习

modelsim 发表于 2012-12-7 13:51:15

本帖最后由 modelsim 于 2012-12-7 14:00 编辑

settimer(&lc,__LINE__,500);return;case __LINE__:

void settimer(char *lc,char line,unsigned char d)
{
*lc=line;
timers=d;
}

这段代码没看懂,请楼主讲解下,那个_LINE_宏有什么用,为什么要加入这个,如何能去掉

smset 发表于 2012-12-7 15:19:08

modelsim 发表于 2012-12-7 13:51 static/image/common/back.gif
settimer(&lc,__LINE__,500);return;case __LINE__:

void settimer(char *lc,char line,unsigned char d)


请直接看34楼的新版本

phone 发表于 2012-12-29 15:50:08

后面的怎么看不到了。

jz701209李 发表于 2013-1-1 19:24:33

谢谢楼主.......

583050603 发表于 2013-4-1 10:17:13

谢谢楼主

dangeranimal 发表于 2013-4-1 10:35:33

好东西,标记下了~

蓝蓝的恋 发表于 2014-3-26 13:18:33

{:sad:}看看先~

shi_90 发表于 2014-3-26 15:27:18

现在还不算很懂慢慢学习!!!

wildcat7261 发表于 2014-4-2 21:43:19

kk                  

yezi2030 发表于 2014-4-6 17:19:11

打算移植到合泰的芯片上HT45F232KROM 128RAM

黄晨0410 发表于 2014-8-16 11:45:37

smset 发表于 2012-11-27 11:13
这个就是一个状态机,只是以行号__LINE__作为状态变量而已。
在切换时,先保存当前行号__LINE__作为状态变 ...

__LINE__这个值会不会超过一个char 即lc 类型值呢??? 请教

jorry 发表于 2016-12-17 21:54:05

学习一下!!!

Billion 发表于 2017-1-13 16:33:06

mark mark mark

lihui163 发表于 2020-11-26 11:44:43

这种实时性效率咋样?

MegaHealth 发表于 2021-5-31 17:11:45

smset 发表于 2012-12-7 00:08
新版本已无需指定定时器的。

楼主,感觉任务展开的逻辑不太对啊
RunTask(task1,1);展开如下:

        currid = 1;
        if (timers == 0)
        {
                   timers = 255;
                   static unsigned char lc = 0;
                   switch (lc)
                  {
                     case 0:
                           while(1){
                                lc = __LINE__;
                                timers = 100;
                                return ;
                                               
                              case __LINE__:
                              LED1=!LED1;
                        }
                ;};
                lc=0;
        }

MegaHealth 发表于 2021-5-31 17:15:19

应该把task里面的while(1)去掉
void task1(){
_SS
    //while(1){
      WaitX(100);
      LED1=!LED1;
   // }
_EE
}

warrenyan7251 发表于 2021-6-1 01:27:40

感谢分享。

chenfzg 发表于 2021-6-1 04:05:24

谢谢,思路不错

lhj200304 发表于 2024-3-16 16:43:27

我记得有个群的,找不见,怎么能加回来?
页: [1]
查看完整版本: 适用于小资源的小小调度器程序(原创)