OSAL的按键设计分析

痛定思痛。 2022-08-06 07:26 121阅读 0赞

转自:http://www.cnblogs.com/wsecurity/p/3259263.html

我们以按键的状态变化为线索,看看各个模块的作用,以及变量是怎么变化的。

按键设计总体上可以分为2部分,按键初始化和按键运行,按键初始化主要负责跟按键相关的IO端口设置、中断设置。按键运行就是OSAL检测按键的过程。

按键初始化

1、初始化按键

在main函数调用函数

  1. void HalDriverInit (void);

HalDriverInit 对HAL层(硬件抽象层)做初始化,并在其内部调用

  1. void HalKeyInit( void );

完成按键的初步初始化,按键初始化主要是把按键对应到IO口,并指定IO口的方向,并且把全局变量HalKeyConfigured设置为FALSE,这是因为虽然按键已经初始化了(指定了端口),但是还没有配置中断,也没有设置key的处理函数(用于向指定task发送key的消息)。

HalKeyInit函数对按键的初始化代码如下:

复制代码

  1. halKeySavedKeys = 0; // Initialize previous key to 0.
  2. HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT); /* Set pin function to GPIO */
  3. HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT); /* Set pin direction to Input */
  4. HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin function to GPIO */
  5. HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin direction to Input */
  6. P2INP |= PUSH2_BV; /* Configure GPIO tri-state. */
  7. /* Initialize callback function */
  8. pHalKeyProcessFunction = NULL;//key的处理函数还未设置,需要在HalKeyConfig函数中设置
  9. /* Start with key is not configured */
  10. HalKeyConfigured = FALSE;

复制代码

代码中将SW6按键绑定到了P0_1端口,P0是一个8bit的端口,P0_1是第二位。当读取P0口时,如果第二位为1,则为高电平,第二位为0,则为低电平。

2、配置按键

在main函数下调用HalDriverInit 函数完成按键初始化后,接着调用函数










void 
InitBoard( uint8 level )

完成按键的配置。

InitBoard函数中执行按键配置,按键配置代码如下:

  1. /* Initialize Key stuff */
  2. OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE; //打开中断
  3. //OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
  4. 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,所以进入按键处理分支:

ExpandedBlockStart.gif

复制代码

  1. uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )
  2. {
  3. .....//删略了其他HAL层收到的硬件消息,例如PERIOD_RSSI_RESET_EVT\HAL_LED_BLINK_EVENT等等
  4. if (events & HAL_KEY_EVENT) //进入按键处理分支
  5. {
  6. /* Check for keys */
  7. HalKeyPoll(); //读取按键的值
  8. /* if interrupt disabled, do next polling */
  9. if (!Hal_KeyIntEnable)
  10. {
  11. osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
  12. }
  13. return events ^ HAL_KEY_EVENT;
  14. }
  15. }

复制代码

Hal_ProcessEvent在按键处理代码中,先调用HalKeyPoll函数读取key的值,HalKeyPoll函数对SW6按键的处理的代码如下

ExpandedBlockStart.gif

复制代码

  1. void HalKeyPoll (void)
  2. {
  3. uint8 keys = 0;
  4. uint8 notify = 0;
  5. if (!(HAL_KEY_SW_6_PORT & HAL_KEY_SW_6_BIT)) /* Key is active low */
  6. {
  7. keys |= HAL_KEY_SW_6; //如果SW_6_PORT是低电平,则将keys的对应位设置为1
  8. }
  9. /* If interrupts are not enabled, previous key status and current key status
  10. * are compared to find out if a key has changed status.
  11. */
  12. if (!Hal_KeyIntEnable)
  13. {
  14. if (keys == halKeySavedKeys)
  15. {
  16. /* Exit - since no keys have changed */
  17. return;
  18. }
  19. else
  20. {
  21. notify = 1;
  22. }
  23. }
  24. else
  25. {
  26. /* Key interrupt handled here */
  27. if (keys)
  28. {
  29. notify = 1;
  30. }
  31. }
  32. /* Store the current keys for comparation next time */
  33. halKeySavedKeys = keys;
  34. /* Invoke Callback if new keys were depressed */
  35. if (notify && (pHalKeyProcessFunction))//pHalKeyProcessFunction实际上就是回调函数,在hal_drive中中使用
  36. {
  37. (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
  38. }
  39. }

复制代码

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函数如下:

ExpandedBlockStart.gif

复制代码

  1. void OnBoard_KeyCallback ( uint8 keys, uint8 state )
  2. {
  3. uint8 shift;
  4. (void)state;
  5. // shift key (S1) is used to generate key interrupt
  6. // applications should not use S1 when key interrupt is enabled
  7. shift = (OnboardKeyIntEnable == HAL_KEY_INTERRUPT_ENABLE) ? false : ((keys & HAL_KEY_SW_6) ? true : false);
  8. if ( OnBoard_SendKeys( keys, shift ) != SUCCESS )//向注册的task发送按键消息
  9. {
  10. //blabla
  11. }
  12. /* If any key is currently pressed down and interrupt
  13. is still enabled, disable interrupt and switch to polling */
  14. if( keys != 0 )
  15. {
  16. if( OnboardKeyIntEnable == HAL_KEY_INTERRUPT_ENABLE )//如果开启了中断,将其设置为不开中断,接下来就按照每100ms调用一次keypoll
  17. {
  18. OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
  19. HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
  20. }
  21. }
  22. /* If no key is currently pressed down and interrupt
  23. is disabled, enable interrupt and turn off polling */
  24. else
  25. {
  26. if( OnboardKeyIntEnable == HAL_KEY_INTERRUPT_DISABLE )//如果key==0,并且中断关闭,则重新打开终端。
  27. {
  28. OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;
  29. HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
  30. }
  31. }
  32. }

复制代码

OnBoard_KeyCallback主要做俩件事,调用OnBoard_SendKeys函数将按键发送给注册过的task,也就是我们自己开发的task。OnBoard_SendKeys代码如下:

复制代码

  1. uint8 OnBoard_SendKeys( uint8 keys, uint8 state )
  2. {
  3. ......
  4. osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );//把按键消息发给注册ID registeredKeysTaskID的task
  5. ......
  6. }

复制代码

OnBoard_KeyCallback做的第二件事是开关中断,当按键触发中断后,OnBoard_KeyCallback将中断关闭,这样在Hal_ProcessEvent函数中将每100ms调用一次HalKeyPoll对key做轮询,并且在按键按下的过程中不再次进入OnBoard_KeyCallback ,这样就不会再次向注册的task发出按键消息。

至此,按键按下瞬间完成,接下来进入的是按下还未弹起的状态。

3、按下还未弹起

在上一个按键按下瞬间状态, 调用了










HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);

关闭中断,此时会进入不配置中断的代码分支,如下:

复制代码

  1. else /* Interrupts NOT enabled */
  2. {
  3. // 关闭中断
  4. HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don't generate interrupt */
  5. HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT); /* Clear interrupt enable bit */
  6. //给HAL task发送HAL_KEY_EVENT,进入key polling
  7. osal_set_event(Hal_TaskID, HAL_KEY_EVENT);
  8. }

复制代码

接下来,由于给hal task发了消息,将进入hal的event处理函数

  1. uint16 Hal_ProcessEvent( uint8 task_id, uint16 events );

因为event=HAL_KEY_EVENT,所以再次进入HAL_KEY_EVENT的处理代码:

复制代码

  1. if (events & HAL_KEY_EVENT) //进入按键处理分支
  2. {
  3. /* Check for keys */
  4. HalKeyPoll();
  5. /* if interrupt disabled, do next polling */
  6. if (!Hal_KeyIntEnable)
  7. {
  8. osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
  9. }
  10. }

复制代码

跟前面处理中断不同的是,这次调用完HalKeyPoll()后,调用了:

  1. osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);

启动了定时器,在100ms后,重新给Hal_TaskID发送HAL_KEY_EVENT消息,重新进入polling。

现在反过来看看按下还未弹起状态下的的HalKeyPoll()做了什么:

ExpandedBlockStart.gif

复制代码

  1. if (!Hal_KeyIntEnable)
  2. {
  3. if (keys == halKeySavedKeys) //如果还是按下状态,则keys没有变化,此时不返回任何值。
  4. {
  5. /* Exit - since no keys have changed */
  6. return;
  7. }
  8. else
  9. {
  10. notify = 1; //如果按键弹起了,则将notify置1
  11. }
  12. }
  13. ....
  14. /* Store the current keys for comparation next time */
  15. halKeySavedKeys = keys;
  16. /* Invoke Callback if new keys were depressed */
  17. if (notify && (pHalKeyProcessFunction)) //如果还是按下状态,notify=0,此时不执行pHalKeyProcessFunction。
  18. { //如果按键弹起,则进入pHalKeyProcessFunction
  19. (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
  20. }

复制代码

如果按下状态没有改变,函数HalKeyPoll()不做任何事,当按键弹起,notify被置1,进入pHalKeyProcessFunction对按键做处理。

pHalKeyProcessFunction其实就是OnBoard_KeyCallback,这个在前面的按键按下瞬间中分析过。现在来看看按键弹起时,OnBoard_KeyCallback怎么处理的:

复制代码

  1. /* If no key is currently pressed down and interrupt
  2. is disabled, enable interrupt and turn off polling */
  3. else
  4. {
  5. if( OnboardKeyIntEnable == HAL_KEY_INTERRUPT_DISABLE )
  6. {
  7. OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;
  8. HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
  9. }
  10. }

复制代码

当key==0,并且中断关闭,这时候重新打开中断。等待下一次按键触发中断。

至此,整个按键过程执行了一轮。

发表评论

表情:
评论列表 (有 0 条评论,121人围观)

还没有评论,来说两句吧...

相关阅读