之前在蓝牙技术群看到好多网友不知道按键流程到底是什么情况,平时也没时间,在群里也一两句说不明白,也就说了下可以去看下zigbee按键流程过程,其实都是相通的,现在特意发帖分享下,希望能起到一个抛砖引玉的作用。

在介绍蓝牙按键流程分析之前,我们需要了解一个概念,那就是就是OSAL。 什么是OSAL呢?
可能大伙对于OS是比较了解的,学了计算机的搞过OS的也基本接触过,简单来说就是一个操作系统抽象层,可以理解为运行在 CC2540 上的操作系统,说操作系统还不能算,TI的OSAL只实现了任务切换和消息机制。并且把协议栈的代码、硬件处理的代码,用户程序的代码等分别放到了 OSAL 层的不同任务处理函数中去了,各任务函数之间通过消息机制、同一个任务之间通过事件的的方式来通信。

什么是 EVENT 事件?
OSAL 为每个任务函数分配了一个 16 位的事件变量,每一位代表一个事件,最高位为 0x8000
表示为系统事件 SYS_EVENT_MSG。其余的15 位留给用户自定义需要的事件。通常事件由定时
器启动,比如一秒后我要点亮 LED2,这就需要发送一个点亮 LED2 的事件,然后等待定时器1s后溢出,于是启动点亮 LED2事件,事件会调用相应的hal 层API点亮LED2。

什么是 MSG 消息

MSG 是比 EVENT 事件更具体并且可以携带数据的一种通信方式,MSG 的标记是按数值,而不是按位。比如 0x01 和 0x02 是两个不同的消息,但对于事件 0x03 则是 0x01 事件和 0x02 事件的组合。MSG 收发使用 osal_msg_send()和 osal_msg_receive(); 当调用 osal_msg_send()发送一个 msg 的同时会在 EVENT 列表中触发一个 message ready event。 (请注意最后一句话,这句话点出了为什么按键时间的触发为何会导致系统事件也接受到了)

现在以 SimpleBLEPeripheral 为例说明按键流程

在 SimpleBLEPeripheral 任务初始化函数中有这样一条代码:

// Register for all key events - This app will handle all key events
  RegisterForKeys( simpleBLEPeripheral_TaskID );

这个函数来自 OnBoard.c 源文件中
/*********************************************************************
* Keyboard Register function
*
* The keyboard handler is setup to send all keyboard changes to
* one task (if a task is registered).
*
* If a task registers, it will get all the keys. You can change this
* to register for individual keys.
*********************************************************************/
uint8 RegisterForKeys( uint8 task_id )
{
  // Allow only the first task
  if ( registeredKeysTaskID == NO_TASK_ID )
  {
    registeredKeysTaskID = task_id;
    return ( true );
  }
  else
    return ( false );
}

向一个全局变量 registeredKeysTaskID中赋值自己的任务 ID,调用了这个函数就能成功注册按键服务,那这个全局变量在何时使用呢?
分析到这里,感觉有点迷糊了,我们可以从顶到下分析。任何一个程序都是从main函数开始的,这点我们要坚信。所以我们首先找到这个main函数
打开SimpleBLEPeripheral_Main.c文件可以看到/**************************************************************************************************
* @fn          main
*
* @brief       Start of application.
*
* @param       none
*
* @return      none
**************************************************************************************************
*/
int main(void)
{
  /* Initialize hardware */
  HAL_BOARD_INIT();

// Initialize board I/O
  InitBoard( OB_COLD );

/* Initialze the HAL driver */
  HalDriverInit();

/* Initialize NV system */
  osal_snv_init();

/* Initialize LL */

/* Initialize the operating system */
  osal_init_system();

/* Enable interrupts */
  HAL_ENABLE_INTERRUPTS();

// Final board initialization
  InitBoard( OB_READY );

#if defined ( POWER_SAVING )
    osal_pwrmgr_device( PWRMGR_BATTERY );
  #endif

/* Start OSAL */
  osal_start_system(); // No Return from here

return 0;
}
我们打开 InitBoard( OB_READY );可以看到如下代码
/*********************************************************************
* @fn      InitBoard()
* @brief   Initialize the CC2540DB Board Peripherals
* @param   level: COLD,WARM,READY
* @return  None
*/
void InitBoard( uint8 level )
{
  if ( level == OB_COLD )
  {
    // Interrupts off
    osal_int_disable( INTS_ALL );
    // Turn all LEDs off
    HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );
    // Check for Brown-Out reset
//    ChkReset();
  }
  else  // !OB_COLD
  {
    /* Initialize Key stuff */
    OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;
    //OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
     HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
  }
}

看到我上面标注的函数了吧?那个是一个按键回调服务注册函数,注册了一个 OnBoard_KeyCallback函数
HalKeyConfig 函数的实现:
将上述的回调函数的地址复制给了函数指针变量。通过跟踪发现该函数的指针变量在按键的轮询函数中调用了,如下图:
/**************************************************************************************************
* @fn      HalKeyPoll
*
* @brief   Called by hal_driver to poll the keys
*
* @param   None
*
* @return  None
**************************************************************************************************/
void HalKeyPoll (void)
{
  uint8 keys = 0;
  uint8 notify = 0;
#if defined (CC2540_MINIDK)
  if (!(HAL_KEY_SW_1_PORT & HAL_KEY_SW_1_BIT))    /* Key is active low */
  {
    keys |= HAL_KEY_SW_1;
  }
  if (!(HAL_KEY_SW_2_PORT & HAL_KEY_SW_2_BIT))    /* Key is active low */
  {
    keys |= HAL_KEY_SW_2;
  }
#else
  if (!(HAL_KEY_SW_6_PORT & HAL_KEY_SW_6_BIT))    /* Key is active low */
  {
    keys |= HAL_KEY_SW_6;
  }

if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT))  /* Key is active HIGH */
  {
    keys = halGetJoyKeyInput();
  }
#endif

/* 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) (keys, HAL_KEY_STATE_NORMAL);

}
}

在这里底层的按键查询函数调用一个函数指针,而非具体的函数,这样就将处理按键的接口留给了上层,上层应用中,叧需解析的函数指针传入的参数 1:keys 就知道是哪个按键被按下了。
我们再回到刚才的 OnBoard_KeyCallback 回调函数处,该回调函数代码如下:
/*********************************************************************
* @fn      OnBoard_KeyCallback
*
* @brief   Callback service for keys
*
* @param   keys  - keys that were pressed
*          state - shifted
*
* @return  void
*********************************************************************/
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  )  //就是这句话将按键消息上传到应用层去处理的
  {
    // 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 5
    {
    }
    // Process SW6 here
    if ( keys & HAL_KEY_SW_6 )  // Switch 6
    {
    }
  }

/* 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);
    }
  }
  /* 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);
    }
  }
}

进入按键消息发送函数中可以看到

/*********************************************************************
* @fn      OnBoard_SendKeys
*
* @brief   Send "Key Pressed" message to application.
*
* @param   keys  - keys that were pressed
*          state - shifted
*
* @return  status
*********************************************************************/
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 );
    }
    return ( SUCCESS );
  }
  else
    return ( FAILURE );
}

主要是将按键时间,状态和按键值打包到信息中最后通过 osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );发送到注册了按键服务的应用层去 ,最终用户按了哪个按键,如何响应该按键在系统事件 SYS_EVENT_MSG 中处理。疑问又来了,为什么通过 osal_msg_send 收发的消息会出现在 SYS_EVENT_MSG 中呢?这个疑问,就是刚才我说过的osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr )会产生一个 a message  ready event in the destination tasks event list.
/*********************************************************************
* @fn      osal_msg_send
*
* @brief
*
*    This function is called by a task to send a command message to
*    another task or processing element.  The sending_task field must
*    refer to a valid task, since the task ID will be used
*    for the response message.  This function will also set a message
*    ready event in the destination tasks event list.
*
*
* @param   uint8 destination_task - Send msg to Task ID
* @param   uint8 *msg_ptr - pointer to new message buffer
*
* @return  SUCCESS, INVALID_TASK, INVALID_MSG_POINTER
*/
uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
{
  return ( osal_msg_enqueue_push( destination_task, msg_ptr, FALSE ) );
}

/*********************************************************************
* @fn      simpleBLEPeripheral_ProcessOSALMsg
*
* @brief   Process an incoming task message.
*
* @param   pMsg - message to process
*
* @return  none
*/
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;
  #endif // #if defined( CC2540_MINIDK )

default:
    // do nothing
    break;
  }
}

在 SimpleBLEPeripheral 中,对按键的响应如下:joystick right(SW2)收发广播的开启和关闭

/*********************************************************************
* @fn      simpleBLEPeripheral_HandleKeys
*
* @brief   Handles all key events for this device.
*
* @param   shift - true if in shift/alt.
* @param   keys - bit field for key events. Valid entries:
*                 HAL_KEY_SW_2
*                 HAL_KEY_SW_1
*
* @return  none
*/
static void simpleBLEPeripheral_HandleKeys( uint8 shift, uint8 keys )
{
  uint8 SK_Keys = 0;

VOID shift;  // Intentionally unreferenced parameter

if ( keys & HAL_KEY_SW_1 )
  {
    SK_Keys |= SK_KEY_LEFT;
  }

if ( keys & HAL_KEY_SW_2 )
  {

SK_Keys |= SK_KEY_RIGHT;

// if device is not in a connection, pressing the right key should toggle
    // advertising on and off
    if( gapProfileState != GAPROLE_CONNECTED )
    {
      uint8 current_adv_enabled_status;
      uint8 new_adv_enabled_status;

//Find the current GAP advertisement status
      GAPRole_GetParameter( GAPROLE_ADVERT_ENABLED, &current_adv_enabled_status ); //这个函数主要就是获得广播的开启和关闭状态

if( current_adv_enabled_status == FALSE )
      {
        new_adv_enabled_status = TRUE;
      }
      else
      {
        new_adv_enabled_status = FALSE;
      }

//change the GAP advertisement status to opposite of current status
      GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &new_adv_enabled_status );
    }

}

// Set the value of the keys state to the Simple Keys Profile;
  // This will send out a notification of the keys state if enabled
  SK_SetParameter( SK_KEY_ATTR, sizeof ( uint8 ), &SK_Keys );
}
#endif // #if defined( CC2540_MINIDK )

现在按键基本分析完了,其实CC2540学习板子是默认开启广播,但是Keyfob就需要按键手动开启了。接下来我会继续分析协议栈的其他功能,如有不足之外,望补充~~~~~~~~~~(花了我几个小时写出来,蛋疼。写文章太费劲了)

转载自:http://bbs.eeworld.com.cn/forum.php?mod=viewthread&tid=428987

TI BLE协议栈 按键流程分析相关推荐

  1. TI-BLE 协议栈(CC2541)peripheral的执行流程分析

      BLE 技术是 Bluetooth SIG 规定的一套通信协议, 在协议变成具体的代码之前, 都只存在文档中, TI. Nordic. CSR 等厂商, 根据 SIG 发布的 BLE 技术协议, ...

  2. android 虚拟按键源码流程分析

    android 虚拟按键流程分析 今天来说说android 的虚拟按键的源码流程.大家都知道,android 系统的状态栏,虚拟按键,下拉菜单,以及通知显示,keyguard 锁屏都是在framewo ...

  3. 【转】TI蓝牙BLE 协议栈代码学习

    BLE就是低功率蓝牙.要着重了解两种设备: dual-mode双模设备:简单说就是向下兼容. single-mode单模设备:仅仅支持BLE. 关于开发主要讲的是单模设备,它可以只靠纽扣电池即可持续工 ...

  4. ble协议栈从零开始八(security manager 最细致分析上)

    SIG BLE MESH 视频 教程https://edu.csdn.net/course/detail/27321​​​​​​​ 一.前言 ble协议栈最难的一章来了,我尽自己的努力把这一章写好.安 ...

  5. 蓝牙BLE设备主机重启回连流程分析

    本文出自:<蓝牙BLE设备主机重启回连流程分析> 如果一个BLE设备已经与蓝牙中心设备连接上,那么当中心设备的断电重启,其依然会和配对过的BLE设备连接上,而不需要重新走配对的流程,这个过 ...

  6. Linux网络协议栈:NAPI机制与处理流程分析(图解)

    Table of Contents NAPI机制 NAPI缺陷 使用 NAPI 先决条件 非NAPI帧的接收 netif_rx - 将网卡中收到的数据包放到系统中的接收队列中 enqueue_to_b ...

  7. 按键,触摸屏流程分析

    按键触摸屏流程分析: WindowManagerService类的构造函数 WindowManagerService()   mQueue = new KeyQ(); 因为 WindowManager ...

  8. NORDIC Thingy:52 蓝牙 BLE 服务 SoC 程序调用流程分析之八, 网盘分享 PPT

    NORDIC Thingy:52 蓝牙 BLE 服务 SoC 程序调用流程分析之八 网盘分享 PPT https://pan.baidu.com/s/1BuhqsBwQIz0Zlwzy7u7SQw

  9. Android 4.0按键事件以及系统流程分析

    Android 4.0中按键的处理流程 按键在Android系统中,有着不同的代表意义.以前的全键盘的手机代码没有阅读过,所以也不是很了解.本人介绍的是在触摸屏的手机上的按键消息的处理流程. 在现在触 ...

最新文章

  1. python name is not defined_PythonNameError: global name 'NAME' is not not defined这个问题怎么解决?...
  2. 安装fiddler做代理,本地开发手机端看效果
  3. 经历就是财富——总结我的26岁
  4. 对象存储 OSS > 产品简介 > 什么是对象存储OSS
  5. nginx 反向代理时丢失端口的解决方案(转)
  6. Python pip 命令详解
  7. Spring Boot不指定包路径就可以扫描启动类所在包及其子包下的类是怎么做到的?
  8. 三大框架 —— 持久层框架MyBatis
  9. 回到十七世纪,让我来编算一本常用对数表(对数结果是小数,要有分数的思想,指数的幂是分数,这样可以实现小数分数转化位整数的幂的求法)
  10. 为什么Dell官方声卡驱动安装不上的原因分析与解决?
  11. 数据结构与算法(java):线性表(链表-双向链表)
  12. 微信小程序外卖平台,免费下载小程序外卖源代码
  13. 史上最强模型 GPT-4 上线:一张手绘草图能生一个网站、60 秒搞定一个游戏开发!
  14. Java中找朋友的代码_找朋友游戏介绍
  15. 付呗聚合支付快速教程——导读
  16. vue移动端用什么数据可视化插件_前端必看的数据可视化入门指南
  17. TCP3次握手为啥挥手却要4次,这下解释明白了
  18. responseBean
  19. Ubuntu 下最好用的pdf阅读器okular
  20. android 省市区三级联动选择器

热门文章

  1. 提高你的Java代码质量吧:推荐在复杂字符串操作中使用正则表达式
  2. RedHat Enterprise LInux 6.3 安装Oracle Database 11g
  3. 【原创】A进程窗口嵌入到B进程窗口中显示
  4. 阶段-关口系统---Stage0发现阶段
  5. 【转】NIO的定义和原理是什么?
  6. mongoDB初识一二三
  7. 微信小程序朋友圈分享图片生成方案实现
  8. Unity FixedUpdate 与 Update 的线程关系实验
  9. lintcode:删除链表中指定元素
  10. rails.vim环境安装(ubuntu)