搜索
bottom↓
楼主: smset

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

  [复制链接]

出0入0汤圆

 楼主| 发表于 2012-12-6 23:46:31 | 显示全部楼层
flor 发表于 2012-12-6 14:30
那个例子嵌套了一个任务,如果嵌套多层呢?            
另外一个问题:c51局部变量覆盖问题存在吗?这两个 ...

多层调用子任务都可以的, 任务函数里,请用静态局部变量。

出0入0汤圆

 楼主| 发表于 2012-12-6 23:58:14 | 显示全部楼层
本帖最后由 smset 于 2012-12-7 23:01 编辑

发布一个更新版本哈,在资源节省度上有所优化:

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

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

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

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

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

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


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

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


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

复制代码

出0入0汤圆

 楼主| 发表于 2012-12-7 00:02:31 | 显示全部楼层
本帖最后由 smset 于 2012-12-7 00:04 编辑

对于WaitX进行了改动,每个延时等待的(等效于一个状态)Rom消耗,在优化Size的情况下减少到8个字节,上一版本为13字节。

同时由于去掉了settimer函数的调用,程序运行效率更高。

出0入0汤圆

发表于 2012-12-7 06:39:12 | 显示全部楼层
很好的东西,先学习了

出0入0汤圆

发表于 2012-12-7 07:50:48 | 显示全部楼层
楼主果然强大啊,佩服,想能出个功能稍微多谢的,因为现在的单片机ram都已经上K了

出0入0汤圆

发表于 2012-12-7 08:29:35 | 显示全部楼层
本帖最后由 dr2001 于 2012-12-7 08:36 编辑
flor 发表于 2012-12-6 14:30
那个例子嵌套了一个任务,如果嵌套多层呢?            
另外一个问题:c51局部变量覆盖问题存在吗?这两个 ...


可以随便一层套一层,只要注意以下问题:
1、每一个ProtoThread,就是所说的任务,需要自己*独立*的保存间断点信息的变量。
2、每一个ProtoThread,需要有状态的判断,即当调用返回后,可以判定它是结束了(正常/有错误),还是从中断点返回。然后,当前的ProtoThread要进行相关的处理:如果是正常,那么继续;如果错误,进行处理;如果只是中断点,那么本层也中断。

如果把这个东西做成数组,可以很容易的做一个Spawn的宏,实现你想要的东西。
对于smset做的东西来说,需要用参数把计数器的信息传下去或者直接访问全局变量信息,因为Callee Yield之后,定时器是由Callee决定的,不是Caller。

出0入0汤圆

发表于 2012-12-7 08:35:14 | 显示全部楼层
smset 发表于 2012-12-6 23:58
发布一个更新版本哈,在资源节省度上有所优化:

真要想省那几个字节,就不要void的ProtoThread函数类型,把定时器的的等待数直接return回去在Caller侧处理就好。

出0入0汤圆

发表于 2012-12-7 09:21:05 | 显示全部楼层
smset 发表于 2012-12-6 23:58
发布一个更新版本哈,在资源节省度上有所优化:


樓主好

這個新的版本, 編譯有警告喔
但是上一版的沒有警告

編譯還是過了, 警告訊息如下. KEIL C 的編譯器 u Vision 4

  1. Build target 'Target 1'
  2. compiling smallest-scheduler.C...
  3. linking...
  4. *** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS
  5.     SEGMENT: ?PR?TASK1?SMALLEST_SCHEDULER
  6. Program Size: data=23.0 xdata=0 code=293
  7. "smallest-scheduler" - 0 Error(s), 1 Warning(s).
复制代码
抄你的最新版源碼

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

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

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

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

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

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


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

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


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

出0入0汤圆

 楼主| 发表于 2012-12-7 09:43:03 | 显示全部楼层
xiaolaba 发表于 2012-12-7 09:21
樓主好

這個新的版本, 編譯有警告喔

抱歉,笔误哈。

void main()

{

        InitT0();

        while(1){

           RunTask(task1,1); //这里错写成task2了,改为task1

           RunTask(task2,2);

    }

}

出0入0汤圆

 楼主| 发表于 2012-12-7 09:44:01 | 显示全部楼层
本帖最后由 smset 于 2012-12-7 09:56 编辑
dr2001 发表于 2012-12-7 08:35
真要想省那几个字节,就不要void的ProtoThread函数类型,把定时器的的等待数直接return回去在Caller侧处 ...


谢谢你得建议,不过我觉得这样的话,RAM消耗是会变大的,而且ROM消耗也不见得减少啊。

出0入0汤圆

 楼主| 发表于 2012-12-7 09:48:31 | 显示全部楼层
本帖最后由 smset 于 2012-12-7 09:50 编辑
dr2001 发表于 2012-12-7 08:29
可以随便一层套一层,只要注意以下问题:
1、每一个ProtoThread,就是所说的任务,需要自己*独立*的保存 ...


子任务嵌套调用可以任意多层的,只要不耗尽整个CPU的RAM公共堆栈就行,每增加一层嵌套,RAM公共堆栈会消耗掉两个字节。

无需调用者传递定时器信息,这个我特意测试过的。
void Task4(){
{
_SS
  ....
_EE
}

void Task3(){
{
_SS
  ...
  CallSub(Task4);
  ...
_EE
}

void Task2(){
{
_SS
  ...
  CallSub(Task3);
  ...
_EE
}

void Task1(){
{
_SS
  ...
  CallSub(Task2);
  ...
_EE
}

出0入0汤圆

发表于 2012-12-7 09:50:31 | 显示全部楼层
多谢分享, 先收藏

出0入0汤圆

发表于 2012-12-7 10:10:48 | 显示全部楼层
顶到100楼哈

出0入0汤圆

发表于 2012-12-7 10:27:49 | 显示全部楼层
smset 发表于 2012-12-7 09:43
抱歉,笔误哈。

void main()

哈...
這次編譯就好了, 謝謝你喔

  1. Build target 'Target 1'
  2. compiling smallest-scheduler.C...
  3. linking...
  4. Program Size: data=23.0 xdata=0 code=293
  5. "smallest-scheduler" - 0 Error(s), 0 Warning(s).
复制代码

出0入0汤圆

发表于 2012-12-7 10:43:37 | 显示全部楼层
多谢lz分享!

出0入0汤圆

发表于 2012-12-7 14:50:59 | 显示全部楼层
本帖最后由 dr2001 于 2012-12-7 14:54 编辑
smset 发表于 2012-12-7 09:44
谢谢你得建议,不过我觉得这样的话,RAM消耗是会变大的,而且ROM消耗也不见得减少啊。 ...


有兴趣可以试试看。一般来说能少点。需要注意,一般来说MCU的ABI返回值都放在REG里,不是进堆栈。

我说的操作的典型结果,是每个间断点:加载LC地址,间址写入立即数(__LINE__),寄存器写入立即数,调用返回。比原来肯定要省指令,反汇编一下看看。


另外,观察

  1. #define CallSub(x) WaitX(0); x(); if (timers[currid]!=255) return;
复制代码
如果x是一个ProtoThread,里边有若干个间断点,会发生什么?
一般Spawn/调用子过程的语义是子过程正常结束后才如何如何,不是偶发的“调度”。

ProtoThread的调用者必然是基于循环的,一次性调用ProtoThread是无意义的,那样Yield无法体现出任何效果。

出0入0汤圆

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

哦,如果真能减少,那我非常有兴趣。不过我的确没很明白你说的具体实现。你能否在基于211楼的版本上,改成你建议的版本,然后编译一下,看看消耗的RAM和ROM数量。这样如果对比出来你的版本更省资源,我一定会考虑进行整改。

另外,CallSub(x)是基于循环的,并非一次姓调用。 不管子任务中间有多少断点,也会忠实执行。
之所以 在X();前面加了一个WaitX(0); 就是为了保存调用的位置。 下次可以继续运行x();这样就实现了循环。 而if (timer[currid]!=255) return 则正是为了保证在子任务没执行完的时候,调用者继续等待。

因此,由于目前这个框架,是经过专门设计的,因此才能保证这种调用效果。

出0入4汤圆

发表于 2012-12-7 15:57:54 | 显示全部楼层
记下来慢慢看

出0入0汤圆

发表于 2012-12-7 17:09:50 | 显示全部楼层
这个,有一个疑问,我看任务调度的唯一标示就是函数指针,也就是task->fp成员,但我看整个代码中并没有对这个参数修改的地方,
如果等待是在任务的最后,这个可以理解,但如果等待是在任务的中间进行,而等待结束之后本来应该继续执行下面的代码,但调度器却直接又跳回到任务函数开始的地方去了,
我看settimer函数倒是有把当前行作为一个Label存到某个变量里去,似乎有准备用于恢复的作用,如果没有猜错settimer的&lc参数就是task->fp?

出0入0汤圆

发表于 2012-12-7 17:10:06 | 显示全部楼层
本帖最后由 leavic 于 2012-12-7 17:14 编辑

哦,看了一下回复,我好像猜对了。

出0入0汤圆

 楼主| 发表于 2012-12-7 17:25:26 | 显示全部楼层
leavic 发表于 2012-12-7 17:10
哦,看了一下回复,我好像猜对了。


不对哦。 lc只是记录行号而已。

不过建议你直接看最新的版本,已经没有fp了。

最新版本在211楼。

出0入0汤圆

发表于 2012-12-7 17:54:25 | 显示全部楼层
收藏再研究

出0入93汤圆

发表于 2012-12-7 18:35:13 | 显示全部楼层
本帖最后由 takashiki 于 2012-12-7 18:47 编辑
smset 发表于 2012-12-7 17:25
不对哦。 lc只是记录行号而已。

不过建议你直接看最新的版本,已经没有fp了。


今天看了楼主的这个调度器,可以看得出来,楼主下了一番功夫。不过还是来泼点凉水,不要见怪,呵呵。就两点:
1、楼主显然在效率方面下了一番功夫,可是对C语言本身却没有下多少功夫优化。
2、楼主的程序风格实在不敢恭维……

针对第二点,我不改进了,使用者自行查找替换。
针对第一点,我说那么几个:
1、在很多CPU中,循环最好是递减而不是递增,这个体现在楼主的void INTT0(void)函数中,因为跟调度器无关,我不改正了。
2、使用指针的效率比起数组,那是高得多,也容易出错的多,哈哈。从这里开刀……其他保持原样不变,宏参数还是那么难看。
  1. //takashiki修改,将数组改成指针,减少ROM和CPU占用。
  2. /****小小调度器开始**********************************************/
  3. #define MAXTASKS 3
  4. unsigned char timers[MAXTASKS];
  5. unsigned char data * ptmr;                                        //这个只针对51,其他CPU请自行移植*_*
  6. #define _SS static unsigned char lc=0; switch(lc){ case 0:
  7. #define _EE ;} lc=0;
  8. #define WaitX(b) {lc = __LINE__; *ptmr=b;} return; case __LINE__:
  9. #define RunTask(a,b) {ptmr = &timers[b]; if ( !*ptmr){ --*ptmr; a();}}
  10. #define CallSub(x) WaitX(0); x(); if (*ptmr!=255) return;
  11. /*****小小调度器结束*********************************************/
复制代码
----------------------------------------
修改原因:
将RunTask宏中的*ptmr = 255修改为 -- *ptmr,因为*ptmr已经是0了,减一肯定等于255。而自减往往比赋值效率高。

出0入0汤圆

发表于 2012-12-7 19:03:41 | 显示全部楼层
好贴,顶上~~~~

出0入0汤圆

 楼主| 发表于 2012-12-7 19:23:47 来自手机 | 显示全部楼层
欢迎指正,我就是要抛砖引玉啊,呵呵。希望你贴出改进后的完整代码,大家来学习哈

出0入0汤圆

 楼主| 发表于 2012-12-7 19:28:16 来自手机 | 显示全部楼层
欢迎指正,我就是要抛砖引玉啊,呵呵。希望你贴出改进后的完整代码,大家来学习哈

出0入93汤圆

发表于 2012-12-7 19:39:37 | 显示全部楼层
smset 发表于 2012-12-7 19:28
欢迎指正,我就是要抛砖引玉啊,呵呵。希望你贴出改进后的完整代码,大家来学习哈 ...


你那个调度器是核心,其他的都只是例程。例程由用户自己完成,优化的太多反而可能会影响用户,就这样了。
调度器我这里是针对51进行了C语言级别的优化,直接替换后就能看到效果了。

出0入0汤圆

发表于 2012-12-7 20:37:23 | 显示全部楼层
收藏再研究

出0入93汤圆

发表于 2012-12-7 21:00:28 | 显示全部楼层
再次研究该调度器,发现对timers的一些赋值过程根本是没有必要的,于是删除。同时,中断服务程序中对于255的判断也同样可以删除。删除之后的结果如下,但只会节省RunTask和CallSub两个过程的一些字节和CPU。
另外,对于一般的任务来说,是无限循环的,不允许跳出的,对于这些不允许跳出的函数来说,后面的lc=0永远执行不到,白白浪费ROM,于是新增了两个宏专门用于无限循环的任务。
修改了宏和参数的名称,使之更易读一些。
  1. /****小小调度器开始**********************************************/
  2. #define MAXTASKS 3
  3. unsigned char timers[MAXTASKS];
  4. unsigned char data *ptmr;                                                         //这个只针对51,其他CPU请自行移植*_*
  5. #define BEGIN_TASK static unsigned char lc = 0; switch(lc){ case 0:
  6. #define END_TASK } lc = 0;
  7. #define BEGIN_TASK_WHILE static unsigned char lc = 0; switch(lc){ case 0: while(1){
  8. #define END_TASK_WHILE }}
  9. #define WaitX(Ticks) lc = __LINE__, *ptmr = Ticks; return; case __LINE__:
  10. #define RunTask(TaskName, TaskId) ptmr = &timers[TaskId]; if(!*ptmr) TaskName();
  11. #define CallSub(TaskName) lc = __LINE__; return; case __LINE__: TaskName(); if(*ptmr) return;
  12. /*****小小调度器结束*********************************************/
复制代码

出0入0汤圆

发表于 2012-12-7 21:16:23 | 显示全部楼层
takashiki 发表于 2012-12-7 21:00
再次研究该调度器,发现对timers的一些赋值过程根本是没有必要的,于是删除。同时,中断服务程序中对于255 ...

代码有以下可以改进的地方:
1、对于51来说,
unsigned char data timers[MAXTASKS];
unsigned char data *ptmr;
否则小模式结果正确,其它模式结果就不对了。指向data的指针去访问不定位置的代码。

2、对BEGIN_TASK_WHILE这样的宏,在ProtoThread上完全没必要,因为其结束退出会导致重新从入口运行,自身就具备while(1)的特征。while(1)需要的一个jmp指令在消耗上和lc=0也差不多了。尤其是51。

供参考。

出0入93汤圆

发表于 2012-12-7 21:21:46 | 显示全部楼层
dr2001 发表于 2012-12-7 21:16
代码有以下可以改进的地方:
1、对于51来说,
unsigned char data timers[MAXTASKS];

是的,确实是这样,谢谢指正。
写成while只是一般对于任务的写法,PT在主函数里本身就已经是无限循环了,确实根本没有必要加入那两个宏。

出0入93汤圆

发表于 2012-12-7 21:26:24 | 显示全部楼层
根据239楼dr2001的意见修改如下,但timers不再分配到data区,而是分配到idata区,这样可寻址空间多出一倍,而代码增加微乎其微。
  1. /****小小调度器开始**********************************************/
  2. #define MAXTASKS 3
  3. //以下定义均针对51,其他CPU请自行移植*_*
  4. unsigned char idata timers[MAXTASKS];
  5. unsigned char idata * data ptmr;
  6. #define BEGIN_TASK static unsigned char lc = 0; switch(lc){ case 0:
  7. #define END_TASK } lc = 0;
  8. #define WaitX(Ticks) lc = __LINE__, *ptmr = Ticks; return; case __LINE__:
  9. #define RunTask(TaskName, TaskId) ptmr = &timers[TaskId]; if(!*ptmr) TaskName();
  10. #define CallSub(TaskName) lc = __LINE__; return; case __LINE__: TaskName(); if(*ptmr) return;
  11. /*****小小调度器结束*********************************************/
复制代码

出0入0汤圆

发表于 2012-12-7 21:46:28 | 显示全部楼层
收藏,学习了!!

出0入0汤圆

发表于 2012-12-7 22:46:40 | 显示全部楼层
收藏了

出0入0汤圆

 楼主| 发表于 2012-12-7 22:49:36 | 显示全部楼层
本帖最后由 smset 于 2012-12-7 23:11 编辑
takashiki 发表于 2012-12-7 21:26
根据239楼dr2001的意见修改如下,但timers不再分配到data区,而是分配到idata区,这样可寻址空间多出一倍, ...


takashiki和dr2001,感谢你们对本调度器的优化和建议。

1) 我对takashiki的版本进行了编译,当有data前缀时, 确实会少7个字节的ROM消耗,而当我取消data前缀时,则编译结果反而比数组方式增加近100字节的ROM消耗。 然而如果加了data, 则失去调度器代码的高度可移植性,这是我不能接受的,至于指针方式的运行速度效率,这个我目前还无法评估,而且对于小资源单片机而言,主要瓶颈在于ram和rom,而执行速度往往不是问题。

所以综合考虑,我还是决定:不采用指针ptimer方式,除非在保证可移植性前提下,更省资源。

2)定时器的赋值,并非你想象那样多余,原有定时器部分的赋值操作是必须的,否则,任务调度会出问题:非循环运行的任务会变为循环运行。

3)_SS,_EE作为任务开始,和结束的标识宏,是为了更加简化编程的输入,所以采用了尽量少的字符。


基于以上3个方面的因素,我觉得还是暂时保持211楼的版本不变。

takashiki的宏参数写法的规范性,确实比我做的好,值得我学习,我已经更新了211楼版本的宏参数写法,感谢!

出0入93汤圆

发表于 2012-12-7 23:16:50 | 显示全部楼层
smset 发表于 2012-12-7 22:49
takashiki和dr2001,感谢你们对本调度器的优化和建议。

1) 我对takashiki的版本进行了编译,当有data前 ...

不是这样的哦。

1、因为51不是标准的C,当然这是因为51的架构所决定的,所以它才要区分data、bdata、idata、pdata、xdata、edata、code、ecode,标准C是不存在这些问题的。因为C51的内存分区,使得通用指针访问极其低效,所以要特别指定。在其他CPU中,有的会像51,大部分则是不像的,指针一般都会比数组快。
移植,是要根据实实在在的硬件来完成的,从而采取最优策略,而不是直接拿来就用。ProtoThread基本不用移植,但他仍然区分编译器是不是GNU C兼容,从而可以得到更有效的优化。

出0入93汤圆

发表于 2012-12-8 08:51:28 | 显示全部楼层
2、这个其实不是什么问题,反而我从中间想到了另一个功能,就是信号。这个信号等价于RTX51Tiny的signal,是信号量、邮箱、事件等等的非常非常弱的子集。

这里用timers[current] = 255表示等待信号,因为T0ISR中,0和255都不会自减,所以255总是保持不变,下一步无法执行到^^^

加入这个几个宏:
  1. #define WaitSignal() WaitX(255)
  2. #define PauseSelf WaitSignal
  3. #define Pause(TaskId) timers[TaskId] = 255
  4. #define SetReady(TaskId) timers[TaskId] = 0
  5. #define SendSignal SetReady
复制代码
一个任务本应该是无限循环的,但如果有了只执行一次的要求,那就在结尾主动删除自己。由于没有使用函数指针,无法真正的删除,这里就用PauseSelf暂停了,万一有需求需要接着执行,那就SetReady吧进入就绪态吧。
WaitX升级了,可以等待延时,也可以等待信号,相当于RTX51Tiny的os_wait(K_TMO | K_SIG, Ticks)了
SetReady和SendSignal一样,与RTX51Tiny的os_set_ready、isr_set_ready、os_send_signal、isr_send_signal功能一致。其实RTX51Tiny的这四个函数同样拥有相同的入口。
直接删除(暂停)某个任务,就让它狂等吧。但是如果删除的任务是自身的话,必须用PauseSelf,否则无法恢复

出0入0汤圆

 楼主| 发表于 2012-12-8 10:04:57 | 显示全部楼层
谢谢, 任务之间的操作,包括信号的实现,我也在构思,一直在考虑一个比较完善的机制来实现os_wait(K_TMO | K_SIG, Ticks)这种效果。 我也是想通过控制定时器值来实现,这点上我们的想法不谋而合。

另外,我觉得任务是可以一次性运行的(但支持被反复调用),这样可以针对某些事件,动态启动相应任务去处理(不是CallSub,而是启动一个独立的任务去处理一个事件)。这需要预装任务机制,这个在下个版本中会实现。

你提的思路,我会认真理解和体会。

出0入0汤圆

发表于 2012-12-8 16:19:00 | 显示全部楼层
学习了
任务调度确实强悍
希望有更多的教程出现
谢谢

出0入0汤圆

发表于 2012-12-8 16:53:03 来自手机 | 显示全部楼层
留个记号,学习

出0入0汤圆

发表于 2012-12-8 17:52:00 | 显示全部楼层
好帖!顶顶顶顶顶顶顶顶顶顶顶顶顶!

出0入0汤圆

发表于 2012-12-8 22:17:38 | 显示全部楼层
我已用到事件或超时等待:
/******************************************************
Micro    : Task_event_wait
Stacks   :
Note     : 等待事件或超时
Updata   : 2012-12-07
*******************************************************/
#define Task_event_wait(event_id,timeover)              \
do{                                                     \
    ptk_set_timer(timeover);                            \
    case (lc_type)__LINE__:                             \
    if(ptk_event_timeover_chk(event_id) == 0)  return;  \
}while(0)

/************************************************
Micro    : ptk_set_timer
Stacks   :
Note     : 线程任务定时设定
Updata   : 2012-12-05
************************************************/
#define ptk_set_timer(timeover)                      \
    do{                                              \
        lc = (lc_type)__LINE__;                      \
        taskSleep[taskID] = timeover;                \
    }while(0)

/************************************************
Function : ptk_event_timeover_chk
Stacks   :
Note     : 线程任务事件及超时检测
Updata   : 2012-12-07
************************************************/
u8 ptk_event_timeover_chk(u8 event_id)
{
        task_event_flag = 0;
       
        if(taskEvent[taskID] & event_id)
        {
            taskEvent[taskID] &= 0xff ^ event_id;
        taskSleep[taskID] = 0;
        task_event_flag = 1;
            return 1;       
        }       
        else if(taskSleep[taskID] == 0)
        {
            return 1;
        }   
        else
        {
        return 0;
    }   
}

出0入0汤圆

发表于 2012-12-8 22:18:48 | 显示全部楼层
以上已在工程中正常使用。

出0入0汤圆

发表于 2012-12-8 22:19:56 | 显示全部楼层
实现类似os_wait(K_TMO | K_SIG, Ticks)这种效果

出0入0汤圆

发表于 2012-12-8 22:21:16 | 显示全部楼层
当timeover = 255时相当无穷等待

出0入0汤圆

发表于 2012-12-9 00:31:35 | 显示全部楼层
eddia2012 发表于 2012-12-8 22:21
当timeover = 255时相当无穷等待

你这个可以理解为删除这个任务吗 ?

出0入0汤圆

发表于 2012-12-9 22:52:39 | 显示全部楼层
211楼的工程我使用了一个仿真文件进行演示,如果使用3个LED进行指示闪烁

  1. //---第一个任务函数---
  2. void  task1(){
  3. _SS
  4.   while(1){
  5.    WaitX(50);
  6.    LED1=!LED1;   
  7.   }
  8. _EE
  9. }
  10. //---第二个任务函数---
  11. void  task2(){
  12. _SS
  13.   while(1){
  14.    WaitX(100);
  15.    LED2=!LED2;   
  16.   }
  17. _EE
  18. }

  19. //-----添加的第三个任务函数------
  20. void  task3(){
  21. _SS
  22.   while(1){
  23.    WaitX(10);
  24.    LED3=!LED3;   
  25.   }
  26. _EE
  27. }
复制代码
然后在组后面进行处理,观察到的三个LED显示,第三个任务函数下面的显示感觉有问题,我不是很懂调度,不知道这样是不是有问题,我看过前面的嵌套写法,那样写的正在做实验,希望楼主指点指点,谢谢

出0入0汤圆

发表于 2012-12-10 09:41:58 | 显示全部楼层
好贴

出0入0汤圆

发表于 2012-12-10 20:09:37 | 显示全部楼层
感觉还是加入函数指针,每个任务多浪费几个字节RAM,这样可以把调度器 功能做强大一点。

出0入0汤圆

 楼主| 发表于 2012-12-11 15:05:40 | 显示全部楼层
本帖最后由 smset 于 2012-12-11 16:27 编辑

在211楼版本基础上,增加了信号量SEM, 希望大家验证一下。

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

  3. /****小小调度器开始**********************************************/
  4. #define MAXTASKS 3
  5. unsigned char currid;
  6. unsigned char timers[MAXTASKS];

  7. #define _SS   static unsigned char lc=0; switch(lc){   case 0:

  8. #define _EE   ;}; lc=0;

  9. //延时等待的tickets 最大为250
  10. #define WaitX(tickets) {lc=__LINE__; timers[currid]=tickets;} return ; case __LINE__:

  11. #define RunTask(TaskName,TaskID) {currid=TaskID;   if (timers[TaskID]==0){timers[TaskID]=255; TaskName();}  }

  12. #define CallSub(SubTaskName)  { WaitX(0); SubTaskName(); if (timers[currid]!=255) return; }

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

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

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

  33. void INTT0(void) interrupt 1 using 1
  34. {
  35.     unsigned char i;
  36.         TL0=0Xff;    //10ms 重装
  37.         TH0=0XDB;//b7;

  38.     for (i=0;i<MAXTASKS;i++){
  39.      if ((timers[i]!=0)&&(timers[i]<=250)) {
  40.            timers[i]--;
  41.          }
  42.     }        
  43. }


  44. void  task1(){
  45. _SS
  46.   while(1){
  47.    WaitX(50);
  48.    LED1=!LED1;   
  49.   }
  50. _EE
  51. }

  52. void  task2(){
  53. _SS
  54.   while(1){
  55.    WaitX(100);
  56.    LED2=!LED2;   
  57.   }
  58. _EE
  59. }


  60. void main()
  61. {
  62.         InitT0();
  63.         while(1){
  64.            RunTask(task1,1);
  65.            RunTask(task2,2);
  66.     }
  67. }
复制代码

出0入8汤圆

发表于 2012-12-11 15:10:27 | 显示全部楼层
多谢lz分享,学习下思想

出0入8汤圆

发表于 2012-12-11 15:12:18 | 显示全部楼层
关注学习!

出0入0汤圆

发表于 2012-12-11 23:56:45 | 显示全部楼层
不错,用这个调试器写了个程序,明天上机测试一下

出0入0汤圆

发表于 2012-12-12 21:18:00 | 显示全部楼层
留标记,学习,希望越来越完善。

出0入0汤圆

发表于 2012-12-12 21:34:45 | 显示全部楼层
改到stm32下,好用,每个任务中又可以写状态机,实在是高。而且在任务中想如何延时都可以。方便。
看看能不能用在小项目中。

出0入55汤圆

发表于 2012-12-12 22:30:57 来自手机 | 显示全部楼层
高手真多,学习。。。。

出0入0汤圆

发表于 2012-12-12 22:32:28 | 显示全部楼层
学习,学习!

出0入0汤圆

发表于 2012-12-13 07:58:02 | 显示全部楼层
本帖最后由 zsmbj 于 2012-12-13 11:40 编辑

移植到avr上,winavr20100110
  1. /****小小调度器开始**********************************************/
  2. #define MAXTASKS 3
  3. unsigned char currid;
  4. unsigned char timers[MAXTASKS];
  5. #define _SS static unsigned char lc=0; switch(lc){   case 0:
  6. #define _EE ;}; lc=0;
  7. #define WaitX(tickets) {lc=__LINE__; timers[currid]=tickets;} return ; case __LINE__:
  8. #define RunTask(TaskName,TaskID) {currid=TaskID; if (timers[TaskID]==0){timers[TaskID]=255; TaskName();}}
  9. #define CallSub(SubTaskName)   WaitX(0); SubTaskName(); if (timers[currid]!=255) return;
  10. /*****小小调度器结束*******************************************************/
  11. #define Updatetimer() {unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers[i]!=0)&&(timers[i]!=255)) timers[i]--;}}

  12. #include <avr/io.h>
  13. #include <avr/interrupt.h>
  14. #include <avr/wdt.h>

  15. /**********************************************************************/
  16. /* These are system macros and defines */
  17. /**********************************************************************/
  18. #define        TIMER1                        625
  19. #define        DIV                                3        

  20. #define LED1                        PINB1
  21. #define LED2                        PINB2
  22. #define led1_on()                PORTB &= ~(1<<LED1)
  23. #define led1_off()                PORTB |= (1<<LED1)
  24. #define led2_on()                PORTB &= ~(1<<LED2)
  25. #define led2_off()                PORTB |= (1<<LED2)

  26. /*******************************
  27.        MCU初始化
  28. *******************************/
  29. void init_system(void)
  30. {
  31.         DDRB=0xff;
  32.         PORTB=0xff;
  33. }        

  34. /*******************************
  35.        Timer1初始化
  36. *******************************/
  37. void init_timer1(void)
  38. {        
  39.         TCCR1B = DIV | (1<<WGM12);
  40.         OCR1A = TIMER1;
  41.         TIMSK |= (1<<OCIE1A);
  42. }

  43. /*******************************
  44.        Timer1 interrupt 10ms
  45. *******************************/
  46. SIGNAL(SIG_OUTPUT_COMPARE1A)
  47. {
  48.         Updatetimer();
  49. }

  50. /*******************************
  51.           task0
  52. *******************************/
  53. void task0(void)
  54. {
  55.         _SS
  56.         while(1)
  57.         {
  58.         WaitX(20);
  59.         led1_on();
  60.         WaitX(40);
  61.         led1_off();
  62.         }
  63.         _EE
  64. }

  65. /*******************************
  66.           task1
  67. *******************************/
  68. void  task1(void)
  69. {
  70.     _SS
  71.     while(1)
  72.     {
  73.         led2_on();
  74.         WaitX(100);
  75.         led2_off();
  76.         WaitX(200);

  77.     }
  78.     _EE
  79. }

  80. /*******************************
  81.           主程序
  82. *******************************/
  83. int main(void)
  84. {
  85.     init_system();
  86.     init_timer1();
  87. //  wdt_enable(4);
  88.     sei();
  89.    
  90.     while(1)
  91.     {
  92. //      wdt_reset();
  93.         RunTask(task0,0);
  94.         RunTask(task1,1);
  95.     }
  96. }
复制代码

出0入0汤圆

发表于 2012-12-13 08:10:55 | 显示全部楼层
发现winavr对 WaitX(tickets)里的 timers[currid]=tickets 优化的不行。

11a:        e0 91 65 00         lds        r30, 0x0065
11e:        f0 e0               ldi        r31, 0x00        ; 0
120:        ee 59               subi        r30, 0x9E        ; 158
122:        ff 4f               sbci        r31, 0xFF        ; 255
124:        88 ec               ldi        r24, 0xC8        ; 200
126:        80 83               st        Z, r24

程序进行了间接寻址,花费了14byte rom,执行了8个时钟周期

如果WaitX(tickets)改成楼主1楼的写法:
#define WaitX(tickets,ID) {lc=__LINE__; timers[ID]=tickets;} return ; case __LINE__:
那么汇编代码如下:
  fe:        88 ec               ldi        r24, 0xC8        ; 200
100:        80 93 63 00         sts        0x0063, r24

程序直接对寄存器进行了赋值,花费了6byte rom,执行了3个时钟周期
改成下面的写法后currid就不需要了,节省了一个byte,每次WaitX节省8byte。不过写代码有些不方便。

不知51对此优化如何。

出0入0汤圆

发表于 2012-12-13 10:34:18 | 显示全部楼层
__LINE__

mark!

出0入0汤圆

发表于 2012-12-13 10:51:02 | 显示全部楼层
东西不错  很有创意

出0入0汤圆

发表于 2012-12-13 11:01:33 | 显示全部楼层
为什么就没人上传一份STM的呢 ?

出0入0汤圆

发表于 2012-12-13 11:26:43 | 显示全部楼层
holts2 发表于 2012-12-13 11:01
为什么就没人上传一份STM的呢 ?

要stm8还是stm32?

出0入0汤圆

发表于 2012-12-13 11:38:52 | 显示全部楼层
本帖最后由 holts2 于 2012-12-13 11:40 编辑
zsmbj 发表于 2012-12-13 11:26
要stm8还是stm32?


简单点, 先上个STM8 看看, 如果两个都有更好

出0入55汤圆

发表于 2012-12-13 11:53:02 | 显示全部楼层
楼主的功底非常好。
研究了一下,发现和我的程序思想差不多。
http://www.amobbs.com/thread-5422394-1-1.html

出0入0汤圆

 楼主| 发表于 2012-12-13 12:27:02 | 显示全部楼层
wxlcj 发表于 2012-12-12 21:34
改到stm32下,好用,每个任务中又可以写状态机,实在是高。而且在任务中想如何延时都可以。方便。
看看能不 ...

,能否贴出示例代码给大家分享一下?

出0入0汤圆

 楼主| 发表于 2012-12-13 12:35:57 | 显示全部楼层
eddia2012 发表于 2012-12-8 22:19
实现类似os_wait(K_TMO | K_SIG, Ticks)这种效果

谢谢,已经实现了一个信号量版本。

出0入0汤圆

发表于 2012-12-13 13:08:03 | 显示全部楼层
smset 发表于 2012-12-13 12:35
谢谢,已经实现了一个信号量版本。


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

请求下, 我在STM8S中写了如上一个例子, 但运行不通, LED1=!LED1永远不会执行, 到if(Trg)就返回了, 不知什么原因

出0入0汤圆

 楼主| 发表于 2012-12-13 13:17:27 | 显示全部楼层
holts2 发表于 2012-12-13 13:08
void  task1(){
static unsigned char lc=0; switch(lc){   case 0:
keyread()

请贴出完整代码。

出0入0汤圆

发表于 2012-12-13 13:57:56 | 显示全部楼层
本帖最后由 holts2 于 2012-12-13 15:12 编辑
smset 发表于 2012-12-13 13:17
请贴出完整代码。


  1. /**
  2.   **********************************************************
  3.   * @file    Project/main.c
  4.   * @author  Holts
  5.   * @version V1.0.0
  6.   * @date    2012-12-9
  7.   * @brief   Main program body
  8.   **********************************************************
  9.         */
  10. /* Includes ----------------------------------------------*/
  11. #include "stm8s.h"              
  12. #include "parameter.h"
  13. #include "ptask.h"
  14. #include "main.h"
  15. #include "HD44780.h"
  16. #include "delay.h"


  17. /* Private typedef ---------------------------------------*/

  18. /* Private macro -----------------------------------------*/

  19. /* Private variables -------------------------------------*/
  20. u8  temp_AD_H;                                           // temporary registers for reading ADC result
  21. u8  temp_AD_L;             // temporary registers for reading ADC result
  22. u8        ADInit;                                                     // flag for ADC initialized
  23. u8        ADSampRdy;                                     // flag for filed of samples ready
  24. u8  AD_samp;                                                   // counter of stored samples
  25. u16 AD_sample[NUMB_SAMP];         // store samples field
  26. u16 AD_avg_value;                                   // average of ADC result
  27.        
  28. u8  peak_memo;                                           // variables for peak level detector
  29. u8  peak_filt;                                           // variables for peak level detector*/
  30.        
  31. volatile u8 currid;
  32. volatile u8 timers[MAXTASKS];

  33. u8  Trg;
  34. u8  Cont;

  35. u8  Mode = MODE_R;
  36. u8  Frq = SEL_100;

  37. volatile bool event = TRUE;
  38. volatile u16 ten_ms = 0;
  39. volatile u16 sec = 0;
  40. volatile u16 min = 0;

  41. /* Define 6 custom characters to display bar graph*/
  42. char STCustom[48] =
  43.   {
  44.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Blank
  45.     0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, // 1column  |
  46.     0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // 2columns ||
  47.     0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, // 3columns |||
  48.     0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, // 4columns ||||
  49.     0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, // 5columns |||||
  50.   };

  51. /* Define DDS */
  52. //查询表中不可装载零值,否则会造成无中断产生
  53. u8 sinB[256] =
  54.   {
  55. 255,255,255,255,255,255,254,254,253,252,252,251,250,249,248,247,246,245,243,242,240,239,237,236,234,232,230,229,227,225,222,220,
  56. 218,216,214,211,209,206,204,201,199,196,194,191,188,185,183,180,177,174,171,168,165,162,159,156,153,150,147,144,140,137,134,131,
  57. 128,125,122,119,116,112,109,106,103,100, 97, 94, 91, 88, 85, 82, 79, 76, 73, 71, 68, 65, 62, 60, 57, 55, 52, 50, 47, 45, 42, 40,
  58.   38, 36, 34, 31, 29, 27, 26, 24, 22, 20, 19, 17, 16, 14, 13, 11, 10,  9,  8,  7,  6,  5,  4,  4,  3,  2,  2,  1,  1,  1,  1,  1,
  59.    1,  1,  1,  1,  1,  1,  2,  2,  3,  4,  4,  5,  6,  7,  8,  9, 10, 11, 13, 14, 16, 17, 19, 20, 22, 24, 26, 27, 29, 31, 34, 36,
  60.   38, 40, 42, 45, 47, 50, 52, 55, 57, 60, 62, 65, 68, 71, 73, 76, 79, 82, 85, 88, 91, 94, 97,100,103,106,109,112,116,119,122,125,
  61. 128,131,134,137,140,144,147,150,153,156,159,162,165,168,171,174,177,180,183,185,188,191,194,196,199,201,204,206,209,211,214,216,
  62. 218,220,222,225,227,229,230,232,234,236,237,239,240,242,243,245,246,247,248,249,250,251,252,252,253,254,254,255,255,255,255,255
  63.   };

  64.   u16 i, count;
  65.   u8 char_pos, roll;
  66.   /* Custom characters can be 8 max. user defined characters from 0 to 7*/
  67.   /* disp[] string is here to generate the display of custom characters on LCD*/
  68.   /* of the bar graph using 8 characters pattern*/
  69.   /* Example: printing "\0\0\0\0\0\0\0\0\n" will display 8 times character 0*/
  70.   /* which is the blank character*/
  71.   /* Example: printing "\5\5\5\5\5\5\5\5\n" will display 8 times character 5*/
  72.   /* which is the 5 columns character*/
  73.   char disp[] = "\0\0\0\0\0\0\0\0\n";
  74.   char message[5][7] = {"STM8S", "Value", "Line", "8-Bit", "Micro"};



  75. /* Private function prototypes ---------------------------*/
  76. /**
  77.   * @brief Count average of samples stored in the u16 field
  78.   * @par Parameters:
  79.   *  *p: pointer to the begin of the field
  80.   * smp: number of samples in the field
  81.   * @retval Average u16 value
  82.   */
  83. u16 u16_average(u16 *p, u8 smp)
  84. {
  85.         u8 i;
  86.         u16 sum;
  87.        
  88.         for(i=0, sum= 0; i < smp; ++i)
  89.                 sum+= *p++;               
  90.         return sum / smp;
  91. }
  92. /* ------------------------------------------------------ */
  93. /**
  94.   * @brief Prepare data for four LED bar of signal and peak indicator
  95.   * @par Parameters:
  96.   * val: Level of the mesured signal [0-4]
  97.   * @retval 4 bits (low nibble) of the composite bar graph information
  98.   */
  99. u8 signal_and_peak_level(u8 val) {
  100.         u8 signal;
  101.         u8 peak;
  102.           
  103.         switch(val) {
  104.                 case 0: peak= 0; signal=  0; break;        //set peak and signal levels
  105.                 case 1: peak= 1; signal=  1; break;
  106.                 case 2: peak= 2; signal=  3; break;
  107.                 case 3: peak= 4; signal=  7; break;
  108.                 case 4: peak= 8; signal= 15; break;
  109.                 default: peak= signal= 15;
  110.         };
  111.         if(peak_filt == 0) {                                                                        // slow fall of peak level indicator
  112.                 if(peak_memo) {
  113.                         peak_memo>>= 1;
  114.                         peak_filt= PEAK_FILTER;
  115.                 };
  116.         }
  117.         else
  118.                 --peak_filt;
  119.         if(peak >= peak_memo) {                                       // check the highest level value
  120.                 peak_memo= peak;                                                 // and copy it to peak indicator
  121.                 peak_filt= PEAK_FILTER;                               // with fall speed refresh
  122.         };
  123.         return (signal | peak_memo);                     // return bar graph information
  124. }

  125. /**
  126.   **********************************************************
  127.   * @brief Configures clocks
  128.   * @par Parameters: None
  129.   * @retval void None
  130.   * @par Required preconditions: None
  131.   **********************************************************
  132.   */
  133. void CLK_Configuration(void)
  134. { /* Fmaster = 16MHz */
  135.   CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);
  136. }

  137. /***********************************************************
  138.   * @brief Configures GPIOs
  139.   * @par Parameters: None
  140.   * @retval void None
  141.   * @par Required preconditions: None
  142.   **********************************************************
  143.   */
  144. void GPIO_Configuration(void)
  145. {
  146.   /* GPIO reset */
  147.   GPIO_DeInit(LCDPort);
  148.   /* Configure LCDPort D0 output push-pull low LCD Bus*/
  149.   GPIO_Init(LCDPort, GPIO_PIN_0, GPIO_MODE_OUT_PP_LOW_FAST);
  150.   /* Configure LCDPort D1  output push-pull low LCD Bus*/
  151.   GPIO_Init(LCDPort, GPIO_PIN_1, GPIO_MODE_OUT_PP_LOW_FAST);
  152.   /* Configure LCDPort D2  output push-pull low LCD Bus*/
  153.   GPIO_Init(LCDPort, GPIO_PIN_2, GPIO_MODE_OUT_PP_LOW_FAST);
  154.   /* Configure LCDPort D3  output push-pull low LCD Bus*/
  155.   GPIO_Init(LCDPort, GPIO_PIN_3, GPIO_MODE_OUT_PP_LOW_FAST);
  156.        
  157.   /* GPIO reset */
  158.   GPIO_DeInit(LCDControlPort);
  159.   /* Configure LCDPort E output push-pull low LCD Enable Pin*/
  160.   GPIO_Init(LCDControlPort, LCD_Enable, GPIO_MODE_OUT_PP_LOW_FAST);
  161.   /* Configure LCDPort RS output push-pull low LCD RS Pin*/
  162.   GPIO_Init(LCDControlPort, LCD_RS, GPIO_MODE_OUT_PP_LOW_FAST);
  163.        
  164.         /* GPIO reset */
  165.   GPIO_DeInit(LCDPwrPort);
  166.   /* Configure LCDPwrOnPort VDD output push-pull low LCD Power Supply*/
  167.   GPIO_Init(LCDPwrPort, LCDPwrPin, GPIO_MODE_OUT_PP_LOW_FAST);

  168.         /* GPIOD reset */
  169.   //GPIO_DeInit(BUTTON_PORT);
  170.         /* Configure PC4 (push button) as input floating */
  171.   GPIO_Init(BUTTON_PORT, BUTTON_PIN, GPIO_MODE_IN_PU_NO_IT);
  172.         /* Configure PD0 (LED1) as output push-pull low (led switched on) */
  173.   //GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_LOW_FAST);
  174.        
  175.         /* ADC reset */
  176.         GPIO_DeInit(ADC_GPIO_PORT);
  177.         GPIO_Init(ADC_GPIO_PORT, ADC_INPUT, GPIO_MODE_IN_FL_NO_IT);
  178.        
  179. }

  180. /***********************************************************
  181.   * @brief TIM1 init
  182.   * @par Parameters: None
  183.   * @retval void None
  184.   * @par Required preconditions: None
  185.   **********************************************************
  186.   */
  187. void TIM1_Config(void)
  188. {
  189.     TIM1_DeInit();
  190.                
  191.                 /* Time Base configuration */
  192.     /*
  193.                    TIM1_Prescaler = 8   分频 16/8 = 2000 KHz
  194.        TIM1_Period = 8000      频率 2000 KHz /(7999 + 1) = 250KHz
  195.        TIM1_CounterMode = TIM1_COUNTERMODE_UP
  196.                          TIM1_RepetitionCounter = 0
  197.     */
  198.                 TIM1_TimeBaseInit(8, TIM1_COUNTERMODE_UP, AUTORELOAD - 1, 0);
  199.                
  200.                 /* Channel 2 Configuration in PWM mode */
  201.     /*
  202.        TIM1_OCMode = TIM1_OCMODE_PWM2
  203.        TIM1_OutputState = TIM1_OUTPUTSTATE_ENABLE
  204.        TIM1_OutputNState = TIM1_OUTPUTNSTATE_ENABLE
  205.        TIM1_Pulse = DutyCycle
  206.        TIM1_OCPolarity = TIM1_OCPOLARITY_LOW
  207.        TIM1_OCNPolarity = TIM1_OCNPOLARITY_HIGH
  208.        TIM1_OCIdleState = TIM1_OCIDLESTATE_SET
  209.        TIM1_OCNIdleState = TIM1_OCIDLESTATE_RESET
  210.     */
  211.                 TIM1_OC1Init(TIM1_OCMODE_PWM2, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_ENABLE, FRQ_100HZ/2,
  212.                TIM1_OCPOLARITY_LOW, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET,
  213.                TIM1_OCNIDLESTATE_RESET);
  214.                 TIM1_OC2Init(TIM1_OCMODE_PWM2, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_ENABLE, FRQ_100HZ/2,
  215.                TIM1_OCPOLARITY_LOW, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET,
  216.                TIM1_OCNIDLESTATE_RESET);                                         
  217.                 TIM1_OC3Init(TIM1_OCMODE_PWM2, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_ENABLE, FRQ_100HZ/2,
  218.                TIM1_OCPOLARITY_LOW, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET,
  219.                TIM1_OCNIDLESTATE_RESET);
  220.     TIM1_OC4Init(TIM1_OCMODE_PWM2, TIM1_OUTPUTSTATE_ENABLE, FRQ_100HZ/2, TIM1_OCPOLARITY_LOW,
  221.                TIM1_OCIDLESTATE_SET);
  222.                                                                        
  223.                 /* TIM1 counter enable */
  224.     TIM1_Cmd(ENABLE);
  225.                  
  226.                 /* TIM1 Main Output Enable */
  227.     TIM1_CtrlPWMOutputs(ENABLE);
  228. }

  229. /***********************************************************
  230.   * @brief TIM2 init
  231.   * @par Parameters: None
  232.   * @retval void None
  233.   * @par Required preconditions: None
  234.   **********************************************************
  235.   */
  236. void TIM2_Config(void)
  237. {
  238.     TIM2_DeInit();
  239.                
  240.                 /* Time Base configuration  
  241.                    TIM1_Prescaler = 8   分频16/8 = 2 MHz
  242.        TIM1_Period = 20      频率 2000 K /(20-1) = 100KHz
  243.     */
  244.                 TIM2_TimeBaseInit(TIM2_PRESCALER_8, AD_STAB);
  245.                 /* Channel 2 Configuration in PWM mode */
  246.     /*
  247.        TIM1_OCMode = TIM1_OCMODE_PWM2
  248.        TIM1_OutputState = TIM1_OUTPUTSTATE_ENABLE
  249.        TIM1_OutputNState = TIM1_OUTPUTNSTATE_ENABLE
  250.        TIM1_Pulse = DutyCycle
  251.        TIM1_OCPolarity = TIM1_OCPOLARITY_LOW
  252.        TIM1_OCNPolarity = TIM1_OCNPOLARITY_HIGH
  253.        TIM1_OCIdleState = TIM1_OCIDLESTATE_SET
  254.        TIM1_OCNIdleState = TIM1_OCIDLESTATE_RESET
  255.     */
  256.                 TIM1_OC1Init(TIM1_OCMODE_PWM2, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_ENABLE, FRQ_100HZ/2,
  257.                TIM1_OCPOLARITY_LOW, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET,
  258.                TIM1_OCNIDLESTATE_RESET);
  259.                 TIM1_OC2Init(TIM1_OCMODE_PWM2, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_ENABLE, FRQ_100HZ/2,
  260.                TIM1_OCPOLARITY_LOW, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET,
  261.                TIM1_OCNIDLESTATE_RESET);                                         
  262.                 TIM1_OC3Init(TIM1_OCMODE_PWM2, TIM1_OUTPUTSTATE_ENABLE, TIM1_OUTPUTNSTATE_ENABLE, FRQ_100HZ/2,
  263.                TIM1_OCPOLARITY_LOW, TIM1_OCNPOLARITY_HIGH, TIM1_OCIDLESTATE_SET,
  264.                TIM1_OCNIDLESTATE_RESET);
  265.     TIM1_OC4Init(TIM1_OCMODE_PWM2, TIM1_OUTPUTSTATE_ENABLE, FRQ_100HZ/2, TIM1_OCPOLARITY_LOW,
  266.                TIM1_OCIDLESTATE_SET);
  267.                        
  268.     /* auto reload register is buferred */
  269.     TIM2_ARRPreloadConfig(ENABLE);
  270.                 /* TIM1 counter enable */
  271.     TIM1_Cmd(ENABLE);
  272.                  
  273.                 /* TIM1 Main Output Enable */
  274.     TIM1_CtrlPWMOutputs(ENABLE);
  275. }


  276. /***********************************************************
  277.   * @brief  Configure TIM4 to generate an update interrupt each 1ms
  278.   * @param  None
  279.   * @retval None
  280.         **********************************************************
  281.   */
  282. void TIM4_Config(void)
  283. {
  284.   /* TIM4 configuration:
  285.    - TIM4CLK is set to 16 MHz, the TIM4 Prescaler is equal to 128 so the TIM1 counter
  286.    clock used is 16 MHz / 128 = 125 000 Hz
  287.   - With 125 000 Hz we can generate time base:
  288.       max time base is 2.048 ms if TIM4_PERIOD = 255 --> (255 + 1) / 125000 = 2.048 ms
  289.       min time base is 0.016 ms if TIM4_PERIOD = 1   --> (  1 + 1) / 125000 = 0.016 ms
  290.   - In this example we need to generate a time base equal to 1 ms
  291.    so TIM4_PERIOD = (0.001 * 125000 - 1) = 124 */

  292.   /* Time base configuration */
  293.   TIM4_TimeBaseInit(TIM4_PRESCALER_128, TIM4_PERIOD_MS);
  294.   /* Clear TIM4 update flag */
  295.   TIM4_ClearFlag(TIM4_FLAG_UPDATE);
  296.   /* Enable update interrupt */
  297.   TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);
  298.   
  299.   /* Enable TIM4 */
  300.   TIM4_Cmd(ENABLE);
  301. }

  302. /***********************************************************
  303.   * @brief Key Read
  304.   * @par Parameters: None
  305.   * @retval void None
  306.   * @par Required preconditions: None
  307.   **********************************************************
  308.   */
  309. void KeyRead( void )
  310. {
  311.     //unsigned char ReadData =  GPIO_ReadInputData(KEY_GPIO_PORT)^0xFF;   
  312.                 //ReadData = ReadData & 0x0E;
  313.                 u8 ReadData  = (GPIO_ReadInputData(BUTTON_PORT)^0x80) & 0x80;
  314.     Trg = ReadData & (ReadData ^ Cont);      
  315.     Cont = ReadData;                 
  316. }

  317. /***********************************************************
  318.   * @brief Delay before completing the action
  319.   * @param[in] function action() to be performed once the delay past
  320.   * @param[in] None
  321.   * @retval void None
  322.   * @par Required preconditions: None
  323.   **********************************************************
  324.   */
  325. void Key_thread()
  326. {
  327.   PTH_BEGIN
  328.           KeyRead();
  329.                 if (Trg)
  330.                 {
  331.                   WaitX(20);
  332.                   KeyRead();
  333.                   if(Cont)  
  334.                         {  
  335.                                  if (Cont & BUTTON_PIN)   
  336.            { Mode = (Mode + 1) % 3; }
  337.                                  if (Cont & KEY_FRQ)
  338.                                          { Frq = (Frq + 1) % 3; }
  339.          if (Cont & KEY_REDUCE)
  340.              { Mode = MODE_R; }
  341.                      if (Cont & KEY_PLUS)
  342.             { Mode = MODE_R; }
  343.                   }
  344.                 }
  345.   PTH_END
  346. }       




  347. /* Public functions --------------------------------------*/
  348. /**
  349.   * @brief Validation firmware main entry point.
  350.   * @par Parameters: None
  351.   * @retval void None
  352.   */  
  353. void main(void)
  354. {
  355.         /* Init CLK */
  356.         CLK_Configuration();

  357.   /* Init GPIOs*/
  358.   /* This function sets GPIOs pins according to LCD pins assignment in main.h*/
  359.   GPIO_Configuration();
  360.        
  361.         /* BEEP calibration */
  362.   //BEEP_LSICalibrationConfig(9000);

  363.   //TIM1_Config();
  364.   
  365.         /* TIM4 configuration */
  366.   TIM4_Config();
  367.        

  368.         //GPIOC->DDR|= 0x02;                                                // PC.1 as push-pull outputs
  369.         //GPIOC->CR1|= 0x02;
  370.                
  371.         //GPIOE->CR1&=~0x40;                                          // PE.6 as a floating input
  372.         //GPIOE->DDR&=~0x40;
  373.        
  374.   //         *** ADC INITIALIZATION ***
  375.        
  376.         // set autoreload register for trigger period
  377.         //TIM1->ARRH= (u8)(AUTORELOAD >> 8);         
  378.         //TIM1->ARRL= (u8)(AUTORELOAD);                          
  379.         TIM1_SetAutoreload(AUTORELOAD);
  380.        
  381.         // set compare register for trigger period
  382.         //TIM1->CCR1H= (u8)((AUTORELOAD-AD_STAB) >> 8);   
  383.         //TIM1->CCR1L= (u8)(AUTORELOAD-AD_STAB);
  384.         TIM1_SetCompare1(AUTORELOAD-AD_STAB);
  385.        
  386.         // auto reload register is buferred
  387.         //TIM1->CR1|= TIM1_CR1_ARPE;          
  388.         TIM1_ARRPreloadConfig(ENABLE);
  389.        
  390.         // CC1REF is used as TRGO
  391.         //TIM1->CR2= (4<<4) & TIM1_CR2_MMS;                           
  392.         TIM1_SelectOutputTrigger(TIM1_TRGOSOURCE_OC1REF);
  393.        
  394.         // CC1REF in PWM 1 mode
  395.         //TIM1->CCMR1= (6<<4) & TIM1_CCMR_OCM;                  
  396.         TIM1_SelectOCxM(TIM1_CHANNEL_1, TIM1_OCMODE_PWM1);
  397.        
  398.         // CC1 interrupt enable
  399.         //TIM1->IER|= TIM1_IER_CC1IE;                     
  400.         TIM1_ITConfig(TIM1_IT_CC1, ENABLE);
  401.        
  402.   // CC1 negative polarity
  403.         //TIM1->CCER1|= TIM1_CCER1_CC1P;          
  404.         TIM1_OC1PolarityConfig(TIM1_OCPOLARITY_LOW);
  405.         // CC1 output enable
  406.         //TIM1->CCER1|= TIM1_CCER1_CC1E;          
  407.   TIM1_CCxCmd(TIM1_CHANNEL_1, ENABLE);
  408.        
  409.         //TIM1->BKR|= TIM1_BKR_MOE;                                                                                               
  410.         TIM1_CtrlPWMOutputs(ENABLE);
  411.        
  412.         // synchronization of TRGO with ADC
  413.         //TIM2->SMCR|=  TIM2_SMCR_MSM;          
  414.         TIM1_SelectMasterSlaveMode(ENABLE);
  415.        
  416.         // timer 1 enable
  417.         //TIM1->CR1|= TIM1_CR1_CEN;                                                      
  418.         TIM1_Cmd(ENABLE);       
  419.                
  420.                
  421.         ADC1->CSR= ADC1_CSR_EOCIE | (3 & ADC1_CSR_CH);  // ADC EOC interrupt enable, channel 3
  422.         ADC1->CR1= 4<<4 & ADC1_CR1_SPSEL;                                  // master clock/8, single conversion
  423.         ADC1->CR2= ADC1_CR2_EXTTRIG;                                                  // external trigger on timer 1 TRGO, left alignment
  424.         ADC1->TDRH= 2;                                                                                        // disable Schmitt trigger on AD input 9
  425.         ADC1->TDRL= 0;                                  //

  426.   // init ADC variables
  427.         AD_samp= 0;                                                    // number of stored samples 0
  428.         ADInit= TRUE;             // ADC initialized
  429.         ADSampRdy= FALSE;         // No sample
  430.        
  431.         ADC1->CR1|= ADC1_CR1_ADON;   // ADC on

  432.         enableInterrupts();                                   // enable all interrupts
  433.        
  434.         /* Set power supply on LCD via LCD Power Pin */
  435.   //LCD_PWRON();
  436.   /* Min. delay to wait before initialization after LCD power ON */
  437.   /* Value is ms units*/
  438.   Delay(100);
  439.   /* Initialization of LCD */
  440.   LCD_INIT();
  441.   /* LCD Clear Display */
  442.   LCD_CLEAR_DISPLAY();
  443.   /* Set @CGRAM address start */
  444.   LCD_CMD(CGRAM_address_start);
  445.   /* Loading 6 characters @CGRAM address from STCustom tab */
  446.   LCD_LOAD_CGRAM(STCustom, 6);

  447.         /* Welcome application message while delay 3000ms */
  448.   /* Set cursor to the chosen position*/
  449.   LCD_LOCATE(1, 1);
  450.   /* Print string on LCD (must be ended with \n)*/
  451.   LCD_printstring(" LCR 2.0 \n");
  452.         LCD_LOCATE(2, 2);
  453.   LCD_printstring("XJW Putian,2011\n");
  454.         Delay(3000);
  455.         LCD_CLEAR_DISPLAY();

  456.   /* Init local variables*/
  457.   roll = 0;
  458.   count = 0;
  459.        
  460.   /* Init global variables*/
  461.   ten_ms = 0;
  462.   sec = 0;
  463.   min = 0;
  464.        
  465.        
  466.   //*** MAIN LOOP ***       
  467.         while (1)
  468.         {
  469.                 /* 键盘处理 */
  470.                 RunTask(Key_thread,1);
  471.                                
  472.                 /* 声音处理 */
  473.                 //Beep_thread(&pt2);
  474.                  
  475.                 /* ADC取样 */
  476.                 if (ADSampRdy == TRUE)  // field of ADC samples is ready?
  477.                 {         // average of samples
  478.                         AD_avg_value= u16_average(&AD_sample[0], AD_samp);
  479.                        
  480.                         AD_samp= 0;                                        // reinitalize ADC variables
  481.                         ADSampRdy= FALSE;
  482.                        
  483.                         // setting LED status       
  484.                         //leds= signal_and_peak_level((u8)((AD_avg_value + 128) / 256));
  485.                        
  486.       /* Display ADC value at the top right*/
  487.                         LCD_LOCATE(1, 11);
  488.       /* Uses special mask*/
  489.       LCD_printf("%5d", AD_avg_value);
  490.                 }
  491.                
  492.                 if (Frq == SEL_100)
  493.                 {
  494.                         LCD_LOCATE(1, 1);
  495.                         LCD_printf("%s", "F 100");
  496.                 }
  497.                 if (Frq == SEL_1K)
  498.                 {
  499.                         LCD_LOCATE(1, 1);
  500.                         LCD_printf("%s", "F 1K ");
  501.                 }
  502.     if (Frq == SEL_7K8)
  503.                 {
  504.                         LCD_LOCATE(1, 1);
  505.                         LCD_printf("%s", "F 7K8");
  506.                 }
  507.                
  508.                 if (Mode == MODE_R)
  509.                 {
  510.                         LCD_LOCATE(1, 10);
  511.                         LCD_printf("%s", "R");
  512.                 }
  513.                 if (Mode == MODE_C)
  514.                 {
  515.                         LCD_LOCATE(1, 10);
  516.                         LCD_printf("%s", "C");
  517.                 }
  518.                 if (Mode == MODE_L)
  519.                 {
  520.                         LCD_LOCATE(1, 10);
  521.                         LCD_printf("%s", "L");
  522.                 }

  523.                   
  524.     /* If event true (1 sec elapsed) then display rolling messages*/
  525.     if (event == TRUE)
  526.       {
  527.         event = FALSE;
  528.         LCD_LOCATE(2, 11);
  529.         /* Message is displayed at the bottom right*/
  530.         /* Uses special mask*/
  531.         LCD_printf("%5s", message[roll]);
  532.         /* Message changes every time (5 times) then return to first word*/
  533.         /* Table index "roll" is set accordingly*/
  534.         roll = (roll + 1) % 5;
  535.       }
  536.                
  537.                 /* Display time counter Min:Sec:Ten ms at the bottom left*/
  538.     LCD_LOCATE(2, 1);
  539.     /* Uses special mask*/
  540.     LCD_printf("%02d:%02d:%02d", min, sec, ten_ms);

  541.     /* Display each 6 (0 to 5) custom characters at same LCD position*/
  542.                 //char_pos = 0;
  543.                 //while (char_pos < 8)
  544.                 //{
  545.       //for (i = 0;i < 6;i++)
  546.       //{
  547.         /* Display bar graph at the top left*/
  548.         //LCD_LOCATE(1, 1);
  549.         //LCD_printstring(disp);
  550.         /* Change custom character to be displayed 0->1->2->3->4->5*/
  551.         //disp[char_pos] = (disp[char_pos] + 1);
  552.        //}
  553.                         /* if custom character 5 reached (all columns filled) maintain it display */
  554.       //disp[char_pos] = 5;
  555.       /* and move to next position of bar graph*/
  556.       //char_pos++;
  557.           //}
  558.        
  559.     /* Reset bar graph display to blank*/
  560.     //for (i = 0;i < 8;i++)
  561.       //disp[i] = 0;
  562.                        
  563.     /* Blink display and wait for a while*/
  564.     //LCD_DISP_OFF();
  565.     //Delay(300);
  566.                
  567.     //LCD_DISP_ON();
  568.     //Delay(1000);
  569.         }
  570. }

  571. #ifdef USE_FULL_ASSERT

  572. /**
  573.   * @brief  Reports the name of the source file and the source line number
  574.   *   where the assert_param error has occurred.
  575.   * @param file: pointer to the source file name
  576.   * @param line: assert_param error line source number
  577.   * @retval : None
  578.   */
  579. void assert_failed(u8* file, u32 line)
  580. {
  581.   /* User can add his own implementation to report the file name and line number,
  582.      ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  583.   /* Infinite loop */
  584.   while (1)
  585.   {}
  586. }
  587. #endif




复制代码

出0入0汤圆

发表于 2012-12-13 14:24:04 | 显示全部楼层
mark

出0入0汤圆

 楼主| 发表于 2012-12-13 14:38:10 | 显示全部楼层
holts2 发表于 2012-12-13 13:57
code

/**

Trg是一个变量,如果=0,就不会执行了。 你确保Trg是非零?

出0入0汤圆

发表于 2012-12-13 15:21:13 | 显示全部楼层
smset 发表于 2012-12-13 14:38
Trg是一个变量,如果=0,就不会执行了。 你确保Trg是非零?

当我按下键时 Trg=1, 放开键Trg=0

如果我按下键, 延时等待 20毫秒( 按键消抖 ) ,然后再次读键盘,  想不明白的是WaitX(20)后面的代码没有执行。

出350入8汤圆

发表于 2012-12-13 16:05:28 | 显示全部楼层
好东西,保存!

出0入0汤圆

发表于 2012-12-14 09:13:48 | 显示全部楼层
移植到stm8上,IAR,芯片为stm8s103f2p6。
  1. /****小小调度器开始**********************************************/
  2. #define MAXTASKS 3
  3. unsigned char currid;
  4. unsigned char timers[MAXTASKS];
  5. #define _SS static unsigned char lc=0; switch(lc){   case 0:
  6. #define _EE ;}; lc=0;
  7. #define WaitX(tickets) {lc=__LINE__; timers[currid]=tickets;} return ; case __LINE__:
  8. #define RunTask(TaskName,TaskID) {currid=TaskID; if (timers[TaskID]==0){timers[TaskID]=255; TaskName();}}
  9. #define CallSub(SubTaskName)   WaitX(0); SubTaskName(); if (timers[currid]!=255) return;
  10. /*****小小调度器结束*******************************************************/
  11. #define Updatetimer() {unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers[i]!=0)&&(timers[i]!=255)) timers[i]--;}}

  12. #include <iostm8s103f2.h>
  13. /**********************************************************************/
  14. /* These are system macros and defines */
  15. /**********************************************************************/
  16. //16000000/314/255 = 100hz
  17. #define         TIM1_Period   (255-1)
  18. #define         TIM1_Div      (628-1)

  19. #define LED1                        2
  20. #define LED2                        3
  21. #define led1_on()                PD_ODR &= ~(1<<LED1)
  22. #define led1_off()                PD_ODR |= (1<<LED1)
  23. #define led2_on()                PD_ODR &= ~(1<<LED2)
  24. #define led2_off()                PD_ODR |= (1<<LED2)

  25. /*******************************
  26.        MCU初始化
  27. *******************************/
  28. void init_system(void)
  29. {
  30.         CLK_CKDIVR = 0x00;
  31.         PD_DDR = (1<<LED1) | (1<<LED2);
  32.         PD_CR1 = (1<<LED1) | (1<<LED2);
  33.         PD_ODR = 0;
  34. }        

  35. /*******************************
  36.        Timer1初始化
  37. *******************************/
  38. void init_timer1(void)
  39. {        
  40.         CLK_PCKENR1 |= 0x80;
  41.        
  42.         TIM1_ARRH = (unsigned char)(TIM1_Period >> 8);
  43.         TIM1_ARRL = (unsigned char)(TIM1_Period >> 0);
  44.        
  45.         TIM1_PSCRH = (unsigned char)(TIM1_Div >> 8);
  46.         TIM1_PSCRL = (unsigned char)(TIM1_Div >> 0);

  47.         TIM1_EGR |= 0x01;
  48.         TIM1_IER = 0x01;
  49.         TIM1_CR1 = 0x01;
  50.         TIM1_SR1 = 0;
  51. }

  52. /*******************************
  53.        Timer1 interrupt 10ms
  54. *******************************/
  55. #pragma vector=TIM1_OVR_UIF_vector
  56. __interrupt void TIM1_OVR_UIF_IRQHandler(void)
  57. {
  58.         TIM1_SR1 = 0;
  59.         Updatetimer();
  60. }

  61. /*******************************
  62.           task0
  63. *******************************/
  64. void task0(void)
  65. {
  66.         _SS
  67.         while(1)
  68.         {
  69.         WaitX(10);
  70.         led1_on();
  71.         WaitX(20);
  72.         led1_off();
  73.         }
  74.         _EE
  75. }

  76. /*******************************
  77.           task1
  78. *******************************/
  79. void  task1(void)
  80. {
  81.     _SS
  82.     while(1)
  83.     {
  84.         led2_on();
  85.         WaitX(50);
  86.         led2_off();
  87.         WaitX(50);

  88.     }
  89.     _EE
  90. }

  91. /*******************************
  92.           主程序
  93. *******************************/
  94. int main(void)
  95. {
  96.     init_system();
  97.     init_timer1();
  98.         asm("rim");  // 开全局中断
  99.    
  100.     while(1)
  101.     {
  102.         RunTask(task0,0);
  103.         RunTask(task1,1);
  104.     }
  105. }
复制代码

出0入0汤圆

 楼主| 发表于 2012-12-14 09:42:10 | 显示全部楼层
zsmbj 发表于 2012-12-14 09:13
移植到stm8上,IAR,芯片为stm8s103f2p6。

谢谢!  请问能否提供编译后的资源消耗数据,RAM多少,ROM多少?便于后续优化。

出0入0汤圆

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

更新版本: 加入信号量,资源更优化。keil下编译: Program Size :  RAM=22,  xdata=0; const=0; code=158;
请验证下,看看在各cpu上有无问题,以及资源消耗情况,谢谢!


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

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

  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;} while(0);
  17. //等待信号量或定时器溢出, 定时器tickets 最大为0xFFFE
  18. #define WaitSemX(sem,tickets)  do { sem=tickets+1; WaitX(0); if(sem>1){ sem--; return;} } while(0);
  19. //发送信号量
  20. #define SendSem(sem)  do {sem=0;} while(0);

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

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

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

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

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


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

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


  55. void main()
  56. {
  57.         InitT0();
  58.         while(1){
  59.            RunTask(task1,0);
  60.            RunTask(task2,1);
  61.     }
  62. }
复制代码

出0入0汤圆

发表于 2012-12-14 11:45:39 | 显示全部楼层
smset 发表于 2012-12-14 10:21
更新版本: 加入信号量,资源更优化。keil下编译: Program Size :  RAM=22,  xdata=0; const=0; code=158;
...

能加写一个信号量应用的例子吗?我不知道怎么用

出0入0汤圆

发表于 2012-12-14 12:28:39 | 显示全部楼层
smset 发表于 2012-12-14 10:21
更新版本: 加入信号量,资源更优化。keil下编译: Program Size :  RAM=22,  xdata=0; const=0; code=158;
...


占用22个ram太夸张了吧。我这个编译到avr mega48上,占用5个ram,rom为332个。不过winavr20100110开始对内存进行了全部处理,而且所有中断都有对应接口,占用了很多rom

占用5个ram是对的,每个任务需要一个timer和一个lc,另外一个是currid,因此是5个。

如果要优化空间应该看看RunTask(task0,0);和task0任务里占用的rom
最好贴出来汇编代码。

出0入0汤圆

发表于 2012-12-14 13:38:27 | 显示全部楼层
本帖最后由 wxlcj 于 2012-12-14 13:39 编辑
smset 发表于 2012-12-13 12:27
,能否贴出示例代码给大家分享一下?


有关硬件配置,在别的文件中,就是设置一下外设。定时器用的sys_tick
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdio.h>
#include <BSP_Init.c>

/************************小小调度器开始**************************/
#define MAXTASKS 4
unsigned char currid;
unsigned char timers[MAXTASKS];

#define _SS   static unsigned char lc=0; switch(lc){   case 0:

#define _EE   ;}; lc=0;

//延时等待的tickets 最大为250
#define WaitX(tickets) {lc=__LINE__; timers[currid]=tickets;} return ; case __LINE__:

#define RunTask(TaskName,TaskID) {currid=TaskID;   if (timers[TaskID]==0){timers[TaskID]=255; TaskName();}  }

#define CallSub(SubTaskName)  { WaitX(0); SubTaskName(); if (timers[currid]!=255) return; }

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

void SCH_Update_BY_SysTick(void)  // 注意计算时标,由系统中断决定
{ unsigned char i;
        for (i=0;i<MAXTASKS;i++)
        { if ((timers!=0)&&(timers<=250))
    {  timers--;
          }
        }
}

void SCH_Init_SysTick(void)
{ if (SysTick_Config(SystemCoreClock / 100))    // 10ms中断一次
  { while (1);            /* 防止硬件出错 */  
  }            
  SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;    // 关闭滴答定时器   
}       
       
void SCH_Start(void)
{ SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;    //打开滴答定时器
}                       


void  task1()
{        _SS
    while(1)
                { WaitX(50);
      GPIO_WriteBit(GPIOC,GPIO_Pin_1 ,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_1)));  
    }
  _EE
}

void  task2()
{ _SS
    while(1)
                { WaitX(100);
      GPIO_WriteBit(GPIOC,GPIO_Pin_13 ,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_13)));
    }
  _EE
}

void  task3()
{ _SS
    while(1)
                {        if(GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_3)==1)
            { WaitX(250);
              GPIO_ResetBits(GPIOC,GPIO_Pin_3);
            }
      else
            {        WaitX(50);
              GPIO_SetBits(GPIOC,GPIO_Pin_3);
            }                                          
    }
  _EE
}

int main(void )
{ RCC_Configuration();
        GPIO_Configuration();
  SCH_Init_SysTick();
        SCH_Start();
  while(1)
        { RunTask(task1,1);
    RunTask(task2,2);
                RunTask(task3,3);
  }
}

出0入0汤圆

发表于 2012-12-14 13:38:44 | 显示全部楼层
smset 发表于 2012-12-14 09:42
谢谢!  请问能否提供编译后的资源消耗数据,RAM多少,ROM多少?便于后续优化。 ...

WaitX如果采用下面的第2种方法去写,编译后的汇编代码会小很多,执行也会变快。
第一种方法是目前使用的:
#define WaitX(tickets) {lc=__LINE__; timers[currid]=tickets;} return ; case __LINE__:
第二种方法是楼主1楼提供的:
#define WaitX(tickets,ID) {lc=__LINE__; timers[ID]=tickets;} return ; case __LINE__:

winavr,MEGA48下,如果WaitX用第1种写法,那么WaitX的汇编代码如下:
11a:        e0 91 65 00         lds        r30, 0x0065
11e:        f0 e0               ldi        r31, 0x00        ; 0
120:        ee 59               subi        r30, 0x9E        ; 158
122:        ff 4f               sbci        r31, 0xFF        ; 255
124:        88 ec               ldi        r24, 0xC8        ; 200
126:        80 83               st        Z, r24
程序花费了14byte rom,执行了8个时钟周期

如果WaitX用第2种写法,那么WaitX的汇编代码如下:
  fe:        88 ec               ldi        r24, 0xC8        ; 200
100:        80 93 63 00         sts        0x0065, r24
程序花费了6byte rom,执行了3个时钟周期
------------------------------------

IAR,STM8S103下,如果WaitX用第1种写法,那么WaitX的汇编代码如下:
   \   00000F C60000                LD        A, L:currid
   \   000012 5F                    CLRW      X
   \   000013 97                    LD        XL, A
   \   000014 A632                  LD        A, #0x32
   \   000016 D70000                LD        (L:timers,X), A
程序花费了10byte rom,执行了5个时钟周期

如果WaitX用第2种写法,那么WaitX的汇编代码如下:
   \   00000F 35320000              MOV       L:timers, #0x32
程序花费了4byte rom,执行了1个时钟周期

出0入0汤圆

 楼主| 发表于 2012-12-14 13:42:38 | 显示全部楼层
本帖最后由 smset 于 2012-12-14 13:45 编辑
zsmbj 发表于 2012-12-14 13:38
WaitX如果采用下面的第2种方法去写,编译后的汇编代码会小很多,执行也会变快。
第一种方法是目前使用的 ...



谢谢!这些数据对我很有用。

我用keil uVision4.00a ,一个空的main(){} 函数编译出来就占用了9个字节的RAM了。不知道为什么。
整个main.c就这2行。

void main(){
}

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


另外,原有的宏可以改成这样: #define _SS   static unsigned char lc=0; switch(lc){   case 0:

改为:                             #define _SS   static unsigned char lc; switch(lc){   case 0:

,会省rom一些。 静态变量默认初值就是0,所以无需再人为=0了。

出0入0汤圆

发表于 2012-12-14 13:56:18 | 显示全部楼层
如果用这种#define WaitX(tickets,ID) {lc=__LINE__; timers[ID]=tickets;} return ; case __LINE__:
那样在每个wait(X,ID)时都要书写ID,是不是会增加错误几率?一般的还是用wait(X)好一点

出0入0汤圆

 楼主| 发表于 2012-12-14 14:13:29 | 显示全部楼层
wxlcj 发表于 2012-12-14 13:56
如果用这种#define WaitX(tickets,ID) {lc=__LINE__; timers=tickets;} return ; case __LINE__:
那样在每 ...

第二种写法确实资源消耗小些,只是写起来不大直观和方便。想想有没有更好的办法。

出0入0汤圆

发表于 2012-12-14 14:22:05 | 显示全部楼层
smset 发表于 2012-12-14 13:42
谢谢!这些数据对我很有用。

我用keil uVision4.00a ,一个空的main(){} 函数编译出来就占用了9个字节 ...

打开编译的.lst看看汇编就知道了,我这里没有51的编译器。没法测试。

lc=0这个写了也没事,编译器自动优化掉了。没有增加代码。

出0入0汤圆

 楼主| 发表于 2012-12-14 14:35:27 | 显示全部楼层
本帖最后由 smset 于 2012-12-14 14:36 编辑
zsmbj 发表于 2012-12-14 13:38
WaitX如果采用下面的第2种方法去写,编译后的汇编代码会小很多,执行也会变快。
第一种方法是目前使用的 ...


请测试这个版本,看WaitX的消耗。应该已经变小了。

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

  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;} while(0);
  16. //等待信号量或定时器溢出, 定时器tickets 最大为0xFFFE
  17. #define WaitSemX(sem,tickets)  do { sem=tickets+1; WaitX(0); if(sem>1){ sem--; return;} } while(0);
  18. //发送信号量
  19. #define SendSem(sem)  do {sem=0;} while(0);

  20. #define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers[i]!=0)&&(timers[i]!=255)) timers[i]--;}
  21. /*****小小调度器结束*******************************************************/
复制代码

出0入0汤圆

发表于 2012-12-14 15:17:29 | 显示全部楼层
smset 发表于 2012-12-14 14:35
请测试这个版本,看WaitX的消耗。应该已经变小了。

WaitX是变小了,不过main函数变大了,实际使用总体rom大了一点,ram多了一个。不合适啊

出0入0汤圆

 楼主| 发表于 2012-12-14 15:36:05 | 显示全部楼层
本帖最后由 smset 于 2012-12-14 16:35 编辑

请用
#define RunTask(TaskName,TaskID) do { currid=TaskID; if (timers[TaskID]==0){ currdt=255; TaskName(); timers[TaskID]=currdt; }}  while(0);
编译

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

  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;} while(0);
  16. //等待信号量或定时器溢出, 定时器tickets 最大为0xFFFE
  17. #define WaitSemX(sem,tickets)  do { sem=tickets+1; WaitX(0); if(sem>1){ sem--; return;} } while(0);
  18. //发送信号量
  19. #define SendSem(sem)  do {sem=0;} while(0);

  20. #define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers[i]!=0)&&(timers[i]!=255)) timers[i]--;}
  21. /*****小小调度器结束*******************************************************/
复制代码
用这个版本,我编译出来ROM还要比原来的版本要小2个字节。

至于会增加一个字节的RAM, 这个确实是代价了。 换来的是WaitX的高效。

因为WaitX在任务函数里面会大量使用,所以这个版本会节省较多的ROM,而且调度运行效率也更高,用1个字节的RAM来换取,总体还是很值得的。

出0入0汤圆

发表于 2012-12-14 16:30:43 | 显示全部楼层
smset 发表于 2012-12-14 15:36
请用
#define RunTask(TaskName,TaskID) do { currid=TaskID; if (timers[TaskID]==0){ currdt=255; TaskNa ...

currid只有callsub里用到了,如果精简一下是否可以去掉?

出0入0汤圆

 楼主| 发表于 2012-12-14 16:40:33 | 显示全部楼层
本帖最后由 smset 于 2012-12-14 17:24 编辑

也可以去掉,现在是最精简版本,也是最新评测版本。

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

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

  19. #define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers[i]!=0)&&(timers[i]!=255)) timers[i]--;}
  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. }
复制代码

出0入0汤圆

发表于 2012-12-14 18:07:18 | 显示全部楼层
smset 发表于 2012-12-14 16:40
也可以去掉,现在是最精简版本,也是最新评测版本。

确认新的代码在winavr上编译会减小12个字节。ram相同

-------- begin --------
Size before:
   text           data            bss            dec            hex        filename
    332              0              5            337            151        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
    320              0              5            325            145        main.elf
Errors: none
-------- end --------

看来优化成功啊,demo上跑也是对的。

出0入0汤圆

 楼主| 发表于 2012-12-14 18:14:13 | 显示全部楼层
zsmbj , 谢谢你的支持, WaitX的优化是很重要的,在正式应用中,WaitX的调用量会远大于现在这个demo,所以优化的效果会更加明显。

出0入0汤圆

发表于 2012-12-14 19:27:30 | 显示全部楼层
请出下信号量应用的例子哦。

出0入0汤圆

发表于 2012-12-14 19:27:48 | 显示全部楼层
请出下信号量应用的例子哦。

出0入0汤圆

发表于 2012-12-14 22:10:52 | 显示全部楼层
本帖最后由 flor 于 2012-12-14 22:13 编辑
smset 发表于 2012-12-14 16:40
也可以去掉,现在是最精简版本,也是最新评测版本。
  1. 08.#define WaitX(tickets)  do {lc=__LINE__; currdt=tickets; return ;} while(0); case __LINE__:
复制代码
是否错了 ?
  1. 08.#define WaitX(tickets)  do {lc=__LINE__; timers[currdt]=tickets; return ;} while(0); case __LINE__:
复制代码

出0入0汤圆

发表于 2012-12-14 22:27:41 | 显示全部楼层
flor 发表于 2012-12-14 22:10
是否错了 ?

没错,这样修改后WaitX的汇编代码可以大大简化。因为WaitX里没有针对数组的赋值,直接对寄存器赋值了。
在Runtask后,再将这个寄存器的值赋值给数组。

出0入0汤圆

发表于 2012-12-14 23:15:32 | 显示全部楼层
顶起,好好学习一下

出0入0汤圆

发表于 2012-12-14 23:46:41 | 显示全部楼层
zsmbj 发表于 2012-12-14 22:27
没错,这样修改后WaitX的汇编代码可以大大简化。因为WaitX里没有针对数组的赋值,直接对寄存器赋值了。
...

这个变量没有其他地方调用啊?

出0入0汤圆

发表于 2012-12-15 00:30:12 | 显示全部楼层
本帖最后由 flor 于 2012-12-15 00:32 编辑

这样写不容易看懂。。。

出0入0汤圆

发表于 2012-12-15 09:07:21 | 显示全部楼层
smset 发表于 2012-12-14 16:40
也可以去掉,现在是最精简版本,也是最新评测版本。

#define WaitX(tickets)  do {lc=__LINE__; currdt=tickets; return ;} while(0); case __LINE__:
#define RunTask(TaskName,TaskID) do { if (timers[TaskID]==0){  currdt=255; TaskName(); timers[TaskID]=currdt; }}  while(0);

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

本版积分规则

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

GMT+8, 2024-4-20 09:03

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

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