ATmega32 发表于 2008-10-17 17:25:41

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

TEvent 20081017

Author: ATmega32



1.数据结构

事件值

uint8EventValue;

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



事件结构体

typedef struct

{

    TLinkLink;

    uint8Flag;

    uint8EventValue;

}TEvent;



任务结构体

typedef struct

{

      uint8          Flag;

      TQueue         EventQueue;

      TTaskExecute   *Execute;

}TTask;



定时器结构体

typedef struct

{

      TLink   Link;

      size_tRel;

      size_tAbs;

      uint8   Flag;

      uint8   TaskId;

      uint8   EventValue;

}TTimer;



休眠结构体

typedefstruct

{

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_COUNT30

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



#define TIMER_MAX_COUNT10

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



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)

ATmega32 发表于 2008-11-28 11:55:29

更新一下:20081128



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

http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_521036.JPG

(原文件名:Image0015.JPG)





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





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

Gorgon 发表于 2008-11-25 15:53:42

感觉稍微复杂了一点……还可以再轻量级一点……

lbxxx 发表于 2008-11-25 15:51:08

好东西~顶一个

ATmega32 发表于 2008-11-25 15:43:14

20081125

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



IAR 5.11B,WinAVR20080610

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

wenwu 发表于 2008-11-25 14:17:33

谢谢了,学习中!

huayan 发表于 2008-11-25 14:00:37

顶一下,以后再看

thoro_avr 发表于 2008-11-25 13:41:29

谢谢,学习一下,楼主,里面TEvent_AVR 用的是IAR 哪个版本的,我用IAR_FOR AVR 5.11B 提示版本不够新

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

ba_wang_mao 发表于 2008-11-25 13:04:51

上官金虹,太感谢了

rainyss 发表于 2008-11-25 12:48:32

哈哈哈哈,只是个加了个消息机制状态机啊,楼上几位怎么扯到陈明计去了?

ATmega32 发表于 2008-11-25 11:08:27

每个任务都有自己一份上下文环境,这个上下文环境包含中断标志。

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



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

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



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

ba_wang_mao 发表于 2008-11-25 08:36:14

如果任务不需要OS_ENTER_CRITICAL(); OS_EXIT_CRITICAL();  我还是不明白,



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

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

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

  

ba_wang_mao 发表于 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);

      }

   }

ATmega32 发表于 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(); 

ba_wang_mao 发表于 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()切换到另外一个任务时,又是从目前就绪表中“优先级最高任务”的断点处开始执行,上面的例子中,并没有开中断的指令呀!

 

ba_wang_mao 发表于 2008-11-24 11:30:53

上官金虹,我有一个疑问,请指教:

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



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

        OS_ENTER_CRITICAL();

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

        {

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

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

        }

        OS_EXIT_CRITICAL();

        return TMO_EVENT;



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

ATmega32 发表于 2008-11-24 13:52:06

这里并不是一直循环等待。



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

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

ATmega32 发表于 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.

ningmeng7294 发表于 2008-11-23 18:41:38

不是很明白

wz007 发表于 2008-11-23 13:20:40

好东西,谢

CRGTOM 发表于 2008-11-18 11:12:50

dddd

ATmega32 发表于 2008-11-7 17:12:02

更新一下;20081107

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

ATmega32 发表于 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.

astudent 发表于 2009-11-19 13:17:23

lz,和你那个teventsmall有何区别?

voidx 发表于 2009-11-19 13:34:48

基本上一样的(可能稍微有点变动)。

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

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

不过都是自己写的玩。

astudent 发表于 2009-11-19 13:45:28

那楼主现在在用什么操作系统?

alexmayer 发表于 2009-11-19 16:56:35

有意思,顶上官兄。
页: [1]
查看完整版本: 尝试写个简单基于事件调度系统TEvent 20081017 【恢复】