搜索
bottom↓
回复: 54
打印 上一主题 下一主题

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

  [复制链接]

出0入0汤圆

跳转到指定楼层
1
发表于 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;
#define  WaitX(a,b)  settimer(&lc,__LINE__,a,b); return ; case __LINE__:
struct TASK {
  char td;
  void (*fp)();
};
#define MAXTASKS 5
struct TASK tasks[MAXTASKS];

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


sbit KEY = P3^2;
unsigned char code numtab[16]={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[0].td=45;  //450mS 灭
  else tasks[0].td=5;  //50ms  亮
}

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


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

while(1){

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

_EE
}



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

          clr_wdt();

        InitT0();

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

    //装载任务:
        tasks[0].fp=ontimer0;
        tasks[1].fp=task1;
        tasks[2].fp=task2;

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

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

2
发表于 2012-11-26 12:03:09 | 只看该作者
真舍得拿出来呀!楼主牛

出0入0汤圆

3
发表于 2012-11-26 12:03:28 | 只看该作者
好人!!!士大夫十分

出0入0汤圆

4
 楼主| 发表于 2012-11-26 13:45:47 | 只看该作者
话不多说,上工程!请验证哈。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入93汤圆

5
发表于 2012-11-26 13:48:07 | 只看该作者
楼主一帖两发是违规的哦

出0入0汤圆

6
 楼主| 发表于 2012-11-26 13:50:21 | 只看该作者
哦,抱歉,我以为两个单片机都需要呢,呵呵。不好意思啦。请版主高抬贵手。

出0入0汤圆

7
发表于 2012-11-26 23:36:27 | 只看该作者

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

出0入0汤圆

8
发表于 2012-11-27 10:05:05 | 只看该作者
MARK一下,谢谢啦!~

出100入85汤圆

9
发表于 2012-11-27 10:44:07 | 只看该作者
替换之后还是不怎么明白,请明白的帮忙解释解释。

//任务三,伪线程写法
void  task2()
{
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[i];
}
for(i=0;i<=9;i++){ //从0--9慢速显示,间隔500mS
  //WaitX(2,50);
  settimer(&lc,__LINE__,2,50); return ; case __LINE__:
  P1=numtab[i];
}
}

  };
  lc=0;
}

出0入0汤圆

10
 楼主| 发表于 2012-11-27 11:13:44 | 只看该作者
这个就是一个状态机,只是以行号__LINE__作为状态变量而已。
在切换时,先保存当前行号__LINE__作为状态变量,并设置下次调用本状态机函数的时刻。
下次调用本函数时,直接跳至改行号__LINE__处,再往下运行。

出0入0汤圆

11
发表于 2012-11-27 11:19:02 | 只看该作者
没太看懂,这个和protothread有关系么?

出0入0汤圆

12
 楼主| 发表于 2012-11-27 13:21:51 | 只看该作者
aworker 发表于 2012-11-27 11:19
没太看懂,这个和protothread有关系么?

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

出0入0汤圆

13
发表于 2012-11-27 13:23:31 | 只看该作者
本帖最后由 aworker 于 2012-11-27 13:41 编辑
smset 发表于 2012-11-27 13:21
取其精髓而已,看来你还没有真正理解protothread


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

出0入0汤圆

14
 楼主| 发表于 2012-11-27 13:58:09 | 只看该作者
你可以再对比下,看看那种用法更简明,不过我一直很崇拜protothread的作者,是他创造了这种写法,我只是应用其思想而已。

出0入0汤圆

15
发表于 2012-11-27 14:06:32 | 只看该作者
mark!学习了!

出0入0汤圆

16
发表于 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有这些东西做基础,似乎更好理解,更易用。

出0入0汤圆

17
发表于 2012-11-27 14:29:10 | 只看该作者
不错,能再讲透彻点更好了

出0入0汤圆

18
发表于 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[MAX_TASKS][MAX_TASK_DEP];//任务堆栈.
unsigned char task_id;    //当前活动任务号


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

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

        SP = task_sp[task_id];
}

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

//从指定的任务开始运行任务调度.调用该宏后,将永不返回.
#define os_start(tid) {task_id = tid,SP = task_sp[tid];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);
}

出0入0汤圆

19
 楼主| 发表于 2012-11-27 22:00:28 | 只看该作者
gaoyichuan000 发表于 2012-11-27 20:55
和这个有什么不一样?
《给51 DIY超轻量级多任务操作系统》
http://www.amobbs.com/forum.php?mod=viewthre ...

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

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

出0入0汤圆

20
发表于 2012-11-27 22:21:34 来自手机 | 只看该作者
好贴,慢慢看!

出0入0汤圆

21
发表于 2012-11-27 22:40:40 来自手机 | 只看该作者
收藏啦~~

出0入0汤圆

22
发表于 2012-11-27 22:42:46 | 只看该作者
本帖最后由 wxlcj 于 2012-11-27 22:44 编辑

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

出0入0汤圆

23
 楼主| 发表于 2012-11-27 22:50:22 | 只看该作者
WatiX()就是休眠啊, 改stm32没啥特殊的,只要能产生定时中断就行。

出0入0汤圆

24
发表于 2012-11-27 22:59:32 | 只看该作者
先收藏,学习一下!

出0入0汤圆

25
发表于 2012-11-28 11:57:55 | 只看该作者
smset 发表于 2012-11-27 22:50
WatiX()就是休眠啊, 改stm32没啥特殊的,只要能产生定时中断就行。

你是说STM32的SysTick定时器吗?

出0入0汤圆

26
发表于 2012-11-28 12:13:26 | 只看该作者
留个足印,以后学习学习。。。

出0入0汤圆

27
发表于 2012-11-28 12:35:10 | 只看该作者
MARk 一下

出0入0汤圆

28
发表于 2012-12-3 11:21:33 | 只看该作者
不错哦,呵呵。

出0入0汤圆

29
发表于 2012-12-3 12:25:02 | 只看该作者
标记下!慢慢看!

出0入0汤圆

30
发表于 2012-12-3 13:10:50 | 只看该作者
mark!学习了!

出0入70汤圆

31
发表于 2012-12-4 20:55:35 | 只看该作者
Mark 调度器
这个是不是发俩贴了啊

出0入0汤圆

32
发表于 2012-12-4 21:11:41 | 只看该作者
强悍,学习了!

出0入0汤圆

33
发表于 2012-12-6 20:51:42 | 只看该作者
本帖最后由 fnems 于 2012-12-6 21:01 编辑

牛!

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

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

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


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

  2. main(void) {
  3.   init();
  4.   while(1) {
  5.     switch(state) {
  6.     case STATE_INIT:
  7.     case STATE_A:
  8.     case STATE_B:
  9.     } /* end switch */
  10.     WFI;
  11.   } /* end while */
  12. }
复制代码

出0入0汤圆

34
 楼主| 发表于 2012-12-7 00:08:56 | 只看该作者
本帖最后由 smset 于 2012-12-7 09:55 编辑

新版本已无需指定定时器的。

  1. /**********************************************************/
  2. #include "STC89C51.h"

  3. /****小小调度器开始**********************************************/
  4. #define MAXTASKS 3
  5. unsigned char currid;
  6. unsigned char timers[MAXTASKS];
  7. #define _SS   static unsigned char lc=0; switch(lc){   case 0:
  8. #define _EE   ;}; lc=0;
  9. #define WaitX(b) {lc=__LINE__; timers[currid]=b;} return ; case __LINE__:
  10. #define RunTask(a,b) {currid=b; if (timers[b]==0){timers[b]=255; a();}}
  11. #define CallSub(x)   WaitX(0); x(); if (timers[currid]!=255) return;
  12. /*****小小调度器结束*******************************************************/

  13. sbit LED1 = P2^1;
  14. sbit LED2 = P2^2;

  15. void InitT0()
  16. {
  17.         TMOD = 0x21;
  18.         IE |= 0x82;  // 12t
  19.         TL0=0Xff;
  20.         TH0=0XDB;//22M---b7;
  21.         TR0 = 1;
  22. }

  23. void INTT0(void) interrupt 1 using 1
  24. {
  25.     unsigned char i;
  26.         TL0=0Xff;    //10ms 重装
  27.         TH0=0XDB;//b7;

  28.     for (i=0;i<MAXTASKS;i++){
  29.      if ((timers[i]!=0)&&(timers[i]!=255)) {
  30.            timers[i]--;
  31.          }
  32.     }       
  33. }


  34. void  task1(){
  35. _SS
  36.   while(1){
  37.    WaitX(100);
  38.    LED1=!LED1;   
  39.   }
  40. _EE
  41. }

  42. void  task2(){
  43. _SS
  44.   while(1){
  45.    WaitX(100);
  46.    LED2=!LED2;   
  47.   }
  48. _EE
  49. }


  50. void main()
  51. {
  52.         InitT0();
  53.         while(1){
  54.            RunTask(task1,1);
  55.            RunTask(task2,2);
  56.     }
  57. }

复制代码

出0入0汤圆

35
发表于 2012-12-7 12:25:40 | 只看该作者
好贴,慢慢学习~~

出0入0汤圆

36
发表于 2012-12-7 13:18:06 | 只看该作者
不错,慢慢学习

出0入0汤圆

37
发表于 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[currid]=d;
}

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

出0入0汤圆

38
 楼主| 发表于 2012-12-7 15:19:08 | 只看该作者
modelsim 发表于 2012-12-7 13:51
settimer(&lc,__LINE__,500);return;case __LINE__:

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

请直接看34楼的新版本

出0入0汤圆

39
发表于 2012-12-29 15:50:08 | 只看该作者
后面的怎么看不到了。

出0入0汤圆

40
发表于 2013-1-1 19:24:33 来自手机 | 只看该作者
谢谢楼主.......

出0入0汤圆

41
发表于 2013-4-1 10:17:13 | 只看该作者
谢谢楼主

出0入0汤圆

42
发表于 2013-4-1 10:35:33 | 只看该作者
好东西,标记下了~

出0入8汤圆

43
发表于 2014-3-26 13:18:33 | 只看该作者
看看先~

出0入0汤圆

44
发表于 2014-3-26 15:27:18 | 只看该作者
现在还不算很懂慢慢学习!!!

出0入0汤圆

45
发表于 2014-4-2 21:43:19 | 只看该作者
kk                  

出0入0汤圆

46
发表于 2014-4-6 17:19:11 | 只看该作者
打算移植到合泰的芯片上  HT45F23  2KROM 128RAM

出0入0汤圆

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

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

出0入0汤圆

48
发表于 2016-12-17 21:54:05 | 只看该作者
学习一下!!!

出0入0汤圆

49
发表于 2017-1-13 16:33:06 | 只看该作者
mark mark mark

出0入0汤圆

50
发表于 2020-11-26 11:44:43 | 只看该作者
这种实时性效率咋样?

出0入0汤圆

51
发表于 2021-5-31 17:11:45 | 只看该作者
smset 发表于 2012-12-7 00:08
新版本已无需指定定时器的。

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

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

                        }
                ;};
                lc=0;
        }

出0入0汤圆

52
发表于 2021-5-31 17:15:19 | 只看该作者
应该把task里面的while(1)去掉
void task1(){
_SS
    //while(1){
        WaitX(100);
        LED1=!LED1;
   // }
_EE
}

出0入0汤圆

53
发表于 2021-6-1 01:27:40 | 只看该作者
感谢分享。

出0入0汤圆

54
发表于 2021-6-1 04:05:24 来自手机 | 只看该作者
谢谢,思路不错

出95入100汤圆

55
发表于 2024-3-16 16:43:27 | 只看该作者
我记得有个群的,找不见,怎么能加回来?
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-25 08:10

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表