OSAL的按键设计分析
转自:http://www.cnblogs.com/wsecurity/p/3259263.html
我们以按键的状态变化为线索,看看各个模块的作用,以及变量是怎么变化的。
按键设计总体上可以分为2部分,按键初始化和按键运行,按键初始化主要负责跟按键相关的IO端口设置、中断设置。按键运行就是OSAL检测按键的过程。
按键初始化
1、初始化按键
在main函数调用函数
void HalDriverInit (void);
HalDriverInit 对HAL层(硬件抽象层)做初始化,并在其内部调用
void HalKeyInit( void );
完成按键的初步初始化,按键初始化主要是把按键对应到IO口,并指定IO口的方向,并且把全局变量HalKeyConfigured设置为FALSE,这是因为虽然按键已经初始化了(指定了端口),但是还没有配置中断,也没有设置key的处理函数(用于向指定task发送key的消息)。
HalKeyInit函数对按键的初始化代码如下:
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,则为低电平。
2、配置按键
在main函数下调用HalDriverInit 函数完成按键初始化后,接着调用函数
void InitBoard( uint8 level ) |
完成按键的配置。
InitBoard函数中执行按键配置,按键配置代码如下:
/* Initialize Key stuff */
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE; //打开中断
//OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback); //调用HalKeyConfig做按键配置,将OnBoard_KeyCallback作为按键处理函数。
HalKeyConfig的作用是设置按键回调函数、配置按键对应IO口的中断。
HalKeyConfig其实还能给OSAL发消息,但是只有在HalKeyConfigured = TRUE的时候才能发消息。因为在初始化按键的时候把HalKeyConfigured 设置为了FALSE,所以第一次调用HalKeyConfig不会发OSAL消息。
按键运行
当OSAL开始运行后,按键的状态有三种:
- 未按下
- 按下的瞬间
- 按下还未弹起
1、未按下
未按下时,OSAL处于等待中断触发的状态中,以P0口的中断为例,P0对应的是SW6按键,中断函数是:
HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR ) |
2、按下的瞬间
当按键被按下,中断也被触发,这时候中断函数HAL_ISR_FUNCTION会调用按键中断处理函数
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。
然后,因为有keys!=0,并且开启了中断,需要将notify = 1。
最后,调用
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL); |
pHalKeyProcessFunction是全局变量,在InitBoard中,已经调用HalKeyConfig将其设置为了OnBoard_KeyCallback。
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发出按键消息。
至此,按键按下瞬间完成,接下来进入的是按下还未弹起的状态。
3、按下还未弹起
在上一个按键按下瞬间状态, 调用了
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。
现在反过来看看按下还未弹起状态下的的HalKeyPoll()做了什么:
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对按键做处理。
pHalKeyProcessFunction其实就是OnBoard_KeyCallback,这个在前面的按键按下瞬间中分析过。现在来看看按键弹起时,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 )
{
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
}
}
当key==0,并且中断关闭,这时候重新打开中断。等待下一次按键触发中断。
至此,整个按键过程执行了一轮。
还没有评论,来说两句吧...