搜索
bottom↓
楼主: smset

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

  [复制链接]

出0入0汤圆

发表于 2013-10-29 20:08:10 | 显示全部楼层
好喜欢 好牛呀   我是一直潜水的会员  今天终于忍不住冒出来了 看到这么好的帖子 我再也忍不住了  我会继续跟踪  继续顶  顶  我努力的顶   

出0入0汤圆

发表于 2013-10-31 07:53:20 | 显示全部楼层
先收藏再学习

出0入8汤圆

发表于 2013-10-31 08:23:27 | 显示全部楼层
mathison 发表于 2013-10-26 16:53
本人C开发新手
义隆的C编译器用起来真痛苦  
有用C做义隆单片机开发的朋友吗 请问你们用什么编译器 ...

用熟了就好了  俺当年用的第一版C编译器  那才叫痛苦(不过也搞定了),现在的改进了不少了。

出0入0汤圆

发表于 2013-10-31 08:39:24 | 显示全部楼层
标记一下

出0入0汤圆

发表于 2013-10-31 08:47:06 | 显示全部楼层
先谢楼主分享

出0入0汤圆

发表于 2013-10-31 10:43:28 | 显示全部楼层
lz实在太强了,一定要mark

出0入0汤圆

发表于 2013-10-31 10:47:52 | 显示全部楼层
感谢各位分享,学习了!!!回去试试在51单片机上做一下~

出0入0汤圆

 楼主| 发表于 2013-10-31 17:14:38 | 显示全部楼层
本帖最后由 smset 于 2013-10-31 17:17 编辑

更新一版,增加对timers清零的操作,本来全局变量应该初值就是0,但是我发现keil编译器有时不这么干。


  1. #include <stc89c51.h>
  2. /****小小调度器开始**********************************************/
  3. #define MAXTASKS 3
  4. volatile unsigned char timers[MAXTASKS];
  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 RunTaskA(TaskName,TaskID) { if (timers[TaskID]==0) {timers[TaskID]=TaskName(); continue;} }   //前面的任务优先保证执行

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

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

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


  23. sbit LED1 = P2^1;
  24. sbit LED2 = P2^2;

  25. sbit LED0 = P2^5;

  26. unsigned char task0(){
  27. _SS
  28.   while(1){
  29.    WaitX(50);
  30.    LED0=!LED0;   
  31.   }
  32. _EE
  33. }

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

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

  50. void InitT0()
  51. {
  52.         TMOD = 0x21;
  53.         IE |= 0x82;  // 12t
  54.         TL0=0Xff;
  55.         TH0=0XDB;
  56.         TR0 = 1;
  57. }

  58. void INTT0(void) interrupt 1 using 1
  59. {
  60.     TL0=0Xff;    //10ms 重装
  61.     TH0=0XDB;//b7;   

  62.     UpdateTimers();

  63.     RunTask(task0,0);//任务0具有精确按时获得执行的权限,要求:task0每次执行消耗时间<0.5个 ticket
  64. }




  65. void main()
  66. {
  67.         InitT0();
  68.         InitTasks(); //初始化任务,实际上是给timers清零
  69.         while(1){
  70. //           RunTask(task0,0);
  71.                 RunTaskA(task1,1);//任务1具有比任务2高的运行权限                  
  72.            RunTaskA(task2,2);//任务2具有低的运行权限                  
  73.       }
  74. }
复制代码

出0入0汤圆

 楼主| 发表于 2013-10-31 17:16:55 | 显示全部楼层
这样就保险了。

出0入0汤圆

发表于 2013-11-1 16:48:38 | 显示全部楼层
Gorgon_Meducer 发表于 2013-1-6 21:57
如果想节省资源,沿着你的路走下去就可以了,因为实际上目前的小小调度器就是裸机,巧妙的借用了宏来固化 ...

我有时也在纠结如何设计更加合理的任务。一个项目来了,应该如何分析、创建任务?有什么指导性思想或一般规律?使得划分的任务高内聚低耦合?
当程序跑起来了,回头看,总感觉任务划分不太合理,不满意。
希望傻孩子这方面指点下,然后有什么资料的可以学习?
谢谢

出0入0汤圆

发表于 2013-11-1 17:41:50 | 显示全部楼层
终于看完了,眼睛好累,呵呵

出0入0汤圆

发表于 2013-11-1 22:54:03 来自手机 | 显示全部楼层
收藏了,谢谢分享啊,有时间用下

出0入296汤圆

发表于 2013-11-4 10:16:22 | 显示全部楼层
hxke 发表于 2013-11-1 16:48
我有时也在纠结如何设计更加合理的任务。一个项目来了,应该如何分析、创建任务?有什么指导性思想或一般 ...

当你已经掌握了划分任务的方法,那么剩下的就是要寻找划分任务的指导性原则。如果你已经能理解到面向对象只是一个
思想,并且已经能够在大部分非面向对象语言中实践,那么建议你看看设计模式的书,比如设计模式之禅——也许一时看
不懂,但时间长就OK了。我不知道技法类书是不是对你仍然有帮助,但我想说,技法类书始终是关于工具的,工具始终
是为思想服务的,所以,如果你已经掌握了一些技法,我建议你多花功夫在编程思想类的书籍上,不要再执着于技法,比如
怎么写调度器更好啊,状态机和操作系统哪个更好啊,浪费时间,那都是浮云,关键是思想。

我不清楚你的具体情况,但有几个基本思想要逐一掌握:
1、链表的思想(或者说技法)
2、数据结构的基本内容
3、操作系统中多任务通信的思想和技法(非常重要),操作系统本身不重要,怎么调度也不重要,关键是所有操作系统都
    共通的基本思想部分,多任务间如何通信的部分。
4、事件驱动的思想——就是任务与任务之间是通过事件来触发的。你可以理解为多米诺骨牌。多米诺骨牌可以认为有很多
    模块组成,但模块与模块之间,往往是触发关系,只要触发了,直到这个模块完成,都不再受外界影响。我希望这个形象
    的比喻能给你关于事件驱动的映像。你在简历任务的时候,如果能把任务与任务之间是如何彼此触发的关系整理成脉络
   基本上任务划分的方式就清晰了。动手做一做就知道,其实不难。
5、黑盒子思想(面向接口开发)
6、设计模式

出0入0汤圆

 楼主| 发表于 2013-11-4 10:56:55 | 显示全部楼层
与715楼同理,静态变量初值也应该是0,不过为了防止某些编译器不按标准来执行,所以也把_lc静态变量显式的赋一个初值。


  1. #include <stc89c51.h>
  2. /****小小调度器开始**********************************************/
  3. #define MAXTASKS 3
  4. volatile unsigned char timers[MAXTASKS];
  5. #define _SS static unsigned char _lc=0; 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 RunTaskA(TaskName,TaskID) { if (timers[TaskID]==0) {timers[TaskID]=TaskName(); continue;} }   //前面的任务优先保证执行

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

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

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


  23. sbit LED1 = P2^1;
  24. sbit LED2 = P2^2;

  25. sbit LED0 = P2^5;

  26. unsigned char task0(){
  27. _SS
  28.   while(1){
  29.    WaitX(50);
  30.    LED0=!LED0;   
  31.   }
  32. _EE
  33. }

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

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

  50. void InitT0()
  51. {
  52.         TMOD = 0x21;
  53.         IE |= 0x82;  // 12t
  54.         TL0=0Xff;
  55.         TH0=0XDB;
  56.         TR0 = 1;
  57. }

  58. void INTT0(void) interrupt 1 using 1
  59. {
  60.     TL0=0Xff;    //10ms 重装
  61.     TH0=0XDB;//b7;   

  62.     UpdateTimers();

  63.     RunTask(task0,0);//任务0具有精确按时获得执行的权限,要求:task0每次执行消耗时间<0.5个 ticket
  64. }




  65. void main()
  66. {
  67.         InitT0();
  68.         InitTasks(); //初始化任务,实际上是给timers清零
  69.         while(1){
  70. //           RunTask(task0,0);
  71.                 RunTaskA(task1,1);//任务1具有比任务2高的运行权限                  
  72.            RunTaskA(task2,2);//任务2具有低的运行权限                  
  73.       }
  74. }
复制代码

出0入0汤圆

发表于 2013-11-6 16:45:56 | 显示全部楼层
爬了700多楼, 受益匪浅, 感谢楼主和各位网友的思想.

出0入4汤圆

发表于 2013-11-7 11:55:25 | 显示全部楼层
smset 发表于 2013-11-4 10:56
与715楼同理,静态变量初值也应该是0,不过为了防止某些编译器不按标准来执行,所以也把_lc静态变量显式的 ...

在freescale的S08上用,这种写法被CW优化掉了
unsigned char  task2(){
_SS
  while(1){                    //information:C4000 Condition always TRUE
   WaitX(100);                        //information:C4001 Condition always FALSE
   LED2=!LED2;   
  }
_EE                            //warning:C5660 Removed dead code
}

出0入4汤圆

发表于 2013-11-7 12:03:52 | 显示全部楼层
本帖最后由 liuqian 于 2013-11-7 12:14 编辑

condition always false的警告是do{}while(0)造成的,不影响。
现在只运行了一个闪灯的任务,removed dead code也正常

出0入0汤圆

发表于 2013-11-8 08:37:19 | 显示全部楼层

出0入0汤圆

发表于 2013-11-8 09:27:06 | 显示全部楼层
学习,参考,收藏!

出0入0汤圆

发表于 2013-11-11 18:41:05 | 显示全部楼层
受益匪浅  真的很经典  超级喜欢  我之前写过 in_timeOS操作系统 也是基于极苛刻资源的单片机 但是现在看来  还是这个比较实用

出0入0汤圆

发表于 2013-11-15 22:32:36 | 显示全部楼层
#define InitTasks() {unsigned char i; for(i=MAXTASKS;i>0 ;i--) timers[i]=0; }
楼主老大这样我怎么运行不正常。
#define InitTasks() {unsigned char i; for(i=0;i<MAXTASKS ;i++) timers[i]=0; }
改成这样就可以正常运行了。。

出0入0汤圆

发表于 2013-11-25 22:29:09 | 显示全部楼层
一个字  绝!!!

出0入0汤圆

发表于 2013-11-25 23:49:32 | 显示全部楼层
真牛啊!

出0入0汤圆

发表于 2014-1-3 17:24:49 | 显示全部楼层
不明觉厉,mark了慢慢研究。

出0入0汤圆

发表于 2014-1-3 17:38:31 | 显示全部楼层
小小调度器,博大精深啊!!!

出0入0汤圆

发表于 2014-1-3 20:07:38 来自手机 | 显示全部楼层
顶顶,好好消化下!

出0入0汤圆

发表于 2014-1-3 21:52:13 | 显示全部楼层
慢慢看看

出0入0汤圆

发表于 2014-1-4 10:11:52 | 显示全部楼层
这个得认真看看,谢谢分享!

出0入0汤圆

发表于 2014-1-21 19:50:37 | 显示全部楼层
能不能给个信号量的例子啊?多谢了

出0入0汤圆

发表于 2014-1-21 21:00:28 | 显示全部楼层
太好了,谢谢楼主

出0入0汤圆

发表于 2014-1-21 21:07:49 | 显示全部楼层
收藏学习。

出0入0汤圆

发表于 2014-1-22 10:00:01 | 显示全部楼层
不错,收藏了

出0入0汤圆

发表于 2014-2-11 08:43:38 | 显示全部楼层
好帖子,收藏了

出0入0汤圆

发表于 2014-2-11 09:37:15 | 显示全部楼层
mark 小小调度器  
116 209 257 330 397 492 540 楼,楼主建议用492版

出0入0汤圆

发表于 2014-2-11 10:24:19 | 显示全部楼层
不明觉历啊。

出0入0汤圆

 楼主| 发表于 2014-2-11 11:01:03 | 显示全部楼层
本帖最后由 smset 于 2014-2-11 14:47 编辑

小小调度器至2012年发布以来,受到了一些坛友们的关注和支持,在此表示感谢!!!

新的一年来了,总要做点新的事情。 在小小调度器这一块,要做些什么呢?

我有一个想法: 将小小调度器和面向对象设计思想结合起来,形成一个新的单片机编程框架。


小小调度器虽然是针对极小资源单片机设计的,但事实上并不是真的只能用于很简单的小产品上,

举例来说,我公司的产品功能实际是比较复杂的,一个CPU要实现完整的tcpip协议栈+基于SD卡的文件系统+mp3解码播放+也有液晶和按键+图形化人机界面+串口+Modbus等等。

也一样采用了小小调度器的任务调度框架,小小调度器也能从容应对这类项目,任务调度的稳定性表现优异,这块一直让我们很放心。

而另一方面,复杂系统的软件代码,客观地说,最好是能结合面向对象OOPC的思想,这几十年面向对象的编程方法在软件领域全面开花不是没有道理的。

哪怕是嵌入式单片机软件代码,只要复杂到一定程度,就可以考虑采用面向对象的编程思想。随着单片机处理能力的普遍增强,我觉得总体来说,以后单片机编程的思想迟早会涉及到面向对象的。

因此我计划基于小小调度器,打造一个基于任务对象的调度系统。

进而,再升华成一种面向任务的嵌入式系统编程方法,这会更加针对嵌入式系统软件开发,,这也算是我自身的一个提升吧。



我认为各种编程方法以及设计模式等等,一个是各有其最佳的应用领域,另一个方面就是都是为了降低开发人员的思考和理解难度。

我觉得针对单片机的开发,面向任务应该是一个比较容易理解的方法,

实际上,我们已经非常习惯于将单片机软件的各个处理部分称为“任务”,比如键盘任务,液晶显示任务,串口任务 ,。。。等等。 而不是称之为键盘状态函数,液晶显示状态函数,串口状态函数。。。。。。

这说明, 我们在直观上已经把任务理解为一个单片机软件系统的基本功能单元,“任务”,Task, 它所蕴含的东西比数据更动态,比状态更完整,也确实更加能精准地表达我们想要的处理过程。



这里举个实例,结合oopc的思想,将小小调度器的任务抽象为一个Task类,然后键盘或串口任务都基于是继承Task类的子类,以上代码在CBuilder验证过:

先贴出Demo代码,结合了lw_oopc:


  1. #ifndef TASK_H_INCLUDED_
  2. #define TASK_H_INCLUDED_

  3. #include "lw_oopc.h"
  4. #include "xxddq.h"

  5. ABS_CLASS(Task)
  6. {
  7.     unsigned char _lc;
  8.     unsigned char _timer;
  9.     unsigned char _index;
  10.    
  11.     void (*init)(Task* t,unsigned char index); // 初始化
  12.     unsigned char  (*execute)(Task* t);

  13. };

  14. CLASS(KeyTask)
  15. {
  16.     EXTENDS(Task);        // 继承Task抽象类
  17.     void (*init)(KeyTask* t,unsigned char index); // 初始化   
  18.     unsigned char  (*execute)(KeyTask* t,unsigned char _index);
  19. };

  20. CLASS(UartTask)
  21. {
  22.     EXTENDS(Task);        // 继承Task抽象类
  23.     unsigned char uartid;
  24.     void (*setuart)(UartTask* t,unsigned char uartindex); // 初始化   
  25.     void (*init)(UartTask* t,unsigned char index); // 初始化
  26.     unsigned char  (*execute)(UartTask* t,unsigned char _index);
  27. };

  28. #endif

  29. #include "stdio.h"
  30. #include "string.h"
  31. #include "task.h"
  32. /* 初始化任务 */

  33. void Task_init(Task* t,unsigned char index)
  34. {
  35.     t->_index=index;
  36.     t->_lc=0;
  37.     t->_timer=0;
  38. }

  39. /* 任务执行 */
  40. unsigned char  Task_execute(Task* t)
  41. {
  42. }

  43. ABS_CTOR(Task)
  44. FUNCTION_SETTING(init, Task_init);
  45. FUNCTION_SETTING(execute, Task_execute);
  46. END_ABS_CTOR

  47. /* 键盘任务 */
  48. void KeyTask_init(KeyTask* t,unsigned char index)
  49. {
  50.    t->Task.init(t,index);
  51. }

  52. unsigned char  KeyTask_execute(KeyTask* t)
  53. {
  54. _SS
  55.   while(1){
  56.     printf("I am KeyTask!\n");
  57.     WaitX(100);
  58.   }
  59. _EE
  60. }


  61. CTOR(KeyTask)
  62. SUPER_CTOR(Task);
  63. FUNCTION_SETTING(init, KeyTask_init);
  64. FUNCTION_SETTING(Task.execute, KeyTask_execute);
  65. END_CTOR


  66. /* Uart任务 */
  67. void UartTask_init(UartTask* t,unsigned char index)
  68. {
  69.    t->Task.init(t,index);
  70. }
  71. void UartTask_setuart(UartTask* t,unsigned char uartindex)
  72. {
  73.    t->uartid=uartindex;
  74. }
  75. unsigned char  UartTask_execute(UartTask* t)
  76. {
  77. _SS
  78.   while(1){
  79.     printf("I am UartTask %d!\n",t->uartid);
  80.     WaitX(200);
  81.   }
  82. _EE
  83. }

  84. CTOR(UartTask)
  85. SUPER_CTOR(Task);
  86. FUNCTION_SETTING(init, UartTask_init);
  87. FUNCTION_SETTING(setuart, UartTask_setuart);
  88. FUNCTION_SETTING(Task.execute, UartTask_execute);
  89. END_CTOR
复制代码

出0入0汤圆

 楼主| 发表于 2014-2-11 11:02:41 | 显示全部楼层
main.c

  1. #include <windows.h>
  2. #include "task.h"
  3. #include <sys/timeb.h>
  4. #include <time.h>

  5. int lasttime;


  6. int main()
  7. {

  8.     char timeline[20];

  9.     int i ;


  10.     KeyTask* keytask = KeyTask_new();    // 创建键盘任务对象
  11.     UartTask* uart0task = UartTask_new();       // 创建串口0任务对象
  12.     UartTask* uart1task = UartTask_new();       // 创建串口1任务对象
  13.     Task* tasklist[3] = { 0 };     // 初始化任务容器(这里是Task指针数组)

  14.     keytask->init(keytask,0);
  15.     uart0task->init(uart0task,1);
  16.     uart1task->init(uart1task,2);

  17.     tasklist[0] = SUPER_PTR(keytask, Task);
  18.     tasklist[1] = SUPER_PTR(uart0task, Task);
  19.     tasklist[2] = SUPER_PTR(uart1task, Task);

  20.     uart0task->setuart(uart0task,0);
  21.     uart1task->setuart(uart1task,1);
  22.         
  23.     // 循环执行任务容器内的任务

  24.     lasttime=0;
  25.    
  26.     while(1)
  27.     {
  28.      //以后移到中断里面执行
  29.      if (timeGetTime()!=lasttime) {
  30.         lasttime=timeGetTime();
  31.         UpdateTimers();
  32.      }

  33.      RunTasks();
  34.      
  35.     }
  36.    
  37.     DeleteTasks();

  38.     printf("\r\n End!");   

  39.     getchar();
  40.     return 0;
  41. }
复制代码

出0入0汤圆

发表于 2014-2-11 11:04:13 来自手机 | 显示全部楼层
好东西收下了

出0入0汤圆

发表于 2014-2-11 14:02:49 | 显示全部楼层
真的很不错

出0入0汤圆

发表于 2014-2-11 14:59:27 | 显示全部楼层
不错,标记备用

出0入0汤圆

发表于 2014-2-11 16:46:43 | 显示全部楼层
“_lc=__LINE__+((__LINE__%256)==0); return tickets ;} while(0); case __LINE__+((__LINE__%256)==0”

这里_lc是uint8类型的, 如__LINE__等于1000,_lc 会截断,但后面会是case 1000

此时如何?

出0入0汤圆

 楼主| 发表于 2014-2-11 18:52:35 来自手机 | 显示全部楼层
会正常运行

出0入4汤圆

发表于 2014-2-12 08:37:24 | 显示全部楼层
标记一下。

出0入0汤圆

发表于 2014-2-12 08:54:50 | 显示全部楼层
马克,以后学习吧……

出0入0汤圆

发表于 2014-2-12 09:10:14 | 显示全部楼层
看过了,学习一下。

出0入0汤圆

发表于 2014-2-12 10:35:07 | 显示全部楼层

不能正常运行了吧,_lc 被截断了,并不等于_LINE_ +(_LINE %256)了,无法正常跳到这个case了吧,下次运行时会从default运行吧

出0入0汤圆

 楼主| 发表于 2014-2-12 11:01:08 | 显示全部楼层
本帖最后由 smset 于 2014-2-12 11:09 编辑
spark51 发表于 2014-2-12 10:35
不能正常运行了吧,_lc 被截断了,并不等于_LINE_ +(_LINE %256)了,无法正常跳到这个case了吧,下次运行 ...


你实际试试就知道了,编译器并不完全是你想象的那样。

unsigned char i,j;

i=1;
j=0;

switch(i){

case 0:
  j=0;
  break;

  case 257:
   j=1;
  break;

  case 2:
   j=2;
  break;

}


你可以去找个C编译环境,运行这个代码,你会发现结果j是等于1的。

出0入13汤圆

发表于 2014-2-12 13:00:08 | 显示全部楼层
好像不错,学习学习

出0入0汤圆

发表于 2014-2-12 13:01:39 | 显示全部楼层
真心不错尼   谢谢楼主

出0入0汤圆

发表于 2014-2-12 14:22:06 | 显示全部楼层
本帖最后由 spark51 于 2014-2-12 14:25 编辑
smset 发表于 2014-2-12 11:01
你实际试试就知道了,编译器并不完全是你想象的那样。

unsigned char i,j;


IAR 6.21测试,内核为Cortex-M3,运行过Switch后,J = 0。

注:看汇编代码是把case 257给优化掉了.(关键此时优化Level已经是none了)

本帖子中包含更多资源

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

x

出0入0汤圆

 楼主| 发表于 2014-2-12 16:06:46 | 显示全部楼层
spark51 发表于 2014-2-12 14:22
IAR 6.21测试,内核为Cortex-M3,运行过Switch后,J = 0。

注:看汇编代码是把case 257给优化掉了.(关键 ...

谢谢你,我做过测试了,你说的情况确存在。

以前我用iar是在high speed优化下,没有这个问题。 但是如果不在high优化的情况下,的确会出现你说的问题。

另外,可能各个编译器确实处理方式不同,在CBuilder编译环境下,无论是没有优化,还是有优化,均不会出现这个问题。

总的来说,这的确是一个隐藏的Bug.  

再次感谢你指出这个问题!

改正的版本已经做好了,发布在下一楼。


出0入0汤圆

 楼主| 发表于 2014-2-12 16:08:42 | 显示全部楼层



  1. #include <stc89c51.h>
  2. /****小小调度器开始**********************************************/
  3. #define MAXTASKS 3
  4. volatile unsigned char timers[MAXTASKS];
  5. #define _SS static unsigned char _lc=0; switch(_lc){default:
  6. #define _EE ;}; _lc=0; return 255;
  7. #define WaitX(tickets)  do {_lc=(__LINE__+((__LINE__%256)==0))%256; return tickets ;} while(0); case (__LINE__+((__LINE__%256)==0))%256:

  8. #define RunTask(TaskName,TaskID) do { if (timers[TaskID]==0) timers[TaskID]=TaskName(); }  while(0);
  9. #define RunTaskA(TaskName,TaskID) { if (timers[TaskID]==0) {timers[TaskID]=TaskName(); continue;} }   //前面的任务优先保证执行

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

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

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


  23. sbit LED1 = P2^1;
  24. sbit LED2 = P2^2;

  25. sbit LED0 = P2^5;

  26. unsigned char task0(){
  27. _SS
  28.   while(1){
  29.    WaitX(50);
  30.    LED0=!LED0;   
  31.   }
  32. _EE
  33. }

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

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

  50. void InitT0()
  51. {
  52.         TMOD = 0x21;
  53.         IE |= 0x82;  // 12t
  54.         TL0=0Xff;
  55.         TH0=0XDB;
  56.         TR0 = 1;
  57. }

  58. void INTT0(void) interrupt 1 using 1
  59. {
  60.     TL0=0Xff;    //10ms 重装
  61.     TH0=0XDB;//b7;   

  62.     UpdateTimers();

  63.     RunTask(task0,0);//任务0具有精确按时获得执行的权限,要求:task0每次执行消耗时间<0.5个 ticket
  64. }




  65. void main()
  66. {
  67.         InitT0();
  68.         InitTasks(); //初始化任务,实际上是给timers清零
  69.         while(1){
  70. //           RunTask(task0,0);
  71.                 RunTaskA(task1,1);//任务1具有比任务2高的运行权限                  
  72.            RunTaskA(task2,2);//任务2具有低的运行权限                  
  73.       }
  74. }
复制代码

出0入0汤圆

发表于 2014-2-12 16:25:13 | 显示全部楼层
楼主发帖啊

出0入0汤圆

发表于 2014-2-12 16:36:18 来自手机 | 显示全部楼层
先留下个记号

出0入0汤圆

发表于 2014-2-12 17:17:45 | 显示全部楼层
不错,学习中。。。。。。。。

出0入0汤圆

发表于 2014-2-13 01:42:20 | 显示全部楼层
任务调度器   有点类似时间片

出0入0汤圆

发表于 2014-2-13 08:32:42 | 显示全部楼层
下班回家试试效果,没玩过

出0入0汤圆

发表于 2014-2-13 09:19:54 | 显示全部楼层
本帖最后由 spark51 于 2014-2-13 09:29 编辑


_lc的类型是不是增加一个宏定义比较好,在8位机上用uint8,在32位机上用uint32

出0入0汤圆

 楼主| 发表于 2014-2-13 10:34:50 | 显示全部楼层
spark51 发表于 2014-2-13 09:19
_lc的类型是不是增加一个宏定义比较好,在8位机上用uint8,在32位机上用uint32 ...

把_lc定义为uint8,或uint16都可以。 用uint32的话,就有点不必了。

出1000入0汤圆

发表于 2014-2-13 11:20:13 | 显示全部楼层
谢谢,学习中。。。。

出0入0汤圆

发表于 2014-2-13 13:27:30 | 显示全部楼层
好东西,学习了.

出0入0汤圆

发表于 2014-2-15 11:44:30 | 显示全部楼层
本帖最后由 klander 于 2014-2-15 11:50 编辑
smset 发表于 2014-2-12 11:01
你实际试试就知道了,编译器并不完全是你想象的那样。


你好,我用HT-IDE3000,运行结果 j=0;

出0入17汤圆

发表于 2014-2-15 11:57:00 | 显示全部楼层

多谢lz分享!

出0入0汤圆

发表于 2014-2-15 21:23:58 | 显示全部楼层
值得学习,值得深入研究!

出0入0汤圆

发表于 2014-2-16 15:49:57 | 显示全部楼层
先收藏再学习

出0入0汤圆

发表于 2014-2-20 15:31:08 | 显示全部楼层

#define WaitX(tickets)  do {_lc=(__LINE__+((__LINE__%256)==0))%256; return tickets ;} while(0); case (__LINE__+((__LINE__%256)==0))%256:

这句没看懂
1. return tickets ;//执行了这句 ,后面这个case (__LINE__+((__LINE__%256)==0))%256:还会执行吗?
2.(__LINE__+((__LINE__%256)==0))%256; //这个语句中,当__LINE__为0或1 时,是不是_lc的值是一样的;

出0入0汤圆

发表于 2014-2-20 16:03:47 | 显示全部楼层
标记下,回头好好看看。。。

出0入0汤圆

发表于 2014-2-20 16:50:09 | 显示全部楼层
mark 试试

出0入0汤圆

发表于 2014-2-20 16:50:28 | 显示全部楼层
mark 试试

出0入0汤圆

发表于 2014-2-20 18:55:26 | 显示全部楼层
好了,等会看看

出0入76汤圆

发表于 2014-2-24 01:41:25 | 显示全部楼层
今天才发现,比原来收藏的时候少了很多楼层...

出0入0汤圆

发表于 2014-2-24 07:36:11 | 显示全部楼层
看起来是蛮简单,道理通顺,应该蛮有效。有空试试

出0入0汤圆

发表于 2014-2-24 08:39:02 | 显示全部楼层
MAKE  ,我以前的定时器是这个写法,调度用了信号,比这复杂些,

出0入0汤圆

发表于 2014-2-24 10:27:32 | 显示全部楼层
观摩完毕,还有些疑问。已经保存为图片,谢谢楼主

出0入0汤圆

发表于 2014-2-24 11:15:29 | 显示全部楼层
学习学习

出0入76汤圆

发表于 2014-2-25 19:57:54 | 显示全部楼层

发现一处小bug: InitTasks() 的timers初始化过程
  1. #define InitTasks() {unsigned char i; for(i=MAXTASKS;i>0 ;i--) timers[i]=0; }
复制代码


应改为:
  1. #define InitTasks() {unsigned char i=MAXTASKS; while(i) timers[--i]=0;}
复制代码


  1. #define InitTasks() {unsigned char i=MAXTASKS; for(;i;) timers[--i]=0;}
复制代码

出0入0汤圆

 楼主| 发表于 2014-2-25 21:16:52 | 显示全部楼层
foxpro2005 发表于 2014-2-25 19:57
发现一处小bug: InitTasks() 的timers初始化过程

是的,谢谢。

出0入0汤圆

发表于 2014-3-21 05:15:57 | 显示全部楼层
先收藏再学习

出0入0汤圆

发表于 2014-3-21 08:33:03 | 显示全部楼层
mark.........

出0入0汤圆

发表于 2014-3-22 08:06:17 | 显示全部楼层
好好学一下

出0入0汤圆

发表于 2014-3-22 10:59:03 | 显示全部楼层
学习下      

出0入0汤圆

发表于 2014-3-22 11:31:24 | 显示全部楼层
感谢分享

出0入0汤圆

发表于 2014-3-22 14:12:08 | 显示全部楼层
学习一下,再顶一个。

出0入0汤圆

发表于 2014-3-22 14:41:20 | 显示全部楼层
玩的太极限了,有空我移植到AT89上面跑一下试试

出0入0汤圆

发表于 2014-3-27 18:33:04 | 显示全部楼层
MARK一下

出0入0汤圆

 楼主| 发表于 2014-3-27 19:13:45 | 显示全部楼层

  1. #include <stc89c51.h>
  2. /****小小调度器开始**********************************************/
  3. #define MAXTASKS 3
  4. volatile unsigned char timers[MAXTASKS];
  5. #define _SS static unsigned char _lc=0; switch(_lc){default:
  6. #define _EE ;}; _lc=0; return 255;
  7. #define WaitX(tickets)  do {_lc=(__LINE__+((__LINE__%256)==0))%256; return tickets ;} while(0); case (__LINE__+((__LINE__%256)==0))%256:

  8. #define RunTask(TaskName,TaskID) do { if (timers[TaskID]==0) timers[TaskID]=TaskName(); }  while(0);
  9. #define RunTaskA(TaskName,TaskID) { if (timers[TaskID]==0) {timers[TaskID]=TaskName(); continue;} }   //前面的任务优先保证执行

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

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

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


  23. sbit LED1 = P2^1;
  24. sbit LED2 = P2^2;

  25. sbit LED0 = P2^5;

  26. unsigned char task0(){
  27. _SS
  28.   while(1){
  29.    WaitX(50);
  30.    LED0=!LED0;   
  31.   }
  32. _EE
  33. }

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

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

  50. void InitT0()
  51. {
  52.         TMOD = 0x21;
  53.         IE |= 0x82;  // 12t
  54.         TL0=0Xff;
  55.         TH0=0XDB;
  56.         TR0 = 1;
  57. }

  58. void INTT0(void) interrupt 1 using 1
  59. {
  60.     TL0=0Xff;    //10ms 重装
  61.     TH0=0XDB;//b7;   

  62.     UpdateTimers();

  63.     RunTask(task0,0);//任务0具有精确按时获得执行的权限,要求:task0每次执行消耗时间<0.5个 ticket
  64. }




  65. void main()
  66. {
  67.         InitT0();
  68.         InitTasks(); //初始化任务,实际上是给timers清零
  69.         while(1){
  70. //           RunTask(task0,0);
  71.                 RunTaskA(task1,1);//任务1具有比任务2高的运行权限                  
  72.            RunTaskA(task2,2);//任务2具有低的运行权限                  
  73.       }
  74. }
复制代码

出0入8汤圆

发表于 2014-3-28 13:31:27 | 显示全部楼层
最新版的编译是不是有警告呀?unreachable code貌似作为小白不好解决

出0入0汤圆

 楼主| 发表于 2014-3-31 10:02:17 | 显示全部楼层
蓝蓝的恋 发表于 2014-3-28 13:31
最新版的编译是不是有警告呀?unreachable code貌似作为小白不好解决

当任务内部,是一个无限次循环时( 如while(1) ),就会出现unreachable code告警,

这个实际上没有副作用,不用管它。

出0入0汤圆

发表于 2014-3-31 14:47:00 | 显示全部楼层
学习一下

出0入0汤圆

发表于 2014-3-31 16:35:04 | 显示全部楼层
mark

出0入0汤圆

发表于 2014-3-31 18:41:18 | 显示全部楼层
正在学习程序架构

出0入0汤圆

发表于 2014-3-31 22:39:05 | 显示全部楼层
学习了,谢谢分享

出0入0汤圆

发表于 2014-4-1 10:56:46 来自手机 | 显示全部楼层
非常好!学习一下。

出0入8汤圆

发表于 2014-4-1 13:08:30 | 显示全部楼层
smset 发表于 2014-3-31 10:02
当任务内部,是一个无限次循环时( 如while(1) ),就会出现unreachable code告警,

这个实际上没有副作 ...

哦,略懂了,谢谢~

出0入0汤圆

发表于 2014-4-1 14:20:54 | 显示全部楼层
我想知道当任务挂起后,下次重新恢复任务的时候怎么搞才能从新开始运行任务,而不是从上次退出的地方继续运行

出0入0汤圆

发表于 2014-4-1 15:07:46 | 显示全部楼层
mark一个,慢慢看

出0入8汤圆

发表于 2014-4-8 13:22:41 | 显示全部楼层
本帖最后由 蓝蓝的恋 于 2014-4-8 13:33 编辑

能再问个问题吗?为什么这个伪线程好像每一个里面都得有WaitX延时才能所有的都被执行呀,不解中!

出0入0汤圆

 楼主| 发表于 2014-4-8 13:37:13 | 显示全部楼层
蓝蓝的恋 发表于 2014-4-8 13:22
能再问个问题吗?为什么这个伪线程好像每一个里面都得有WaitX延时才能所有的都被执行呀,不解中!  ...

是啊,在任务函数里面就是要通过WaitX才释放cpu资源。

这也是非抢占式任务调度的特点。

出0入0汤圆

发表于 2014-4-8 14:25:11 | 显示全部楼层
已经成功移植到合泰的MCU上。芯片选用HT45F23A,上传HT-IDE3000 7.82的工程文件。HT45F23A.h一定要选用我上传文件里面带的,原版头文件没有按位定义输出端口

本帖子中包含更多资源

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

x

出0入0汤圆

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

本版积分规则

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

GMT+8, 2024-5-19 18:54

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

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