搜索
bottom↓
楼主: smset

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

  [复制链接]

出0入0汤圆

发表于 2012-12-15 09:09:14 | 显示全部楼层
#define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers[i]!=0)&&(timers[i]!=255)) timers[i]--;}
会报错,
#define UpdateTimers() {unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers[i]!=0)&&(timers[i]!=255)) timers[i]--;}}
这样就没报

出0入0汤圆

发表于 2012-12-15 09:12:02 | 显示全部楼层
这个是仿真文件,5个task只能跑2个

本帖子中包含更多资源

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

x

出0入0汤圆

发表于 2012-12-15 10:32:08 | 显示全部楼层
#define _EE   ;}; lc=0;

哪位XD能解释下, lc=0 这个永远都不会运行, 放在这是有什么用意啊?

出0入0汤圆

 楼主| 发表于 2012-12-15 10:58:21 来自手机 | 显示全部楼层
holts2 发表于 2012-12-15 10:32
#define _EE   ;}; lc=0;

哪位XD能解释下, lc=0 这个永远都不会运行, 放在这是有什么用意啊? {:sweat: ...

会在任务结束时执行,以便下次用

出0入0汤圆

发表于 2012-12-15 12:45:51 | 显示全部楼层
smset 发表于 2012-12-15 10:58
会在任务结束时执行,以便下次用

void  task1(){
static unsigned char lc=0; switch(lc){   case 0:
  while(1){
  // WaitX(50);
  {lc=62; timers[1]=50;} return ; case 62:
   LED1=!LED1;   
  }
         ;}; lc=0;
}


还是看不懂啊, 如上任务的出口在 return, 永远都不会执行到         ;}; lc=0;

出0入0汤圆

 楼主| 发表于 2012-12-15 13:14:12 来自手机 | 显示全部楼层
holts2 发表于 2012-12-15 12:45
void  task1(){
static unsigned char lc=0; switch(lc){   case 0:
  while(1){

并非所有的任务都是永远循环执行啊

出0入93汤圆

发表于 2012-12-15 13:25:58 | 显示全部楼层
本帖最后由 takashiki 于 2012-12-15 13:32 编辑

根据楼主的各个更新版本,终于又Kill掉了一些字节,并且专门针对51进行了优化。其他CPU暂时没有优化,使用时再说吧。
使用信号量时代码会增大一些,因此放了一个宏SIGNAL,允许使用或关闭信号量。
对于一个任务中存在大量的Wait时,使用WaitEx会减少ROM和CPU的消耗,但程序比较复杂了一些。因为很多CPU能够查表(比如51的JMP散转指令)而不是依次比较。
另外,延时和信号安排方式也改了,定义了SIGNAL宏时,最高位表示等待信号,低7位表示等待延时,没有定义时,1~255都是等待延时。=0时则任务就行,主函数会进行调度。
SendSignal只对等待信号有效,对等待延时无效。
SetReady同时对等待信号和等待延时有效。
所有任务都默认是无限循环的,这个很楼主的不一致。如果只需要执行一次或几次就停止,那么需要手动删除,使用DeleteSelf宏即可。
任务的延时不再直接写入Timers,也不和楼主一样写入变量,而是写入返回值,类似AvrX操作系统。
  1. //12MHz振荡器,12分频,约每10ms中断一次          
  2. #include <REGX51.H>

  3. typedef unsigned char uchar;

  4. /****小小调度器开始**********************************************/
  5. #define MAXTASKS 2
  6. #define SIGNAL                                        //允许使用信号量,代码量会有一定的增加

  7. uchar Timers[MAXTASKS];

  8. #define BEGIN_TASK(TaskName) uchar TaskName(){ static uchar lc; switch(lc){case 0:
  9. #define END_TASK   } lc = 0; return 0;}
  10. #define Wait(Ticks)  {lc = __LINE__; return Ticks; } case __LINE__:
  11. #define RunTask(TaskName, TaskID) { if (Timers[TaskID] == 0){ Timers[TaskID] = TaskName(); }}
  12. #define SetReady(TaskId) Timers[TaskId] = 0
  13. #define WaitEx(Index, Ticks)  {lc = Index; return Ticks; } case Index:
  14.                           
  15. #ifdef __C51__
  16.     #define CallSub(SubTaskName) {lc = __LINE__; case __LINE__:{ ACC = SubTaskName(); if(ACC) return ACC;}}  
  17.     #define UpdateTimersEx() uchar idata* data ptmr = &Timers[MAXTASKS];  \
  18.         while(ptmr != (uchar idata*)&Timers[0]){                                       \
  19.             ptmr --;                                                                           \
  20.             ACC = *ptmr;                                                                   \
  21.             ACC ^= 0x80;                                                                  \
  22.             if(ACC == 0) continue;                                                       \
  23.             ACC ^= 0x80;                                                                  \
  24.             if(ACC == 0x81) ACC = 0;                                                  \
  25.             if(ACC) ACC--;                                                                   \
  26.             *ptmr = ACC; }
  27. #else
  28.     #define CallSub(SubTaskName) {lc = __LINE__; case __LINE__:{ uchar subTime = SubTaskName();        if(subTime) return subTime;}}  
  29.     #define UpdateTimersEx() uchar * ptmr = &Timers[MAXTASKS];         \
  30.         while(ptmr != (uchar*)&Timers[0]){                                        \
  31.             ptmr --;                                                              \
  32.             if(*ptmr == 0x80) continue;                                   \
  33.             if(*ptmr == 0x81) *ptmr = 0;                                \
  34.             if(*ptmr) *ptmr--;}
  35. #endif          

  36. #ifdef SIGNAL
  37. #define WaitSignal() Wait(128)
  38. #define WaitSignalAndTimeOut(Tick) Wait(128 + Tick)
  39. #define PauseSelf WaitSignal
  40. #define DeleteSelf() return 128       
  41. #define DeleteTask(TaskId) Timers[TaskId] = 128
  42. #define SendSignal(TaskId) if(Timers[TaskId] >= 128){Timers[TaskId] = 0;}         
  43. #define WaitSignalEx(Index) WaitEx(Index, 128)
  44. #define WaitSignalAndTimeOutEx(Index, Tick) WaitEx(Index, 128 + Tick)
  45. #define UpdateTimers UpdateTimersEx
  46. #else
  47. #define UpdateTimers() uchar i; for(i = MAXTASKS; i > 0; i--){ if(Timers[i-1]) Timers[i-1]--; }
  48. #endif                                                                          

  49. /*****小小调度器结束*******************************************************/
  50.                                  
  51. sbit LED1 = P2^1;
  52. sbit LED2 = P2^2;

  53. void T0_ISR(void) interrupt 1 using 1
  54. {
  55.     UpdateTimers();
  56.     TH0 = 0xD9;                                                        //大约10ms重装,为了简单,没有进行补偿
  57. }


  58. BEGIN_TASK(Task1)                                                //测试加强版Wait,更加节省时间和空间
  59.    WaitEx(1, 2);
  60.    LED1=!LED1;  
  61.    WaitEx(2, 3);
  62.    LED1=!LED1;  
  63.    WaitEx(3, 4);
  64.    LED1=!LED1;  
  65.    WaitEx(4, 5);
  66.    LED1=!LED1;  
  67.    WaitEx(5, 6);
  68.    LED1=!LED1;  
  69.    WaitEx(6, 7);
  70.    LED1=!LED1;  
  71.    WaitSignalEx(7);                                                //等待信号, 同时等待信号和超时用:WaitSignalAndTimeOut
  72.    LED1=!LED1;  
  73. END_TASK

  74. BEGIN_TASK(SubTask)
  75.         if(LED2){
  76.                 Wait(5);
  77.                 SendSignal(0);                                        //发送信号
  78.         }
  79. END_TASK

  80. BEGIN_TASK(Task2)
  81.    Wait(3);
  82.    LED2=!LED2;  
  83.    CallSub(SubTask);                                        //调用子任务
  84. END_TASK

  85. void main()        {
  86.     IE = 0x82;
  87.     TCON = 0x10;
  88.     TMOD = 1;
  89.     TH0 = 0xD9;
  90.     while(1){
  91.         RunTask(Task1,0);                                //任务调用
  92.         RunTask(Task2,1);
  93.     }
  94. }
复制代码
典型的,在51上,Wait只需要消耗三条指令,共6个字节。
  1.     92:    Wait(3);
  2. C:0x00C7    75125C   MOV      0x12,#0x5C
  3. C:0x00CA    7F03     MOV      R7,#0x03
  4. C:0x00CC    22       RET      
  5.     93:    LED2=!LED2;   
  6. C:0x00CD    B2A2     CPL      LED2(0xA0.2)
复制代码
修改原因:CODE段排版怎么都排不好,晕死

出0入0汤圆

 楼主| 发表于 2012-12-15 17:27:25 | 显示全部楼层
本帖最后由 smset 于 2012-12-15 20:47 编辑

非常不错!你又提供了一个亮点哈:任务函数带返回值, 我重新评估了任务函数带返回值的方案,首先会省掉1个字节的ram,然后WaitX的效率提高了。任务函数带返回值的方案唯一的问题是每个Wait编译出的增量字节是9字节,比我最后版本会多一个字节(299楼的WaitX增量Rom是8字节)。
但经过我查看汇编代码,实际上带返回值的运行效率是更高的,所以我需要总体评估一下。

楼上的版本:1) Wait和 WaitEx没有区别的。不知道为何要设置两个宏
                  2)BEGIN_TASK和END_TASK似乎不实用:用户没有办法声明任务的静态变量了,这个宏把函数名和变量声明区占完了。

出0入0汤圆

 楼主| 发表于 2012-12-15 17:46:30 来自手机 | 显示全部楼层
本帖最后由 smset 于 2012-12-15 19:50 编辑

在接来的版本中增加高优先级任务抢占机制。时钟这部分会统一考虑调整。

出0入0汤圆

发表于 2012-12-15 20:54:25 | 显示全部楼层
smset 发表于 2012-12-15 17:27
非常不错!你又提供了一个亮点哈:任务函数带返回值, 我重新评估了任务函数带返回值的方案,首先会省掉1个 ...

采用任务函数返回值的方法,很不错啊。我在Mega8上测试,节省rom而且运行会变快。编译器winavr20100110
Size before:
   text           data            bss            dec            hex        filename
    326              0              7            333            14d        main.elf
avr-gcc -c -mmcu=atmega8 -I. -g -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-ahlms=main.lst main.c -o main.o
avr-gcc -mmcu=atmega8 -Wl,-Map=main.map,--cref main.o   -lm --output main.elf
avr-objcopy -O ihex -R .eeprom main.elf main.hex
avr-objdump -h -S main.elf > main.lss
Size after:
   text           data            bss            dec            hex        filename
    338              0              8            346            15a        main.elf
新代码节省了一个byte的ram,rom减少了12byte。

以下是原来的程序编译的汇编代码,这里仅仅列出task0和runtask两个部分代码的区别
runtask原始代码的汇编:
                RunTask(task0,0);
11e:        1f ef               ldi        r17, 0xFF        ; 255
120:        80 91 62 00         lds        r24, 0x0062
124:        88 23               and        r24, r24
126:        39 f4               brne        .+14             ; 0x136 <main+0x34>
128:        10 93 67 00         sts        0x0067, r17
12c:        b7 df               rcall        .-146            ; 0x9c <task0>
12e:        80 91 67 00         lds        r24, 0x0067
132:        80 93 62 00         sts        0x0062, r24
新代码的汇编:
                RunTask(task0,0);
11a:        1f ef               ldi        r17, 0xFF        ; 255
11c:        80 91 62 00         lds        r24, 0x0062
120:        88 23               and        r24, r24
122:        29 f4               brne        .+10             ; 0x12e <main+0x30>
124:        10 93 62 00         sts        0x0062, r17
128:        b9 df               rcall        .-142            ; 0x9c <task0>
12a:        80 93 62 00         sts        0x0062, r24
可以看出原来的代码由于传递数据需要currdt变量,因此在rcall前进行了255的赋值,之后又取出再赋值给tmers。
而新的代码由于没有了变量,因此在rcall钱直接给timers赋值255,之后通过rcall的返回值直接赋值。节省了取出currdt的这个时间。

task0里的WaitX的原始代码的汇编:
                WaitX(20);
  ba:        84 e6               ldi        r24, 0x64        ; 100
  bc:        80 93 61 00         sts        0x0061, r24
  c0:        84 e1               ldi        r24, 0x14        ; 20
  c2:        80 93 67 00         sts        0x0067, r24
  c6:        08 95               ret
新代码的汇编
                WaitX(20);
  ba:        84 e6               ldi        r24, 0x64        ; 100
  bc:        80 93 61 00         sts        0x0061, r24
  c0:        84 e1               ldi        r24, 0x14        ; 20
  c2:        08 95               ret
上面的汇编,首先是对lc进行赋值,给0x0061寄存器,而等待20这个部分原始代码需要把这个数据传给currdt寄存器,再返回。
而新的代码由于没有了变量,因此直接给内部r24寄存器,然后返回。

因此采用任务函数带返回值的方式不仅节省了rom空间,节省了一个ram,而且程序运行也更快。

/****小小调度器开始**********************************************/
#define MAXTASKS 5
unsigned char timers[MAXTASKS];
#define _SS static unsigned char lc; switch(lc){case 0:
#define _EE ;}; lc=0; return 0;
#define WaitX(tickets)  do {lc=__LINE__; return tickets ;} while(0); case __LINE__:
#define RunTask(TaskName,TaskID) do { if (timers[TaskID]==0){ timers[TaskID]=255; timers[TaskID]=TaskName(); }}  while(0);
#define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers!=0)&&(timers!=255)) timers--;}
/*****小小调度器结束*******************************************************/

出0入0汤圆

 楼主| 发表于 2012-12-15 21:46:10 来自手机 | 显示全部楼层
本帖最后由 smset 于 2012-12-15 21:58 编辑
zsmbj 发表于 2012-12-15 20:54
采用任务函数返回值的方法,很不错啊。我在Mega8上测试,节省rom而且运行会变快。编译器winavr20100110
S ...


是的,任务带返回的方案的运行效率确实更佳,至于Rom可能不同编译器微有差异,_EE那里应该返回255。在大家的努力下,我们越来越接近完美方案了,呵呵

出0入0汤圆

 楼主| 发表于 2012-12-15 21:50:16 来自手机 | 显示全部楼层
下一步,我打算设计在中断里执行1毫秒级高优先级任务的方案,敬请合作共同打造。

出0入0汤圆

 楼主| 发表于 2012-12-15 22:18:48 | 显示全部楼层
formatme 发表于 2012-12-15 09:12
这个是仿真文件,5个task只能跑2个

你再检查下吧, WaitX(1000);已经溢出了。 WaitX的最大数是254.   对应2.54秒。

出0入93汤圆

发表于 2012-12-15 22:25:20 | 显示全部楼层
smset 发表于 2012-12-15 17:27
非常不错!你又提供了一个亮点哈:任务函数带返回值, 我重新评估了任务函数带返回值的方案,首先会省掉1个 ...

使用两个不同的宏的目的是为了产生更加高效的代码,当然也就更加底层了,更麻烦了……
当大量的WaitEx按顺序写下去的时候,编译器会选择更加高效的代码,而不是依次比较。
  1.     67: BEGIN_TASK(Task1)                                          //测试加强版Wait,更加节省时间和空间
  2. C:0x0041    E510     MOV      A,0x10
  3. C:0x0043    B40800   CJNE     A,#0x08,C:0046
  4. C:0x0046    504E     JNC      C:0096
  5. C:0x0048    90004E   MOV      DPTR,#0x004E
  6. C:0x004B    25E0     ADD      A,ACC(0xE0)
  7. C:0x004D    73       JMP      @A+DPTR                                   //这里会散转,ROM和CPU都优化了
  8. C:0x004E    015E     AJMP     C:005E               
  9. C:0x0050    0164     AJMP     C:0064
  10. C:0x0052    016C     AJMP     C:006C
  11. C:0x0054    0174     AJMP     C:0074
  12. C:0x0056    017C     AJMP     C:007C
  13. C:0x0058    0184     AJMP     C:0084
  14. C:0x005A    018C     AJMP     C:008C
  15. C:0x005C    0194     AJMP     C:0094
  16.     68:    WaitEx(1, 2);
  17. C:0x005E    751001   MOV      0x10,#0x01
  18. C:0x0061    7F02     MOV      R7,#0x02
  19. C:0x0063    22       RET      
  20.     69:    LED1=!LED1;   
  21. C:0x0064    B2A1     CPL      LED1(0xA0.1)
  22.     70:    WaitEx(2, 3);
  23. C:0x0066    751002   MOV      0x10,#0x02
  24. C:0x0069    7F03     MOV      R7,#0x03
  25. C:0x006B    22       RET      
  26.     71:    LED1=!LED1;   
  27. ... ...
复制代码

出0入0汤圆

 楼主| 发表于 2012-12-15 22:29:39 | 显示全部楼层
本帖最后由 smset 于 2012-12-15 22:31 编辑

可Wait也是7字节rom,3个周期啊。你是说散转那里会优化吧,不过这个可能有点依赖于特定编译器了。

出0入93汤圆

发表于 2012-12-16 08:01:00 | 显示全部楼层
还有,你为什么要说我写的BEGIN_TASK、END_TASK宏把变量声明区占光了,无法提供静态变量呢?是可以的,我经常这样用。
C语言的变量定义不一定要放在函数开始哦,只要放在除了switch以外的任何一个花括号开始都可以。

比如这样:
  1. BEGIN_TASK(Task2)
  2. {                                                                      //这里独立成块
  3.     static uchar j = 2;                                            //这里放置静态变量
  4.     Wait(3);
  5.     j ++;                                                            //跨越阻塞使用静态变量
  6.     LED2=!LED2;  
  7.     CallSub(SubTask);                                           //调用子任务
  8. }
  9. END_TASK
复制代码

出0入0汤圆

 楼主| 发表于 2012-12-16 08:22:53 来自手机 | 显示全部楼层
takashiki 发表于 2012-12-16 08:01
还有,你为什么要说我写的BEGIN_TASK、END_TASK宏把变量声明区占光了,无法提供静态变量呢?是可以的,我经 ...

标准C语言变量声明必须放代码前面,C++才可以到处放。

出0入93汤圆

发表于 2012-12-16 08:30:41 | 显示全部楼层
smset 发表于 2012-12-16 08:22
标准C语言变量声明必须放代码前面,C++才可以到处放。

标准C语言的变量声明必须放在块的开头,C++可以到处放。Pascal才是必须放到代码开头(var子节)的,你弄错了吧。
放在不同的位置,变量的作用域是不同的。C的变量的生命周期和C++也不一致,C的明显比较NC一些,能够跨越块而继续生存。

出0入93汤圆

发表于 2012-12-16 08:38:52 | 显示全部楼层
我弄个例子给你看看,基于标准的ANSI C,不包含任何扩展语法:
  1. int main(void){
  2.         myFunc();
  3.         int x = 0;    //C中出错,应放在myFunc();之前。C++中不出错,x的生命周期一直延续到函数返回位置
  4.         return x ;
  5. }
复制代码
改成这样:
  1. int main(void){
  2.         myFunc();
  3.         {int x = 0;}    //C中不出错,C++中出错,x的生命周期到这里就结束了,而C中x的生命周期一次延续到函数返回位置
  4.         return x;        //C++中x变量未定义,因为上面的x的生命周期已经结束了,而C则一直存在而不会出错
  5. }
复制代码
两种都兼容的:
  1. int main(void){
  2.         myFunc();
  3.         {
  4.                  int x = 0;
  5.                  return x;
  6.         }
  7. }
复制代码

出0入0汤圆

 楼主| 发表于 2012-12-16 10:07:06 | 显示全部楼层
本帖最后由 smset 于 2012-12-16 11:59 编辑

如编译器支持C99, 但是很多编译器是ANSI C89, 就不支持,

你用keil编译试试看,在TASKBEGIN宏 的下一行声明一个变量,肯定会编译报错。

而如果要求声明的变量都加花括号里,那太别扭了吧,我们相信大多数人的c语言编程,都不会习惯在声明变量时,增加一对花括号的。

宏定义本义是为了编程更方便,如果反而因此带来麻烦,那就没必要用这个宏啊。
------------

哦,我又看了BEGIN_TASK用法,这样用也没有问题。takashiki C语言功力深厚,佩服啊。

出0入0汤圆

 楼主| 发表于 2012-12-16 10:10:46 | 显示全部楼层
本帖最后由 smset 于 2012-12-16 23:02 编辑

结合takashiki提出的任务带返回的新思路,再更新一版,请评测:

  1. #include "stc89c51.h"

  2. /****小小调度器开始**********************************************/
  3. #define MAXTASKS 2
  4. static unsigned char timers[MAXTASKS];
  5. unsigned char currdt;
  6. #define _SS static unsigned char _lc; switch(_lc){case 0:
  7. #define _EE ;}; _lc=0; return 255;
  8. #define WaitX(tickets)  do {_lc=__LINE__; return tickets ;} while(0); case __LINE__:
  9. #define RunTask(TaskName,TaskID) do { if (timers[TaskID]==0) timers[TaskID]=TaskName(); }  while(0);

  10. #define CallSub(SubTaskName) do { _lc=__LINE__; return 0; case __LINE__:  currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
  11. #define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers[i]!=0)&&(timers[i]!=255)) timers[i]--;}

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

  21. /*****小小调度器结束*******************************************************/
  22. sbit LED1 = P2^1;
  23. sbit LED2 = P2^2;

  24. void InitT0()
  25. {
  26.         TMOD = 0x21;
  27.         IE |= 0x82;  // 12t
  28.         TL0=0Xff;
  29.         TH0=0XDB;//22M---b7;
  30.         TR0 = 1;
  31. }

  32. void INTT0(void) interrupt 1 using 1
  33. {
  34.     UpdateTimers();

  35.     TL0=0Xff;    //10ms 重装
  36.     TH0=0XDB;   
  37. }


  38. unsigned char task1(){
  39. _SS
  40.   while(1){
  41.    WaitX(50);
  42.    LED1=!LED1;   
  43.   }
  44. _EE
  45. }

  46. unsigned char task2(){
  47. _SS
  48.   while(1){
  49.    WaitX(100);
  50.    LED2=!LED2;   
  51.   }
  52. _EE
  53. }


  54. void main()
  55. {
  56.         InitT0();
  57.         while(1){
  58.            RunTask(task1,0);
  59.            RunTask(task2,1);
  60.             //如需降低功耗,在这里执行待机指令,可降低功耗,对实时性没有任何影响
  61.         }
  62. }

复制代码
小小调度器为全开源,全免费使用,首次发布于www.amobbs.com , 转载请注明出处。

出0入0汤圆

发表于 2012-12-16 10:45:05 来自手机 | 显示全部楼层
昨天试了一下.关注

出0入0汤圆

发表于 2012-12-16 11:03:32 | 显示全部楼层
takashiki 发表于 2012-12-15 22:25
使用两个不同的宏的目的是为了产生更加高效的代码,当然也就更加底层了,更麻烦了……
当大量的WaitEx按 ...

这个可以使用include宏的方法实现自动递增,不需要人工参与。
只不过代码上很难看。

出0入0汤圆

发表于 2012-12-16 11:07:03 | 显示全部楼层
smset 发表于 2012-12-15 22:29
可Wait也是7字节rom,3个周期啊。你是说散转那里会优化吧,不过这个可能有点依赖于特定编译器了。
...

switch的跳转表优化估计属于编译器的基础功能了,只要不是太简单的编译器,一般都会有这个功能。

对于高级一点的编译器,控制case的值在M*2^N的行号上,能获得一样的效果。

出0入0汤圆

 楼主| 发表于 2012-12-16 11:23:40 | 显示全部楼层
dr2001 发表于 2012-12-16 11:03
这个可以使用include宏的方法实现自动递增,不需要人工参与。
只不过代码上很难看。 ...

具体如何实现呢,能否给个实例哦。

出0入0汤圆

发表于 2012-12-16 11:49:48 | 显示全部楼层
smset 发表于 2012-12-16 11:23
具体如何实现呢,能否给个实例哦。

http://www.amobbs.com/thread-5511786-1-1.html
可以参考这个帖子。

主要是对编码风格有较大影响。

出0入0汤圆

发表于 2012-12-16 14:18:42 | 显示全部楼层
学习中,

出0入0汤圆

发表于 2012-12-16 18:28:26 | 显示全部楼层
谢谢分享

出0入0汤圆

发表于 2012-12-16 21:34:16 | 显示全部楼层
smset 发表于 2012-12-16 10:10
结合takashiki提出的任务带返回的新思路,再更新一版,请评测:

currdt只在callsub中使用,定义为块中的局部变量是不是省1字节?

出0入0汤圆

发表于 2012-12-16 21:51:25 | 显示全部楼层
本帖最后由 小柯师傅 于 2012-12-16 22:32 编辑

楼主,真的很感谢你的程序,330楼的最新程序更棒了!
但是我用这个程序开始是好的,后来Data Segment增加后出现了问题,问题是RunTask好像不运行了,由于我的51无法调试,我试验了很多次发现问题是这句,if (timers[TaskID]==0).
后来变量定义的地方我修改成unsigned char timers[MAXTASKS] = {0,0,0,0,0};就完全没问题了,不知道是否是我这边的问题,请求搂主指点,谢谢?

出0入0汤圆

发表于 2012-12-16 22:34:04 | 显示全部楼层
希望加入事件触发优化。

出0入0汤圆

发表于 2012-12-16 22:37:16 | 显示全部楼层
马克~!!!!

出0入0汤圆

 楼主| 发表于 2012-12-16 22:42:17 | 显示全部楼层
本帖最后由 smset 于 2012-12-16 23:24 编辑
小柯师傅 发表于 2012-12-16 21:51
楼主,真的很感谢你的程序,330楼的最新程序更棒了!
但是我用这个程序开始是好的,后来Data Segment增加后 ...


您好,不是你的问题,是调度器程序的一个bug。

谢谢,您帮我我们发现了一个bug:因为timers定义时未初始化,所以timers数组的初始值是随机的, 如果恰好随机值是255,那么相应任务就不运行了。

所以请定义为static unsigned char timers[MAXTASKS]; 这样初值自动默认为0;

330楼已经据此更新。再次感谢!

-------------------------------------------------

也可能不是变量定义的问题,检查下你的程序有没有数组越界,或指针越界的情况。

出0入0汤圆

发表于 2012-12-16 22:55:10 | 显示全部楼层
smset 发表于 2012-12-16 22:42
您好,不是你的问题,是调度器程序的一个bug。

谢谢,您帮我我们发现了一个bug:因为timers定义时未初始 ...

你的这个解释不对吧。
原330楼写作:
unsigned char timers[MAXTASKS];
新330楼写作:
static unsigned char timers[MAXTASKS];

但都是作为全局变量定义,C语言规定全局变量不初始化时,默认初始化为0,所以有无static,就初始值而言是没有区别的。

出0入0汤圆

 楼主| 发表于 2012-12-16 23:09:57 | 显示全部楼层
本帖最后由 smset 于 2012-12-16 23:15 编辑

嗯,看来339楼的问题,不是这个原因引起。 但从339楼描述的解决方案来看,又是timers初值不为0引起的。

不过我在一个产品上这样用,data段也增加了,并没发现339楼所述问题。

出50入255汤圆

发表于 2012-12-16 23:21:41 | 显示全部楼层
所以我才会在我的代码里面加了这个。
void sms_os_InitTimers(void)
{
        INT8U i;
    for (i=0;i<MAXTASKS;i++){
     timers[i]=0;
    }
}
第一个应用正在进行中...

出0入0汤圆

发表于 2012-12-16 23:30:41 | 显示全部楼层
smset 发表于 2012-12-16 22:42
您好,不是你的问题,是调度器程序的一个bug。

谢谢,您帮我我们发现了一个bug:因为timers定义时未初始 ...

谢谢您的回复,由于我使用您的程序没有放到主程序,是另外新建了个os.c 和os.h (以后可能很多人也会把核心调度的放入另外个文件) ,所以没用static ,用的extern ,最终是用变量初始化解决的。

出0入0汤圆

 楼主| 发表于 2012-12-16 23:31:02 | 显示全部楼层
xy-mcu 发表于 2012-12-16 23:21
所以我才会在我的代码里面加了这个。
void sms_os_InitTimers(void)
{

这么近,我也在成都高新南区呢。   

出50入255汤圆

发表于 2012-12-17 02:11:14 | 显示全部楼层
smset 发表于 2012-12-16 23:31
这么近,我也在成都高新南区呢。

呵呵,说不定还都是从同一个集团出来的,说不定还认识哦。

出0入0汤圆

发表于 2012-12-17 08:18:46 | 显示全部楼层
smset 发表于 2012-12-16 10:10
结合takashiki提出的任务带返回的新思路,再更新一版,请评测:小小调度器为全开源,全免费使用,首次发布于 ...

这个版本的CallSub是编译不过去的。我改成了如下可以编译过去,而且由于变量只有这个定义才使用,因此放在这里,可以节省一个ram。请测试。

#define CallSub(SubTaskName) unsigned char currdt; do {WaitX(0);  currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
头像被屏蔽

出0入0汤圆

发表于 2012-12-17 09:52:52 | 显示全部楼层
PIC16F627A  PICC V9.8  很精簡了。

本帖子中包含更多资源

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

x

出0入0汤圆

 楼主| 发表于 2012-12-17 10:03:42 | 显示全部楼层
topdog 发表于 2012-12-17 09:52
PIC16F627A  PICC V9.8  很精簡了。

能否贴出该例子代码?
头像被屏蔽

出0入0汤圆

发表于 2012-12-17 10:07:47 | 显示全部楼层
本帖最后由 topdog 于 2012-12-17 10:13 编辑
smset 发表于 2012-12-17 10:03
能否贴出该例子代码?


基本使用你330樓使用的例子
/**********************************************************/
#include            "htc.h"
#include                 "OS_TMCU.H"
//------configure bit seti
__CONFIG (INTIO&WDTDIS&PWRTEN&MCLREN&BOREN&LVPDIS&PROTECT&UNPROTECT);

/****小小调度器开始**********************************************/
#define MAXTASKS 2
static unsigned char timers[MAXTASKS];
unsigned char currdt;
#define _SS static unsigned char _lc; switch(_lc){case 0:
#define _EE ;}; _lc=0; return 255;
#define WaitX(tickets)  do {_lc=__LINE__; return tickets ;} while(0); case __LINE__:
#define RunTask(TaskName,TaskID) do { if (timers[TaskID]==0) timers[TaskID]=TaskName(); }  while(0);

#define CallSub(SubTaskName) do { _lc=__LINE__; return 0; case __LINE__:  currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
#define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;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);

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

//****************************
//
//****************************
void ChipInit(void)
{

// Disable AD
//ANSEL=0;
//ANSELH=0;

//---IO Setting
//--PORTA
/*
#define         KEY_ZONE_I            RA0   
#define         KEY_LEVEL_I            RA1   
#define         FB_I                RA2
#define         KEY_POWER_I            RA3
#define         SEAT_I                RA4   
#define         RS_I                RA5
#define         BACK_LED_HI_O        RA6
#define         IR_O                RA7        //传   
*/
TRISA=0b00111111;

//--PORTB
/*
#define         LR_I                RB0
#define         BACK_LED_LOW_O        RB1
#define         WAIST_LED_LOW_O    RB2
#define         LEG_LED_LOW_O        RB3
#define         LEG_LED_HI_O        RB4
#define         WAIST_LED_HI_O        RB5
#define         PGC_O                RB6
#define         KEEP_PGD_O            RB7
*/
TRISB=0b00000001;
#ifdef    PIC_DEBUG   
TRISB=0b00000111;
#else
TRISB=0b00000001;
#endif


PORTA=0;
PORTB=0;

//    ?   ゑ  /PWM 家塊
VRCON=0;
CMCON=0x07;

OSCF=TRUE;                        //int osc 4Mhz
//---T0 Setting
// OPTION select  
OPTION=0b10000000;//             1:2
TMR0=205;// 0.1msec
//----T2 Setting
//PIE1=0b00000000;//
//PIR1=0;
// T2CON=0b00000000;//
//PIR2=250;//250*4=1000(1msec)
//TMR2=0;

// USART Setting
#ifdef    PIC_DEBUG   
TXSTA=0;
RCSTA=0;
BRGH=1;
SPBRG=25;            //  初4MHz  9600BPS     0.16

SPEN=1;                //        
TXEN=1;                //        竟
CREN=0;                //綳     
PEIE=0;
RCIE=0;

#endif

}

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

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


void main()
{
        ChipInit();
        T0IF=FALSE;
           T0IE=TRUE;
        GIE=TRUE;
        KEEP_PGD_O=TRUE;
        while(1){
                //LED1=TRUE;
                        //LED2=TRUE;
          RunTask(task1,0);
           RunTask(task2,1);
                 //  RunTask(Subtask3,2);
    }
}

//****************************
// interrupt sub
//****************************
void interrupt InterruptTig(void)
{     
// uchar i;  
      
//timer manager
    if (Timer2msec>=100)
        {
        Timer2msec=0;
        T2msecFlag=TRUE;
                UpdateTimers() ;  
        }
    else
        Timer2msec++;
        
//timer manager
   
TMR0=218;
    T0IF=FALSE;


}

出0入0汤圆

 楼主| 发表于 2012-12-17 10:16:18 | 显示全部楼层
本帖最后由 smset 于 2012-12-17 10:18 编辑
topdog 发表于 2012-12-17 10:07
基本使用你330樓使用的例子
/**********************************************************/
#include           ...


不错,是按330楼的版本改的。

CallSub不是在main()里面调用的,是任务里面调用子任务用的。

另外,贴代码可以在[  c o d e ]        [ / c o d e ] 之间插入(去掉括号内的空格),就可以正常显示。
头像被屏蔽

出0入0汤圆

发表于 2012-12-17 10:20:34 | 显示全部楼层
smset 发表于 2012-12-17 10:03
能否贴出该例子代码?

中斷中優化后,去掉了以前沒用的2ms標誌后,效果更好。ram和rom都有減少

本帖子中包含更多资源

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

x

出0入0汤圆

 楼主| 发表于 2012-12-17 10:32:05 | 显示全部楼层
很好啊,这些数据很有价值。

出0入0汤圆

 楼主| 发表于 2012-12-17 10:56:01 | 显示全部楼层
本帖最后由 smset 于 2012-12-17 10:58 编辑
zsmbj 发表于 2012-12-17 08:18
这个版本的CallSub是编译不过去的。我改成了如下可以编译过去,而且由于变量只有这个定义才使用,因此放 ...


可以编译的啊。

你的写法,我编译了,没发现省RAM呢。 结果是一样的。

另外,按你的写法,如果一个任务函数有两处调用CallSub,那 currdt就重复定义了啊,这个不妥当哦。

出0入93汤圆

发表于 2012-12-17 11:15:05 | 显示全部楼层
smset 发表于 2012-12-16 10:07
如编译器支持C99, 但是很多编译器是ANSI C89, 就不支持,

你用keil编译试试看,在TASKBEGIN宏 的下一行声 ...

这个变通一下,宏定义时强制加入一对括号,嘿嘿。
  1. #define BEGIN_TASK(TaskName) uchar TaskName(){ static uchar lc; switch(lc){case 0: {
  2. #define END_TASK   }} lc = 0; return 0;}
复制代码
这下使用者没话说了吧。

我提供BEGIN_TASK宏的原因是提示使用者,这个函数的地位不一般,它是任务函数,不能像其他普通函数一样随意的调用,约定只能由RunTask和CallSub调用。

出0入0汤圆

发表于 2012-12-17 11:24:13 | 显示全部楼层
smset 发表于 2012-12-17 10:56
可以编译的啊。

你的写法,我编译了,没发现省RAM呢。 结果是一样的。

#define CallSub(SubTaskName) do { _lc=__LINE__; return 0; case __LINE__:  currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);


/*******************************
          subtask
*******************************/
unsigned char subtask(void)
{
        _SS
        while(1)
        {
        WaitX(20);
                led1_on();
        WaitX(30);
                led1_off();
    }
        _EE
}

/*******************************
          task0
*******************************/
unsigned char task0(void)
{
        _SS
        while(1)
        {
        WaitX(10);
        led1_tgl();
                CallSub(subtask);
    }
        _EE
}

编译不通过,提示:Error[Pe020]: identifier "_lc" is undefined

出0入93汤圆

发表于 2012-12-17 11:37:33 | 显示全部楼层
zsmbj 发表于 2012-12-17 11:24
#define CallSub(SubTaskName) do { _lc=__LINE__; return 0; case __LINE__:  currdt=SubTaskName(); if ...

他这是笔误,_lc前面的下划线多余了,你多虑了。嘿嘿。

出0入0汤圆

发表于 2012-12-17 11:38:43 | 显示全部楼层
takashiki 发表于 2012-12-17 11:37
他这是笔误,_lc前面的下划线多余了,你多虑了。嘿嘿。

哈哈,果然啊,我直接copy了,所以smset根本没有编译试试,否则肯定也会不通过,哈哈!

出0入0汤圆

 楼主| 发表于 2012-12-17 11:47:38 | 显示全部楼层
本帖最后由 smset 于 2012-12-17 11:52 编辑
zsmbj 发表于 2012-12-17 11:38
哈哈,果然啊,我直接copy了,所以smset根本没有编译试试,否则肯定也会不通过,哈哈! ...


瞎说哈,我肯定编译通过了的啊,不然我怎么知道这个版本更优化呢, 我要看汇编代码的啊。

330楼新版,lc统一更改为_lc, 表示这是一个“系统”变量 ,这不是笔误。是你自己“断章取义”导致的后果哈。

这样即使使用者无意中也定义了一个变量刚好名为lc。也不冲突。




出0入0汤圆

发表于 2012-12-17 13:30:33 | 显示全部楼层
mark一下

出0入0汤圆

 楼主| 发表于 2012-12-17 18:56:10 | 显示全部楼层
dr2001 发表于 2012-12-7 08:35
真要想省那几个字节,就不要void的ProtoThread函数类型,把定时器的的等待数直接return回去在Caller侧处 ...

的确是这样,一语中的!

出0入0汤圆

 楼主| 发表于 2012-12-17 18:59:45 | 显示全部楼层
dr2001 发表于 2012-12-16 11:49
http://www.amobbs.com/thread-5511786-1-1.html
可以参考这个帖子。

确实是个新思路,不过这样写,恐怕以后读代码的要骂人了。呵呵。

能否评估下330楼的版本的运行效率,以及提出优化方案? 谢谢!
头像被屏蔽

出0入0汤圆

发表于 2012-12-17 21:17:28 | 显示全部楼层
貌似如下的子函數調用,只能執行task2;不知何故。
                        if(keycode)
                                {
                                LED5=TRUE;
                                LED6=FALSE;
                        CallSub(task1);
                                }
                        else
                                {
                                LED5=FALSE;
                                LED6=TRUE;
                        CallSub(task2);
                                }
通過一段時間的使用,在PIC上很多奇怪的問題。

出0入0汤圆

发表于 2012-12-17 21:50:29 | 显示全部楼层
topdog 发表于 2012-12-17 21:17
貌似如下的子函數調用,只能執行task2;不知何故。
                        if(keycode)
                                {

使用ProtoThread要特别注意通过间断点的时候,自动变量会丢失的。尤其是对于宏替换的实现,有时候会忘记这个情况,导致问题。

代码片段不利于发Bug,如果不妨碍知识产权,建议贴出较多的代码段才好讨论。
头像被屏蔽

出0入0汤圆

发表于 2012-12-18 08:20:18 | 显示全部楼层
dr2001 发表于 2012-12-17 21:50
使用ProtoThread要特别注意通过间断点的时候,自动变量会丢失的。尤其是对于宏替换的实现,有时候会忘记 ...

應該就是“自动变量会丢失的”,不知道如何規避呢

  1. /**********************************************************/
  2. #include            "htc.h"
  3. #include                 "OS_TMCU.H"
  4. //------configure bit seti
  5. __CONFIG (INTIO&WDTDIS&PWRTEN&MCLREN&BOREN&LVPDIS&PROTECT&UNPROTECT);

  6. /****小小调度器开始**********************************************/
  7. #define MAXTASKS 6
  8. static unsigned char timers[MAXTASKS];
  9. unsigned char currdt;
  10. #define _SS static unsigned char _lc; switch(_lc){case 0:
  11. #define _EE ;}; _lc=0; return 255;
  12. #define WaitX(tickets)  do {_lc=__LINE__; return tickets ;} while(0); case __LINE__:
  13. #define RunTask(TaskName,TaskID) do { if (timers[TaskID]==0) timers[TaskID]=TaskName(); }  while(0);

  14. #define CallSub(SubTaskName) do { _lc=__LINE__; return 0; case __LINE__:  currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
  15. #define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers[i]!=0)&&(timers[i]!=255)) timers[i]--;}

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

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

  26. //****************************
  27. //
  28. //****************************
  29. void ChipInit(void)
  30. {

  31. // Disable AD
  32. //ANSEL=0;
  33. //ANSELH=0;

  34. //---IO Setting
  35. //--PORTA
  36. /*
  37. #define         KEY_ZONE_I            RA0   
  38. #define         KEY_LEVEL_I            RA1   
  39. #define         FB_I                RA2
  40. #define         KEY_POWER_I            RA3
  41. #define         SEAT_I                RA4   
  42. #define         RS_I                RA5
  43. #define         BACK_LED_HI_O        RA6
  44. #define         IR_O                RA7        //传   
  45. */
  46. TRISA=0b00111111;

  47. //--PORTB
  48. /*
  49. #define         LR_I                RB0
  50. #define         BACK_LED_LOW_O        RB1
  51. #define         WAIST_LED_LOW_O    RB2
  52. #define         LEG_LED_LOW_O        RB3
  53. #define         LEG_LED_HI_O        RB4
  54. #define         WAIST_LED_HI_O        RB5
  55. #define         PGC_O                RB6
  56. #define         KEEP_PGD_O            RB7
  57. */
  58. TRISB=0b00000001;
  59. #ifdef    PIC_DEBUG   
  60. TRISB=0b00000111;
  61. #else
  62. TRISB=0b00000001;
  63. #endif


  64. PORTA=0;
  65. PORTB=0;

  66. //    ?   ゑ  /PWM 家塊
  67. VRCON=0;
  68. CMCON=0x07;

  69. OSCF=TRUE;                        //int osc 4Mhz
  70. //---T0 Setting
  71. // OPTION select  
  72. OPTION=0b10000000;//             1:2
  73. TMR0=205;// 0.1msec
  74. //----T2 Setting
  75. //PIE1=0b00000000;//
  76. //PIR1=0;
  77. // T2CON=0b00000000;//
  78. //PIR2=250;//250*4=1000(1msec)
  79. //TMR2=0;

  80. // USART Setting
  81. #ifdef    PIC_DEBUG   
  82. TXSTA=0;
  83. RCSTA=0;
  84. BRGH=1;
  85. SPBRG=25;            //  初4MHz  9600BPS     0.16

  86. SPEN=1;                //        
  87. TXEN=1;                //        竟
  88. CREN=0;                //綳     
  89. PEIE=0;
  90. RCIE=0;

  91. #endif

  92. }

  93. uchar   task1(){
  94. _SS
  95.   while(1){
  96.    WaitX(10);
  97.    LED1=!LED1;   
  98.   }
  99. _EE
  100. }

  101. uchar  task2(){
  102. _SS
  103.   while(1){
  104.    WaitX(200);
  105.    LED2=!LED2;   
  106.   }
  107. _EE
  108. }

  109. uchar KEYSCAN()
  110. {
  111. _SS
  112.         while(1)
  113.                 {
  114.         WaitX(1);
  115.         keynewcode=0;
  116.         if(!KEY_POWER_I)
  117.                 keynewcode |=POWERKEY;
  118.         if(!KEY_LEVEL_I)
  119.                 keynewcode|=LEVELkEY;
  120.         if(!KEY_ZONE_I)
  121.                 keynewcode|=ZONEkEY;

  122.         if(keynewcode!=0)
  123.                 {
  124.                 if(keycode==keynewcode)
  125.                         {
  126.                         if(keyCount>4)
  127.                                 {
  128.                                 KeyActiveyFlag=TRUE;
  129.                                 //LED1=TRUE;
  130.                                 }
  131.                         else
  132.                                 {
  133.                         keyCount++;
  134.                         //keycode=keynewcode;
  135.                                 }
  136.                         }
  137.                 else
  138.                         {
  139.                         keycode=keynewcode;
  140.                         keyCount=0;
  141.                         }
  142.                
  143.                 }
  144.         else
  145.                 {
  146.                 keyCount=0;
  147.                 keylongCount=0;
  148.                 }
  149.                 }
  150.         _EE
  151. }

  152. uchar  task3()
  153.         {
  154.         _SS
  155.                 while(1)
  156.                         {
  157.                         WaitX(10);
  158.                         //CallSub(task1);
  159.                         //keycode=1;                               
  160.                                 switch(keycode)
  161.                                         {
  162.                                         case POWERKEY:
  163.                                                 LED5=TRUE;
  164.                                                 LED6=FALSE;
  165.                                                 CallSub(task1);
  166.                                                 break;
  167.                                                 case LEVELkEY:
  168.                                                 LED5=FALSE;
  169.                                                 LED6=TRUE;
  170.                                                 CallSub(task2);
  171.                                                 break;
  172.                                                 default :
  173.                                                         LED5=FALSE;
  174.                                                         LED6=FALSE;
  175.                                         }
  176.                         }
  177.                 _EE

  178. }
  179. uchar        KEYDISPOSE()
  180.         {

  181.                                 {
  182.                                 KeyActiveyFlag=FALSE;
  183.                                 switch(keycode)
  184.                                         {
  185.                                         case POWERKEY:
  186.                                                 LED3=TRUE;
  187.                                                 LED4=FALSE;
  188.                                                 break;
  189.                                                 case LEVELkEY:
  190.                                                 LED3=FALSE;
  191.                                                 LED4=TRUE;
  192.                                                 break;
  193.                                                 default :
  194.                                                         LED3=FALSE;
  195.                                                         LED4=FALSE;
  196.                                         }
  197.                                                        

  198.                
  199.                                 }
  200.        
  201.        
  202.         }
  203. void main()
  204. {
  205.         ChipInit();
  206.         T0IF=FALSE;
  207.            T0IE=TRUE;
  208.         GIE=TRUE;
  209.         KEEP_PGD_O=TRUE;
  210.         while(1){
  211.                         RunTask(KEYSCAN,0);
  212.                         RunTask(KEYDISPOSE,1);
  213.                         RunTask(task3, 2);
  214.     }
  215. }

  216. //****************************
  217. // interrupt sub
  218. //****************************
  219. void interrupt InterruptTig(void)
  220. {     
  221. // uchar i;  
  222.       
  223. //timer manager

  224.         //        UpdateTimers() ;  

  225.     if (Timer2msec>=100)
  226.         {
  227.         Timer2msec=0;
  228.         T2msecFlag=TRUE;
  229.                 UpdateTimers() ;  
  230.         }
  231.     else
  232.         Timer2msec++;
  233.      
  234.         
  235. //timer manager
  236.    
  237. TMR0=218;
  238.     T0IF=FALSE;


  239. }
复制代码
头像被屏蔽

出0入0汤圆

发表于 2012-12-18 08:28:03 | 显示全部楼层
但是定義成如下的變量都有問題volatile  uchar keycode;

出0入0汤圆

发表于 2012-12-18 09:04:43 | 显示全部楼层
本帖最后由 ifree64 于 2012-12-18 09:15 编辑
topdog 发表于 2012-12-18 08:28
但是定義成如下的變量都有問題volatile  uchar keycode;


你的问题可能出在这里:
  1. uchar  task3()
  2. {
  3.         _SS
  4.                 while(1)
  5.                 {
  6.                         WaitX(10);
  7.                         //CallSub(task1);
  8.                         //keycode=1;                                
  9.                         switch(keycode)
  10.                         {
  11.                                 case POWERKEY:
  12.                                         LED5=TRUE;
  13.                                         LED6=FALSE;
  14.                                         CallSub(task1);
  15.                                         break;
  16.                                 case LEVELkEY:
  17.                                         LED5=FALSE;
  18.                                         LED6=TRUE;
  19.                                         CallSub(task2);
  20.                                         break;
  21.                                 default :
  22.                                         LED5=FALSE;
  23.                                         LED6=FALSE;
  24.                         }
  25.                 }
  26.         _EE

  27. }
复制代码
_SS和_EE宏为你的代码插入了switch,这样当嵌套使用switch时,代码出现问题。你的代码将展开为:
  1. uchar  task3()
  2. {
  3.         static unsigned char _lc; switch(_lc)
  4.         {
  5.                 case 0:
  6.                 while(1)
  7.                 {
  8.                         WaitX(10);
  9.                         //CallSub(task1);
  10.                         //keycode=1;                                
  11.                         switch(keycode)
  12.                         {
  13.                                 case POWERKEY:
  14.                                         LED5=TRUE;
  15.                                         LED6=FALSE;
  16.                                         do { _lc=__LINE__; return 0; case __LINE__:  currdt=task1(); if(currdt!=255) return currdt;} while(0);
  17.                                         break;
  18.                                 case LEVELkEY:
  19.                                         LED5=FALSE;
  20.                                         LED6=TRUE;
  21.                                         do { _lc=__LINE__; return 0; case __LINE__:  currdt=task2(); if(currdt!=255) return currdt;} while(0);
  22.                                         break;
  23.                                 default :
  24.                                         LED5=FALSE;
  25.                                         LED6=FALSE;
  26.                         }
  27.                 }
  28.         };
  29.         _lc=0;
  30.         return 255;
  31. }
复制代码
你代码中通过CallSub插入的case __LINE__: 成为了内部switch的标签,因此外部switch标签在进行分支查找时,无法找到原来保存的位置,自然task代码就不会运行了。
解决方法可以是将内部的switch代码改为if实现。

PS:宏的这种使用固然“巧妙”,自己看到的代码和编译器看到的代码“不一致”,这往往成为是错误的根源。这也是我不喜欢过度使用宏代码的原因。

出0入0汤圆

 楼主| 发表于 2012-12-18 09:39:08 | 显示全部楼层
topdog 发表于 2012-12-18 08:20
應該就是“自动变量会丢失的”,不知道如何規避呢

    任务函数内部不能再用switch语句了。 这种情况请用 if else。

头像被屏蔽

出0入0汤圆

发表于 2012-12-18 09:41:33 | 显示全部楼层
ifree64 发表于 2012-12-18 09:04
你的问题可能出在这里:_SS和_EE宏为你的代码插入了switch,这样当嵌套使用switch时,代码出现问题。你的 ...

谢谢指正switch的用法的确有问题,但是修改成如下方式同样也是有问题,只执行task2。发现keycode变量丢失了,keycode定义成volatile uchar 型也没有效果。
if(keycode)
                                {
                                LED5=TRUE;
                                LED6=FALSE;
                        CallSub(task1);
                                }
                        else
                                {
                                LED5=FALSE;
                                LED6=TRUE;
                        CallSub(task2);
                                }

出25入12汤圆

发表于 2012-12-18 09:59:57 | 显示全部楼层
mark 待用,以前一直用 keil的 tiny51,所以不想换片子

出0入0汤圆

发表于 2012-12-18 10:02:16 | 显示全部楼层
topdog 发表于 2012-12-18 09:41
谢谢指正switch的用法的确有问题,但是修改成如下方式同样也是有问题,只执行task2。发现keycode变量丢失 ...

在你前面贴出的代码里没有找到定义keycode的地方。

出0入0汤圆

 楼主| 发表于 2012-12-18 10:02:19 | 显示全部楼层
本帖最后由 smset 于 2012-12-18 10:05 编辑
topdog 发表于 2012-12-18 09:41
谢谢指正switch的用法的确有问题,但是修改成如下方式同样也是有问题,只执行task2。发现keycode变量丢失 ...


uchar  task2(){
_SS
  while(1){
   WaitX(200);
   LED2=!LED2;   
  }
_EE
}

task2里面有没有和keycode相关的代码,只执行taks2,你是怎么发现keycode变量丢失呢? 而且keycode在哪里定义的?
另外,任务内部使用的变量,保险起见,都定义成静态局部变量 (static)
头像被屏蔽

出0入0汤圆

发表于 2012-12-18 10:10:15 | 显示全部楼层
smset 发表于 2012-12-18 10:02
uchar  task2(){
_SS
  while(1){

PIC里面定义在#include "OS_TMCU.H"
我测试过对比过使用 KEYDISPOSE()处理时,keycode就是可以正常判断的
而task3中,使用宏的方式就不能正确判断,并且如果在函数task3中if判断前加keycode=1; 赋值后,就可以
运行task1了,不然就会运行task2,所有可以确定if else中keycode丢失了。

出0入0汤圆

发表于 2012-12-18 10:14:07 | 显示全部楼层
本帖最后由 dr2001 于 2012-12-18 10:16 编辑
topdog 发表于 2012-12-18 09:41
谢谢指正switch的用法的确有问题,但是修改成如下方式同样也是有问题,只执行task2。发现keycode变量丢失 ...


还没有看你贴出的代码,所以不知道是否是丢自动变量导致的问题,但是对你提出的疑问进行简要的说明。

呃,丢动态变量值这个问题是ProtoThread Stackless实现必然遭遇的,解决办法有很多,可根据具体情况选用:
1、如果变量值是可恢复的,比如,可以通过调用一个函数获得(读某个寄存器啊,标志位啊,etc),那么在函数开头,switch语句执行之前,对内部自动变量进行初始化即可。缺点是每次调用的开销增加。
2、变量值不可恢复,并且函数代码不需要重入,内部变量可以static。这么干的缺点是费RAM,因为静态分配,编译器无法复用该变量了。人工复用是没有问题的。
3、自己写一个简单的Malloc/Free函数,用函数参数传来传去。

用ProtoThread带来了好处和坏处,究竟用不用,如何选用适当的方案,需要根据具体的应用环境“剪裁”。

出0入0汤圆

 楼主| 发表于 2012-12-18 10:47:19 | 显示全部楼层
本帖最后由 smset 于 2012-12-18 10:51 编辑

首先要确认KEYSCAN里面对keycode赋值没有。

根据你的提法,keycode是定位为全局变量的,而全局变量根本不存在丢失的问题。只有局部变量才存在丢失的问题。
所以我觉得问题不在于是否使用了protothread机制。

您可以再检查跟踪一下KEYSCAN任务的执行情况到底如何,而不是盯着task3。

还有 KEYDISPOSE 不是一个任务函数,也没有返回, 你怎么也放在RunTask里面运行?
头像被屏蔽

出0入0汤圆

发表于 2012-12-18 10:51:41 | 显示全部楼层
smset 发表于 2012-12-18 10:47
首先要确认KEYSCAN里面对keycode赋值没有。

根据你的提法,keycode是定位为全局变量的,而全局变量根本不 ...

没错,keycode定义为全局变量的,
我测试过
KEYDISPOSE()使用状态机方式处理,keycode就是可以正常判断的
task3()使用宏方式,keycode就出现值不对的情况。
也感觉很奇怪

出0入0汤圆

 楼主| 发表于 2012-12-18 11:10:36 | 显示全部楼层
topdog 发表于 2012-12-18 10:51
没错,keycode定义为全局变量的,
我测试过
KEYDISPOSE()使用状态机方式处理,keycode就是可以正常判断的 ...

能贴出修改后的完整代码么?
头像被屏蔽

出0入0汤圆

发表于 2012-12-18 11:14:11 | 显示全部楼层
smset 发表于 2012-12-18 10:47
首先要确认KEYSCAN里面对keycode赋值没有。

根据你的提法,keycode是定位为全局变量的,而全局变量根本不 ...

发现很一个线索,当keycode出现在嵌套调用子函数时出现问题。不嵌套子函数是不会出现问题的。
头像被屏蔽

出0入0汤圆

发表于 2012-12-18 11:20:05 | 显示全部楼层
smset 发表于 2012-12-18 11:10
能贴出修改后的完整代码么?
  1. /**********************************************************/
  2. #include            "htc.h"
  3. #include                 "OS_TMCU.H"
  4. //------configure bit seti
  5. __CONFIG (INTIO&WDTDIS&PWRTEN&MCLREN&BOREN&LVPDIS&PROTECT&UNPROTECT);

  6. /****小小调度器开始**********************************************/
  7. #define MAXTASKS 6
  8. static unsigned char timers[MAXTASKS];
  9. unsigned char currdt;
  10. #define _SS static unsigned char _lc; switch(_lc){case 0:
  11. #define _EE ;}; _lc=0; return 255;
  12. #define WaitX(tickets)  do {_lc=__LINE__; return tickets ;} while(0); case __LINE__:
  13. #define RunTask(TaskName,TaskID) do { if (timers[TaskID]==0) timers[TaskID]=TaskName(); }  while(0);

  14. #define CallSub(SubTaskName) do { _lc=__LINE__; return 0; case __LINE__:  currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
  15. #define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers[i]!=0)&&(timers[i]!=255)) timers[i]--;}

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

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

  26. //****************************
  27. //
  28. //****************************
  29. void ChipInit(void)
  30. {

  31. // Disable AD
  32. //ANSEL=0;
  33. //ANSELH=0;

  34. //---IO Setting
  35. //--PORTA
  36. /*
  37. #define         KEY_ZONE_I            RA0   
  38. #define         KEY_LEVEL_I            RA1   
  39. #define         FB_I                RA2
  40. #define         KEY_POWER_I            RA3
  41. #define         SEAT_I                RA4   
  42. #define         RS_I                RA5
  43. #define         BACK_LED_HI_O        RA6
  44. #define         IR_O                RA7        //传   
  45. */
  46. TRISA=0b00111111;

  47. //--PORTB
  48. /*
  49. #define         LR_I                RB0
  50. #define         BACK_LED_LOW_O        RB1
  51. #define         WAIST_LED_LOW_O    RB2
  52. #define         LEG_LED_LOW_O        RB3
  53. #define         LEG_LED_HI_O        RB4
  54. #define         WAIST_LED_HI_O        RB5
  55. #define         PGC_O                RB6
  56. #define         KEEP_PGD_O            RB7
  57. */
  58. TRISB=0b00000001;
  59. #ifdef    PIC_DEBUG   
  60. TRISB=0b00000111;
  61. #else
  62. TRISB=0b00000001;
  63. #endif


  64. PORTA=0;
  65. PORTB=0;

  66. //    ?   ゑ  /PWM 家塊
  67. VRCON=0;
  68. CMCON=0x07;

  69. OSCF=TRUE;                        //int osc 4Mhz
  70. //---T0 Setting
  71. // OPTION select  
  72. OPTION=0b10000000;//             1:2
  73. TMR0=205;// 0.1msec
  74. //----T2 Setting
  75. //PIE1=0b00000000;//
  76. //PIR1=0;
  77. // T2CON=0b00000000;//
  78. //PIR2=250;//250*4=1000(1msec)
  79. //TMR2=0;

  80. // USART Setting
  81. #ifdef    PIC_DEBUG   
  82. TXSTA=0;
  83. RCSTA=0;
  84. BRGH=1;
  85. SPBRG=25;            //  初4MHz  9600BPS     0.16

  86. SPEN=1;                //        
  87. TXEN=1;                //        竟
  88. CREN=0;                //綳     
  89. PEIE=0;
  90. RCIE=0;

  91. #endif

  92. }

  93. uchar   task1(){
  94. _SS
  95.   while(1){
  96.    WaitX(10);
  97.    LED1=!LED1;   
  98.   }
  99. _EE
  100. }

  101. uchar  task2(){
  102. _SS
  103.   while(1){
  104.    WaitX(200);
  105.    LED2=!LED2;   
  106.   }
  107. _EE
  108. }

  109. uchar KEYSCAN()
  110. {
  111. _SS
  112.         while(1)
  113.                 {
  114.         WaitX(1);
  115.         keynewcode=0;
  116.         if(!KEY_POWER_I)
  117.                 keynewcode |=POWERKEY;
  118.         if(!KEY_LEVEL_I)
  119.                 keynewcode|=LEVELkEY;
  120.         if(!KEY_ZONE_I)
  121.                 keynewcode|=ZONEkEY;

  122.         if(keynewcode!=0)
  123.                 {
  124.                 if(keycode==keynewcode)
  125.                         {
  126.                         if(keyCount>4)
  127.                                 {
  128.                                 KeyActiveyFlag=TRUE;
  129.                                 //LED1=TRUE;
  130.                                 }
  131.                         else
  132.                                 {
  133.                         keyCount++;
  134.                         //keycode=keynewcode;
  135.                                 }
  136.                         }
  137.                 else
  138.                         {
  139.                         keycode=keynewcode;
  140.                         keyCount=0;
  141.                         }
  142.                
  143.                 }
  144.         else
  145.                 {
  146.                 keyCount=0;
  147.                 keylongCount=0;
  148.                 }
  149.                 }
  150.        
  151.         _EE
  152. }

  153. uchar  task3()
  154.         {
  155.         _SS
  156.                 while(1)
  157.                         {
  158.                         WaitX(10);
  159.                         if(keycode==POWERKEY)
  160.                                 {
  161.                                                 LED5=TRUE;
  162.                                                 LED6=FALSE;
  163.                         //CallSub(task1);   [color=SandyBrown]//此处有问题,调用子函数后,不能正常运行[/color]
  164.                                 }
  165.                         else
  166.                                 {
  167.                                                 LED5=FALSE;
  168.                                                 LED6=TRUE;
  169.                         //CallSub(task2);  //此处有问题,调用子函数后,不能正常运行
  170.                                
  171.                                 }

  172.                         }
  173.                 _EE

  174. }
  175. uchar        KEYDISPOSE()  [color=Red]//可以正常运行[/color]
  176.         {

  177.                                 {
  178.                                 KeyActiveyFlag=FALSE;
  179.                                 switch(keycode)
  180.                                         {
  181.                                         case POWERKEY:
  182.                                                 LED3=TRUE;
  183.                                                 LED4=FALSE;
  184.                                                 break;
  185.                                                 case LEVELkEY:
  186.                                                 LED3=FALSE;
  187.                                                 LED4=TRUE;
  188.                                                 break;
  189.                                                 default :
  190.                                                         LED3=FALSE;
  191.                                                         LED4=FALSE;
  192.                                         }
  193.                                                        

  194.                
  195.                                 }
  196.        
  197.        
  198.         }
  199. void main()
  200. {
  201.         ChipInit();
  202.         T0IF=FALSE;
  203.            T0IE=TRUE;
  204.         GIE=TRUE;
  205.         KEEP_PGD_O=TRUE;
  206.         while(1){
  207.                         RunTask(KEYSCAN,0);
  208.                         RunTask(KEYDISPOSE,1);
  209.                         RunTask(task3, 2);
  210.     }
  211. }

  212. //****************************
  213. // interrupt sub
  214. //****************************
  215. void interrupt InterruptTig(void)
  216. {     
  217. // uchar i;  
  218.       
  219. //timer manager

  220.         //        UpdateTimers() ;  

  221.     if (Timer2msec>=100)
  222.         {
  223.         Timer2msec=0;
  224.         T2msecFlag=TRUE;
  225.                 UpdateTimers() ;  
  226.         }
  227.     else
  228.         Timer2msec++;
  229.      
  230.         
  231. //timer manager
  232.    
  233. TMR0=218;
  234.     T0IF=FALSE;


  235. }
复制代码

出0入93汤圆

发表于 2012-12-18 11:28:22 | 显示全部楼层
topdog 发表于 2012-12-18 11:20

请问你那个task3调用了子任务如何退出这个子任务?
去掉你所有任务中的while(1)吧,你会发现你有收获的。

出0入0汤圆

 楼主| 发表于 2012-12-18 11:32:24 | 显示全部楼层
本帖最后由 smset 于 2012-12-18 11:38 编辑
takashiki 发表于 2012-12-18 11:28
请问你那个task3调用了子任务如何退出这个子任务?
去掉你所有任务中的while(1)吧,你会发现你有收获的。 ...


对,还是takashiki厉害。

task2 任务写成永远循环执行了啊。 task3一旦调用task2,就永远在task2里面运行。

就好比一个子函数写成死循环了一个样,所以task1和task2是要去掉while(1)的。

但并不是所有,根据你的程序意图来看, 除了task1和task2这两个子任务外,其他任务是不用去掉while(1)的。

不论是顶层任务,还是子任务:

在任务函数,里面是是否使用while(1),完全是根据实际逻辑需要来决定的。

如果任务需要一直循环执行,那么就用while(1),

如果任务不需要循环运行,那么就不用while(1)。

如果任务需要运行几次,那么就用for(i=0;i<count;i++) 来控制循环次数。

就和你平时写其他普通函数的思路是一样的。


出0入0汤圆

 楼主| 发表于 2012-12-18 11:40:06 | 显示全部楼层
topdog 发表于 2012-12-18 11:20

去掉task1和task2里面的 while(1)
头像被屏蔽

出0入0汤圆

发表于 2012-12-18 13:42:37 | 显示全部楼层
smset 发表于 2012-12-18 11:40
去掉task1和task2里面的 while(1)

的确是死循环的问题,当时写程序是就是照样画瓢
呵呵,结果没有学好变画虎不成反类犬。
确认不是调度器的问题。

出0入0汤圆

发表于 2012-12-18 14:11:01 | 显示全部楼层
强力的mark一下子.嘎嘎.

出0入0汤圆

发表于 2012-12-18 14:44:22 | 显示全部楼层
smset 发表于 2012-12-17 18:59
确实是个新思路,不过这样写,恐怕以后读代码的要骂人了。呵呵。

能否评估下330楼的版本的运行效率,以 ...

330楼的代码的一点点看法:

1 static unsigned char timers[MAXTASKS]考虑修改为:
volatile unsigned char timers[MAXTASKS] = { 0 }; 给定全部初值为0,同时加上volatile防止寄存器优化。是否加static可以根据实际所需的作用域决定。

2 _SS, _EE, WaitX的实现,就通常情形来说,精简度已经很高了。但具体到某个编译器来说,可能还有提升,但似乎没什么意义了。
目前的WaitX,在51系统上,理论上只需要MOV Mem, #Imm; MOV Reg, #Imm; RET三条指令。由于要返回两个值,基本上就是最优了。
如果所用的编译器支持Struct in Reg返回(准确的说,是ABI支持),那么还可以节约一个字节,变成MOV Reg, #Imm; MOV Reg, #Imm; RET:方法就是定义一个结构体,里边两个char,返回整个结构体,看起来代码复杂的东西,编译后的结果不一定复杂。

switch/case方法效率软肋是在被调用时候找到case的额外开销:如果case的值(LC)不具备JumpTable的可优化性,那么每多一个WaitX,就意味着至少要多一个CMP Reg, #imm和JEQ Addr;序列比较在实际运行中效率是很低下的,尤其是状态数量多了之后。
当然,目前看,此事是代码风格,编译器依赖,符合标准三者不可得兼的事情,必须加以选择。

3 基于ProtoThread的Stackless特征,并且适当考虑通用性,可以考虑_SS中的变量定义拆出来;要么单独一个宏定义,要么用函数参数传。这样在switch之前的代码可以每次都执行到,能够用于动态恢复某些局部变量。在特定场合是有价值的,但不是处处有价值。

51系统是MCU系统里一个比较特殊的情形,尤其是Keil,呵呵。

出0入0汤圆

 楼主| 发表于 2012-12-18 15:13:39 | 显示全部楼层
dr2001 发表于 2012-12-18 14:44
330楼的代码的一点点看法:

1 static unsigned char timers[MAXTASKS]考虑修改为:

非常感谢! 真的是很有深度的评测!  

第3项中,把_SS独立出去然后动态恢复变量的方法,我还看得不是很明白,能否再具体些?

出0入0汤圆

发表于 2012-12-18 15:37:36 | 显示全部楼层
smset 发表于 2012-12-18 15:13
非常感谢! 真的是很有深度的评测!  

第3项中,把_SS独立出去然后动态恢复变量的方法,我还看得不是很 ...


考察代码片段

  1. uint8_t Task(uint8_t SystemSignal) {
  2.   static uint8_t _lc = 0.

  3.   uint16_t dma_handler;
  4.   dma_handler=GetCurrDmaHandler();

  5.   if(SystemSignal == FatalError) {
  6.     //Do Something Urgent.
  7.   }

  8.   switch(lc){
  9.   case ...:
  10.   //Use dma_handler.
  11.   ...
  12. }
复制代码
1、ProtoThread函数开始之后,switch之前的代码是每次调用ProtoThread必然会执行到的。
2、有一些状态变量之类的东西,其实是没必要保存的,因为可以通过调用别的函数重新得到。当然,这种情形不会特别经常出现,但是确实有,典型就是DMA的缓存控制。
另外,如果要构造一个迭代器(Iterator),也就是说For里边会有WaitX,为了省内存,动态分配内存或者动态分配一个地址当计数器变量也完全可以;这样就有“恢复”ProtoThread中的相关变量需求,要不然就用参数传进来。
3、另外一个就是系统事件的处理,这个典型见于contiki。这个就更少见了。

也就是说,通常情况下,_SS是能满足需求的;在特殊情况下,是会有要求拆开的。

出0入0汤圆

 楼主| 发表于 2012-12-18 16:01:56 | 显示全部楼层
在_SS之前是可以定义变量的。

unsigned char Task(){
   unsigned int  dma_handler;  

  _SS
   ...
   WaitX(20);
   dma_handler=GetCurrDmaHandler();
  //Use dma_handler.

_EE
}

不知这样是否能满足要求?

出0入0汤圆

发表于 2012-12-18 16:04:31 | 显示全部楼层
如果每次都要用的话,就很不方便,同样的代码次次执行。

反正这是少数情形下才有的需求,不支持也没什么问题。

出0入93汤圆

发表于 2012-12-18 16:10:32 | 显示全部楼层
dr2001 发表于 2012-12-18 14:44
330楼的代码的一点点看法:

1 static unsigned char timers[MAXTASKS]考虑修改为:

Keil C51是比较RZ了点,优化很差~~~
你说的
2 _SS, _EE, WaitX的实现,就通常情形来说,精简度已经很高了。但具体到某个编译器来说,可能还有提升,但似乎没什么意义了。
,我在51上试了,可以。每次Wait占用5个字节,通过R6、R7分别传递出来。但我实现的每个子任务会多占用一个字节RAM,当然这个RAM也可以通过绝对定位用作其他用途,其实内存并没有多消耗。

出0入0汤圆

发表于 2012-12-18 16:45:15 | 显示全部楼层
上学的时候   就这个问题  还跟我们单片机老师   好好讨论了一下
头像被屏蔽

出0入0汤圆

发表于 2012-12-18 17:06:16 | 显示全部楼层
takashiki 发表于 2012-12-18 16:10
Keil C51是比较RZ了点,优化很差~~~
你说的,我在51上试了,可以。每次Wait占用5个字节,通过R6、R7分 ...

pic上SS EE WAITX自动优化代码

本帖子中包含更多资源

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

x

出0入0汤圆

 楼主| 发表于 2012-12-18 17:19:31 | 显示全部楼层
我觉得keil优化也还行啊,呵呵

本帖子中包含更多资源

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

x

出0入0汤圆

发表于 2012-12-18 17:45:33 | 显示全部楼层
AVR的waitx:
  1. unsigned char task1(void)
  2. {
  3.         _SS
  4. 11a:        80 91 60 00         lds        r24, 0x0060
  5. 11e:        88 23               and        r24, r24
  6. 120:        19 f0               breq        .+6              ; 0x128 <task1+0xe>
  7. 122:        84 36               cpi        r24, 0x64        ; 100
  8. 124:        41 f4               brne        .+16             ; 0x136 <task1+0x1c>
  9. 126:        05 c0               rjmp        .+10             ; 0x132 <task1+0x18>
  10.         while(1)
  11.         {
  12.                 WaitX(10);
  13. 128:        84 e6               ldi        r24, 0x64        ; 100
  14. 12a:        80 93 60 00         sts        0x0060, r24
  15. 12e:        8a e0               ldi        r24, 0x0A        ; 10
  16. 130:        08 95               ret
  17.                 led2_on();
  18. 132:        c4 98               cbi        0x18, 4        ; 24
  19. 134:        f9 cf               rjmp        .-14             ; 0x128 <task1+0xe>
  20.         //        WaitX(20);
  21.         //        led2_off();
  22.         }
  23.         _EE
  24. 136:        10 92 60 00         sts        0x0060, r1
  25. 13a:        8f ef               ldi        r24, 0xFF        ; 255
  26. }
  27. 13c:        08 95               ret
复制代码

出0入0汤圆

发表于 2012-12-18 17:47:53 | 显示全部楼层
stm8的WaitX:
  1.     107          unsigned char task1(void)
  2.     108          {
  3.     109              _SS
  4.    \                     task1:
  5.    \   000000 C60000                LD        A, L:??lc_2
  6.    \   000003 2706                  JREQ      L:??task1_0
  7.    \   000005 A070                  SUB       A, #0x70
  8.    \   000007 2709                  JREQ      L:??task1_1
  9.    \   000009 200D                  JRA       L:??task1_2
  10.     110              while(1)
  11.     111              {
  12.     112                  WaitX(10);
  13.    \                     ??task1_0:
  14.    \   00000B 35700000              MOV       L:??lc_2, #0x70
  15.    \   00000F A60A                  LD        A, #0xa
  16.    \   000011 81                    RET
  17.     113                       led2_on();
  18.    \                     ??task1_1:
  19.    \   000012 7217500F              BRES      L:0x500f, #0x3
  20.    \   000016 20F3                  JRA       L:??task1_0
  21.     114              }
  22.     115              _EE
  23.    \                     ??task1_2:
  24.    \   000018 725F0000              CLR       L:??lc_2
  25.    \   00001C A6FF                  LD        A, #0xff
  26.    \   00001E 81                    RET
复制代码

出0入0汤圆

发表于 2012-12-18 17:58:40 | 显示全部楼层
还是51厉害啊,只有26bytes,avr有36bytes,stm8有32bytes。

出0入0汤圆

 楼主| 发表于 2012-12-18 18:29:53 | 显示全部楼层
本帖最后由 smset 于 2012-12-19 13:00 编辑

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

敬请评测该版本,看是否还能优化:

  1. /****小小调度器开始**********************************************/
  2. #define MAXTASKS 2
  3. static unsigned char timers[MAXTASKS];
  4. unsigned char currdt;
  5. #define _SS static unsigned char _lc; switch(_lc){default:
  6. #define _EE ;}; _lc=0; return 255;
  7. #define WaitX(tickets)  do {_lc=__LINE__+((__LINE__%256)==0); return tickets ;} while(0); case __LINE__+((__LINE__%256)==0):
  8. #define RunTask(TaskName,TaskID) do { if (timers[TaskID]==0) timers[TaskID]=TaskName(); }  while(0);

  9. #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);
  10. #define UpdateTimers() unsigned char i; for(i=MAXTASKS;i>0 ;i--){if((timers[i-1]!=0)&&(timers[i-1]!=255)) timers[i-1]--;}

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

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

  21. sbit LED1 = P2^1;
  22. sbit LED2 = P2^2;

  23. void InitT0()
  24. {
  25.         TMOD = 0x21;
  26.         IE |= 0x82;  // 12t
  27.         TL0=0Xff;
  28.         TH0=0XDB;//22M---b7;
  29.         TR0 = 1;
  30. }

  31. void INTT0(void) interrupt 1 using 1
  32. {
  33.     UpdateTimers();

  34.     TL0=0Xff;    //10ms 重装
  35.     TH0=0XDB;//b7;   
  36. }


  37. void  task1(){
  38. _SS
  39.   while(1){
  40.    WaitX(50);
  41.    LED1=!LED1;   
  42.   }
  43. _EE
  44. }

  45. void  task2(){
  46. _SS
  47.   while(1){
  48.    WaitX(100);
  49.    LED2=!LED2;   
  50.   }
  51. _EE
  52. }


  53. void main()
  54. {
  55.         InitT0();
  56.         while(1){
  57.            RunTask(task1,0);
  58.            RunTask(task2,1);
  59.     }
  60. }

复制代码
在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) 稳定性代价:小小调度器本质上仅仅是几个宏而已,未涉及任何对内部寄存器或堆栈的操作,避免了引入不稳定风险因素,所有操作都在可预见,可把控的前提下进行。
--------------------------------------------------------------------------------

出0入93汤圆

发表于 2012-12-18 19:31:53 | 显示全部楼层
smset 发表于 2012-12-18 18:29
优化无止境!呵呵,330楼看似不能再优化了,但我再尝试做一次优化:

敬请评测该版本,看是否还能优化:在k ...


可以的,按照388楼的第二条,WaitX是可以继续优化的,优化的程度与CPU相关。51可以优化到4个字节,AVR可以优化到6个字节,STM8可以到5个字节,而且运行时间可以缩短。

全部的我还没有写出来,只弄出了一部分:
51专用优化版部分代码:
  1. union __CTCB{                                           //BIG ENDIAN
  2.         struct {
  3.                 uchar LC;
  4.                 uchar Delay;
  5.         } Param;
  6.         ushort Value;
  7. } __TCB [MAXTASKS];                                                //任务控制块

  8. #define BEGIN_TASK(TaskId) void Task##TaskId(){ switch(__TCB[TaskId].Param.LC){case 0: {
  9. #define END_TASK   }} DPTR = 0; return;}
  10. #define WaitX(Ticks)  DPTR = (__LINE__ << 8) | (Ticks); return; case __LINE__:
  11. #define RunTask(TaskId) { if (__TCB[TaskId].Param.Delay == 0) {Task##TaskId(); __TCB[TaskId].Value = DPTR; } }                           
  12. #define CallSub(TaskId) Task##TaskId(); __TCB[TaskId].Value = DPTR; if(DPL) { DPH = __LINE__; return; }
复制代码
部分汇编代码(一个完整的任务):
  1.     83: BEGIN_TASK(1)
  2. C:0x00E2    E512     MOV      A,0x12
  3. C:0x00E4    24AB     ADD      A,#0xAB
  4. C:0x00E6    6018     JZ       C:0100
  5. C:0x00E8    2455     ADD      A,#0x55
  6. C:0x00EA    7016     JNZ      C:0102
  7.     84:    CallSub(2);                                //调用子任务
  8. C:0x00EC    3108     ACALL    Task2(C:0108)
  9. C:0x00EE    858314   MOV      0x14,DPH(0x83)
  10. C:0x00F1    858215   MOV      0x15,DPTR(0x82)
  11. C:0x00F4    E582     MOV      A,DPTR(0x82)
  12. C:0x00F6    6004     JZ       C:00FC
  13. C:0x00F8    758354   MOV      DPH(0x83),#0x54
  14. C:0x00FB    22       RET      
  15.     85:    WaitX(3);                                    //WaitX只占用了4个字节,两条指令
  16. C:0x00FC    905503   MOV      DPTR,#0x5503
  17. C:0x00FF    22       RET      
  18.     86:    LED2=!LED2;   
  19. C:0x0100    B2A2     CPL      LED2(0xA0.2)
  20.     87: END_TASK
  21. C:0x0102    E4       CLR      A                   //这个优化得多差劲啊,直接MOV DPTR, #0不就完了么?
  22. C:0x0103    F583     MOV      DPH(0x83),A
  23. C:0x0105    F582     MOV      DPTR(0x82),A
  24. C:0x0107    22       RET      
复制代码
Keil C51的优化能力,我实在是无法吐槽了,真的很烂。对于DPTR的优化差,@R1从来不会用,内存搬运时把R0折腾死R1也只在旁边冷眼旁观,绝不参与。两个字节拼成一个整型他居然真的去计算一次,……实在太多了。
MDK的RVCT优化能力很强,和Keil C51完全不可同日而语,甩不了九条街甩八条街是没问题的。

出0入93汤圆

发表于 2012-12-18 19:43:49 | 显示全部楼层
zsmbj 发表于 2012-12-18 17:58
还是51厉害啊,只有26bytes,avr有36bytes,stm8有32bytes。

51的结构决定了他的代码密度高,因为它是CISC指令集,却又拥有大量的通用寄存器。
RISC对内存操作太差使得效率变低,代码变长。当然,PIC是个特例,他的代码密度更高。
stm8因为没有通用寄存器的关系,使用内存操作明显占用更多的代码。

按照388楼的第二条可以减少一些对内存的操作,直接保存到寄存器中。

出0入0汤圆

 楼主| 发表于 2012-12-18 19:58:53 来自手机 | 显示全部楼层
zsmbj 发表于 2012-12-18 17:58
还是51厉害啊,只有26bytes,avr有36bytes,stm8有32bytes。

按400楼的版本,51下的Task应该只有十几个字节了

出0入0汤圆

发表于 2012-12-18 20:23:01 | 显示全部楼层
本帖最后由 dr2001 于 2012-12-18 20:28 编辑
takashiki 发表于 2012-12-18 19:31
可以的,按照388楼的第二条,WaitX是可以继续优化的,优化的程度与CPU相关。51可以优化到4个字节,AVR可 ...


直接往DPTR写数据啊……这分明是在挑衅Keil C51的常用寄存器追踪能力么。
建议此事慎重,不知道Keil C51对保护DPTR是怎么定义的,如果手册里没有明确写明的话,有潜在风险。
头像被屏蔽

出0入0汤圆

发表于 2012-12-18 21:17:23 | 显示全部楼层
這個是PIC使用前後比較

本帖子中包含更多资源

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

x

出0入0汤圆

 楼主| 发表于 2012-12-18 21:35:17 | 显示全部楼层
本帖最后由 smset 于 2012-12-18 21:36 编辑
topdog 发表于 2012-12-18 21:17
這個是PIC使用前後比較


谢谢,看来rom也少了10%以上。看来400楼的优化是普遍有效的。

出0入0汤圆

发表于 2012-12-18 21:41:58 | 显示全部楼层
#define UpdateTimers() unsigned char i; for(i=MAXTASKS;i>0 ;i--){if((timers[i-1]!=0)&&(timers[i-1]!=255)) timers[i-1]--;}

这句好像并不能减小代码,请将:
#define MAXTASKS 5再试试。
我在winavr下测试,这个方法比原来会增加2个字节rom


出0入0汤圆

 楼主| 发表于 2012-12-18 21:57:06 | 显示全部楼层
zsmbj 发表于 2012-12-18 21:41
#define UpdateTimers() unsigned char i; for(i=MAXTASKS;i>0 ;i--){if((timers!=0)&&(timers!=255)) time ...

keil下 ,会少2字节rom,一直会少2字节。

另外,400楼的CallSub 应该是:


#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);


已经更新。

出0入0汤圆

 楼主| 发表于 2012-12-18 22:03:52 | 显示全部楼层
本帖最后由 smset 于 2012-12-18 22:09 编辑

另外,void型任务函数的版本我再次评估了一下,rom效率也相当高。而且:WaitX增量rom要低1个字节,这个很有吸引力。

我都有点纠结了,究竟409楼与400楼两个版本,哪种方式综合指标更优,请各位帮忙对比分析。

[/code]
#include "stc89c51.h"
/****小小调度器开始**********************************************/
#define MAXTASKS 2
unsigned char currdt;
static unsigned char timers[MAXTASKS];
#define _SS   static unsigned char lc; switch(lc){default:
#define _EE   ;}; lc=0;
#define WaitX(tickets)  do {lc=__LINE__+(__LINE__%256==0); currdt=tickets; return ;} while(0); case __LINE__+(__LINE__%256==0):
#define RunTask(TaskName,TaskID) do { if (timers[TaskID]==0){  currdt=255; TaskName(); timers[TaskID]=currdt; }}  while(0);
#define CallSub(SubTaskName) do { lc=__LINE__+(__LINE__%256==0); currdt=0; return; case __LINE__+(__LINE__%256==0):  SubTaskName();  if (currdt!=255) return;}  while(0);

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

#define UpdateTimers() unsigned char i; for(i=MAXTASKS;i>0;i--){if((timers[i-1]!=0)&&(timers[i-1]!=255)) timers[i-1]--;}
/*****小小调度器结束*******************************************************/



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;   
}


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

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


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

[/code]

出0入0汤圆

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

本版积分规则

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

GMT+8, 2024-4-26 12:26

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

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