|
发表于 2019-4-15 21:17:23
|
显示全部楼层
向楼主致敬!
前面的楼层太多,我也没依次细看,比如1223楼的代码似乎也不错。
按照本人的编程习惯,参考了一些其它的程序,稍作了一些修改,将定时器改为了16位;
本以为char改为int就行,没想到在51单片机上WaitX(500)的时候出了BUG:延时不准。
经过多次测试总算明白了,8位机在读写16位变量时,若被中断打断,且中断里对该变量有写操作,则会出问题。
解决办法一是在读写16位变量时关中断(类似OS_ENTER_CRITICAL()),二是增加标志位使在主循环操作变量时中断里不对其操作。
以下为核心代码:
- /*--------------------------------------------------
- <小小调度器.C>
- 利用switch语句实现的合作式调度器
- 参考1:https://www.cnblogs.com/songdechiu/p/5793717.html
- <<Protothread 机制>>
- 参考2:http://www.amobbs.com/thread-5508723-1-1.html
- <<再出个调度器,极小资源单片机值得一用>>
- 作者:smset
- 参考3:https://www.amobbs.com/forum.php?mod=viewthread&tid=5702323&highlight=%E8%B0%83%E5%BA%A6%E5%99%A8
- <<小小调度器1.1重新整理版,8位16位32位可以选择定义>>
- 作者:xyb852
- LCW修改:timer为int型
- LINE的处理参考问星整理版
- (__LINE__%255)+1,使行号为:1~255 (避开0,因为0是第一个case标签)
- _SS改名为_BEGIN
- _EE改名为_END
- default改为case 0 (万一后面误写了case 0,可引发error)
- currdt类型改为static
- 16位timers会比原版占用更多的内存
- 注意事项:
- 1.任务函数不超过254行,否则可能引起 __LINE__%255+1 重复
- 2.任务内部的变量,如果要跨过等待或延时,需定义为静态变量.
- 3.任务中慎用switch,不要在新的switch中WaitX、WaitSem,否则可能引起case 混乱
- 4.任务函数内不要再用return。
- 5.运行任务的TaskID不要重复
- 6.由CallSub调用的任务,不能两个地方同时调用,主循环也不能再RunTask。
- 7.timer定义为16位时有个问题,读取timer时需关中断,否则可能在256附近读到0,
- (8位机读取16位是分两次读取的,中间被中断打断并写入则会引起BUG)
- 也可另加循环调用WaitX实现长延时,这样就不需关中断了。
- 若不关中断,读两次连续为0也可以确定其为0;
- 写入timer时增加tmren标志,只有tmren置1后中断里才会进行减计数,这样就不会乱了。
- //--------------------------------------------------*/
- /****小小调度器开始**********************************************/
- //最大任务数
- #define MAXTASKS 3
- //最大等待时间,此时任务停止
- #define TICKET_MAX 0xFFFF
- //任务类型,也是定时器的数据类型
- #define TASK unsigned int
- #define TIMER unsigned int
- //记录标号,下次进入函数就往下进行
- #define LC_SET() _lc=(__LINE__%255)+1; case (__LINE__%255)+1:
- //初始化定时器
- #define InitTasks() {unsigned char i; for(i=0;i<MAXTASKS;i++) timers[i]=0; }
- //任务开始
- #define _BEGIN static volatile unsigned char _lc=0; switch(_lc){case 0:
- //任务结束,不再进入
- #define _END } _lc=0; return TICKET_MAX;
- //延时
- #define WaitX(tickets) do {_lc=(__LINE__%255)+1; return tickets ;} while(0); case (__LINE__%255)+1:
- //运行任务
- //#define RunTask(TaskName,TaskID) do{ if (timers[TaskID]==0) timers[TaskID]=TaskName(); }while(0);
- /*
- //16位timer,读写时需关中断,避免读写过程中被修改
- #define RunTask(TaskName,TaskID) do{EA=0;timert=timers[TaskID];EA=1;\
- if (timert==0) {timert=TaskName();\
- EA=0;timers[TaskID]=timert;EA=1;}}while(0);
- */
- //16位timer,两次读取都为0才认为是0,写入时要关使能位(或关中断)。
- #define RunTask(TaskName,TaskID) do\
- {\
- if((timers[TaskID]==0)&&(timers[TaskID]==0))\
- {\
- timert=TaskName();\
- tmren[TaskID]=0;\
- timers[TaskID]=timert;\
- tmren[TaskID]=1;\
- }\
- }while(0);
- //continue使前面的任务优先保证执行
- //#define RunTaskA(TaskName,TaskID) { if (timers[TaskID]==0) {timers[TaskID]=TaskName(); continue;} }
- #define RunTaskA(TaskName,TaskID) {\
- if((timers[TaskID]==0)&&(timers[TaskID]==0))\
- {\
- timert=TaskName();\
- tmren[TaskID]=0;\
- timers[TaskID]=timert;\
- tmren[TaskID]=1;\
- continue;\
- }\
- }
- //调用任务函数(去掉了return 0)
- #define CallSub(SubTaskName) do{static TIMER currdt;LC_SET();currdt=SubTaskName();if(currdt!=TICKET_MAX) return currdt;} while(0);
- //#define CallSub(SubTaskName) do{static TIMER currdt; _lc=(__LINE__%255)+1; return 0;case (__LINE__%255)+1:currdt=SubTaskName();if(currdt!=TICKET_MAX) return currdt;} while(0);
- //定时中断里调用此函数用于计时
- //#define UpdateTimers() {unsigned char i; for(i=0;i<MAXTASKS;i++){if((timers[i]!=0)&&(timers[i]!=TICKET_MAX)) timers[i]--;}}
- //tmren[i]非0才计数
- #define UpdateTimers() {unsigned char i; for(i=0;i<MAXTASKS;i++){if((tmren[i]!=0)&&(timers[i]!=0)&&(timers[i]!=TICKET_MAX)) timers[i]--;}}
- //信号量类型
- #define SEM TIMER
- //初始化信号量
- #define InitSem(sem) sem=0;
- //等待信号量(作了改动,return 0能更快响应,return 1 则可释放CPU一段时间)
- #define WaitSem(sem) do{ sem=1; LC_SET(); if (sem>0) return 0;} while(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; LC_SET(); if(sem>1){ sem--;return 1;} } while(0);
- //#define WaitSemX(sem,tickets) do{ sem=tickets+1; WaitX(0); if(sem>1){ sem--;return 1;} } while(0);
- //发送信号量
- #define SendSem(sem) sem=0;
- //等待条件成立(return 0能更快响应,return 1 则可释放CPU一段时间)
- #define WaitCond(cond) do {LC_SET();if(!(cond))return 0; }while(0);
- //--------------------------------------------------上面的可作为头文件,下面的不可重复定义
- //定义定时器数组
- volatile TIMER timers[MAXTASKS]={0};//注意初值要为0
- volatile char tmren[MAXTASKS]={0};//定时器使能
- volatile TIMER timert;//暂存
- //定义信号量
- SEM Sem1;
- char test1=0;
- /*****小小调度器结束*******************************************************/
- TASK task1(void)
- {
- static char i=0;
- char n;
- _BEGIN
- for(n=0;n<3;n++)
- U1Send(0xAA);
- switch(n)
- {
- case 1:U1Send(1);break;
- case 2:U1Send(2);break;
- case 3:U1Send(3);break;
- }
- U1Send(1);
- LED_ON;
- WaitX(500);
- U1Send(2);
- LED_OFF;
- WaitX(1000);
- U1Send(3);
- LED_ON;
- WaitX(500);
- U1Send(4);
- LED_OFF;
- WaitX(500);
- SendSem(Sem1);//发送信号量
- for(i=0;i<10;i++)
- {
- U1Send(5+i);
- WaitX(1000);
- test1++;
- }
- _END
- }
- TASK task3(void)
- {
- _BEGIN
- WaitX(10);
- U1Send(0x31);
- _END
- }
- TASK task2(void)
- {
- static char i=0x26;
- _BEGIN
- //WaitSem(Sem1);//等待信号量
- WaitSemX(Sem1,1000);//只等1秒
- U1Send(0x21);
- WaitX(500);
- U1Send(0x22);
- WaitX(1000);
- U1Send(0x23);
- WaitCond(test1>8);//等待条件
- U1Send(0x24);
- CallSub(task3);//调用任务
- U1Send(0x25);
- while(1)
- {
- LED_XOR;
- U1Send(i++);
- WaitX(500);
- }
- _END
- }
- /*
- //1KHz
- void timer0(void) interrupt 1
- {
- UpdateTimers();
- }
- void main(void)
- {
- init_devices();//初始化串口、定时器等
- InitTasks();//可省
- while(1)
- {
- RunTask(task1,0);
- RunTask(task2,1);
- //PCON|=1;//睡眠
- }
- }*/
复制代码 |
|