OSAL的按键设计分析
void HalDriverInit (void);
HalDriverInit 对HAL层(硬件抽象层)做初始化,并在其内部调用
void HalKeyInit( void );
完成按键的初步初始化,按键初始化主要是把按键对应到IO口,并指定IO口的方向,并且把全局变量HalKeyConfigured设置为FALSE,这是因为虽然按键已经初始化了(指定了端口),但是还没有配置中断,也没有设置key的处理函数(用于向指定task发送key的消息)。
halKeySavedKeys = 0; // Initialize previous key to 0. HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT); /* Set pin function to GPIO */ HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT); /* Set pin direction to Input */ HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin function to GPIO */ HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin direction to Input */ P2INP |= PUSH2_BV; /* Configure GPIO tri-state. */ /* Initialize callback function */ pHalKeyProcessFunction = NULL;//key的处理函数还未设置,需要在HalKeyConfig函数中设置 /* Start with key is not configured */ HalKeyConfigured = FALSE;
代码中将SW6按键绑定到了P0_1端口,P0是一个8bit的端口,P0_1是第二位。当读取P0口时,如果第二位为1,则为高电平,第二位为0,则为低电平。
void InitBoard( uint8 level )
完成按键的配置。
/* Initialize Key stuff */ OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE; //打开中断 //OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE; HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback); //调用HalKeyConfig做按键配置,将OnBoard_KeyCallback作为按键处理函数。
HalKeyConfig的作用是设置按键回调函数、配置按键对应IO口的中断。
- 未按下
- 按下的瞬间
- 按下还未弹起
HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )
2、按下的瞬间
void halProcessKeyInterrupt (void)
对按键作出处理,并且调用osal_start_timerEx 函数在HAL_KEY_DEBOUNCE_VALUE时间(25ms,防止按键抖动)后向OSAL的HAL task发出HAL_KEY_EVENT消息:
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);
OSAL把这个消息发给Hal_TaskID,Hal task会接受这个消息,继而调用Hal_ProcessEvent函数,Hal_ProcessEvent包括了多种硬件处理,例如LED UART KEY等,因为接受的消息是HAL_KEY_EVENT,所以进入按键处理分支:
uint16 Hal_ProcessEvent( uint8 task_id, uint16 events ) { .....//删略了其他HAL层收到的硬件消息,例如PERIOD_RSSI_RESET_EVT\HAL_LED_BLINK_EVENT等等 if (events & HAL_KEY_EVENT) //进入按键处理分支 { /* Check for keys */ HalKeyPoll(); //读取按键的值 /* if interrupt disabled, do next polling */ if (!Hal_KeyIntEnable) { osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100); } return events ^ HAL_KEY_EVENT; } }
Hal_ProcessEvent在按键处理代码中,先调用HalKeyPoll函数读取key的值,HalKeyPoll函数对SW6按键的处理的代码如下
void HalKeyPoll (void) { uint8 keys = 0; uint8 notify = 0; if (!(HAL_KEY_SW_6_PORT & HAL_KEY_SW_6_BIT)) /* Key is active low */ { keys |= HAL_KEY_SW_6; //如果SW_6_PORT是低电平,则将keys的对应位设置为1 } /* If interrupts are not enabled, previous key status and current key status * are compared to find out if a key has changed status. */ if (!Hal_KeyIntEnable) { if (keys == halKeySavedKeys) { /* Exit - since no keys have changed */ return; } else { notify = 1; } } else { /* Key interrupt handled here */ if (keys) { notify = 1; } } /* Store the current keys for comparation next time */ halKeySavedKeys = keys; /* Invoke Callback if new keys were depressed */ if (notify && (pHalKeyProcessFunction))//pHalKeyProcessFunction实际上就是回调函数,在hal_drive中中使用 { (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL); } }
HalKeyPoll首先判断sw6的端口是否按下,如果按下,此时HAL_KEY_SW_6_PORT是低电平,值为0。
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
pHalKeyProcessFunction是全局变量,在InitBoard中,已经调用HalKeyConfig将其设置为了OnBoard_KeyCallback。
void OnBoard_KeyCallback ( uint8 keys, uint8 state ) { uint8 shift; (void)state; // shift key (S1) is used to generate key interrupt // applications should not use S1 when key interrupt is enabled shift = (OnboardKeyIntEnable == HAL_KEY_INTERRUPT_ENABLE) ? false : ((keys & HAL_KEY_SW_6) ? true : false); if ( OnBoard_SendKeys( keys, shift ) != SUCCESS )//向注册的task发送按键消息 { //blabla } /* If any key is currently pressed down and interrupt is still enabled, disable interrupt and switch to polling */ if( keys != 0 ) { if( OnboardKeyIntEnable == HAL_KEY_INTERRUPT_ENABLE )//如果开启了中断,将其设置为不开中断,接下来就按照每100ms调用一次keypoll { OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE; HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback); } } /* If no key is currently pressed down and interrupt is disabled, enable interrupt and turn off polling */ else { if( OnboardKeyIntEnable == HAL_KEY_INTERRUPT_DISABLE )//如果key==0,并且中断关闭,则重新打开终端。 { OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE; HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback); } } }
OnBoard_KeyCallback主要做俩件事,调用OnBoard_SendKeys函数将按键发送给注册过的task,也就是我们自己开发的task。OnBoard_SendKeys代码如下:
uint8 OnBoard_SendKeys( uint8 keys, uint8 state ) { ...... osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );//把按键消息发给注册ID registeredKeysTaskID的task ...... }
OnBoard_KeyCallback做的第二件事是开关中断,当按键触发中断后,OnBoard_KeyCallback将中断关闭,这样在Hal_ProcessEvent函数中将每100ms调用一次HalKeyPoll对key做轮询,并且在按键按下的过程中不再次进入OnBoard_KeyCallback ,这样就不会再次向注册的task发出按键消息。
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
关闭中断,此时会进入不配置中断的代码分支,如下:
else /* Interrupts NOT enabled */ { // 关闭中断 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 */ //给HAL task发送HAL_KEY_EVENT,进入key polling osal_set_event(Hal_TaskID, HAL_KEY_EVENT); }
接下来,由于给hal task发了消息,将进入hal的event处理函数
uint16 Hal_ProcessEvent( uint8 task_id, uint16 events );
因为event=HAL_KEY_EVENT,所以再次进入HAL_KEY_EVENT的处理代码:
if (events & HAL_KEY_EVENT) //进入按键处理分支 { /* Check for keys */ HalKeyPoll(); /* if interrupt disabled, do next polling */ if (!Hal_KeyIntEnable) { osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100); } }
跟前面处理中断不同的是,这次调用完HalKeyPoll()后,调用了:
osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
启动了定时器,在100ms后,重新给Hal_TaskID发送HAL_KEY_EVENT消息,重新进入polling。
if (!Hal_KeyIntEnable) { if (keys == halKeySavedKeys) //如果还是按下状态,则keys没有变化,此时不返回任何值。 { /* Exit - since no keys have changed */ return; } else { notify = 1; //如果按键弹起了,则将notify置1 } } .... /* Store the current keys for comparation next time */ halKeySavedKeys = keys; /* Invoke Callback if new keys were depressed */ if (notify && (pHalKeyProcessFunction)) //如果还是按下状态,notify=0,此时不执行pHalKeyProcessFunction。 { //如果按键弹起,则进入pHalKeyProcessFunction (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL); }
如果按下状态没有改变,函数HalKeyPoll()不做任何事,当按键弹起,notify被置1,进入pHalKeyProcessFunction对按键做处理。
/* If no key is currently pressed down and interrupt is disabled, enable interrupt and turn off polling */ else { if( OnboardKeyIntEnable == HAL_KEY_INTERRUPT_DISABLE ) { OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE; HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback); } }
当key==0,并且中断关闭,这时候重新打开中断。等待下一次按键触发中断。