formatme
发表于 2012-12-15 09:09:14
#define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers!=0)&&(timers!=255)) timers--;}
会报错,
#define UpdateTimers() {unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers!=0)&&(timers!=255)) timers--;}}
这样就没报
formatme
发表于 2012-12-15 09:12:02
这个是仿真文件,5个task只能跑2个
holts2
发表于 2012-12-15 10:32:08
#define _EE ;}; lc=0;
哪位XD能解释下, lc=0 这个永远都不会运行, 放在这是有什么用意啊? {:sweat:}
smset
发表于 2012-12-15 10:58:21
holts2 发表于 2012-12-15 10:32
#define _EE ;}; lc=0;
哪位XD能解释下, lc=0 这个永远都不会运行, 放在这是有什么用意啊? {:sweat: ...
会在任务结束时执行,以便下次用
holts2
发表于 2012-12-15 12:45:51
smset 发表于 2012-12-15 10:58 static/image/common/back.gif
会在任务结束时执行,以便下次用
voidtask1(){
static unsigned char lc=0; switch(lc){ case 0:
while(1){
// WaitX(50);
{lc=62; timers=50;} return ; case 62:
LED1=!LED1;
}
;}; lc=0;
}
还是看不懂啊, 如上任务的出口在 return, 永远都不会执行到 ;}; lc=0;
smset
发表于 2012-12-15 13:14:12
holts2 发表于 2012-12-15 12:45
voidtask1(){
static unsigned char lc=0; switch(lc){ case 0:
while(1){
并非所有的任务都是永远循环执行啊
takashiki
发表于 2012-12-15 13:25:58
本帖最后由 takashiki 于 2012-12-15 13:32 编辑
根据楼主的各个更新版本,终于又Kill掉了一些字节,并且专门针对51进行了优化。其他CPU暂时没有优化,使用时再说吧。
使用信号量时代码会增大一些,因此放了一个宏SIGNAL,允许使用或关闭信号量。
对于一个任务中存在大量的Wait时,使用WaitEx会减少ROM和CPU的消耗,但程序比较复杂了一些。因为很多CPU能够查表(比如51的JMP散转指令)而不是依次比较。
另外,延时和信号安排方式也改了,定义了SIGNAL宏时,最高位表示等待信号,低7位表示等待延时,没有定义时,1~255都是等待延时。=0时则任务就行,主函数会进行调度。
SendSignal只对等待信号有效,对等待延时无效。
SetReady同时对等待信号和等待延时有效。
所有任务都默认是无限循环的,这个很楼主的不一致。如果只需要执行一次或几次就停止,那么需要手动删除,使用DeleteSelf宏即可。
任务的延时不再直接写入Timers,也不和楼主一样写入变量,而是写入返回值,类似AvrX操作系统。//12MHz振荡器,12分频,约每10ms中断一次
#include <REGX51.H>
typedef unsigned char uchar;
/****小小调度器开始**********************************************/
#define MAXTASKS 2
#define SIGNAL //允许使用信号量,代码量会有一定的增加
uchar Timers;
#define BEGIN_TASK(TaskName) uchar TaskName(){ static uchar lc; switch(lc){case 0:
#define END_TASK } lc = 0; return 0;}
#define Wait(Ticks){lc = __LINE__; return Ticks; } case __LINE__:
#define RunTask(TaskName, TaskID) { if (Timers == 0){ Timers = TaskName(); }}
#define SetReady(TaskId) Timers = 0
#define WaitEx(Index, Ticks){lc = Index; return Ticks; } case Index:
#ifdef __C51__
#define CallSub(SubTaskName) {lc = __LINE__; case __LINE__:{ ACC = SubTaskName(); if(ACC) return ACC;}}
#define UpdateTimersEx() uchar idata* data ptmr = &Timers;\
while(ptmr != (uchar idata*)&Timers){ \
ptmr --; \
ACC = *ptmr; \
ACC ^= 0x80; \
if(ACC == 0) continue; \
ACC ^= 0x80; \
if(ACC == 0x81) ACC = 0; \
if(ACC) ACC--; \
*ptmr = ACC; }
#else
#define CallSub(SubTaskName) {lc = __LINE__; case __LINE__:{ uchar subTime = SubTaskName(); if(subTime) return subTime;}}
#define UpdateTimersEx() uchar * ptmr = &Timers; \
while(ptmr != (uchar*)&Timers){ \
ptmr --; \
if(*ptmr == 0x80) continue; \
if(*ptmr == 0x81) *ptmr = 0; \
if(*ptmr) *ptmr--;}
#endif
#ifdef SIGNAL
#define WaitSignal() Wait(128)
#define WaitSignalAndTimeOut(Tick) Wait(128 + Tick)
#define PauseSelf WaitSignal
#define DeleteSelf() return 128
#define DeleteTask(TaskId) Timers = 128
#define SendSignal(TaskId) if(Timers >= 128){Timers = 0;}
#define WaitSignalEx(Index) WaitEx(Index, 128)
#define WaitSignalAndTimeOutEx(Index, Tick) WaitEx(Index, 128 + Tick)
#define UpdateTimers UpdateTimersEx
#else
#define UpdateTimers() uchar i; for(i = MAXTASKS; i > 0; i--){ if(Timers) Timers--; }
#endif
/*****小小调度器结束*******************************************************/
sbit LED1 = P2^1;
sbit LED2 = P2^2;
void T0_ISR(void) interrupt 1 using 1
{
UpdateTimers();
TH0 = 0xD9; //大约10ms重装,为了简单,没有进行补偿
}
BEGIN_TASK(Task1) //测试加强版Wait,更加节省时间和空间
WaitEx(1, 2);
LED1=!LED1;
WaitEx(2, 3);
LED1=!LED1;
WaitEx(3, 4);
LED1=!LED1;
WaitEx(4, 5);
LED1=!LED1;
WaitEx(5, 6);
LED1=!LED1;
WaitEx(6, 7);
LED1=!LED1;
WaitSignalEx(7); //等待信号, 同时等待信号和超时用:WaitSignalAndTimeOut
LED1=!LED1;
END_TASK
BEGIN_TASK(SubTask)
if(LED2){
Wait(5);
SendSignal(0); //发送信号
}
END_TASK
BEGIN_TASK(Task2)
Wait(3);
LED2=!LED2;
CallSub(SubTask); //调用子任务
END_TASK
void main() {
IE = 0x82;
TCON = 0x10;
TMOD = 1;
TH0 = 0xD9;
while(1){
RunTask(Task1,0); //任务调用
RunTask(Task2,1);
}
}典型的,在51上,Wait只需要消耗三条指令,共6个字节。 92: Wait(3);
C:0x00C7 75125C MOV 0x12,#0x5C
C:0x00CA 7F03 MOV R7,#0x03
C:0x00CC 22 RET
93: LED2=!LED2;
C:0x00CD B2A2 CPL LED2(0xA0.2)修改原因:CODE段排版怎么都排不好,晕死
smset
发表于 2012-12-15 17:27:25
本帖最后由 smset 于 2012-12-15 20:47 编辑
非常不错!你又提供了一个亮点哈:任务函数带返回值, 我重新评估了任务函数带返回值的方案,首先会省掉1个字节的ram,然后WaitX的效率提高了。任务函数带返回值的方案唯一的问题是每个Wait编译出的增量字节是9字节,比我最后版本会多一个字节(299楼的WaitX增量Rom是8字节)。
但经过我查看汇编代码,实际上带返回值的运行效率是更高的,所以我需要总体评估一下。
楼上的版本:1) Wait和 WaitEx没有区别的。不知道为何要设置两个宏
2)BEGIN_TASK和END_TASK似乎不实用:用户没有办法声明任务的静态变量了,这个宏把函数名和变量声明区占完了。
smset
发表于 2012-12-15 17:46:30
本帖最后由 smset 于 2012-12-15 19:50 编辑
在接来的版本中增加高优先级任务抢占机制。时钟这部分会统一考虑调整。
zsmbj
发表于 2012-12-15 20:54:25
smset 发表于 2012-12-15 17:27 static/image/common/back.gif
非常不错!你又提供了一个亮点哈:任务函数带返回值, 我重新评估了任务函数带返回值的方案,首先会省掉1个 ...
采用任务函数返回值的方法,很不错啊。我在Mega8上测试,节省rom而且运行会变快。编译器winavr20100110
Size before:
text data bss dec hex filename
326 0 7 333 14d main.elf
avr-gcc -c -mmcu=atmega8 -I. -g -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-ahlms=main.lst main.c -o main.o
avr-gcc -mmcu=atmega8 -Wl,-Map=main.map,--cref main.o -lm --output main.elf
avr-objcopy -O ihex -R .eeprom main.elf main.hex
avr-objdump -h -S main.elf > main.lss
Size after:
text data bss dec hex filename
338 0 8 346 15a main.elf
新代码节省了一个byte的ram,rom减少了12byte。
以下是原来的程序编译的汇编代码,这里仅仅列出task0和runtask两个部分代码的区别
runtask原始代码的汇编:
RunTask(task0,0);
11e: 1f ef ldi r17, 0xFF ; 255
120: 80 91 62 00 lds r24, 0x0062
124: 88 23 and r24, r24
126: 39 f4 brne .+14 ; 0x136 <main+0x34>
128: 10 93 67 00 sts 0x0067, r17
12c: b7 df rcall .-146 ; 0x9c <task0>
12e: 80 91 67 00 lds r24, 0x0067
132: 80 93 62 00 sts 0x0062, r24
新代码的汇编:
RunTask(task0,0);
11a: 1f ef ldi r17, 0xFF ; 255
11c: 80 91 62 00 lds r24, 0x0062
120: 88 23 and r24, r24
122: 29 f4 brne .+10 ; 0x12e <main+0x30>
124: 10 93 62 00 sts 0x0062, r17
128: b9 df rcall .-142 ; 0x9c <task0>
12a: 80 93 62 00 sts 0x0062, r24
可以看出原来的代码由于传递数据需要currdt变量,因此在rcall前进行了255的赋值,之后又取出再赋值给tmers。
而新的代码由于没有了变量,因此在rcall钱直接给timers赋值255,之后通过rcall的返回值直接赋值。节省了取出currdt的这个时间。
task0里的WaitX的原始代码的汇编:
WaitX(20);
ba: 84 e6 ldi r24, 0x64 ; 100
bc: 80 93 61 00 sts 0x0061, r24
c0: 84 e1 ldi r24, 0x14 ; 20
c2: 80 93 67 00 sts 0x0067, r24
c6: 08 95 ret
新代码的汇编
WaitX(20);
ba: 84 e6 ldi r24, 0x64 ; 100
bc: 80 93 61 00 sts 0x0061, r24
c0: 84 e1 ldi r24, 0x14 ; 20
c2: 08 95 ret
上面的汇编,首先是对lc进行赋值,给0x0061寄存器,而等待20这个部分原始代码需要把这个数据传给currdt寄存器,再返回。
而新的代码由于没有了变量,因此直接给内部r24寄存器,然后返回。
因此采用任务函数带返回值的方式不仅节省了rom空间,节省了一个ram,而且程序运行也更快。
/****小小调度器开始**********************************************/
#define MAXTASKS 5
unsigned char timers;
#define _SS static unsigned char lc; switch(lc){case 0:
#define _EE ;}; lc=0; return 0;
#define WaitX(tickets)do {lc=__LINE__; return tickets ;} while(0); case __LINE__:
#define RunTask(TaskName,TaskID) do { if (timers==0){ timers=255; timers=TaskName(); }}while(0);
#define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers!=0)&&(timers!=255)) timers--;}
/*****小小调度器结束*******************************************************/
smset
发表于 2012-12-15 21:46:10
本帖最后由 smset 于 2012-12-15 21:58 编辑
zsmbj 发表于 2012-12-15 20:54
采用任务函数返回值的方法,很不错啊。我在Mega8上测试,节省rom而且运行会变快。编译器winavr20100110
S ...
是的,任务带返回的方案的运行效率确实更佳,至于Rom可能不同编译器微有差异,_EE那里应该返回255。在大家的努力下,我们越来越接近完美方案了,呵呵
smset
发表于 2012-12-15 21:50:16
下一步,我打算设计在中断里执行1毫秒级高优先级任务的方案,敬请合作共同打造。
smset
发表于 2012-12-15 22:18:48
formatme 发表于 2012-12-15 09:12 static/image/common/back.gif
这个是仿真文件,5个task只能跑2个
你再检查下吧, WaitX(1000);已经溢出了。 WaitX的最大数是254. 对应2.54秒。
takashiki
发表于 2012-12-15 22:25:20
smset 发表于 2012-12-15 17:27 static/image/common/back.gif
非常不错!你又提供了一个亮点哈:任务函数带返回值, 我重新评估了任务函数带返回值的方案,首先会省掉1个 ...
使用两个不同的宏的目的是为了产生更加高效的代码,当然也就更加底层了,更麻烦了……
当大量的WaitEx按顺序写下去的时候,编译器会选择更加高效的代码,而不是依次比较。 67: BEGIN_TASK(Task1) //测试加强版Wait,更加节省时间和空间
C:0x0041 E510 MOV A,0x10
C:0x0043 B40800 CJNE A,#0x08,C:0046
C:0x0046 504E JNC C:0096
C:0x0048 90004E MOV DPTR,#0x004E
C:0x004B 25E0 ADD A,ACC(0xE0)
C:0x004D 73 JMP @A+DPTR //这里会散转,ROM和CPU都优化了
C:0x004E 015E AJMP C:005E
C:0x0050 0164 AJMP C:0064
C:0x0052 016C AJMP C:006C
C:0x0054 0174 AJMP C:0074
C:0x0056 017C AJMP C:007C
C:0x0058 0184 AJMP C:0084
C:0x005A 018C AJMP C:008C
C:0x005C 0194 AJMP C:0094
68: WaitEx(1, 2);
C:0x005E 751001 MOV 0x10,#0x01
C:0x0061 7F02 MOV R7,#0x02
C:0x0063 22 RET
69: LED1=!LED1;
C:0x0064 B2A1 CPL LED1(0xA0.1)
70: WaitEx(2, 3);
C:0x0066 751002 MOV 0x10,#0x02
C:0x0069 7F03 MOV R7,#0x03
C:0x006B 22 RET
71: LED1=!LED1;
... ...
smset
发表于 2012-12-15 22:29:39
本帖最后由 smset 于 2012-12-15 22:31 编辑
可Wait也是7字节rom,3个周期啊。你是说散转那里会优化吧,不过这个可能有点依赖于特定编译器了。
takashiki
发表于 2012-12-16 08:01:00
还有,你为什么要说我写的BEGIN_TASK、END_TASK宏把变量声明区占光了,无法提供静态变量呢?是可以的,我经常这样用。
C语言的变量定义不一定要放在函数开始哦,只要放在除了switch以外的任何一个花括号开始都可以。
比如这样:BEGIN_TASK(Task2)
{ //这里独立成块
static uchar j = 2; //这里放置静态变量
Wait(3);
j ++; //跨越阻塞使用静态变量
LED2=!LED2;
CallSub(SubTask); //调用子任务
}
END_TASK
smset
发表于 2012-12-16 08:22:53
takashiki 发表于 2012-12-16 08:01
还有,你为什么要说我写的BEGIN_TASK、END_TASK宏把变量声明区占光了,无法提供静态变量呢?是可以的,我经 ...
标准C语言变量声明必须放代码前面,C++才可以到处放。
takashiki
发表于 2012-12-16 08:30:41
smset 发表于 2012-12-16 08:22 static/image/common/back.gif
标准C语言变量声明必须放代码前面,C++才可以到处放。
标准C语言的变量声明必须放在块的开头,C++可以到处放。Pascal才是必须放到代码开头(var子节)的,你弄错了吧。
放在不同的位置,变量的作用域是不同的。C的变量的生命周期和C++也不一致,C的明显比较NC一些,能够跨越块而继续生存。
takashiki
发表于 2012-12-16 08:38:52
我弄个例子给你看看,基于标准的ANSI C,不包含任何扩展语法:int main(void){
myFunc();
int x = 0; //C中出错,应放在myFunc();之前。C++中不出错,x的生命周期一直延续到函数返回位置
return x ;
}改成这样:int main(void){
myFunc();
{int x = 0;} //C中不出错,C++中出错,x的生命周期到这里就结束了,而C中x的生命周期一次延续到函数返回位置
return x; //C++中x变量未定义,因为上面的x的生命周期已经结束了,而C则一直存在而不会出错
}两种都兼容的:int main(void){
myFunc();
{
int x = 0;
return x;
}
}
smset
发表于 2012-12-16 10:07:06
本帖最后由 smset 于 2012-12-16 11:59 编辑
如编译器支持C99, 但是很多编译器是ANSI C89, 就不支持,
你用keil编译试试看,在TASKBEGIN宏 的下一行声明一个变量,肯定会编译报错。
而如果要求声明的变量都加花括号里,那太别扭了吧,我们相信大多数人的c语言编程,都不会习惯在声明变量时,增加一对花括号的。
宏定义本义是为了编程更方便,如果反而因此带来麻烦,那就没必要用这个宏啊。
------------
哦,我又看了BEGIN_TASK用法,这样用也没有问题。takashiki C语言功力深厚,佩服啊。
smset
发表于 2012-12-16 10:10:46
本帖最后由 smset 于 2012-12-16 23:02 编辑
结合takashiki提出的任务带返回的新思路,再更新一版,请评测:
#include "stc89c51.h"
/****小小调度器开始**********************************************/
#define MAXTASKS 2
static unsigned char timers;
unsigned char currdt;
#define _SS static unsigned char _lc; switch(_lc){case 0:
#define _EE ;}; _lc=0; return 255;
#define WaitX(tickets)do {_lc=__LINE__; return tickets ;} while(0); case __LINE__:
#define RunTask(TaskName,TaskID) do { if (timers==0) timers=TaskName(); }while(0);
#define CallSub(SubTaskName) do { _lc=__LINE__; return 0; case __LINE__:currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
#define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers!=0)&&(timers!=255)) timers--;}
#define SEM unsigned int
//初始化信号量
#define InitSem(sem) sem=0;
//等待信号量
#define WaitSem(sem) do{ sem=1; WaitX(0); if (sem>0) return 1;} while(0);
//等待信号量或定时器溢出, 定时器tickets 最大为0xFFFE
#define WaitSemX(sem,tickets)do { sem=tickets+1; WaitX(0); if(sem>1){ sem--;return 1;} } while(0);
//发送信号量
#define SendSem(sem)do {sem=0;} while(0);
/*****小小调度器结束*******************************************************/
sbit LED1 = P2^1;
sbit LED2 = P2^2;
void InitT0()
{
TMOD = 0x21;
IE |= 0x82;// 12t
TL0=0Xff;
TH0=0XDB;//22M---b7;
TR0 = 1;
}
void INTT0(void) interrupt 1 using 1
{
UpdateTimers();
TL0=0Xff; //10ms 重装
TH0=0XDB;
}
unsigned char task1(){
_SS
while(1){
WaitX(50);
LED1=!LED1;
}
_EE
}
unsigned char task2(){
_SS
while(1){
WaitX(100);
LED2=!LED2;
}
_EE
}
void main()
{
InitT0();
while(1){
RunTask(task1,0);
RunTask(task2,1);
//如需降低功耗,在这里执行待机指令,可降低功耗,对实时性没有任何影响
}
}
小小调度器为全开源,全免费使用,首次发布于www.amobbs.com , 转载请注明出处。
stm8s
发表于 2012-12-16 10:45:05
昨天试了一下.关注
dr2001
发表于 2012-12-16 11:03:32
takashiki 发表于 2012-12-15 22:25 static/image/common/back.gif
使用两个不同的宏的目的是为了产生更加高效的代码,当然也就更加底层了,更麻烦了……
当大量的WaitEx按 ...
这个可以使用include宏的方法实现自动递增,不需要人工参与。
只不过代码上很难看。
dr2001
发表于 2012-12-16 11:07:03
smset 发表于 2012-12-15 22:29 static/image/common/back.gif
可Wait也是7字节rom,3个周期啊。你是说散转那里会优化吧,不过这个可能有点依赖于特定编译器了。
...
switch的跳转表优化估计属于编译器的基础功能了,只要不是太简单的编译器,一般都会有这个功能。
对于高级一点的编译器,控制case的值在M*2^N的行号上,能获得一样的效果。
smset
发表于 2012-12-16 11:23:40
dr2001 发表于 2012-12-16 11:03 static/image/common/back.gif
这个可以使用include宏的方法实现自动递增,不需要人工参与。
只不过代码上很难看。 ...
具体如何实现呢,能否给个实例哦。
dr2001
发表于 2012-12-16 11:49:48
smset 发表于 2012-12-16 11:23 static/image/common/back.gif
具体如何实现呢,能否给个实例哦。
http://www.amobbs.com/thread-5511786-1-1.html
可以参考这个帖子。
主要是对编码风格有较大影响。
cpwander
发表于 2012-12-16 14:18:42
学习中,
newselect
发表于 2012-12-16 18:28:26
谢谢分享
flor
发表于 2012-12-16 21:34:16
smset 发表于 2012-12-16 10:10 static/image/common/back.gif
结合takashiki提出的任务带返回的新思路,再更新一版,请评测:
currdt只在callsub中使用,定义为块中的局部变量是不是省1字节?
小柯师傅
发表于 2012-12-16 21:51:25
本帖最后由 小柯师傅 于 2012-12-16 22:32 编辑
楼主,真的很感谢你的程序,330楼的最新程序更棒了!
但是我用这个程序开始是好的,后来Data Segment增加后出现了问题,问题是RunTask好像不运行了,由于我的51无法调试,我试验了很多次发现问题是这句,if (timers==0).
后来变量定义的地方我修改成unsigned char timers = {0,0,0,0,0};就完全没问题了,不知道是否是我这边的问题,请求搂主指点,谢谢?
eddia2012
发表于 2012-12-16 22:34:04
{:smile:}希望加入事件触发优化。
826282925
发表于 2012-12-16 22:37:16
马克~!!!!
smset
发表于 2012-12-16 22:42:17
本帖最后由 smset 于 2012-12-16 23:24 编辑
小柯师傅 发表于 2012-12-16 21:51 static/image/common/back.gif
楼主,真的很感谢你的程序,330楼的最新程序更棒了!
但是我用这个程序开始是好的,后来Data Segment增加后 ...
您好,不是你的问题,是调度器程序的一个bug。
谢谢,您帮我我们发现了一个bug:因为timers定义时未初始化,所以timers数组的初始值是随机的, 如果恰好随机值是255,那么相应任务就不运行了。
所以请定义为static unsigned char timers; 这样初值自动默认为0;
330楼已经据此更新。再次感谢!
-------------------------------------------------
也可能不是变量定义的问题,检查下你的程序有没有数组越界,或指针越界的情况。
ifree64
发表于 2012-12-16 22:55:10
smset 发表于 2012-12-16 22:42 static/image/common/back.gif
您好,不是你的问题,是调度器程序的一个bug。
谢谢,您帮我我们发现了一个bug:因为timers定义时未初始 ...
你的这个解释不对吧。
原330楼写作:
unsigned char timers;
新330楼写作:
static unsigned char timers;
但都是作为全局变量定义,C语言规定全局变量不初始化时,默认初始化为0,所以有无static,就初始值而言是没有区别的。
smset
发表于 2012-12-16 23:09:57
本帖最后由 smset 于 2012-12-16 23:15 编辑
嗯,看来339楼的问题,不是这个原因引起。 但从339楼描述的解决方案来看,又是timers初值不为0引起的。
不过我在一个产品上这样用,data段也增加了,并没发现339楼所述问题。
xy-mcu
发表于 2012-12-16 23:21:41
所以我才会在我的代码里面加了这个。
void sms_os_InitTimers(void)
{
INT8U i;
for (i=0;i<MAXTASKS;i++){
timers=0;
}
}
第一个应用正在进行中...
小柯师傅
发表于 2012-12-16 23:30:41
smset 发表于 2012-12-16 22:42 static/image/common/back.gif
您好,不是你的问题,是调度器程序的一个bug。
谢谢,您帮我我们发现了一个bug:因为timers定义时未初始 ...
谢谢您的回复,由于我使用您的程序没有放到主程序,是另外新建了个os.c 和os.h (以后可能很多人也会把核心调度的放入另外个文件) ,所以没用static ,用的extern ,最终是用变量初始化解决的。
smset
发表于 2012-12-16 23:31:02
xy-mcu 发表于 2012-12-16 23:21 static/image/common/back.gif
所以我才会在我的代码里面加了这个。
void sms_os_InitTimers(void)
{
这么近,我也在成都高新南区呢。 {:handshake:}
xy-mcu
发表于 2012-12-17 02:11:14
smset 发表于 2012-12-16 23:31 static/image/common/back.gif
这么近,我也在成都高新南区呢。
呵呵,说不定还都是从同一个集团出来的,说不定还认识哦。
zsmbj
发表于 2012-12-17 08:18:46
smset 发表于 2012-12-16 10:10 static/image/common/back.gif
结合takashiki提出的任务带返回的新思路,再更新一版,请评测:小小调度器为全开源,全免费使用,首次发布于 ...
这个版本的CallSub是编译不过去的。我改成了如下可以编译过去,而且由于变量只有这个定义才使用,因此放在这里,可以节省一个ram。请测试。
#define CallSub(SubTaskName) unsigned char currdt; do {WaitX(0);currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
topdog
发表于 2012-12-17 09:52:52
PIC16F627APICC V9.8很精簡了。
smset
发表于 2012-12-17 10:03:42
topdog 发表于 2012-12-17 09:52 static/image/common/back.gif
PIC16F627APICC V9.8很精簡了。
{:smile:} 能否贴出该例子代码?
topdog
发表于 2012-12-17 10:07:47
本帖最后由 topdog 于 2012-12-17 10:13 编辑
smset 发表于 2012-12-17 10:03 static/image/common/back.gif
能否贴出该例子代码?
基本使用你330樓使用的例子
/**********************************************************/
#include "htc.h"
#include "OS_TMCU.H"
//------configure bit seti
__CONFIG (INTIO&WDTDIS&PWRTEN&MCLREN&BOREN&LVPDIS&PROTECT&UNPROTECT);
/****小小调度器开始**********************************************/
#define MAXTASKS 2
static unsigned char timers;
unsigned char currdt;
#define _SS static unsigned char _lc; switch(_lc){case 0:
#define _EE ;}; _lc=0; return 255;
#define WaitX(tickets)do {_lc=__LINE__; return tickets ;} while(0); case __LINE__:
#define RunTask(TaskName,TaskID) do { if (timers==0) timers=TaskName(); }while(0);
#define CallSub(SubTaskName) do { _lc=__LINE__; return 0; case __LINE__:currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
#define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers!=0)&&(timers!=255)) timers--;}
#define SEM unsigned int
//初始化信号量
#define InitSem(sem) sem=0;
//等待信号量
#define WaitSem(sem) do{ sem=1; WaitX(0); if (sem>0) return 1;} while(0);
//等待信号量或定时器溢出, 定时器tickets 最大为0xFFFE
#define WaitSemX(sem,tickets)do { sem=tickets+1; WaitX(0); if(sem>1){ sem--;return 1;} } while(0);
//发送信号量
#define SendSem(sem)do {sem=0;} while(0);
/*****小小调度器结束*******************************************************/
//****************************
//
//****************************
void ChipInit(void)
{
// Disable AD
//ANSEL=0;
//ANSELH=0;
//---IO Setting
//--PORTA
/*
#define KEY_ZONE_I RA0
#define KEY_LEVEL_I RA1
#define FB_I RA2
#define KEY_POWER_I RA3
#define SEAT_I RA4
#define RS_I RA5
#define BACK_LED_HI_O RA6
#define IR_O RA7 //传
*/
TRISA=0b00111111;
//--PORTB
/*
#define LR_I RB0
#define BACK_LED_LOW_O RB1
#define WAIST_LED_LOW_O RB2
#define LEG_LED_LOW_O RB3
#define LEG_LED_HI_O RB4
#define WAIST_LED_HI_O RB5
#define PGC_O RB6
#define KEEP_PGD_O RB7
*/
TRISB=0b00000001;
#ifdef PIC_DEBUG
TRISB=0b00000111;
#else
TRISB=0b00000001;
#endif
PORTA=0;
PORTB=0;
// ? ゑ/PWM 家塊
VRCON=0;
CMCON=0x07;
OSCF=TRUE; //int osc 4Mhz
//---T0 Setting
// OPTION select
OPTION=0b10000000;// 1:2
TMR0=205;// 0.1msec
//----T2 Setting
//PIE1=0b00000000;//
//PIR1=0;
// T2CON=0b00000000;//
//PIR2=250;//250*4=1000(1msec)
//TMR2=0;
// USART Setting
#ifdef PIC_DEBUG
TXSTA=0;
RCSTA=0;
BRGH=1;
SPBRG=25; //初4MHz9600BPS 0.16
SPEN=1; //
TXEN=1; // 竟
CREN=0; //綳
PEIE=0;
RCIE=0;
#endif
}
uchar task1(){
_SS
while(1){
WaitX(50);
LED1=!LED1;
}
_EE
}
uchartask2(){
_SS
while(1){
WaitX(100);
LED2=!LED2;
}
_EE
}
void main()
{
ChipInit();
T0IF=FALSE;
T0IE=TRUE;
GIE=TRUE;
KEEP_PGD_O=TRUE;
while(1){
//LED1=TRUE;
//LED2=TRUE;
RunTask(task1,0);
RunTask(task2,1);
//RunTask(Subtask3,2);
}
}
//****************************
// interrupt sub
//****************************
void interrupt InterruptTig(void)
{
// uchar i;
//timer manager
if (Timer2msec>=100)
{
Timer2msec=0;
T2msecFlag=TRUE;
UpdateTimers() ;
}
else
Timer2msec++;
//timer manager
TMR0=218;
T0IF=FALSE;
}
smset
发表于 2012-12-17 10:16:18
本帖最后由 smset 于 2012-12-17 10:18 编辑
topdog 发表于 2012-12-17 10:07 static/image/common/back.gif
基本使用你330樓使用的例子
/**********************************************************/
#include ...
不错,是按330楼的版本改的。
CallSub不是在main()里面调用的,是任务里面调用子任务用的。
另外,贴代码可以在 [ / c o d e ] 之间插入(去掉括号内的空格),就可以正常显示。
topdog
发表于 2012-12-17 10:20:34
smset 发表于 2012-12-17 10:03 static/image/common/back.gif
能否贴出该例子代码?
中斷中優化后,去掉了以前沒用的2ms標誌后,效果更好。ram和rom都有減少
smset
发表于 2012-12-17 10:32:05
很好啊,这些数据很有价值。
smset
发表于 2012-12-17 10:56:01
本帖最后由 smset 于 2012-12-17 10:58 编辑
zsmbj 发表于 2012-12-17 08:18 static/image/common/back.gif
这个版本的CallSub是编译不过去的。我改成了如下可以编译过去,而且由于变量只有这个定义才使用,因此放 ...
可以编译的啊。
你的写法,我编译了,没发现省RAM呢。 结果是一样的。
另外,按你的写法,如果一个任务函数有两处调用CallSub,那 currdt就重复定义了啊,这个不妥当哦。
takashiki
发表于 2012-12-17 11:15:05
smset 发表于 2012-12-16 10:07 static/image/common/back.gif
如编译器支持C99, 但是很多编译器是ANSI C89, 就不支持,
你用keil编译试试看,在TASKBEGIN宏 的下一行声 ...
这个变通一下,宏定义时强制加入一对括号,嘿嘿。#define BEGIN_TASK(TaskName) uchar TaskName(){ static uchar lc; switch(lc){case 0: {
#define END_TASK }} lc = 0; return 0;}这下使用者没话说了吧。
我提供BEGIN_TASK宏的原因是提示使用者,这个函数的地位不一般,它是任务函数,不能像其他普通函数一样随意的调用,约定只能由RunTask和CallSub调用。
zsmbj
发表于 2012-12-17 11:24:13
smset 发表于 2012-12-17 10:56 static/image/common/back.gif
可以编译的啊。
你的写法,我编译了,没发现省RAM呢。 结果是一样的。
#define CallSub(SubTaskName) do { _lc=__LINE__; return 0; case __LINE__:currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
/*******************************
subtask
*******************************/
unsigned char subtask(void)
{
_SS
while(1)
{
WaitX(20);
led1_on();
WaitX(30);
led1_off();
}
_EE
}
/*******************************
task0
*******************************/
unsigned char task0(void)
{
_SS
while(1)
{
WaitX(10);
led1_tgl();
CallSub(subtask);
}
_EE
}
编译不通过,提示:Error: identifier "_lc" is undefined
takashiki
发表于 2012-12-17 11:37:33
zsmbj 发表于 2012-12-17 11:24 static/image/common/back.gif
#define CallSub(SubTaskName) do { _lc=__LINE__; return 0; case __LINE__:currdt=SubTaskName(); if ...
他这是笔误,_lc前面的下划线多余了,你多虑了。嘿嘿。
zsmbj
发表于 2012-12-17 11:38:43
takashiki 发表于 2012-12-17 11:37 static/image/common/back.gif
他这是笔误,_lc前面的下划线多余了,你多虑了。嘿嘿。
哈哈,果然啊,我直接copy了,所以smset根本没有编译试试,否则肯定也会不通过,哈哈!
smset
发表于 2012-12-17 11:47:38
本帖最后由 smset 于 2012-12-17 11:52 编辑
zsmbj 发表于 2012-12-17 11:38 static/image/common/back.gif
哈哈,果然啊,我直接copy了,所以smset根本没有编译试试,否则肯定也会不通过,哈哈! ...
{:biggrin:} 瞎说哈,我肯定编译通过了的啊,不然我怎么知道这个版本更优化呢, 我要看汇编代码的啊。
330楼新版,lc统一更改为_lc, 表示这是一个“系统”变量 ,这不是笔误。是你自己“断章取义”导致的后果哈。
这样即使使用者无意中也定义了一个变量刚好名为lc。也不冲突。
muniao
发表于 2012-12-17 13:30:33
mark一下{:lol:}
smset
发表于 2012-12-17 18:56:10
dr2001 发表于 2012-12-7 08:35 static/image/common/back.gif
真要想省那几个字节,就不要void的ProtoThread函数类型,把定时器的的等待数直接return回去在Caller侧处 ...
的确是这样,一语中的!
smset
发表于 2012-12-17 18:59:45
dr2001 发表于 2012-12-16 11:49 static/image/common/back.gif
http://www.amobbs.com/thread-5511786-1-1.html
可以参考这个帖子。
确实是个新思路,不过这样写,恐怕以后读代码的要骂人了。呵呵。
能否评估下330楼的版本的运行效率,以及提出优化方案? 谢谢!
topdog
发表于 2012-12-17 21:17:28
貌似如下的子函數調用,只能執行task2;不知何故。
if(keycode)
{
LED5=TRUE;
LED6=FALSE;
CallSub(task1);
}
else
{
LED5=FALSE;
LED6=TRUE;
CallSub(task2);
}
通過一段時間的使用,在PIC上很多奇怪的問題。
dr2001
发表于 2012-12-17 21:50:29
topdog 发表于 2012-12-17 21:17 static/image/common/back.gif
貌似如下的子函數調用,只能執行task2;不知何故。
if(keycode)
{
使用ProtoThread要特别注意通过间断点的时候,自动变量会丢失的。尤其是对于宏替换的实现,有时候会忘记这个情况,导致问题。
代码片段不利于发Bug,如果不妨碍知识产权,建议贴出较多的代码段才好讨论。
topdog
发表于 2012-12-18 08:20:18
dr2001 发表于 2012-12-17 21:50 static/image/common/back.gif
使用ProtoThread要特别注意通过间断点的时候,自动变量会丢失的。尤其是对于宏替换的实现,有时候会忘记 ...
應該就是“自动变量会丢失的”,不知道如何規避呢
/**********************************************************/
#include "htc.h"
#include "OS_TMCU.H"
//------configure bit seti
__CONFIG (INTIO&WDTDIS&PWRTEN&MCLREN&BOREN&LVPDIS&PROTECT&UNPROTECT);
/****小小调度器开始**********************************************/
#define MAXTASKS 6
static unsigned char timers;
unsigned char currdt;
#define _SS static unsigned char _lc; switch(_lc){case 0:
#define _EE ;}; _lc=0; return 255;
#define WaitX(tickets)do {_lc=__LINE__; return tickets ;} while(0); case __LINE__:
#define RunTask(TaskName,TaskID) do { if (timers==0) timers=TaskName(); }while(0);
#define CallSub(SubTaskName) do { _lc=__LINE__; return 0; case __LINE__:currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
#define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers!=0)&&(timers!=255)) timers--;}
#define SEM unsigned int
//初始化信号量
#define InitSem(sem) sem=0;
//等待信号量
#define WaitSem(sem) do{ sem=1; WaitX(0); if (sem>0) return 1;} while(0);
//等待信号量或定时器溢出, 定时器tickets 最大为0xFFFE
#define WaitSemX(sem,tickets)do { sem=tickets+1; WaitX(0); if(sem>1){ sem--;return 1;} } while(0);
//发送信号量
#define SendSem(sem)do {sem=0;} while(0);
/*****小小调度器结束*******************************************************/
//****************************
//
//****************************
void ChipInit(void)
{
// Disable AD
//ANSEL=0;
//ANSELH=0;
//---IO Setting
//--PORTA
/*
#define KEY_ZONE_I RA0
#define KEY_LEVEL_I RA1
#define FB_I RA2
#define KEY_POWER_I RA3
#define SEAT_I RA4
#define RS_I RA5
#define BACK_LED_HI_O RA6
#define IR_O RA7 //传
*/
TRISA=0b00111111;
//--PORTB
/*
#define LR_I RB0
#define BACK_LED_LOW_O RB1
#define WAIST_LED_LOW_O RB2
#define LEG_LED_LOW_O RB3
#define LEG_LED_HI_O RB4
#define WAIST_LED_HI_O RB5
#define PGC_O RB6
#define KEEP_PGD_O RB7
*/
TRISB=0b00000001;
#ifdef PIC_DEBUG
TRISB=0b00000111;
#else
TRISB=0b00000001;
#endif
PORTA=0;
PORTB=0;
// ? ゑ/PWM 家塊
VRCON=0;
CMCON=0x07;
OSCF=TRUE; //int osc 4Mhz
//---T0 Setting
// OPTION select
OPTION=0b10000000;// 1:2
TMR0=205;// 0.1msec
//----T2 Setting
//PIE1=0b00000000;//
//PIR1=0;
// T2CON=0b00000000;//
//PIR2=250;//250*4=1000(1msec)
//TMR2=0;
// USART Setting
#ifdef PIC_DEBUG
TXSTA=0;
RCSTA=0;
BRGH=1;
SPBRG=25; //初4MHz9600BPS 0.16
SPEN=1; //
TXEN=1; // 竟
CREN=0; //綳
PEIE=0;
RCIE=0;
#endif
}
uchar task1(){
_SS
while(1){
WaitX(10);
LED1=!LED1;
}
_EE
}
uchartask2(){
_SS
while(1){
WaitX(200);
LED2=!LED2;
}
_EE
}
uchar KEYSCAN()
{
_SS
while(1)
{
WaitX(1);
keynewcode=0;
if(!KEY_POWER_I)
keynewcode |=POWERKEY;
if(!KEY_LEVEL_I)
keynewcode|=LEVELkEY;
if(!KEY_ZONE_I)
keynewcode|=ZONEkEY;
if(keynewcode!=0)
{
if(keycode==keynewcode)
{
if(keyCount>4)
{
KeyActiveyFlag=TRUE;
//LED1=TRUE;
}
else
{
keyCount++;
//keycode=keynewcode;
}
}
else
{
keycode=keynewcode;
keyCount=0;
}
}
else
{
keyCount=0;
keylongCount=0;
}
}
_EE
}
uchartask3()
{
_SS
while(1)
{
WaitX(10);
//CallSub(task1);
//keycode=1;
switch(keycode)
{
case POWERKEY:
LED5=TRUE;
LED6=FALSE;
CallSub(task1);
break;
case LEVELkEY:
LED5=FALSE;
LED6=TRUE;
CallSub(task2);
break;
default :
LED5=FALSE;
LED6=FALSE;
}
}
_EE
}
uchar KEYDISPOSE()
{
{
KeyActiveyFlag=FALSE;
switch(keycode)
{
case POWERKEY:
LED3=TRUE;
LED4=FALSE;
break;
case LEVELkEY:
LED3=FALSE;
LED4=TRUE;
break;
default :
LED3=FALSE;
LED4=FALSE;
}
}
}
void main()
{
ChipInit();
T0IF=FALSE;
T0IE=TRUE;
GIE=TRUE;
KEEP_PGD_O=TRUE;
while(1){
RunTask(KEYSCAN,0);
RunTask(KEYDISPOSE,1);
RunTask(task3, 2);
}
}
//****************************
// interrupt sub
//****************************
void interrupt InterruptTig(void)
{
// uchar i;
//timer manager
// UpdateTimers() ;
if (Timer2msec>=100)
{
Timer2msec=0;
T2msecFlag=TRUE;
UpdateTimers() ;
}
else
Timer2msec++;
//timer manager
TMR0=218;
T0IF=FALSE;
}
topdog
发表于 2012-12-18 08:28:03
但是定義成如下的變量都有問題volatileuchar keycode;
ifree64
发表于 2012-12-18 09:04:43
本帖最后由 ifree64 于 2012-12-18 09:15 编辑
topdog 发表于 2012-12-18 08:28 static/image/common/back.gif
但是定義成如下的變量都有問題volatileuchar keycode;
你的问题可能出在这里:uchartask3()
{
_SS
while(1)
{
WaitX(10);
//CallSub(task1);
//keycode=1;
switch(keycode)
{
case POWERKEY:
LED5=TRUE;
LED6=FALSE;
CallSub(task1);
break;
case LEVELkEY:
LED5=FALSE;
LED6=TRUE;
CallSub(task2);
break;
default :
LED5=FALSE;
LED6=FALSE;
}
}
_EE
}_SS和_EE宏为你的代码插入了switch,这样当嵌套使用switch时,代码出现问题。你的代码将展开为:uchartask3()
{
static unsigned char _lc; switch(_lc)
{
case 0:
while(1)
{
WaitX(10);
//CallSub(task1);
//keycode=1;
switch(keycode)
{
case POWERKEY:
LED5=TRUE;
LED6=FALSE;
do { _lc=__LINE__; return 0; case __LINE__:currdt=task1(); if(currdt!=255) return currdt;} while(0);
break;
case LEVELkEY:
LED5=FALSE;
LED6=TRUE;
do { _lc=__LINE__; return 0; case __LINE__:currdt=task2(); if(currdt!=255) return currdt;} while(0);
break;
default :
LED5=FALSE;
LED6=FALSE;
}
}
};
_lc=0;
return 255;
}你代码中通过CallSub插入的case __LINE__: 成为了内部switch的标签,因此外部switch标签在进行分支查找时,无法找到原来保存的位置,自然task代码就不会运行了。
解决方法可以是将内部的switch代码改为if实现。
PS:宏的这种使用固然“巧妙”,自己看到的代码和编译器看到的代码“不一致”,这往往成为是错误的根源。这也是我不喜欢过度使用宏代码的原因。
smset
发表于 2012-12-18 09:39:08
topdog 发表于 2012-12-18 08:20 static/image/common/back.gif
應該就是“自动变量会丢失的”,不知道如何規避呢
任务函数内部不能再用switch语句了。 这种情况请用 if else。
topdog
发表于 2012-12-18 09:41:33
ifree64 发表于 2012-12-18 09:04 static/image/common/back.gif
你的问题可能出在这里:_SS和_EE宏为你的代码插入了switch,这样当嵌套使用switch时,代码出现问题。你的 ...
谢谢指正switch的用法的确有问题,但是修改成如下方式同样也是有问题,只执行task2。发现keycode变量丢失了,keycode定义成volatile uchar 型也没有效果。
if(keycode)
{
LED5=TRUE;
LED6=FALSE;
CallSub(task1);
}
else
{
LED5=FALSE;
LED6=TRUE;
CallSub(task2);
}
chenerbox2
发表于 2012-12-18 09:59:57
mark 待用,以前一直用 keil的 tiny51,所以不想换片子
ifree64
发表于 2012-12-18 10:02:16
topdog 发表于 2012-12-18 09:41 static/image/common/back.gif
谢谢指正switch的用法的确有问题,但是修改成如下方式同样也是有问题,只执行task2。发现keycode变量丢失 ...
在你前面贴出的代码里没有找到定义keycode的地方。
smset
发表于 2012-12-18 10:02:19
本帖最后由 smset 于 2012-12-18 10:05 编辑
topdog 发表于 2012-12-18 09:41 static/image/common/back.gif
谢谢指正switch的用法的确有问题,但是修改成如下方式同样也是有问题,只执行task2。发现keycode变量丢失 ...
uchartask2(){
_SS
while(1){
WaitX(200);
LED2=!LED2;
}
_EE
}
task2里面有没有和keycode相关的代码,只执行taks2,你是怎么发现keycode变量丢失呢? 而且keycode在哪里定义的?
另外,任务内部使用的变量,保险起见,都定义成静态局部变量 (static)
topdog
发表于 2012-12-18 10:10:15
smset 发表于 2012-12-18 10:02 static/image/common/back.gif
uchartask2(){
_SS
while(1){
PIC里面定义在#include "OS_TMCU.H"
我测试过对比过使用 KEYDISPOSE()处理时,keycode就是可以正常判断的
而task3中,使用宏的方式就不能正确判断,并且如果在函数task3中if判断前加keycode=1; 赋值后,就可以
运行task1了,不然就会运行task2,所有可以确定if else中keycode丢失了。
dr2001
发表于 2012-12-18 10:14:07
本帖最后由 dr2001 于 2012-12-18 10:16 编辑
topdog 发表于 2012-12-18 09:41 static/image/common/back.gif
谢谢指正switch的用法的确有问题,但是修改成如下方式同样也是有问题,只执行task2。发现keycode变量丢失 ...
还没有看你贴出的代码,所以不知道是否是丢自动变量导致的问题,但是对你提出的疑问进行简要的说明。
呃,丢动态变量值这个问题是ProtoThread Stackless实现必然遭遇的,解决办法有很多,可根据具体情况选用:
1、如果变量值是可恢复的,比如,可以通过调用一个函数获得(读某个寄存器啊,标志位啊,etc),那么在函数开头,switch语句执行之前,对内部自动变量进行初始化即可。缺点是每次调用的开销增加。
2、变量值不可恢复,并且函数代码不需要重入,内部变量可以static。这么干的缺点是费RAM,因为静态分配,编译器无法复用该变量了。人工复用是没有问题的。
3、自己写一个简单的Malloc/Free函数,用函数参数传来传去。
用ProtoThread带来了好处和坏处,究竟用不用,如何选用适当的方案,需要根据具体的应用环境“剪裁”。
smset
发表于 2012-12-18 10:47:19
本帖最后由 smset 于 2012-12-18 10:51 编辑
首先要确认KEYSCAN里面对keycode赋值没有。
根据你的提法,keycode是定位为全局变量的,而全局变量根本不存在丢失的问题。只有局部变量才存在丢失的问题。
所以我觉得问题不在于是否使用了protothread机制。
您可以再检查跟踪一下KEYSCAN任务的执行情况到底如何,而不是盯着task3。
还有 KEYDISPOSE 不是一个任务函数,也没有返回, 你怎么也放在RunTask里面运行?
topdog
发表于 2012-12-18 10:51:41
smset 发表于 2012-12-18 10:47 static/image/common/back.gif
首先要确认KEYSCAN里面对keycode赋值没有。
根据你的提法,keycode是定位为全局变量的,而全局变量根本不 ...
没错,keycode定义为全局变量的,
我测试过
KEYDISPOSE()使用状态机方式处理,keycode就是可以正常判断的
task3()使用宏方式,keycode就出现值不对的情况。
也感觉很奇怪
smset
发表于 2012-12-18 11:10:36
topdog 发表于 2012-12-18 10:51 static/image/common/back.gif
没错,keycode定义为全局变量的,
我测试过
KEYDISPOSE()使用状态机方式处理,keycode就是可以正常判断的 ...
能贴出修改后的完整代码么?
topdog
发表于 2012-12-18 11:14:11
smset 发表于 2012-12-18 10:47 static/image/common/back.gif
首先要确认KEYSCAN里面对keycode赋值没有。
根据你的提法,keycode是定位为全局变量的,而全局变量根本不 ...
发现很一个线索,当keycode出现在嵌套调用子函数时出现问题。不嵌套子函数是不会出现问题的。
topdog
发表于 2012-12-18 11:20:05
smset 发表于 2012-12-18 11:10 static/image/common/back.gif
能贴出修改后的完整代码么?
/**********************************************************/
#include "htc.h"
#include "OS_TMCU.H"
//------configure bit seti
__CONFIG (INTIO&WDTDIS&PWRTEN&MCLREN&BOREN&LVPDIS&PROTECT&UNPROTECT);
/****小小调度器开始**********************************************/
#define MAXTASKS 6
static unsigned char timers;
unsigned char currdt;
#define _SS static unsigned char _lc; switch(_lc){case 0:
#define _EE ;}; _lc=0; return 255;
#define WaitX(tickets)do {_lc=__LINE__; return tickets ;} while(0); case __LINE__:
#define RunTask(TaskName,TaskID) do { if (timers==0) timers=TaskName(); }while(0);
#define CallSub(SubTaskName) do { _lc=__LINE__; return 0; case __LINE__:currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
#define UpdateTimers() unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers!=0)&&(timers!=255)) timers--;}
#define SEM unsigned int
//初始化信号量
#define InitSem(sem) sem=0;
//等待信号量
#define WaitSem(sem) do{ sem=1; WaitX(0); if (sem>0) return 1;} while(0);
//等待信号量或定时器溢出, 定时器tickets 最大为0xFFFE
#define WaitSemX(sem,tickets)do { sem=tickets+1; WaitX(0); if(sem>1){ sem--;return 1;} } while(0);
//发送信号量
#define SendSem(sem)do {sem=0;} while(0);
/*****小小调度器结束*******************************************************/
//****************************
//
//****************************
void ChipInit(void)
{
// Disable AD
//ANSEL=0;
//ANSELH=0;
//---IO Setting
//--PORTA
/*
#define KEY_ZONE_I RA0
#define KEY_LEVEL_I RA1
#define FB_I RA2
#define KEY_POWER_I RA3
#define SEAT_I RA4
#define RS_I RA5
#define BACK_LED_HI_O RA6
#define IR_O RA7 //传
*/
TRISA=0b00111111;
//--PORTB
/*
#define LR_I RB0
#define BACK_LED_LOW_O RB1
#define WAIST_LED_LOW_O RB2
#define LEG_LED_LOW_O RB3
#define LEG_LED_HI_O RB4
#define WAIST_LED_HI_O RB5
#define PGC_O RB6
#define KEEP_PGD_O RB7
*/
TRISB=0b00000001;
#ifdef PIC_DEBUG
TRISB=0b00000111;
#else
TRISB=0b00000001;
#endif
PORTA=0;
PORTB=0;
// ? ゑ/PWM 家塊
VRCON=0;
CMCON=0x07;
OSCF=TRUE; //int osc 4Mhz
//---T0 Setting
// OPTION select
OPTION=0b10000000;// 1:2
TMR0=205;// 0.1msec
//----T2 Setting
//PIE1=0b00000000;//
//PIR1=0;
// T2CON=0b00000000;//
//PIR2=250;//250*4=1000(1msec)
//TMR2=0;
// USART Setting
#ifdef PIC_DEBUG
TXSTA=0;
RCSTA=0;
BRGH=1;
SPBRG=25; //初4MHz9600BPS 0.16
SPEN=1; //
TXEN=1; // 竟
CREN=0; //綳
PEIE=0;
RCIE=0;
#endif
}
uchar task1(){
_SS
while(1){
WaitX(10);
LED1=!LED1;
}
_EE
}
uchartask2(){
_SS
while(1){
WaitX(200);
LED2=!LED2;
}
_EE
}
uchar KEYSCAN()
{
_SS
while(1)
{
WaitX(1);
keynewcode=0;
if(!KEY_POWER_I)
keynewcode |=POWERKEY;
if(!KEY_LEVEL_I)
keynewcode|=LEVELkEY;
if(!KEY_ZONE_I)
keynewcode|=ZONEkEY;
if(keynewcode!=0)
{
if(keycode==keynewcode)
{
if(keyCount>4)
{
KeyActiveyFlag=TRUE;
//LED1=TRUE;
}
else
{
keyCount++;
//keycode=keynewcode;
}
}
else
{
keycode=keynewcode;
keyCount=0;
}
}
else
{
keyCount=0;
keylongCount=0;
}
}
_EE
}
uchartask3()
{
_SS
while(1)
{
WaitX(10);
if(keycode==POWERKEY)
{
LED5=TRUE;
LED6=FALSE;
//CallSub(task1); //此处有问题,调用子函数后,不能正常运行
}
else
{
LED5=FALSE;
LED6=TRUE;
//CallSub(task2);//此处有问题,调用子函数后,不能正常运行
}
}
_EE
}
uchar KEYDISPOSE()//可以正常运行
{
{
KeyActiveyFlag=FALSE;
switch(keycode)
{
case POWERKEY:
LED3=TRUE;
LED4=FALSE;
break;
case LEVELkEY:
LED3=FALSE;
LED4=TRUE;
break;
default :
LED3=FALSE;
LED4=FALSE;
}
}
}
void main()
{
ChipInit();
T0IF=FALSE;
T0IE=TRUE;
GIE=TRUE;
KEEP_PGD_O=TRUE;
while(1){
RunTask(KEYSCAN,0);
RunTask(KEYDISPOSE,1);
RunTask(task3, 2);
}
}
//****************************
// interrupt sub
//****************************
void interrupt InterruptTig(void)
{
// uchar i;
//timer manager
// UpdateTimers() ;
if (Timer2msec>=100)
{
Timer2msec=0;
T2msecFlag=TRUE;
UpdateTimers() ;
}
else
Timer2msec++;
//timer manager
TMR0=218;
T0IF=FALSE;
}
takashiki
发表于 2012-12-18 11:28:22
topdog 发表于 2012-12-18 11:20 static/image/common/back.gif
请问你那个task3调用了子任务如何退出这个子任务?
去掉你所有任务中的while(1)吧,你会发现你有收获的。
smset
发表于 2012-12-18 11:32:24
本帖最后由 smset 于 2012-12-18 11:38 编辑
takashiki 发表于 2012-12-18 11:28 static/image/common/back.gif
请问你那个task3调用了子任务如何退出这个子任务?
去掉你所有任务中的while(1)吧,你会发现你有收获的。 ...
对,还是takashiki厉害。
task2 任务写成永远循环执行了啊。 task3一旦调用task2,就永远在task2里面运行。
就好比一个子函数写成死循环了一个样,所以task1和task2是要去掉while(1)的。
但并不是所有,根据你的程序意图来看, 除了task1和task2这两个子任务外,其他任务是不用去掉while(1)的。
不论是顶层任务,还是子任务:
在任务函数,里面是是否使用while(1),完全是根据实际逻辑需要来决定的。
如果任务需要一直循环执行,那么就用while(1),
如果任务不需要循环运行,那么就不用while(1)。
如果任务需要运行几次,那么就用for(i=0;i<count;i++) 来控制循环次数。
就和你平时写其他普通函数的思路是一样的。
smset
发表于 2012-12-18 11:40:06
topdog 发表于 2012-12-18 11:20 static/image/common/back.gif
去掉task1和task2里面的 while(1)
topdog
发表于 2012-12-18 13:42:37
smset 发表于 2012-12-18 11:40 static/image/common/back.gif
去掉task1和task2里面的 while(1)
的确是死循环的问题,当时写程序是就是照样画瓢
呵呵,结果没有学好变画虎不成反类犬。
确认不是调度器的问题。
sweet_136
发表于 2012-12-18 14:11:01
强力的mark一下子.嘎嘎.
dr2001
发表于 2012-12-18 14:44:22
smset 发表于 2012-12-17 18:59 static/image/common/back.gif
确实是个新思路,不过这样写,恐怕以后读代码的要骂人了。呵呵。
能否评估下330楼的版本的运行效率,以 ...
330楼的代码的一点点看法:
1 static unsigned char timers考虑修改为:
volatile unsigned char timers = { 0 }; 给定全部初值为0,同时加上volatile防止寄存器优化。是否加static可以根据实际所需的作用域决定。
2 _SS, _EE, WaitX的实现,就通常情形来说,精简度已经很高了。但具体到某个编译器来说,可能还有提升,但似乎没什么意义了。
目前的WaitX,在51系统上,理论上只需要MOV Mem, #Imm; MOV Reg, #Imm; RET三条指令。由于要返回两个值,基本上就是最优了。
如果所用的编译器支持Struct in Reg返回(准确的说,是ABI支持),那么还可以节约一个字节,变成MOV Reg, #Imm; MOV Reg, #Imm; RET:方法就是定义一个结构体,里边两个char,返回整个结构体,看起来代码复杂的东西,编译后的结果不一定复杂。
switch/case方法效率软肋是在被调用时候找到case的额外开销:如果case的值(LC)不具备JumpTable的可优化性,那么每多一个WaitX,就意味着至少要多一个CMP Reg, #imm和JEQ Addr;序列比较在实际运行中效率是很低下的,尤其是状态数量多了之后。
当然,目前看,此事是代码风格,编译器依赖,符合标准三者不可得兼的事情,必须加以选择。
3 基于ProtoThread的Stackless特征,并且适当考虑通用性,可以考虑_SS中的变量定义拆出来;要么单独一个宏定义,要么用函数参数传。这样在switch之前的代码可以每次都执行到,能够用于动态恢复某些局部变量。在特定场合是有价值的,但不是处处有价值。
51系统是MCU系统里一个比较特殊的情形,尤其是Keil,呵呵。
smset
发表于 2012-12-18 15:13:39
dr2001 发表于 2012-12-18 14:44 static/image/common/back.gif
330楼的代码的一点点看法:
1 static unsigned char timers考虑修改为:
非常感谢! 真的是很有深度的评测!
第3项中,把_SS独立出去然后动态恢复变量的方法,我还看得不是很明白,能否再具体些?
dr2001
发表于 2012-12-18 15:37:36
smset 发表于 2012-12-18 15:13 static/image/common/back.gif
非常感谢! 真的是很有深度的评测!
第3项中,把_SS独立出去然后动态恢复变量的方法,我还看得不是很 ...
考察代码片段
uint8_t Task(uint8_t SystemSignal) {
static uint8_t _lc = 0.
uint16_t dma_handler;
dma_handler=GetCurrDmaHandler();
if(SystemSignal == FatalError) {
//Do Something Urgent.
}
switch(lc){
case ...:
//Use dma_handler.
...
}
1、ProtoThread函数开始之后,switch之前的代码是每次调用ProtoThread必然会执行到的。
2、有一些状态变量之类的东西,其实是没必要保存的,因为可以通过调用别的函数重新得到。当然,这种情形不会特别经常出现,但是确实有,典型就是DMA的缓存控制。
另外,如果要构造一个迭代器(Iterator),也就是说For里边会有WaitX,为了省内存,动态分配内存或者动态分配一个地址当计数器变量也完全可以;这样就有“恢复”ProtoThread中的相关变量需求,要不然就用参数传进来。
3、另外一个就是系统事件的处理,这个典型见于contiki。这个就更少见了。
也就是说,通常情况下,_SS是能满足需求的;在特殊情况下,是会有要求拆开的。
smset
发表于 2012-12-18 16:01:56
在_SS之前是可以定义变量的。
unsigned char Task(){
unsigned intdma_handler;
_SS
...
WaitX(20);
dma_handler=GetCurrDmaHandler();
//Use dma_handler.
_EE
}
不知这样是否能满足要求?
dr2001
发表于 2012-12-18 16:04:31
如果每次都要用的话,就很不方便,同样的代码次次执行。
反正这是少数情形下才有的需求,不支持也没什么问题。
takashiki
发表于 2012-12-18 16:10:32
dr2001 发表于 2012-12-18 14:44 static/image/common/back.gif
330楼的代码的一点点看法:
1 static unsigned char timers考虑修改为:
Keil C51是比较RZ了点,优化很差~~~
你说的2 _SS, _EE, WaitX的实现,就通常情形来说,精简度已经很高了。但具体到某个编译器来说,可能还有提升,但似乎没什么意义了。,我在51上试了,可以。每次Wait占用5个字节,通过R6、R7分别传递出来。但我实现的每个子任务会多占用一个字节RAM,当然这个RAM也可以通过绝对定位用作其他用途,其实内存并没有多消耗。
flyhorse
发表于 2012-12-18 16:45:15
上学的时候 就这个问题还跟我们单片机老师 好好讨论了一下
topdog
发表于 2012-12-18 17:06:16
takashiki 发表于 2012-12-18 16:10 static/image/common/back.gif
Keil C51是比较RZ了点,优化很差~~~
你说的,我在51上试了,可以。每次Wait占用5个字节,通过R6、R7分 ...
pic上SS EE WAITX自动优化代码
smset
发表于 2012-12-18 17:19:31
我觉得keil优化也还行啊,呵呵
zsmbj
发表于 2012-12-18 17:45:33
AVR的waitx:unsigned char task1(void)
{
_SS
11a: 80 91 60 00 lds r24, 0x0060
11e: 88 23 and r24, r24
120: 19 f0 breq .+6 ; 0x128 <task1+0xe>
122: 84 36 cpi r24, 0x64 ; 100
124: 41 f4 brne .+16 ; 0x136 <task1+0x1c>
126: 05 c0 rjmp .+10 ; 0x132 <task1+0x18>
while(1)
{
WaitX(10);
128: 84 e6 ldi r24, 0x64 ; 100
12a: 80 93 60 00 sts 0x0060, r24
12e: 8a e0 ldi r24, 0x0A ; 10
130: 08 95 ret
led2_on();
132: c4 98 cbi 0x18, 4 ; 24
134: f9 cf rjmp .-14 ; 0x128 <task1+0xe>
// WaitX(20);
// led2_off();
}
_EE
136: 10 92 60 00 sts 0x0060, r1
13a: 8f ef ldi r24, 0xFF ; 255
}
13c: 08 95 ret
zsmbj
发表于 2012-12-18 17:47:53
stm8的WaitX: 107 unsigned char task1(void)
108 {
109 _SS
\ task1:
\ 000000 C60000 LD A, L:??lc_2
\ 000003 2706 JREQ L:??task1_0
\ 000005 A070 SUB A, #0x70
\ 000007 2709 JREQ L:??task1_1
\ 000009 200D JRA L:??task1_2
110 while(1)
111 {
112 WaitX(10);
\ ??task1_0:
\ 00000B 35700000 MOV L:??lc_2, #0x70
\ 00000F A60A LD A, #0xa
\ 000011 81 RET
113 led2_on();
\ ??task1_1:
\ 000012 7217500F BRES L:0x500f, #0x3
\ 000016 20F3 JRA L:??task1_0
114 }
115 _EE
\ ??task1_2:
\ 000018 725F0000 CLR L:??lc_2
\ 00001C A6FF LD A, #0xff
\ 00001E 81 RET
zsmbj
发表于 2012-12-18 17:58:40
还是51厉害啊,只有26bytes,avr有36bytes,stm8有32bytes。
smset
发表于 2012-12-18 18:29:53
本帖最后由 smset 于 2012-12-19 13:00 编辑
优化无止境!呵呵,330楼看似不能再优化了,但我再尝试做一次优化:
敬请评测该版本,看是否还能优化:
/****小小调度器开始**********************************************/
#define MAXTASKS 2
static unsigned char timers;
unsigned char currdt;
#define _SS static unsigned char _lc; switch(_lc){default:
#define _EE ;}; _lc=0; return 255;
#define WaitX(tickets)do {_lc=__LINE__+((__LINE__%256)==0); return tickets ;} while(0); case __LINE__+((__LINE__%256)==0):
#define RunTask(TaskName,TaskID) do { if (timers==0) timers=TaskName(); }while(0);
#define CallSub(SubTaskName) do { _lc=__LINE__+((__LINE__%256)==0); return 0; case __LINE__+((__LINE__%256)==0):currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
#define UpdateTimers() unsigned char i; for(i=MAXTASKS;i>0 ;i--){if((timers!=0)&&(timers!=255)) timers--;}
#define SEM unsigned int
//初始化信号量
#define InitSem(sem) sem=0;
//等待信号量
#define WaitSem(sem) do{ sem=1; WaitX(0); if (sem>0) return 1;} while(0);
//等待信号量或定时器溢出, 定时器tickets 最大为0xFFFE
#define WaitSemX(sem,tickets)do { sem=tickets+1; WaitX(0); if(sem>1){ sem--;return 1;} } while(0);
//发送信号量
#define SendSem(sem)do {sem=0;} while(0);
/*****小小调度器结束*******************************************************/
sbit LED1 = P2^1;
sbit LED2 = P2^2;
void InitT0()
{
TMOD = 0x21;
IE |= 0x82;// 12t
TL0=0Xff;
TH0=0XDB;//22M---b7;
TR0 = 1;
}
void INTT0(void) interrupt 1 using 1
{
UpdateTimers();
TL0=0Xff; //10ms 重装
TH0=0XDB;//b7;
}
voidtask1(){
_SS
while(1){
WaitX(50);
LED1=!LED1;
}
_EE
}
voidtask2(){
_SS
while(1){
WaitX(100);
LED2=!LED2;
}
_EE
}
void main()
{
InitT0();
while(1){
RunTask(task1,0);
RunTask(task2,1);
}
}
在keil下编译,又减少了18字节的ROM(超过10%了)。应该运行效率会更高。
------------------以下为说明-----------------------------------
小小调度器任务函数的写法主要注意的,主要有三点:
1) 任务函数内部变量,建议都用静态局部变量来定义。
2) 任务函数内不能用switch语句。
3) 任务函数内,不能用return语句。 因为return已经被赋予任务延时的特定意义。(这是返回型任务函数版本的一个强制要求)
这三点,并不会明显造成写程序的不方便。
---------------------------
从裸奔到使用OS操作系统或调度系统的代价主要有:
硬件资源代价(对RAM和ROM的消耗),学习代价(学会其原理,并掌握其用法),移植代价(往不同cpu上移植的工作量),效率代价(使用调度系统后带来的额外cpu负担),商业代价(版权费用),稳定性代价(是否引入潜在不稳定因素,或者增大bug跟踪调试工作量)。
从这几方面来讲,应用小小调度器的代价,都是非常小的。
1) 硬件资源代价: 前面的优化版本已经说明问题。keil下,本例程ram消耗 : 22字节,rom消耗126字节.
2) 学习代价: 小小调度器总共只有十多行代码,如果我们做一个简单的解释说明,理解起来其实是很快的。我相信学习时间比其他调度系统要短。
3) 移植代价: 几乎没有什么移植工作量,对于各种cpu,几乎是通吃。
4) 效率代价: 我们一直在努力优化,相信调度效率已经不低了。比如任务切换时间,应该是可以做到uS级别,甚至亚uS级别。
5) 商业代价: 小小本调度器为免费使用,无需支付任何费用。
6) 稳定性代价:小小调度器本质上仅仅是几个宏而已,未涉及任何对内部寄存器或堆栈的操作,避免了引入不稳定风险因素,所有操作都在可预见,可把控的前提下进行。
--------------------------------------------------------------------------------
takashiki
发表于 2012-12-18 19:31:53
smset 发表于 2012-12-18 18:29 static/image/common/back.gif
优化无止境!呵呵,330楼看似不能再优化了,但我再尝试做一次优化:
敬请评测该版本,看是否还能优化:在k ...
可以的,按照388楼的第二条,WaitX是可以继续优化的,优化的程度与CPU相关。51可以优化到4个字节,AVR可以优化到6个字节,STM8可以到5个字节,而且运行时间可以缩短。
全部的我还没有写出来,只弄出了一部分:
51专用优化版部分代码:union __CTCB{ //BIG ENDIAN
struct {
uchar LC;
uchar Delay;
} Param;
ushort Value;
} __TCB ; //任务控制块
#define BEGIN_TASK(TaskId) void Task##TaskId(){ switch(__TCB.Param.LC){case 0: {
#define END_TASK }} DPTR = 0; return;}
#define WaitX(Ticks)DPTR = (__LINE__ << 8) | (Ticks); return; case __LINE__:
#define RunTask(TaskId) { if (__TCB.Param.Delay == 0) {Task##TaskId(); __TCB.Value = DPTR; } }
#define CallSub(TaskId) Task##TaskId(); __TCB.Value = DPTR; if(DPL) { DPH = __LINE__; return; } 部分汇编代码(一个完整的任务): 83: BEGIN_TASK(1)
C:0x00E2 E512 MOV A,0x12
C:0x00E4 24AB ADD A,#0xAB
C:0x00E6 6018 JZ C:0100
C:0x00E8 2455 ADD A,#0x55
C:0x00EA 7016 JNZ C:0102
84: CallSub(2); //调用子任务
C:0x00EC 3108 ACALL Task2(C:0108)
C:0x00EE 858314 MOV 0x14,DPH(0x83)
C:0x00F1 858215 MOV 0x15,DPTR(0x82)
C:0x00F4 E582 MOV A,DPTR(0x82)
C:0x00F6 6004 JZ C:00FC
C:0x00F8 758354 MOV DPH(0x83),#0x54
C:0x00FB 22 RET
85: WaitX(3); //WaitX只占用了4个字节,两条指令
C:0x00FC 905503 MOV DPTR,#0x5503
C:0x00FF 22 RET
86: LED2=!LED2;
C:0x0100 B2A2 CPL LED2(0xA0.2)
87: END_TASK
C:0x0102 E4 CLR A //这个优化得多差劲啊,直接MOV DPTR, #0不就完了么?
C:0x0103 F583 MOV DPH(0x83),A
C:0x0105 F582 MOV DPTR(0x82),A
C:0x0107 22 RET Keil C51的优化能力,我实在是无法吐槽了,真的很烂。对于DPTR的优化差,@R1从来不会用,内存搬运时把R0折腾死R1也只在旁边冷眼旁观,绝不参与。两个字节拼成一个整型他居然真的去计算一次,……实在太多了。
MDK的RVCT优化能力很强,和Keil C51完全不可同日而语,甩不了九条街甩八条街是没问题的。
takashiki
发表于 2012-12-18 19:43:49
zsmbj 发表于 2012-12-18 17:58 static/image/common/back.gif
还是51厉害啊,只有26bytes,avr有36bytes,stm8有32bytes。
51的结构决定了他的代码密度高,因为它是CISC指令集,却又拥有大量的通用寄存器。
RISC对内存操作太差使得效率变低,代码变长。当然,PIC是个特例,他的代码密度更高。
stm8因为没有通用寄存器的关系,使用内存操作明显占用更多的代码。
按照388楼的第二条可以减少一些对内存的操作,直接保存到寄存器中。
smset
发表于 2012-12-18 19:58:53
zsmbj 发表于 2012-12-18 17:58
还是51厉害啊,只有26bytes,avr有36bytes,stm8有32bytes。
按400楼的版本,51下的Task应该只有十几个字节了
dr2001
发表于 2012-12-18 20:23:01
本帖最后由 dr2001 于 2012-12-18 20:28 编辑
takashiki 发表于 2012-12-18 19:31 static/image/common/back.gif
可以的,按照388楼的第二条,WaitX是可以继续优化的,优化的程度与CPU相关。51可以优化到4个字节,AVR可 ...
直接往DPTR写数据啊……这分明是在挑衅Keil C51的常用寄存器追踪能力么。
建议此事慎重,不知道Keil C51对保护DPTR是怎么定义的,如果手册里没有明确写明的话,有潜在风险。
topdog
发表于 2012-12-18 21:17:23
這個是PIC使用前後比較
smset
发表于 2012-12-18 21:35:17
本帖最后由 smset 于 2012-12-18 21:36 编辑
topdog 发表于 2012-12-18 21:17 static/image/common/back.gif
這個是PIC使用前後比較
谢谢,看来rom也少了10%以上。看来400楼的优化是普遍有效的。
zsmbj
发表于 2012-12-18 21:41:58
#define UpdateTimers() unsigned char i; for(i=MAXTASKS;i>0 ;i--){if((timers!=0)&&(timers!=255)) timers--;}
这句好像并不能减小代码,请将:
#define MAXTASKS 5再试试。
我在winavr下测试,这个方法比原来会增加2个字节rom
smset
发表于 2012-12-18 21:57:06
zsmbj 发表于 2012-12-18 21:41 static/image/common/back.gif
#define UpdateTimers() unsigned char i; for(i=MAXTASKS;i>0 ;i--){if((timers!=0)&&(timers!=255)) time ...
keil下 ,会少2字节rom,一直会少2字节。
另外,400楼的CallSub 应该是:
#define CallSub(SubTaskName) do { _lc=__LINE__+((__LINE__%256)==0); return 0; case __LINE__+((__LINE__%256)==0):currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
已经更新。
smset
发表于 2012-12-18 22:03:52
本帖最后由 smset 于 2012-12-18 22:09 编辑
另外,void型任务函数的版本我再次评估了一下,rom效率也相当高。而且:WaitX增量rom要低1个字节,这个很有吸引力。
我都有点纠结了,究竟409楼与400楼两个版本,哪种方式综合指标更优,请各位帮忙对比分析。
#include "stc89c51.h"
/****小小调度器开始**********************************************/
#define MAXTASKS 2
unsigned char currdt;
static unsigned char timers;
#define _SS static unsigned char lc; switch(lc){default:
#define _EE ;}; lc=0;
#define WaitX(tickets)do {lc=__LINE__+(__LINE__%256==0); currdt=tickets; return ;} while(0); case __LINE__+(__LINE__%256==0):
#define RunTask(TaskName,TaskID) do { if (timers==0){currdt=255; TaskName(); timers=currdt; }}while(0);
#define CallSub(SubTaskName) do { lc=__LINE__+(__LINE__%256==0); currdt=0; return; case __LINE__+(__LINE__%256==0):SubTaskName();if (currdt!=255) return;}while(0);
#define SEM unsigned int
//初始化信号量
#define InitSem(sem) sem=0;
//等待信号量
#define WaitSem(sem) do{ sem=1; WaitX(0); if (sem>0) return;} while(0);
//等待信号量或定时器溢出, 定时器tickets 最大为0xFFFE
#define WaitSemX(sem,tickets)do { sem=tickets+1; WaitX(0); if(sem>1){ sem--; return;} } while(0);
//发送信号量
#define SendSem(sem)do {sem=0;} while(0);
#define UpdateTimers() unsigned char i; for(i=MAXTASKS;i>0;i--){if((timers!=0)&&(timers!=255)) timers--;}
/*****小小调度器结束*******************************************************/
sbit LED1 = P2^1;
sbit LED2 = P2^2;
void InitT0()
{
TMOD = 0x21;
IE |= 0x82;// 12t
TL0=0Xff;
TH0=0XDB;//22M---b7;
TR0 = 1;
}
void INTT0(void) interrupt 1 using 1
{
UpdateTimers();
TL0=0Xff; //10ms 重装
TH0=0XDB;//b7;
}
voidtask1(){
_SS
while(1){
WaitX(50);
LED1=!LED1;
}
_EE
}
voidtask2(){
_SS
while(1){
WaitX(100);
LED2=!LED2;
}
_EE
}
void main()
{
InitT0();
while(1){
RunTask(task1,0);
RunTask(task2,1);
}
}
shamork
发表于 2012-12-18 22:05:37
看了一会儿,没看懂。。。求解
页:
1
2
3
[4]
5
6
7
8
9
10
11
12
13