apollo3 plus蓝牙篇(一)

  • 设置自定义MAC地址及蓝牙功率

首先定义宏AM_CUSTOM_BDADDR 

#ifndef AM_CUSTOM_BDADDR 
#definne AM_CUSTOM_BDADDR 
#endif

在hciDrvWrite函数中

#ifdef AM_CUSTOM_BDADDR
    if (type == HCI_CMD_TYPE)
    {
        uint16_t opcode;
        BYTES_TO_UINT16(opcode, pData);

        if (HCI_OPCODE_RESET == opcode)
        {

            extern uint8_t g_BLEMacAddress[6];
            am_hal_mcuctrl_device_t sDevice;
            am_hal_mcuctrl_info_get(AM_HAL_MCUCTRL_INFO_DEVICEID, &sDevice); // 获取MAC地址
					
            g_BLEMacAddress[0] = sDevice.ui32ChipID0;
            g_BLEMacAddress[1] = sDevice.ui32ChipID0 >> 8;
            g_BLEMacAddress[2] = sDevice.ui32ChipID0 >> 16;
					
	    g_BLEMacAddress[3] = sDevice.ui32ChipID0 ^ (sDevice.ui32ChipID0 >> 8);
            g_BLEMacAddress[4] = (sDevice.ui32ChipID0 >> 8) ^ (sDevice.ui32ChipID0 >> 16);
            g_BLEMacAddress[5] = ~(sDevice.ui32ChipID0 >> 16) ^ g_BLEMacAddress[4];

            HciVendorSpecificCmd(0xFC32, 6, g_BLEMacAddress);  // 设置MAC地址
        }
	 HciVsA3_SetRfPowerLevelEx(TX_POWER_LEVEL_PLUS_3P0_dBm); // 设置蓝牙功率
    }
#endif

配置事件处理句柄 

radio_task.c 文件中的 exactle_stack_init()函数用以初始化蓝牙协议栈相关内 容,在其中,除了需要配置 BLE 蓝牙本身基础服务与属性外,也配置了应用所需的消 息处理句柄: 

   handlerId = WsfOsSetNextHandler(FitHandler); 
   FitHandlerInit(handlerId); 
 
   handlerId = WsfOsSetNextHandler(HciDrvHandler); 
   HciDrvHandlerInit(handlerId); 

FitHandler 用以处理蓝牙任务信息交互; 
HciDrvHandler 用以处理芯片内部主核心与 BLE 模组信息交互; 

 配置服务与属性 

radio_task.c 文件中的 FitStart 函数用以注册用户服务以及属性. 

void FitStart(void) 
{ 
  /* Register for stack callbacks */ 
  DmRegister(fitDmCback); 
  DmConnRegister(DM_CLIENT_ID_APP, fitDmCback); 
  AttRegister(fitAttCback); 
  AttConnRegister(AppServerConnCback); 
  AttsCccRegister(FIT_NUM_CCC_IDX, (attsCccSet_t *) fitCccSet, fitCccCback); 
 
  /* Register for app framework callbacks */ 
  AppUiBtnRegister(fitBtnCback); 
 
  /* Initialize attribute server database */ 
  SvcCoreAddGroup(); 
  SvcHrsCbackRegister(NULL, HrpsWriteCback); 
  SvcHrsAddGroup(); 
  SvcDisAddGroup(); 
  SvcBattCbackRegister(BasReadCback, NULL); 
  SvcBattAddGroup(); 
  SvcRscsAddGroup(); 
 
  /* Set running speed and cadence features */ 
  RscpsSetFeatures(RSCS_ALL_FEATURES); 
 
  /* Reset the device */ 
  DmDevReset(); 
}

程 序 中 注 册 一 个 DM_CLIENT_ID_APP 为 用 户 自 定 义 服 务 , 注 册 一 个 AppServerConnCback 为该服务的回调函数,该服务下有 FIT_NUM_CCC_IDX 个属性, 属性的配置与回调函数分别为 fitCccSet 与 fitCccCback.

/************************************************************************************************** 
  Client Characteristic Configuration Descriptors 
**************************************************************************************************/ 
 
/*! enumeration of client characteristic configuration descriptors */ 
enum 
{ 
  FIT_GATT_SC_CCC_IDX,                    /*! GATT service, service changed characteristic */ 
  FIT_HRS_HRM_CCC_IDX,                    /*! Heart rate service, heart rate monitor characteristic */ 
  FIT_BATT_LVL_CCC_IDX,                   /*! Battery service, battery level characteristic */ 
  FIT_RSCS_SM_CCC_IDX,                   /*! Runninc speed and cadence measurement characteristic */ 
  FIT_NUM_CCC_IDX 
}; 
 
/*! client characteristic configuration descriptors settings, indexed by above enumeration */ 
static const attsCccSet_t fitCccSet[FIT_NUM_CCC_IDX] = 
{ 
  /* cccd handle          value range               security level */ 
  {GATT_SC_CH_CCC_HDL,    ATT_CLIENT_CFG_INDICATE,  DM_SEC_LEVEL_NONE},   /* FIT_GATT_SC_CCC_IDX */ 
  {HRS_HRM_CH_CCC_HDL,    ATT_CLIENT_CFG_NOTIFY,    DM_SEC_LEVEL_NONE},   /* FIT_HRS_HRM_CCC_IDX */ 
  {BATT_LVL_CH_CCC_HDL,   ATT_CLIENT_CFG_NOTIFY,    DM_SEC_LEVEL_NONE},   /* FIT_BATT_LVL_CCC_IDX */ 
  {RSCS_RSM_CH_CCC_HDL,   ATT_CLIENT_CFG_NOTIFY,    DM_SEC_LEVEL_NONE}    /* FIT_RSCS_SM_CCC_IDX */ 
}; 

属性配置分两部分,前一部分是每个属性配属一个专用索引,后一部分定义该属性 的句柄名,属性的权限范围以及安全级别. 

之后针对各个属性配置各自的 profile,如 Hrs 为心率,Batt 为电池,Rscs 为计步. 如果该属性需要有读写操作需要处理则注册读写的回调函数. 

 配置广播包内容 

广播包基础配置位于 fit_main.c 中,内容如下 

static const appAdvCfg_t fitAdvCfg = 
{ 
  {0,     0,     0},                  /*! Advertising durations in ms */ 
  {  800,     0,     0}                   /*! Advertising intervals in 0.625 ms units */ 
}; 

这里我们可以得到基础广播时间间隔为 800*0.625=500ms.对广播时长不限时. 

广播包内容: 

/************************************************************************************************** 
  Advertising Data 
**************************************************************************************************/ 
 
/*! advertising data, discoverable mode */ 
static const uint8_t fitAdvDataDisc[] = 
{ 
  /*! flags */ 
  2,                                      /*! length */ 
  DM_ADV_TYPE_FLAGS,                      /*! AD type */ 
  DM_FLAG_LE_GENERAL_DISC |               /*! flags */ 
   DM_FLAG_LE_BREDR_NOT_SUP, 
 
  /*! tx power */ 
  2,                                      /*! length */ 
  DM_ADV_TYPE_TX_POWER,                   /*! AD type */ 
  0,                                      /*! tx power */ 
 
  /*! service UUID list */ 
  9,                                      /*! length */ 
  DM_ADV_TYPE_16_UUID,                    /*! AD type */ 
  UINT16_TO_BYTES(ATT_UUID_HEART_RATE_SERVICE), 
  UINT16_TO_BYTES(ATT_UUID_RUNNING_SPEED_SERVICE), 
  UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE), 
  UINT16_TO_BYTES(ATT_UUID_BATTERY_SERVICE) 
}; 
 

可配置广播数据类型如下

#define DM_ADV_TYPE_FLAGS           0x01  /*!< \brief Flag bits */
#define DM_ADV_TYPE_16_UUID_PART    0x02  /*!< \brief Partial list of 16 bit UUIDs */
#define DM_ADV_TYPE_16_UUID         0x03  /*!< \brief Complete list of 16 bit UUIDs */
#define DM_ADV_TYPE_32_UUID_PART    0x04  /*!< \brief Partial list of 32 bit UUIDs */
#define DM_ADV_TYPE_32_UUID         0x05  /*!< \brief Complete list of 32 bit UUIDs */
#define DM_ADV_TYPE_128_UUID_PART   0x06  /*!< \brief Partial list of 128 bit UUIDs */
#define DM_ADV_TYPE_128_UUID        0x07  /*!< \brief Complete list of 128 bit UUIDs */
#define DM_ADV_TYPE_SHORT_NAME      0x08  /*!< \brief Shortened local name */
#define DM_ADV_TYPE_LOCAL_NAME      0x09  /*!< \brief Complete local name */
#define DM_ADV_TYPE_TX_POWER        0x0A  /*!< \brief TX power level */
#define DM_ADV_TYPE_SM_TK_VALUE     0x10  /*!< \brief Security manager TK value */
#define DM_ADV_TYPE_SM_OOB_FLAGS    0x11  /*!< \brief Security manager OOB flags */
#define DM_ADV_TYPE_CONN_INTERVAL   0x12  /*!< \brief Slave preferred connection interval */
#define DM_ADV_TYPE_SIGNED_DATA     0x13  /*!< \brief Signed data */
#define DM_ADV_TYPE_16_SOLICIT      0x14  /*!< \brief Service soliticiation list of 16 bit UUIDs */
#define DM_ADV_TYPE_128_SOLICIT     0x15  /*!< \brief Service soliticiation list of 128 bit UUIDs */
#define DM_ADV_TYPE_SERVICE_DATA    0x16  /*!< \brief Service data - 16-bit UUID */
#define DM_ADV_TYPE_PUBLIC_TARGET   0x17  /*!< \brief Public target address */
#define DM_ADV_TYPE_RANDOM_TARGET   0x18  /*!< \brief Random target address */
#define DM_ADV_TYPE_APPEARANCE      0x19  /*!< \brief Device appearance */
#define DM_ADV_TYPE_ADV_INTERVAL    0x1A  /*!< \brief Advertising interval */
#define DM_ADV_TYPE_BD_ADDR         0x1B  /*!< \brief LE Bluetooth device address */
#define DM_ADV_TYPE_ROLE            0x1C  /*!< \brief LE role */
#define DM_ADV_TYPE_32_SOLICIT      0x1F  /*!< \brief Service soliticiation list of 32 bit UUIDs */
#define DM_ADV_TYPE_SVC_DATA_32     0x20  /*!< \brief Service data - 32-bit UUID */
#define DM_ADV_TYPE_SVC_DATA_128    0x21  /*!< \brief Service data - 128-bit UUID */
#define DM_ADV_TYPE_LESC_CONFIRM    0x22  /*!< \brief LE Secure Connections confirm value */
#define DM_ADV_TYPE_LESC_RANDOM     0x23  /*!< \brief LE Secure Connections random value */
#define DM_ADV_TYPE_URI             0x24  /*!< \brief URI */
#define DM_ADV_TYPE_MANUFACTURER    0xFF  /*!< \brief Manufacturer specific data */

蓝牙广播名称

/*! scan data, discoverable mode */ 
static const uint8_t fitScanDataDisc[] = 
{ 
  /*! device name */ 
  4,                                      /*! length */ 
  DM_ADV_TYPE_LOCAL_NAME,                 /*! AD type */ 
  'F', 
  'i', 
  't' 
}; 

 广播包有两种.fitAdvDataDisc 为原始广播包,也即设备在广播状态下向外部广播 的内容.fitScanDataDisc 为搜索响应包,即当设备接收到一个搜索(scan)的请求, 即返回该数据包向搜索中的主机提供更多信息. 

广播包中包括多个字段,在数组中我们以分段形式作以区分,以下为其中一个字段: 

 /*! device name */ 
  4,                                      /*! length */ 
  DM_ADV_TYPE_LOCAL_NAME,                 /*! AD type */ 
  'F', 
  'i', 
  't' 

第一个字节为该字段长度,这里是4表示4个字节,注意整个数组的字节长度之和不 能超过 31 字节; 

第二个字节为该字段类型,DM_ADV_TYPE_LOCAL_NAME 表明这个字段是设备名; 
后面几个字节为字段内容,由于本字段定义为设备名,所以该设备名为”Fit” 

配置连结

Apollo3 可同时连接多个设备,在 fit_main.c 文件中做如下定义: 

/*! configurable parameters for slave */ 
static const appSlaveCfg_t fitSlaveCfg = 
{ 
  FIT_CONN_MAX,                           /*! Maximum connections */ 
}; 

配置为只连接一个设备.而连接配置如下: 

/*! configurable parameters for connection parameter update */ 
static const appUpdateCfg_t fitUpdateCfg = 
{ 
  3000,                                      /*! Connection idle period in ms before attempting 
                                              connection parameter update; set to zero to disable */ 
  48,                                    /*! Minimum connection interval in 1.25ms units */ 
  60,                                    /*! Maximum connection interval in 1.25ms units */ 
  4,                                      /*! Connection latency */ 
  600,                                    /*! Supervision timeout in 10ms units */ 
  5                                       /*! Number of update attempts before giving up */ 
}; 

配置为连接后间隔时间60(48*1.25)至75(60*1.25)毫秒之间.每4个心跳包后发一 个响应包.6(600*10)秒无心跳包超时断连. 

 消息处理 

程序中将所有发生的事件消息通过句柄传递给消息处理机制,例如: 
 WsfMsgSend(fitHandlerId, pMsg); 
之后在 fit_main.c 文件中的 fitProcMsg 函数做统一消息事件管理.

/*************************************************************************************************/ 
/*! 
 *  \fn     fitProcMsg 
 * 
 *  \brief  Process messages from the event handler. 
 * 
 *  \param  pMsg    Pointer to message. 
 * 
 *  \return None. 
 */ 
/*************************************************************************************************/ 
static void fitProcMsg(fitMsg_t *pMsg) 
{ 
  uint8_t uiEvent = APP_UI_NONE; 
 
  switch(pMsg->hdr.event) 
  { 
//定时上报计步结果 
    case FIT_RUNNING_TIMER_IND: 
      fitSendRunningSpeedMeasurement((dmConnId_t)pMsg->ccc.hdr.param); 
      break; 
//定时上报心率 
    case FIT_HR_TIMER_IND: 
      HrpsProcMsg(&pMsg->hdr); 
      break; 
//定时上报电池电量 
    case FIT_BATT_TIMER_IND: 
      BasProcMsg(&pMsg->hdr); 
      break; 
//属性信息上报 
    case ATTS_HANDLE_VALUE_CNF: 
      HrpsProcMsg(&pMsg->hdr); 
      BasProcMsg(&pMsg->hdr); 
      break; 
//属性信息交互 
    case ATTS_CCC_STATE_IND: 
      fitProcCccState(pMsg); 
      break; 
//包长配置更新 
    case ATT_MTU_UPDATE_IND: 
   APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu); 
 break; 
//设备完成复位(发送密钥) 
    case DM_RESET_CMPL_IND: 
      DmSecGenerateEccKeyReq(); 
      uiEvent = APP_UI_RESET_CMPL; 
      break; 
//设备开始广播 
    case DM_ADV_START_IND: 
      uiEvent = APP_UI_ADV_START; 
      break; 
//设备停止广播 
    case DM_ADV_STOP_IND: 
      uiEvent = APP_UI_ADV_STOP; 
      break; 
//设备建立连接 
    case DM_CONN_OPEN_IND: 
      HrpsProcMsg(&pMsg->hdr); 
      BasProcMsg(&pMsg->hdr); 
      // AppSlaveSecurityReq(1); 
      uiEvent = APP_UI_CONN_OPEN; 
      break; 
//设备关闭连接 
    case DM_CONN_CLOSE_IND: 
      fitClose(pMsg); 
      uiEvent = APP_UI_CONN_CLOSE; 
      break; 
//配对完成 
    case DM_SEC_PAIR_CMPL_IND: 
      uiEvent = APP_UI_SEC_PAIR_CMPL; 
      break; 
//配对失败 
    case DM_SEC_PAIR_FAIL_IND: 
      uiEvent = APP_UI_SEC_PAIR_FAIL; 
      break; 
//设备连接要求加密 
    case DM_SEC_ENCRYPT_IND: 
      uiEvent = APP_UI_SEC_ENCRYPT; 
      break; 
//加密失败 
    case DM_SEC_ENCRYPT_FAIL_IND: 
      uiEvent = APP_UI_SEC_ENCRYPT_FAIL; 
      break; 
//配对要求 PIN 或 OOB 数据 
    case DM_SEC_AUTH_REQ_IND: 
      AppHandlePasskey(&pMsg->dm.authReq); 
      break; 
//生成 ECC 密钥事件 
    case DM_SEC_ECC_KEY_IND: 
      fitSetup(pMsg); 
      DmSecSetEccKey(&pMsg->dm.eccMsg.data.key); 
      break; 
//加密比较 
    case DM_SEC_COMPARE_IND: 
      AppHandleNumericComparison(&pMsg->dm.cnfInd); 
      break; 
       
    default: 
      break; 
  } 
 
  if (uiEvent != APP_UI_NONE) 
  { 
    AppUiAction(uiEvent); 
  } 
} 

更多事件信息定义请参考 dm_api.h 文件

连接与断开 

连接建立返回 DM_CONN_OPEN_IND 事件. 
断开连接可以調用 app_main.c 文件中的 AppConnClose(dmConnId_t connId)函数. 

数据传输 

可以在属性配置好的回调函数中响应主机发出的数据请求,也可以主动向上位机要 求刷新带有 Notification 权限的属性内容,例如在 rscps_main.c 文件中的RscpsSendSpeedMeasurement 函数刷新计步数据调用如下命令发送参数: 
  /* Transmit notification */ 
  AttsHandleValueNtf(connId, RSCS_RSM_HDL, len, msg); 

 

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值