|
自己一直游荡于各位大神的帖子之中汲取养分,学到了不少东西!怀着缅怀大神和激动的心情,我把自己分析的按键事件逐句的记录下来,说实话这种幼儿园的水平不值得粘出来,不过事事都有第一次嘛,饮水思源,把大神们的无私精神继续下去很重要!
zigbee的硬件也是单片机所以按键也无非两种方式:轮询和中断!
中断法:当有按键按下时,首先进入按键中断处理程序,里面有osal_start_timerEx()这个函数,用于设置按键处理程序,即调用Hal_ProcessEvent(),得到按键的值,并通过一系列调用传到应用层中。后面的步骤和上面一样我就不说了。该方法只调用osal_start_timerEx()一次,没有按键按下的时候,不会调用Hal_ProcessEvent()函数。也不存在循环的概念。
查询法: 通过该函数HalKeyConfig(),调用osal_start_timerEx(),再执行Hal_ProcessEvent(),再调用osal_start_timerEx()一次(该函数包含在hal_processEvent()中,如果选择的查询法,这该函数执行,如果选择的是中断方式则不执行)。从而实现循环,及每隔1毫秒执行一次Hal_ProcessEvent()函数。后面过程与上面一样。该方法调用osal_start_timerEx()二次(得到一次键值用的次数),目的就是为了实现轮询。
两方法的共同点:除了调用osal_start_timerEx()的方法和次数不同外,其余都一样。方法1利用中断方法调用,只使用一次。方法2程序执行时就调用,使用两次。归根结底,要得到按键值,必须设置事件发生标志,操作系统查询到有按键事件发生标志,调用hal层事件处理函数,得到按键的值。
============================================
具体分析如下:
在main主函数中初始化调用了void InitBoard( uint8 level )函数
void InitBoard( uint8 level )
{
if ( level == OB_COLD )
{
// IAR does not zero-out this byte below the XSTACK.
*(uint8 *)0x0 = 0;
// Interrupts off
osal_int_disable( INTS_ALL );
// Check for Brown-Out reset
ChkReset();
}
else // !OB_COLD
{
/* Initialize Key stuff */
#ifndef ENDDEVICE
HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);//协调、路由的按键采用轮询,回调函数设为OnBoard_KeyCallback
#else
HalKeyConfig(HAL_KEY_INTERRUPT_ENABLE, OnBoard_KeyCallback);//终端采用中断,‍回调函数设为OnBoard_KeyCallback
‍ #endif
}
}
在HalKeyConfig函数中若是中断则配置中断方式,若是轮询则置事件发送标识 HAL_KEY_EVENT
void HalKeyConfig(bool interruptEnable, halKeyCBack_t cback)
{
if ((Hal_KeyIntEnable = interruptEnable))
{
HAL_KEY_CLR_INT(); // Clear spurious ints.
PICTL |= 0x01; // P1ICONL: Falling edge ints on pins 0-3.
P1IEN |= PUSH1_BV | PUSH2_BV; // Enable specific P1 bits for ints by bit mask.
IEN2 |= 0x10; // Enable general P1 interrupts.
}
else
{
(void)osal_set_event(Hal_TaskID, HAL_KEY_EVENT);
}
pHalKeyProcessFunction = cback;
}
随后osal系统发现了tasksEvents[Hal_TaskID]项不为0,就调用其对应的任务处理函数 Hal_ProcessEvent
在Hal_ProcessEvent 函数中查询事件标识HAL_KEY_EVENT
uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )
{
uint8 *msgPtr;
(void)task_id; // Intentionally unreferenced parameter
if ( events & SYS_EVENT_MSG )
{
msgPtr = osal_msg_receive(Hal_TaskID);
while (msgPtr)
{
/* Do something here - for now, just deallocate the msg and move on */
/* De-allocate */
osal_msg_deallocate( msgPtr );
/* Next */
msgPtr = osal_msg_receive( Hal_TaskID );
}
return events ^ SYS_EVENT_MSG;
}
if ( events & HAL_LED_BLINK_EVENT )
{
#if (defined (BLINK_LEDS)) && (HAL_LED == TRUE)
HalLedUpdate();
#endif /* BLINK_LEDS && HAL_LED */
return events ^ HAL_LED_BLINK_EVENT;
}
if (events & HAL_KEY_EVENT)
{
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
/* Check for keys */
HalKeyPoll();
/* if interrupt disabled, do next polling */
if (!Hal_KeyIntEnable)//轮询模式就100ms后会在进入这个函数的
{
osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
}
#endif // HAL_KEY
return events ^ HAL_KEY_EVENT;
}
#ifdef POWER_SAVING
if ( events & HAL_SLEEP_TIMER_EVENT )
{
halRestoreSleepLevel();
return events ^ HAL_SLEEP_TIMER_EVENT;
}
#endif
#ifdef CC2591_COMPRESSION_WORKAROUND
if ( events & PERIOD_RSSI_RESET_EVT )
{
macRxResetRssi();
return (events ^ PERIOD_RSSI_RESET_EVT);
}
#endif
/* Nothing interested, discard the message */
return 0;
}
在上面Hal_ProcessEvent 函数中根据事件标识HAL_KEY_EVENT 执行HalKeyPoll();函数
void HalKeyPoll(void)
{
uint8 newKeys;
if (Hal_KeyIntEnable)//中断模式入口
{
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState);
newKeys = isrKeys;
isrKeys = 0;
HAL_EXIT_CRITICAL_SECTION(intState);
}
else//轮询模式的入口
{
uint8 keys = HalKeyRead();//获得S1按键值0x20
newKeys = (halKeys ^ keys) & keys;
halKeys = keys;
}
if (newKeys && pHalKeyProcessFunction)
{
(pHalKeyProcessFunction)(newKeys, HAL_KEY_STATE_NORMAL);
}
}
在HalKeyPoll函数中因为轮询模式则函数会调用HalKeyRead函数读取按键值
uint8 HalKeyRead ( void )
{
uint8 keys = 0;
#ifndef A8GATEWAY
if (HAL_PUSH_BUTTON1())//若是低电平则if条件成立
{
keys |= HAL_KEY_SW_6;
}
#endif
#if (PROJECT == 3) && (SENSOR_TYPE == 1)
if (HAL_PUSH_BUTTON2())
{
keys |= HAL_KEY_CURTAIN1;
}
if (HAL_PUSH_BUTTON3())
{
keys |= HAL_KEY_CURTAIN2;
}
if (HAL_PUSH_BUTTON4())
{
keys |= HAL_KEY_CURTAIN3;
}
#endif
#if (HAL_JOYSTICK == TRUE)
if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT)) /* Key is active low */
{
keys |= halGetJoyKeyInput();
}
#endif
return keys;
}
在HalKeyRead把按键值给到keys之后,在根据函数调用关系一步一步的往回看,HalKeyRead 在HalKeyPoll 函数中把键值给到newKeys 后继续执行下面的语句:
if (newKeys && pHalKeyProcessFunction)//若键值不为0并且回调函数不为空那就执行回调函数
{
(pHalKeyProcessFunction)(newKeys, HAL_KEY_STATE_NORMAL);
}
至于回调函数,程序在InitBoard 初始化过程中通过HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback)函数配置为OnBoard_KeyCallback 函数了!
void OnBoard_KeyCallback ( uint8 keys, uint8 state )
{
#ifndef A8GATEWAY
uint8 shift;
(void)state;
shift = (keys & HAL_KEY_SW_6) ? true : false;
if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )//成功将按键消息送到任务消息表并置上了一级事件标识SYS_EVENT_MSG 和二级事件标识KEY_CHANGE 后这里返回ZSuccess
{
// Process SW1 here
if ( keys & HAL_KEY_SW_1 ) // Switch 1
{
}
// Process SW2 here
if ( keys & HAL_KEY_SW_2 ) // Switch 2
{
}
// Process SW3 here
if ( keys & HAL_KEY_SW_3 ) // Switch 3
{
}
// Process SW4 here
if ( keys & HAL_KEY_SW_4 ) // Switch 4
{
}
// Process SW5 here
if ( keys & HAL_KEY_SW_5 ) // Switch
{
}
// Process SW6 here
if ( keys & HAL_KEY_SW_6 ) // Switch 6
{
}
}
#endif
}
上面回调函数中调用了OnBoard_SendKeys 函数:
uint8 OnBoard_SendKeys( uint8 keys, uint8 state )
{
keyChange_t *msgPtr;
if ( registeredKeysTaskID != NO_TASK_ID )//注册过按键事件
{
// Send the address to the task
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 );//把按键事件送到OSAL任务事件表中
}
return ( ZSuccess );
}
else
return ( ZFailure );
}
在用osal_msg_send 函数将按键消息送入OSAL任务事件表时,其本身又设置了SYS_EVENT_MSG 事件标识
uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
{
if ( msg_ptr == NULL )
return ( INVALID_MSG_POINTER );
if ( destination_task >= tasksCnt )
{
osal_msg_deallocate( msg_ptr );
return ( INVALID_TASK );
}
// Check the message header
if ( OSAL_MSG_NEXT( msg_ptr ) != NULL ||
OSAL_MSG_ID( msg_ptr ) != TASK_NO_TASK )
{
osal_msg_deallocate( msg_ptr );
return ( INVALID_MSG_POINTER );
}
OSAL_MSG_ID( msg_ptr ) = destination_task;
// queue message
osal_msg_enqueue( &osal_qHead, msg_ptr );
// Signal the task that a message is waiting
osal_set_event( destination_task, SYS_EVENT_MSG );
return ( SUCCESS );
}
完成事件标记最后一步的是osal_set_event ,它的任务就是把对应的tasksEvents[task_id]其中的某一位 置为非0
uint8 osal_set_event( uint8 task_id, uint16 event_flag )
{
if ( task_id < tasksCnt )
{
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // Hold off interrupts
tasksEvents[task_id] |= event_flag; // Stuff the event bit(s)
HAL_EXIT_CRITICAL_SECTION(intState); // Release interrupts
return ( SUCCESS );
}
else
{
return ( INVALID_TASK );
}
}
经过层层调用然后返回到函数的调用处继续执行下面的语句,最后以一句
return events ^ HAL_KEY_EVENT结束按键事件的触发流程
然后OSAL系统再次轮询到被osal_set_event 置为非0的那个tasksEvents[Hal_TaskID],和按键触发流程一样去调用它对应相同顺序的那个处理函数, 在这个函数中对消息包层层剥离,根据剥出来的事件标识:SYS_EVENT_MSG 、KEY_CHANGE 找到正确的处理函数,再根据具体消息内容,比如keys=0x20找到具体的处理,至此系统对于你没事按按键的事就此罢休了!
|
|