This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

【CC2541测评】CC2541迷你开发套件使用心得分享—基于套件的PPT播放器 by lyc830

Other Parts Discussed in Thread: CC2541
前言
上一篇分享是HidEmuKbd工程和BTool的联合调试,整个实验下来,对HidEmuKbd如何建立连接、如何处理按键消息都有了很好地理解。既然能够和BTool联合调试,那么配合USB Dongle就可以实现和PC的通信了。因此,在之前工作的基础上,进一步实现一个PPT播放器,进一步挖掘套件的潜力、熟悉官方提供的例程。
硬件准备
CC2541MINI开发套件全部成员,即:Keyfob+USB Dongle+CC Debugger
整体思路
官方例程和无线键盘、无线遥控相关的工程有3个,如下图所示;

本文的设计就是借助这3个例程完成的。
仅从工程名称就可以看出,HIDAdvRemote和HIDAdvRemoteDongle是一对配套工程,其中HIDAdvRemote是无线遥控器的工程,HIDAdvRemoteDongle是USB Dongle的工程。这对工程的使用可以参考文档《TI_CC2541_ARC_User_Guide》,如下图所示。

从图中可以看出,这里的遥控器和套件里的Keyfob,差距还是很大的。
在上一篇分享中我们可以知道,HidEmuKbd工程对应的硬件平台是Keyfob,而且已经可以成功发出按键消息。所以,只要做到USB Dongle能够接收并正确处理HidEmuKbd发出的消息,那么Keyfob上的两个按键就“摇身一变”,成了标准键盘上的按键。
经过以上分析,可想而知,想要完成我们的设计,最好的办法是将HidEmuKbd和HIDAdvRemoteDongle配合使用。可行的方案有以下两种:
方案一:由于HIDAdvRemote和HIDAdvRemoteDongle工程是配套的,所以两者的Profile是一致的,或者说HIDAdvRemoteDongle是根据HIDAdvRemote的Profile对接收的数据进行处理的。那么,可以将HIDAdvRemote使用的Profile拿过来给HidEmuKbd工程使用,这样的话,HIDAdvRemoteDongle就可以成功解析出HidEmuKbd发出的消息。(网络上可以找到具体操作步骤)
方案二:按照HidEmuKbd工程的Profile修改HIDAdvRemoteDongle数据处理相关内容,从而成功解析出有用数据。
方案比较:从难易程度上来看,方案一比方案二简单很多,照葫芦画瓢,很容易就能够实现想要的功能。我按照这个方法把功能实现之后,发现自己没有任何实质性的收获。所以,我又尝试了方案二,选择这个方案,就必须对HIDAdvRemoteDongle例程有比较好的理解才行。这次没有文档可以参考了,只能硬啃代码。
分析和解决问题过程
  • 不修改HidEmuKbd和HIDAdvRemoteDongle工程,是否能直接工作
    对两个例程不做修改,分别烧写入Keyfob和USB Dongle,按以下流程建立连接:
  • 按下USB Dongle的S2键,开始扫描。此时红色LED闪烁。
  • 按下Keyfob任意键,开始广播。连接建立成功后,USB Dongle上的LED会变绿。
  • 连接建立成功后,按下Keyfob任意一个按键,发现USB Dongle红色LED会闪烁,但没有实现相应功能。
    这就表明,消息已经成功发出,也能够成功接收,但是功能为什么没实现呢?调试看看吧。
  • 阅读源码,可以定位到相关函数为hidappSendInReport,其作用是将收到的HID report发出去。
  • 在hidappSendInReport函数中设置断点,可以发现程序会停在下图所示的位置
  • 我们发出的是key report,但是在Dongle端被解析成了mouse report,通过判断条件可以看出是report的句柄出了问题。
  • 到了这里,又有一个快捷的解决办法:把endPoint的值换成USB_HID_KBD_EP就可以了(我没有试,是论坛里很早的一个帖子提出的解决办法)。
  • 用上述快捷方法虽然达到了目的,但是并没有真正解决问题。现在问题变成了mouseCharHandle、keyCharHandle的值是从哪里来的?我在这里纠结了好久,找了很久不知道是怎么赋值的,还跑去论坛发了帖子提问,不过还是没有得到很好的答复,最终还是自己定位到了具体代码,解决了问题。获取Handle的大致流程如以下流程图所示(不代表程序具体运行过程,仅用于帮助理解程序)。
  • 定位到最终的代码位置如下,注意中文注释即可。
    case ATT_READ_BY_TYPE_RSP:
      // Response from Discover all Characteristics.
      // Success indicates packet with characteristic discoveries.
      if ( pPkt->hdr.status == SUCCESS )
      {
        attReadByTypeRsp_t *pRsp = &pPkt->msg.readByTypeRsp;
        uint8 idx = 0;

        if ( serviceToDiscover == GATT_SERVICE_UUID )
        {
          // We have discovered the GATT Service Characteristic handle
          serviceChangeHandle = BUILD_UINT16( pRsp->pDataList[3],
                                              pRsp->pDataList[4] );
          
          // Break to skip next part, it doesn't apply here
          break;
        }

        // Search characteristics for those with notification permissions.
        while( idx < ((pRsp->numPairs * pRsp->len) - 1) )
        {
          // Check permissions of characteristic for notification permission
          if ( (pRsp->pDataList[idx+2] & GATT_PROP_NOTIFY ) )
          {
/*            uint16* pHandle = (mouseCharHandle == GATT_INVALID_HANDLE) ? &mouseCharHandle : &keyCharHandle ;

            if ( pHandle == &keyCharHandle && consumerCtrlCharHandle == GATT_INVALID_HANDLE )
            {
              pHandle = ( keyCharHandle == GATT_INVALID_HANDLE ) ? &keyCharHandle : &consumerCtrlCharHandle;
            }
*/
            // 此处决定了3个handle的赋值顺序,按照HIDEmuKbd工程的Profile确定取handle的顺序
            // HIDEmuKbd工程没有mouse和cc report handle,为尽量少做更改,这里对赋值顺序进行了变更,而不是删除
//----------------------------------------------_ bgn of add by cuter ----------------------------------------------------------
// 2019.09.04
            uint16* pHandle = (keyCharHandle == GATT_INVALID_HANDLE) ? &keyCharHandle : &mouseCharHandle ;
            if ( pHandle == &mouseCharHandle && consumerCtrlCharHandle == GATT_INVALID_HANDLE )
            {
              pHandle = ( mouseCharHandle == GATT_INVALID_HANDLE ) ? &mouseCharHandle : &consumerCtrlCharHandle;
            }
//----------------------------------------------- end of add by cuter ----------------------------------------------------------
            
            if ( *pHandle == GATT_INVALID_HANDLE )
            {
              *pHandle = BUILD_UINT16( pRsp->pDataList[idx+3], pRsp->pDataList[idx+4] );//从返回消息中提取handle
            }
          }

          idx += pRsp->len;
        }
      }
      // This indicates that there is no more characteristic data
      // to be discovered within the given handle range.
      else if ( pPkt->hdr.status == bleProcedureComplete )
      {
        if ( serviceToDiscover == GATT_SERVICE_UUID )
        {
          // Begin Service Discovery of HID Service
          serviceToDiscover = HID_SERV_UUID;
          hidappDiscoverService( connHandle, HID_SERV_UUID );
          // Break to skip next part, it doesn't apply yet.
          break;
        }

        if ( enableCCCDs == TRUE )
        {
          mouseCCCHandle = mouseCharHandle + 1;

          // Begin configuring the characteristics for notifications
          hidappEnableNotification( connHandle, mouseCCCHandle );
        }
        else
        {
          serviceDiscComplete = TRUE;
        }
      }
      break;

测试视频
http://player.youku.com/embed/XNDM0MzQ4MDEyOA==&#39; frameborder=0 &#39;allowfullscreen
总结
整个学习过程中,对OSAL的消息处理机制、一些返回消息的数据结构、服务注册过程、Profile等方面有了更好地理解。基本上可以顺利地进行项目开发了。