我是靠谱客的博主 不安野狼,最近开发中收集的这篇文章主要介绍CC2541 OSAL中按键处理过程全解析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

首先要区分,按键的处理和PROFILES中的按键服务是完全两个维度,两码事。simplekeys.c是一种profile,类似simpleGATTprofile.c。
本文关注协议栈中按键是怎么从底层单片机一路向上,送到应用层处理的。
1、顺着代码逻辑从头看起,涉及到按键的最早是在main函数中先调用了HalDriverInit函数,void HalDriverInit (void)调用了HalKeyInit();设置IO口功能和方向,然后将全局变量HalKeyConfigured = FALSE;
2、main函数的osal_init_system中调用了SimpleBLEPeripheral.c 的 SimpleBLEPeripheral_Init ,其中有
RegisterForKeys( simpleBLEPeripheral_TaskID );//其中仅仅做一件事,参数赋值给全局变量:registeredKeysTaskID = task_id;
3、接着还是main函数中一系列初始化的后半部分有:InitBoard( OB_READY ); 其中就干了两件事:先把全局变量OnboardKeyIntEnable赋值1;
4、然后InitBoard中调用HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
HalKeyConfig做了什么事呢?

赋值给一个全局变量: 	pHalKeyProcessFunction = cback;  也就是pHalKeyProcessFunction =OnBoard_KeyCallback后面会用到这个全局变量。
开启中断、设置IO口中断方式。
然后在if (Hal_KeyIntEnable)前提下:
 if (HalKeyConfigured == TRUE)//这里第一次进来,所以不走这个。
    {
      osal_stop_timerEx(Hal_TaskID, HAL_KEY_EVENT);  /* Cancel polling if active */
    }
最后 HalKeyConfigured = TRUE;

到这里就可以等单片机的中断信号了。

IAR中常用的中断函数定义方式为:

#pragma vector=T1_VECTOR	//(h文件中定义的中断向量) 
__interrupt void 中断服务程序名任意(void) 
{ 
//中断处理程序 
} 

2540协议栈类似,不过是加了宏定义绕了个弯:

HAL_ISR_FUNCTION( halKeyPort2Isr, P2INT_VECTOR )
{
  HAL_ENTER_ISR();
  if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT)
  {
    halProcessKeyInterrupt();
  }
  CLEAR_SLEEP_MODE();
  HAL_EXIT_ISR();
  return;
}

void halProcessKeyInterrupt (void)
{
  bool valid=FALSE;
  if (HAL_KEY_SW_2_PXIFG & HAL_KEY_SW_2_BIT)  /* Interrupt Flag has been set by SW2 */
  {
    HAL_KEY_SW_2_PXIFG = ~(HAL_KEY_SW_2_BIT); /* Clear Interrupt Flag */
    valid = TRUE;
  }
  if (valid)
  {
    osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);//开启定时器,消抖
  }
}
也就是给 tasksArr[]事件处理表中第二个任务,hal层,设置个事件,对应的处理函数就是Hal_ProcessEvent,osal操作系统检查到这里的时候就会直接调用这个函数了。

Hal_ProcessEvent( uint8 task_id, uint16 events )中有:

if (events & HAL_KEY_EVENT)
  {
    HalKeyPoll();//按键扫描!!!
  }
if (!Hal_KeyIntEnable) //* 如果没开中断,就定时扫描按键是否有键值。
  {
    osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
  }

5、HalKeyPoll()做了什么呢?
会通过 if (!(HAL_KEY_SW_1_PORT & HAL_KEY_SW_1_BIT)) 判断确定是哪个按键,然后触发回调函数 (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);也就是调用我们之前设置好的OnBoard_KeyCallback函数了,其中keys就是代表哪个按键。

void HalKeyPoll (void)
{
  uint8 keys = 0; uint8 notify = 0;
  if (!(HAL_KEY_SW_2_PORT & HAL_KEY_SW_2_BIT))    /* Key is active low */
  {
    keys |= HAL_KEY_SW_2;
  }
  /* 查询方式下的按键消抖判断,主线不用。难点,配合下面理解。 */
  if (!Hal_KeyIntEnable)
  {if (keys == halKeySavedKeys){ return;}   else{notify = 1; }}// keys=0也通知,此时正是查询过程中呢。keys=0说明按键松开了。
  else
  {//中断方式下,在中断服务函数中的定时器已经消抖,所以此时如果有键值,就代表有按键。
    /* Key interrupt handled here */
    if (keys)
    {
      notify = 1;
    }
  }
  halKeySavedKeys = keys;//保存按键值,给下次消抖用
  if (notify && (pHalKeyProcessFunction))
  {
    (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
  }
}

6、 OnBoard_KeyCallback中调用了OnBoard_SendKeys。这个是主线,请继续看7.
OnBoard_SendKey之后还调用了下面这些???这是难点。难以理解,然而并没什么用,不影响我们按照平常的方式使用按键。

/* 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 )
    {
      OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
      HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
      /* 其中有 osal_set_event(Hal_TaskID, HAL_KEY_EVENT);又来了一遍,又设置了HAL层的事件,又到了Hal_ProcessEvent中,又是以下这	一段:
if (events & HAL_KEY_EVENT)
  { HalKeyPoll();//这次是在if (!Hal_KeyIntEnable)条件下进入的。此时按键值和上次中断进入的时候一样则return,不一样则调用回调通知上层。作用:能让我们知道按键什么时候恢复了。中断那一步时按键按下通知应用层一个keys了。这次按键值变了,说明可能是松开了,把keys=0又通知一次应用层。
    if (!Hal_KeyIntEnable)
    {      osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);    }如果HalKeyPoll中return了,那就接着来,一直监督什么时候按键松开。
    */
    
    }//续上正文。
  }
  
  else//跟踪函数总结为:如果这时候按键没有了,keys==0了。就重新开中断,osal_stop_timerEx(Hal_TaskID, HAL_KEY_EVENT); 停那个100的延时事件。它是指查询到键值回零后、由查询方式转为中断方式时,停止定时器。
  {
    if( OnboardKeyIntEnable == HAL_KEY_INTERRUPT_DISABLE )
    {
      OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;
      HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
    }
  }

小结这个难点:
这段代码是实现中断方式和扫描方式互相切换。作用是检测键值是否松开:
1)当“硬件上的按键不按为高电平,按下为低电平”时:下降沿触发时我们可以知道何时
按键松开。
2)当“硬件上的按键不按为低电平,按下为高电平”时:上升沿触发时我们可以知道何时
按键松开。
假设举例:
中断方式检测按键按下、扫描方式检测按键松开。(按键松开会通知用户)
①按键 S1 按下
触发上升沿中断,发送键值“ 0x20”给应用层。
由中断触发方式切换成扫描方式。
②按键 S1 松开
扫描到引脚变化,则发送键值“ 0x00”给应用层。不过我们基本不会用、管这个。
由扫描方式切换回中断触发方式。这属于非主线内容了。

6、 OnBoard_SendKeys中有 osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );
通过 osal_msg_send 传递的消息, 都会到选中的任务下的默认事件下(此时也就是simpleBLEPeripheral_TaskID 任务的 SYS_EVENT_MSG 事件)。
发送消息给应用层,最终我们熟悉的应用层事件-》消息处理函数中就可以进行我们的处理了。registeredKeysTaskID在register的时候就设置好了我们的应用层任务。

说到底,按键中断的获取和标志位的复位是下层完成的,按键后产生什么样的效果则完全是应用层来进行处理。
应用层就是:

static void simpleBLEPeripheral_ProcessOSALMsg( osal_event_hdr_t *pMsg )
{
  switch ( pMsg->event )
  {
  #if defined( CC2540_MINIDK )
    case KEY_CHANGE:
      simpleBLEPeripheral_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );
      break;
 .....

simpleBLEPeripheral_HandleKeys函数中进行我们想要的操作,比如按键后改变是否允许广播的设置。

最后

以上就是不安野狼为你收集整理的CC2541 OSAL中按键处理过程全解析的全部内容,希望文章能够帮你解决CC2541 OSAL中按键处理过程全解析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部