搜索
bottom↓
回复: 26

尝试写个简单基于事件调度系统TEvent 20081017 【恢复】

[复制链接]

出0入0汤圆

发表于 2008-10-17 17:25:41 | 显示全部楼层 |阅读模式
TEvent 20081017

Author: ATmega32



1.数据结构

事件值

uint8  EventValue;

其中EVT_TASK_ENTRY(0XFF)和EVT_TASK_DELAY(0XFE)内核已使用



事件结构体

typedef struct

{

    TLink  Link;

    uint8  Flag;

    uint8  EventValue;

}TEvent;



任务结构体

typedef struct

{

        uint8          Flag;

        TQueue         EventQueue;

        TTaskExecute   *Execute;

}TTask;



定时器结构体

typedef struct

{

        TLink   Link;

        size_t  Rel;

        size_t  Abs;

        uint8   Flag;

        uint8   TaskId;

        uint8   EventValue;

}TTimer;



休眠结构体

typedef  struct

{

volatile uint8 SleepEnable;

  volatile uint8 SleepLevel;

}

TSleepBlock;



2.需要配置的地方(在EV_Config.h)

#define TASK_MAX_COUNT   3     

最大任务数,用户可使用的任务ID为0到TASK_MAX_COUNT-1



#define EVENT_MAX_COUNT  30

最大事件数,EventTab[EVENT_MAX_COUNT]为用户时间提供存储空间



#define TIMER_MAX_COUNT  10

最大定时器数,TimerTab[TIMER_MAX_COUNT]为用户定时器提供存储空间



3.用户接口函数

void EVT_FreeMemorry(TEvent *Event);

释放事件存储空间



TEvent *EVT_AllocMemory(void);

分配事件存储空间,如果分配成功,返回事件结构体指针,如果分配失败,返回NULL



TEvent * EVT_Create(uint8 EventValue);

创建一个事件,包含分配事件存储空间并赋值事件值,如果分配成功,返回事件结构体指针,如果分配失败,返回NULL



uint8 EVT_Scheduler(void);

内核调度,如果内核调度对所有任务循环调度一周,如果没有任何事件,则返回0,否则,返回1

此返回值可作为系统进入休眠条件



void TSK_Init(TTaskExecute *Execute,uint8 TaskId);

任务初始化,初始化时,允许内核调度执行任务事件



void TSK_InitWithEntryEvent(TTaskExecute *Execute,uint8 TaskId);

任务初始化,初始化时,允许内核调度执行任务事件,并触发一个EVT_TASK_ENTRY(0XFF)的事件



void TSK_Disable(uint8 TaskId);

禁止内核调度执行任务事件



void TSK_Enable(uint8 TaskId);

允许内核调度执行任务事件,任务初始化设为允许



void TSK_EventPutLock(uint8 TaskId);

禁止向ID为TaskId的任务事件队列写入事件



void TSK_EventPutUnlock(uint8 TaskId);

允许向ID为TaskId的任务事件队列写入事件



void TSK_EventGetLock(uint8 TaskId);

禁止从ID为TaskId的任务事件队列读取事件



void TSK_EventGetUnlock(uint8 TaskId);

允许从ID为TaskId的任务事件队列读取事件



void TSK_EventLock(uint8 TaskId);

禁止向ID为TaskId的任务事件队列写入和读取事件



void TSK_EventUnlock(uint8 TaskId);

允许向ID为TaskId的任务事件队列写入和读取事件



void TSK_EventClear(uint8 TaskId);

清除ID为TaskId的任务事件队列里的所有事件



TEvent * EVT_PostToTask(uint8 EventValue,uint8 TaskId);

向ID为TaskId的任务触发一个事件,事件值为EventValue,如果成功,返回事件结构体指针,否则,返回NULL



TTimer * EVT_DelayPostToTask(uint8 EventValue,size_t delay,uint8 TaskId);

分配一个定时器,此定时器将在一定延时后,向ID为TaskId的任务触发一个事件。

如果分配定时器成功,返回定时器结构体指针,否则,返回NULL



TEvent * EVT_Post(uint8 EventValue);

向当前任务触发一个事件,事件值为EventValue,如果成功,返回事件结构体指针,否则,返回NULL

此函数只在能在任务中使用



TTimer * EVT_DelayPost(uint8 EventValue,size_t delay);

分配一个定时器,此定时器将在一定延时后,向当前任务触发一个事件。

如果分配定时器成功,返回定时器结构体指针,否则,返回NULL。

此函数只能在任务中使用



TTimer * TSK_Delay(size_t delay)

等同于EVT_DelayPost(EVT_TASK_DELAY,delay),实际上并非函数,是一个宏

分配一个定时器,此定时器将在一定延时后,向当前任务触发一个事件值为EVT_TASK_DELAY的事件。

如果分配定时器成功,返回定时器结构体指针,否则,返回NULL。

此函数只能在任务中使用



void EVT_Delete(uint8 TaskId,TEvent *Event);

删除ID为TaskId的任务的事件队列里Event事件结构体指针指向的事件



void TMR_TicksHander(void);

定时器节拍处理



TTimer *TMR_AllocMemory(void);

分配一个定时器存储空间,如果成功,返回定时器结构体指针,否则,返回NULL



void TMR_FeeMemory(TTimer *Timer);

释放定时器存储空间



void TMR_Delete(TTimer *Timer);

删除定时器队列里Timer定时器结构体指针指向的定时器结构体



void TMR_Clear(void);

删除定时器队列里所有定时器



TTimer * TMR_Post(size_t Abs,uint8 Flag,uint8 TaskId,uint8 EventValue);

创建一个定时器并加入到定时器队列,在Abs个节拍延时后,向ID为TaskId的任务触发一个事件值为EventValue的事件,

如果Flag为TMR_ONESHOT(0X01),则事件触发后,定时器会被删除,

如果Flag为TMR_PERIODIC(0x02),则会周期性触发事件



void SLP_Enable(void);

允许调度内核休眠



void SLP_Disable(void);

禁止调度内核休眠



uint8 SLP_IsEnable(void);

判断内核使用允许休眠



void SLP_SetLevel(uint8);

设置休眠等级



uint8 SLP_GetLevel(void);

读取休眠等级





点击此处下载 ourdev_459012.rar(文件大小:124K) (原文件名:test_tevent.rar)

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

 楼主| 发表于 2008-11-28 11:55:29 | 显示全部楼层
更新一下:20081128



增加TEvent在CC2430,MSP430F1611,LM3S1138和C++Builder下移植。



(原文件名:Image0015.JPG)





点击此处下载 ourdev_521076.rar(文件大小:2.73M) (原文件名:TEvent.rar)





本贴被 ATmega32 编辑过,最后修改时间:2008-11-29,08:44:00.

出0入0汤圆

发表于 2008-11-25 15:53:42 | 显示全部楼层
感觉稍微复杂了一点……还可以再轻量级一点……

出0入0汤圆

发表于 2008-11-25 15:51:08 | 显示全部楼层
好东西~顶一个

出0入0汤圆

 楼主| 发表于 2008-11-25 15:43:14 | 显示全部楼层
20081125

点击此处下载 ourdev_513455.rar(文件大小:263K) (原文件名:TEvent.rar) 



IAR 5.11B,WinAVR20080610

共用一个工程,PD7接LED,闪烁。

出0入0汤圆

发表于 2008-11-25 14:17:33 | 显示全部楼层
谢谢了,学习中!

出0入0汤圆

发表于 2008-11-25 14:00:37 | 显示全部楼层
顶一下,以后再看

出0入0汤圆

发表于 2008-11-25 13:41:29 | 显示全部楼层
谢谢,学习一下,楼主,里面TEvent_AVR 用的是IAR 哪个版本的,我用IAR_FOR AVR 5.11B 提示版本不够新

本贴被 thoro_avr 编辑过,最后修改时间:2008-11-25,13:51:03.

出0入0汤圆

发表于 2008-11-25 13:04:51 | 显示全部楼层
上官金虹,太感谢了

出0入0汤圆

发表于 2008-11-25 12:48:32 | 显示全部楼层
哈哈哈哈,只是个加了个消息机制状态机啊,楼上几位怎么扯到陈明计去了?

出0入0汤圆

 楼主| 发表于 2008-11-25 11:08:27 | 显示全部楼层
每个任务都有自己一份上下文环境,这个上下文环境包含中断标志。

也就是说,每个任务都有自己的中断标志位。



任务A切换到B任务时,会从B任务上下文环境读取B任务的中断标志位,此时的中断标志位与任务A切换任务之前是否关闭中断无关。

当再次切换到任务A是,会从任务A上下文环境读取中断标志位,恢复到任务A切换之前的中断状态。



本贴被 ATmega32 编辑过,最后修改时间:2008-11-25,11:08:54.

出0入0汤圆

发表于 2008-11-25 08:36:14 | 显示全部楼层
如果任务不需要OS_ENTER_CRITICAL(); OS_EXIT_CRITICAL();  我还是不明白,



  如果任务主动放弃CPU,以K_TMO为参数调用延时节拍函数OS_Wait时,首先执行

OS_ENTER_CRITICAL()时,已经关闭了中断,那么,如果在每个任务中不“添加” OS_EXIT_CRITICAL()指令,  

那不是非要等到“当前任务”再次获得CPU,才开中断吗?

  

出0入0汤圆

发表于 2008-11-24 16:53:32 | 显示全部楼层
喔,终于看到了。陈明计的例子2如下:



void TaskC(void)

{

    uint8 x,y;

    

    while (1)

    {

        OS_ENTER_CRITICAL();

        x = random(80);

        y = random(20);

        PC_DispChar(x, y + 5, '3', DISP_FGND_LIGHT_GRAY);    

        OS_EXIT_CRITICAL();

        OSWait(K_TMO,1);

    }    

}



  总结:是否必须按照如下格式编写任务(我刚刚学SMALL RTOS,问题比较幼稚,请不要见笑。)

  void Task(void)

   {

      //该任务的初始化代码

   while (1)

     {

       OS_ENTER_CRITICAL();     

       //任务代码

       OS_EXIT_CRITICAL();

        OSWait(K_TMO,1);

      }

   }

出0入0汤圆

 楼主| 发表于 2008-11-24 22:35:40 | 显示全部楼层
一般的任务不需要OS_ENTER_CRITICAL(); OS_EXIT_CRITICAL(); 



        x = random(80); 

        y = random(20); 

        PC_DispChar(x, y + 5, '3', DISP_FGND_LIGHT_GRAY);  

这段函数是不可重入的。(任务在调用这几个函数时,不允许其他任何任务或者中断调用这两个函数),

所以必须加上OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(); 

出0入0汤圆

发表于 2008-11-24 16:38:28 | 显示全部楼层
以下是陈明计的一个例子:



#include "config.h"



void TaskA(void);

void TaskB(void);

void TaskC(void);



void main(void)

{

    OSInit();



  TMOD = (TMOD & 0XF0) | 0X01;

  TL0 = 0x0;

  TH0 = 0x0;

  TR0 = 1;

  ET0 = 1;



    OSTaskCreate(TaskA, NULL, 0);

    OSTaskCreate(TaskB, NULL, 1);

    OSTaskCreate(TaskC, NULL, 2);



    while(1)

    {

        PCON = PCON | 0x01;                     /* CPU进入休眠状态 */

    }

}





void TaskA(void)

{



    while (1)

    {

        OSWait(K_TMO,5);

    } 

}



void TaskB(void)

{

    while (1)

    {

        OSWait(K_TMO,10);

    }

}



void TaskC(void)

{

    while (1)

    {

        OSWait(K_TMO,15);

    }

}





从上面的例子可以看出,

  调用OSSched()切换到另外一个任务时,又是从目前就绪表中“优先级最高任务”的断点处开始执行,上面的例子中,并没有开中断的指令呀!

 

出0入0汤圆

发表于 2008-11-24 11:30:53 | 显示全部楼层
上官金虹,我有一个疑问,请指教:

  在SMALL RTOS51中OS_Wait()系统函数中,



 case K_TMO:                                 /* 等待超时,即延时一段时间 */

        OS_ENTER_CRITICAL();

        while (OSWaitTick[OSTaskID] != 0)       /* 判断超时时间是否到   */

        {

            OSClearSignal(OSTaskID);            /* 任务进入等待状态     */

            OSSched();                          /* 运行下一个任务       */

        }

        OS_EXIT_CRITICAL();

        return TMO_EVENT;



 问题是:当超时未到时,中断一直是关闭的,那么这段时间不是无法进入中断服务程序了吗?

出0入0汤圆

 楼主| 发表于 2008-11-24 13:52:06 | 显示全部楼层
这里并不是一直循环等待。



当延时时间未到,调用OSSched()会切换到另外的任务,当切换到某个任务后,中断自然会打开。

本贴被 ATmega32 编辑过,最后修改时间:2008-11-24,13:52:38.

出0入0汤圆

 楼主| 发表于 2008-11-24 00:06:13 | 显示全部楼层
我个人觉得还蛮好用的。



这个调度内核最大优点是可移植性好。

全部用C写的,没有汇编。

我AVR,MSP430,CC2430,STM32,LM3S都用这个。



有什么问题大家一起讨论。





循环调度部分,这样写感觉更清晰些:

   while(1) 

    { 

          while(EVT_Scheduler());

          if(SLP_IsEnable())  

           {

            //休眠

           }

    }

本贴被 ATmega32 编辑过,最后修改时间:2008-11-24,00:09:20.

出0入0汤圆

发表于 2008-11-23 18:41:38 | 显示全部楼层
不是很明白

出0入0汤圆

发表于 2008-11-23 13:20:40 | 显示全部楼层
好东西,谢

出0入0汤圆

发表于 2008-11-18 11:12:50 | 显示全部楼层
dddd

出0入0汤圆

 楼主| 发表于 2008-11-7 17:12:02 | 显示全部楼层
更新一下;20081107

点击此处下载 ourdev_487505.rar(文件大小:293K) (原文件名:TEvent.rar) 

出0入0汤圆

 楼主| 发表于 2008-10-17 17:27:21 | 显示全部楼层
简单例子:



#include "config.h"



#define TASK1ID   0

#define TASK2ID   1



#define EVT_LED_ON  0

#define EVT_LED_OFF 1



uint8 task1(uint8 EventValue)

{

    switch(EventValue)

        {

              case EVT_TASK_ENTRY:              //任务入口事件,任务初始化 TSK_InitWithEntryEvent(task2,TASK2ID)时触发了EVT_TASK_ENTRY事件

                    EVT_Post(EVT_LED_ON);       //向当前任务触发一个EVT_LED_ON事件

                break;



                case EVT_LED_ON:                

                    PORTD&=~_BV(7);

                    EVT_DelayPost(EVT_LED_OFF,OS_TICKS_PER_SEC/10);  //创建一个定时器,延时1/10 S后,向所在任务触发一个EVT_LED_OFF事件,事件触发后,定时器会北删除

                break;



                case EVT_LED_OFF:

                PORTD|=_BV(7);

               EVT_DelayPost(EVT_LED_ON,OS_TICKS_PER_SEC/10);    //创建一个定时器,延时1/10 S后,向所在任务触发一个EVT_LED_ON事件,事件触发后,定时器会北删除 

                break;

          

        }

        return 0;

       //返回值0:表示内核继续扫描当前任务是否还有其它事件

}







uint8 task2(uint8 EventValue)   //事件只用来触发任务运行,不区分事件值具体是多少,任务按步(step)运行。 

{

     EventValue=EventValue;     

     

     static uint8 step=0;



         if(step==0)

         {

                PORTD|=_BV(6);

                PORTD&=~_BV(4);

                step=1;

                TSK_Delay(OS_TICKS_PER_SEC/10); 

               //创建一个定时器,延时1/10 S后,触发一个EVT_TASK_DELAY事件,事件触发后,定时器会北删除

                

            return 1;

            //返回值1:表示内核将扫描下一个任务的事件,不管当前任务是否还有其它事件

         

         }

                  if(step==1)

         {

                PORTD|=_BV(4);

                PORTD&=~_BV(5);

                step=2;

                TSK_Delay(OS_TICKS_PER_SEC/10);

                

            return 1;

         }

                  if(step==2)

         {

                PORTD|=_BV(5);

                PORTD&=~_BV(6);

                step=0;

                TSK_Delay(OS_TICKS_PER_SEC/10);

            return 1;

         }

         return 0;

         

}













int main()

{

          DDRD=_BV(4)|_BV(5)|_BV(6)|_BV(7);

          PORTD=_BV(4)|_BV(5)|_BV(6)|_BV(7);



          TCNT2 = 0;

          TCCR2A=_BV(WGM21);  

          TCCR2B = T2_CLK_DIV64;

          OCR2A=OCR2A_INIT;

          TIMSK2=_BV(OCIE2A);

          

          TSK_InitWithEntryEvent(task1,TASK1ID);  //任务初始化,并触发一个EVT_TASK_ENTRY事件

          TSK_InitWithEntryEvent(task2,TASK2ID);  //任务初始化,并触发一个EVT_TASK_ENTRY事件



          sei();



     while(1)

    {

          if(EVT_Scheduler())          //任务调度

        {

       

        }

        else       //全部任务扫描一次,没有任何事件,内核处于空闲状态

       {

          if(SLP_IsEnable())     //如果允许休眠

          {

         

              if(SLP_GetLevel()==NORMAL_SLEEP)  //如果休眠级别是一般休眠

             {

              

                          //一般休眠 

             }

             else                             //如果休眠级别是深度休眠

            {

                          //深度休眠

                          

            }

          }

       }

    }

}



SIGNAL(TIMER2_COMPA_vect)

{



  TMR_TicksHander();            //系统节拍



}







本贴被 ATmega32 编辑过,最后修改时间:2008-10-17,17:35:23.

出0入0汤圆

发表于 2009-11-19 13:17:23 | 显示全部楼层
lz,和你那个teventsmall有何区别?

出0入0汤圆

发表于 2009-11-19 13:34:48 | 显示全部楼层
基本上一样的(可能稍微有点变动)。

我原本是打算把tevent分成teventtiny,teventsmall。
(teventsmall就是原来的tevent)
不过后来teventsmall完全都没有再使用,已经从硬盘删除了。

teventtiny我现在改名叫VoidEvent,
另外,还有一个VoidTask,是多任务抢占内核。

不过都是自己写的玩。

出0入0汤圆

发表于 2009-11-19 13:45:28 | 显示全部楼层
那楼主现在在用什么操作系统?

出0入0汤圆

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

本版积分规则

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

GMT+8, 2024-7-23 17:16

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

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