以按键事件为例阐述消息传递机制

来源:互联网 发布:可以听pdf的软件 编辑:程序博客网 时间:2024/05/20 09:24

以按键事件为例阐述消息传递机制

这里主要想说的是按键事件发生后,app层是怎么收到消息的一些想法。
main.c:        执行InitBoard( OB_COLD )           // OB_COLD宏定义为0        {             if ( level == OB_COLD )                {                *(uint8 *)0x0 = 0;                 osal_int_disable( INTS_ALL )                 }        } 

这个函数执行完毕系统关禁止中断

    然后执行HalDriverInit (void)    //头文件有#define HAL_KEY TRUE            {               ......                      #if (defined HAL_KEY) && (HAL_KEY == TRUE)             HalKeyInit();            #endif              .....                       }   

———————————————————————————————————————
hal_key.c:

    执行HalKeyInit( void )  //这是关于按键初始化的配置,比如回调函数,按键储存变量            {                halKeySavedKeys = 0;                .......                pHalKeyProcessFunction  = NULL;//回调函数为空                HalKeyConfigured = FALSE;   //按键还没有配置            }

执行完返回main.c

———————————————————————————————————————
顺着main函数的函数体往下执行,再次遇到

InitBoard(  OB_READY  );            // #define OB_READY 2        {            ......        else           {           HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);         }        }

hal_key.c:
// #define HAL_KEY_INTERRUPT_DISABLE 0x00

    执行  HalKeyConfig ( bool  interruptEnable , halKeyCBack_t   cback )        {          Hal_KeyIntEnable = interruptEnable;  //将0x00赋值给Hal_KeyIntEnable          pHalKeyProcessFunction = cback;            /*回调函数赋值给 pHalKeyProcessFunction,往上查定义得到        static  halKeyCBack_t  pHalKeyProcessFunction,再往上查得到        typedef  void  (*halKeyCBack_t) (uint8 keys, uint8 state);         说白了pHalKeyProcessFunction就是个函数指针*/        ......        else          {       HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don't generate interrupt */       HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT);   /* Clear interrupt enable bit */       osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE);                //       #define HAL_KEY_EVENT         0x0001 HAL_KEY_POLLING_VALUE定义为500ms,开始计时任务,每100ms触发一次。意思就是HAL_KEY_EVENT这个事件100ms发生一次  }    HalKeyConfigured = TRUE;}

——————————————————————————————————————
到此为止,按键相关的初始化都搞完了。接下来就是等第一个100ms
现在第一个100ms来了。

main.c:    osal_start_system()        for(;;)    {  osal_run_system();}

________________________________________________________________________________________________

执行  osal_run_system();    {        osalTimeUpdate();         Hal_ProcessPoll();        //上面两个函数实现了计时和事件置位        ......        events = tasksEvents[idx];        tasksEvents[idx] = 0;        events = (tasksArr[idx])( idx, events );    }

主循环里面检测到Hal_TaskID这个任务被置位(至于osal_start_timerEx是怎么让任务置位这个问题我还没弄懂,暂且当做已知…),保存在event,然后把任务清0。进入事件处理函数(这里为什么叫事件处理函数而不是叫任务处理函数呢?我想是因为一个任务可以接受处理很多个事件,这个函数只是个入口,函数体里面是会具体区分什么事件的)。
———————————————————————————————————————

hal_drivers.c    Hal_ProcessEvent( uint8 task_id, uint16 events )    {        if (events & HAL_KEY_EVENT)         {            #if (defined HAL_KEY) && (HAL_KEY == TRUE)                HalKeyPoll();           if (!Hal_KeyIntEnable)            {              osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);            }           #endif // HAL_KEY        return events ^ HAL_KEY_EVENT;        }  }

这里想说,osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE)传递给Hal_TaskID这个任务的事件是HAL_KEY_EVENT。(events & HAL_KEY_EVENT)的结果就是1,进入if判断,HalKeyPoll()获得是哪个按键按下了。然后继续osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100),为什么要这样做呢?因为在osal_run_system()里面有tasksEvents[idx] = 0 意味着这个事件被处理过了,但是谁知道什么时候有人要按键呢,所以只能不停的查询。
——————————————————————————————————————————
看到这里,好像还是没说怎么通知app层啊?仔细找找,原来在HalKeyPoll();里hal_key.c:

HalKeyPoll(){if (!Hal_KeyIntEnable)  {    if (keys == halKeySavedKeys)    {           return;    }       halKeySavedKeys = keys;  }  else  {/* Key interrupt handled here */  }      if (keys && (pHalKeyProcessFunction))  {    (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);  }

创建halKeySavedKeys变量是为了下一次触发事件的时候和keys进行比较,如果相同则说明
这一次轮询没有按键,就return回去(橙色代码)。如果keys有值并且回调函数已经定义了的话就调用回调函数。
#define HAL_KEY_STATE_NORMAL 0x00
———————————————————————————————————————
Onboard.c:
回调函数前面说了是个函数指针,实际上调用:

OnBoard_KeyCallback ( uint8 keys, uint8 state ){    ......    if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )    .......}OnBoard_SendKeys( uint8 keys, uint8 state ){    //在这里正式填充消息结构体(不是那种无线消息),而是“keyChange_t”,我觉得是这个层次向app层发消息的特定的结构体,是不是别的层向app层发送的话有别的结构体样式?    if ( registeredKeysTaskID != NO_TASK_ID )    {        msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );    if ( msgPtr )    {      msgPtr->hdr.event = KEY_CHANGE;      msgPtr->state = state;      msgPtr->keys = keys;      osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );    }    return ( ZSuccess );  }

在文件开头有static uint8 registeredKeysTaskID = NO_TASK_ID;
在这个文件里有函数RegisterForKeys( uint8 task_id ) 。函数实现了registeredKeysTaskID = task_id。 那么到底谁调用了这个函数呢?全局查找之后发现在sampleapp.c里面调用了它,这里就明白了很多教程说使用按键就一定要先调用按键注册函数,因为如果不这样做的话if循环根本进不去….

define KEY_CHANGE 0xC0 这是一个事件的编号,猜想应该还有很多事件的宏定义散落在不同的文件里,但是好像记得说过事件是独热码编码的,那怎么会有0xc0这种东西,暂时保留疑问….

———————————————————————————————————————————

OSAL.c:    osal_msg_send(destination_task, (uint8 *)msgPtr )    {        ......        osal_msg_enqueue( &osal_qHead, msg_ptr );        osal_set_event( destination_task, SYS_EVENT_MSG );        ......    }

// destination_task就是app层的id
//这里就正式将消息传给destination_task,也就是SampleApp_TaskID。其实我们口中说的将消息传给谁,程序中的实现是这样的:将这个消息加入消息队列(所有任务都能访问的一个队列),然后那个消息传给了哪个任务,就将那个任务事件标志位置1(main循环就能轮询到),那个任务对应的事件处理函数里面就会去取属于自己的消息(函数是不知道属于自己的消息放在哪里的,所以它一定要将整个消息队列遍历一次,这个说法在osal_msg_receive( SampleApp_TaskID )的函数体中可以证实)

————————————————————————————————————————————

OSAL.c:osal_set_event( uint8 task_id, uint16 event_flag ){.......tasksEvents[task_id] |= event_flag;......}

这句话牛逼啦,event_flag就是传递的参数SYS_EVENT_MSG,也就是0x8000.
task¬_id下标对应的tasksevents就变成了0x8000。为什么要这样?理由 1.统一规定一个强制的参数作为事件发生的标志,清晰明了 2.后面的事件处理函数好判断

_ ____________________________________________________________________
sampleapp.c:
现在任务已经被置位,并进入了事件处理函数

SampleApp_ProcessEvent( uint8 task_id, uint16 events ){       ......    if ( events & SYS_EVENT_MSG )  //没有上面的event_flag,这个if是进不去的。    {        ........    while ( MSGpkt )    {      switch ( MSGpkt->hdr.event )      {        ......          case KEY_CHANGE:          SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );            break;        }        ......  }}

SYS_EVENT_MSG告诉你有事件发生了,发生的事什么事件通过(MSGpkt->hdr.event)来判断,通过switch语句跳转到不同的处理函数上。
———————————————————————————————————————
关于消息从产生到传递到处理暂时只能理解这个份上了….

0 0
原创粉丝点击