lilijin1995 发表于 2023-3-14 11:26

分享CH582的MultiCentral一主二从实现接收两蓝牙从机noti

本帖最后由 lilijin1995 于 2023-3-14 14:34 编辑

背景:
之前有使用过Multicentral还是之前的CH58x_BLE_LIB_V1.41版本,连接两个其他品牌蓝牙芯片FR8012HAQ从机,是可以直接接收到两个从机的notify的。现在两个从机都改成CH582了。然后连接之后,只收到了先连接的那个从机的noti,然后一看Multicentral,只实现了连接0的:如下图


一开始我也是想直接添加连接1的,但第一次添加的时候我应该是忘记调用了,然后才没有成功,我还特意找了WCH的FAE问了,是直接根据连接0的重写一遍就行。

软件说明:
添加连接1的处理之前,我们先来看看BLE Central是怎么启动的:
void Central_Init()
{
    centralTaskId = TMOS_ProcessEventRegister(Central_ProcessEvent);

    // Setup GAP
    GAP_SetParamValue(TGAP_DISC_SCAN, DEFAULT_SCAN_DURATION);
    GAP_SetParamValue(TGAP_CONN_EST_INT_MIN, DEFAULT_MIN_CONNECTION_INTERVAL);
    GAP_SetParamValue(TGAP_CONN_EST_INT_MAX, DEFAULT_MAX_CONNECTION_INTERVAL);
    GAP_SetParamValue(TGAP_CONN_EST_SUPERV_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);

    // Setup the GAP Bond Manager
    {
      uint32_t passkey = DEFAULT_PASSCODE;
      uint8_tpairMode = DEFAULT_PAIRING_MODE;
      uint8_tmitm = DEFAULT_MITM_MODE;
      uint8_tioCap = DEFAULT_IO_CAPABILITIES;
      uint8_tbonding = DEFAULT_BONDING_MODE;

      GAPBondMgr_SetParameter(GAPBOND_CENT_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey);
      GAPBondMgr_SetParameter(GAPBOND_CENT_PAIRING_MODE, sizeof(uint8_t), &pairMode);
      GAPBondMgr_SetParameter(GAPBOND_CENT_MITM_PROTECTION, sizeof(uint8_t), &mitm);
      GAPBondMgr_SetParameter(GAPBOND_CENT_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
      GAPBondMgr_SetParameter(GAPBOND_CENT_BONDING_ENABLED, sizeof(uint8_t), &bonding);
    }

    // Init Connection Item
    centralInitConnItem(centralTaskId, centralConnList);
    // Initialize GATT Client
    GATT_InitClient();
    // Register to receive incoming ATT Indications/Notifications
    GATT_RegisterForInd(centralTaskId);
    // Setup a delayed profile startup
    tmos_set_event(centralTaskId, START_DEVICE_EVT);
}wch的BLE协议实现好像都是基于TMOS的,在Central_Init这个我们看到最后一行代码这里tmos_set_event(centralTaskId, START_DEVICE_EVT)直接启动设备启动事件,我们直接看事件回调函数Central_ProcessEvent里面的START_DEVICE_EVTuint16_t Central_ProcessEvent(uint8_t task_id, uint16_t events)
{
    if(events & SYS_EVENT_MSG)
    {
      uint8_t *pMsg;

      if((pMsg = tmos_msg_receive(centralTaskId)) != NULL)
      {
            central_ProcessTMOSMsg((tmos_event_hdr_t *)pMsg);
            // Release the TMOS message
            tmos_msg_deallocate(pMsg);
      }
      // return unprocessed events
      return (events ^ SYS_EVENT_MSG);
    }

    if(events & START_DEVICE_EVT)
    {
      // Start the Device
      GAPRole_CentralStartDevice(centralTaskId, ¢ralBondCB, ¢ralRoleCB);
      return (events ^ START_DEVICE_EVT);
    }
}在GAPRole_CentralStartDevice(centralTaskId, ¢ralBondCB, ¢ralRoleCB)里面又实现了
绑定管理回调函数:
// Bond Manager Callbacks
static gapBondCBs_t centralBondCB = {
    centralPasscodeCB,
    centralPairStateCB
};以及GAP角色回调函数
// GAP Role Callbacks
static gapCentralRoleCB_t centralRoleCB = {
    centralRssiCB,      // RSSI callback
    centralEventCB,       // Event callback
    centralHciMTUChangeCB // MTU change callback
};centralEventCB这个事件回调函数里面全是GAP连接相关的事件的处理,GAP连接相关的事件可以CH58xBLE_LIB.h里面找到#define GAP_DEVICE_INIT_DONE_EVENT            0x00 //!< Sent when the Device Initialization is complete.This event is sent as an tmos message defined as gapDeviceInitDoneEvent_t.
#define GAP_DEVICE_DISCOVERY_EVENT            0x01 //!< Sent when the Device Discovery Process is complete. This event is sent as an tmos message defined as gapDevDiscEvent_t.
#define GAP_ADV_DATA_UPDATE_DONE_EVENT          0x02 //!< Sent when the Advertising Data or SCAN_RSP Data has been updated. This event is sent as an tmos message defined as gapAdvDataUpdateEvent_t.
#define GAP_MAKE_DISCOVERABLE_DONE_EVENT      0x03 //!< Sent when the Make Discoverable Request is complete. This event is sent as an tmos message defined as gapMakeDiscoverableRspEvent_t.
#define GAP_END_DISCOVERABLE_DONE_EVENT         0x04 //!< Sent when the Advertising has ended. This event is sent as an tmos message defined as gapEndDiscoverableRspEvent_t.
#define GAP_LINK_ESTABLISHED_EVENT            0x05 //!< Sent when the Establish Link Request is complete. This event is sent as an tmos message defined as gapEstLinkReqEvent_t.
#define GAP_LINK_TERMINATED_EVENT               0x06 //!< Sent when a connection was terminated. This event is sent as an tmos message defined as gapTerminateLinkEvent_t.
#define GAP_LINK_PARAM_UPDATE_EVENT             0x07 //!< Sent when an Update Parameters Event is received. This event is sent as an tmos message defined as gapLinkUpdateEvent_t.
我们在GAP_DEVICE_INFO_EVENT里面过滤一下"Simple Peripheral"广播的peripheral如下代码:
      case GAP_DEVICE_INFO_EVENT:
      {
            // Add device to list
            if(strstr(pEvent->deviceInfo.pEvtData,"Simple Peripheral")!=NULL)
            {
                PRINT("%s\r\n",pEvent->deviceInfo.pEvtData);
                centralAddDeviceInfo(pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType);

            }
//            // Add device to list
//            centralAddDeviceInfo(pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType);
      }这样MultiCentral就只添加Simple Peripheral了。
接着修改对端设备地址,根据MAC连接的,并且我们只需要2个外设设备,所以这里修改一下对端设备地址和连接数:
#ifndef CENTRAL_MAX_CONNECTION
#define CENTRAL_MAX_CONNECTION            2
#endif
// Peer device address
static peerAddrDefItem_t PeerAddrDef = {
    {0x3d, 0x61, 0x85, 0x26, 0x3b, 0x38 },
    {0x34, 0x53, 0x24, 0x7b, 0x54, 0x50 }
};
GAP_LINK_ESTABLISHED_EVENT是建立连接的事件,在这里wch已经实现了连接0的了,如下:                  //连接0
                  if(connItem == CONNECT0_ITEM)
                  {
                        centralConnList.procedureInProgress = TRUE;

                        // Initiate service discovery
                        tmos_start_task(centralConnList.taskID, START_SVC_DISCOVERY_EVT, DEFAULT_SVC_DISCOVERY_DELAY);

                        // Initiate connect parameter update
                        tmos_start_task(centralConnList.taskID, START_PARAM_UPDATE_EVT, DEFAULT_PARAM_UPDATE_DELAY);

                        // Start RSSI polling
                        tmos_start_task(centralConnList.taskID, START_READ_RSSI_EVT, DEFAULT_RSSI_PERIOD);
                  }这里直接复制连接0的即可:                  //连接1
                  else if(connItem == CONNECT1_ITEM)
                  {
                        centralConnList.procedureInProgress = TRUE;

                        // Initiate service discovery
                        tmos_start_task(centralConnList.taskID, START_SVC_DISCOVERY_EVT, DEFAULT_SVC_DISCOVERY_DELAY);

                        // Initiate connect parameter update
                        tmos_start_task(centralConnList.taskID, START_PARAM_UPDATE_EVT, DEFAULT_PARAM_UPDATE_DELAY);

                        // Start RSSI polling
                        tmos_start_task(centralConnList.taskID, START_READ_RSSI_EVT, DEFAULT_RSSI_PERIOD);
                  }我们可以看到,这里启动了发现服务,参数更新,RSSI任务,那我们对应的也需要修改一下。连接0的是connect0_ProcessEvent处理的,那么我们直接复制修改如下:
static uint16_t connect1_ProcessEvent(uint8_t task_id, uint16_t events)
{
    if(events & START_SVC_DISCOVERY_EVT)
    {
      // start service discovery
      centralConnIistStartDiscovery_1();
      return (events ^ START_SVC_DISCOVERY_EVT);
    }

    if(events & START_READ_OR_WRITE_EVT)
    {
      if(centralConnList.procedureInProgress == FALSE)
      {
            if(centralDoWrite)
            {
                // Do a write
                attWriteReq_t req;

                req.cmd = FALSE;
                req.sig = FALSE;
                req.handle = centralConnList.charHdl;
                req.len = 1;
                req.pValue = GATT_bm_alloc(centralConnList.connHandle, ATT_WRITE_REQ, req.len, NULL, 0);
                if(req.pValue != NULL)
                {
                  *req.pValue = centralCharVal;

                  if(GATT_WriteCharValue(centralConnList.connHandle, &req, centralTaskId) == SUCCESS)
                  {
                        centralConnList.procedureInProgress = TRUE;
                        centralDoWrite = !centralDoWrite;
                        tmos_start_task(centralConnList.taskID, START_READ_OR_WRITE_EVT, DEFAULT_READ_OR_WRITE_DELAY);
                  }
                  else
                  {
                        GATT_bm_free((gattMsg_t *)&req, ATT_WRITE_REQ);
                  }
                }
            }
            else
            {
                // Do a read
                attReadReq_t req;

                req.handle = centralConnList.charHdl;
                if(GATT_ReadCharValue(centralConnList.connHandle, &req, centralTaskId) == SUCCESS)
                {
                  centralConnList.procedureInProgress = TRUE;
                  centralDoWrite = !centralDoWrite;
                }
            }
      }
      return (events ^ START_READ_OR_WRITE_EVT);
    }

    if(events & START_PARAM_UPDATE_EVT)
    {
      // start connect parameter update
      GAPRole_UpdateLink(centralConnList.connHandle,
                           DEFAULT_UPDATE_MIN_CONN_INTERVAL,
                           DEFAULT_UPDATE_MAX_CONN_INTERVAL,
                           DEFAULT_UPDATE_SLAVE_LATENCY,
                           DEFAULT_UPDATE_CONN_TIMEOUT);

      return (events ^ START_PARAM_UPDATE_EVT);
    }

    if(events & START_WRITE_CCCD_EVT)
    {
      if(centralConnList.procedureInProgress == FALSE)
      {
            // Do a write
            attWriteReq_t req;

            req.cmd = FALSE;
            req.sig = FALSE;
            req.handle = centralConnList.cccHdl;
            req.len = 2;
            req.pValue = GATT_bm_alloc(centralConnList.connHandle, ATT_WRITE_REQ, req.len, NULL, 0);
            if(req.pValue != NULL)
            {
                req.pValue = 1;
                req.pValue = 0;

                if(GATT_WriteCharValue(centralConnList.connHandle, &req, centralTaskId) == SUCCESS)
                {
                  centralConnList.procedureInProgress = TRUE;
                }
                else
                {
                  GATT_bm_free((gattMsg_t *)&req, ATT_WRITE_REQ);
                }
            }
      }
      return (events ^ START_WRITE_CCCD_EVT);
    }

    if(events & START_READ_RSSI_EVT)
    {
      GAPRole_ReadRssiCmd(centralConnList.connHandle);
      tmos_start_task(centralConnList.taskID, START_READ_RSSI_EVT, DEFAULT_RSSI_PERIOD);
      return (events ^ START_READ_RSSI_EVT);
    }
    // Discard unknown events
    return 0;
}把CONNECT0_ITEM改成CONNECT1_ITEM,编译一下会发现centralConnIistStartDiscovery_1还没实现,这里也是复制粘贴修改
static void centralConnIistStartDiscovery_1(void)
{
    uint8_t uuid = {LO_UINT16(SIMPLEPROFILE_SERV_UUID),
                                    HI_UINT16(SIMPLEPROFILE_SERV_UUID)};

    // Initialize cached handles
    centralConnList.svcStartHdl = centralConnList.svcEndHdl = centralConnList.charHdl = 0;

    centralConnList.discState = BLE_DISC_STATE_SVC;

    // Discovery simple BLE service
    GATT_DiscPrimaryServiceByUUID(centralConnList.connHandle,
                                  uuid,
                                  ATT_BT_UUID_SIZE,
                                  centralTaskId);
}然后回到,Central_ProcessEvent添加调用
    // 连接1的任务处理
    else if(task_id == centralConnList.taskID)
    {
      return connect1_ProcessEvent(task_id, events);
    }
如果直接编译下载的话,估计跟我第一次修改实现的一样,还是只能接收先连接的,只有一个外设的noti,
因为我还差了一步:
/*********************************************************************
* @fn      centralGATTDiscoveryEvent
*
* @brief   Process GATT discovery event
*
* @returnnone
*/
static void centralGATTDiscoveryEvent(uint8_t connItem, gattMsgEvent_t *pMsg)
{
    attReadByTypeReq_t req;
    //连接0的枚举
    if(connItem == CONNECT0_ITEM)
    {
      if(centralConnList.discState == BLE_DISC_STATE_SVC)
      {
            // Service found, store handles
            if(pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
               pMsg->msg.findByTypeValueRsp.numInfo > 0)
            {
                centralConnList.svcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
                centralConnList.svcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);

                // Display Profile Service handle range
                PRINT("Found Profile Service handle : %x ~ %x \n", centralConnList.svcStartHdl, centralConnList.svcEndHdl);
            }
            // If procedure complete
            if((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
                pMsg->hdr.status == bleProcedureComplete) ||
               (pMsg->method == ATT_ERROR_RSP))
            {
                if(centralConnList.svcStartHdl != 0)
                {
                  // Discover characteristic
                  centralConnList.discState = BLE_DISC_STATE_CHAR;
                  req.startHandle = centralConnList.svcStartHdl;
                  req.endHandle = centralConnList.svcEndHdl;
                  req.type.len = ATT_BT_UUID_SIZE;
                  req.type.uuid = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);
                  req.type.uuid = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);

                  GATT_ReadUsingCharUUID(centralConnList.connHandle, &req, centralTaskId);
                }
            }
      }
      else if(centralConnList.discState == BLE_DISC_STATE_CHAR)
      {
            // Characteristic found, store handle
            if(pMsg->method == ATT_READ_BY_TYPE_RSP &&
               pMsg->msg.readByTypeRsp.numPairs > 0)
            {
                centralConnList.charHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList,
                                                               pMsg->msg.readByTypeRsp.pDataList);
                centralConnList.procedureInProgress = FALSE;

                // Start do read or write
                tmos_start_task(centralConnList.taskID, START_READ_OR_WRITE_EVT, DEFAULT_READ_OR_WRITE_DELAY);

                // Display Characteristic 1 handle
                PRINT("Found Characteristic 1 handle : %x \n", centralConnList.charHdl);
            }

            if((pMsg->method == ATT_READ_BY_TYPE_RSP &&
                pMsg->hdr.status == bleProcedureComplete) ||
                (pMsg->method == ATT_ERROR_RSP))
            {
                // Discover characteristic
                centralConnList.discState = BLE_DISC_STATE_CCCD;
                req.startHandle = centralConnList.svcStartHdl;
                req.endHandle = centralConnList.svcEndHdl;
                req.type.len = ATT_BT_UUID_SIZE;
                req.type.uuid = LO_UINT16(GATT_CLIENT_CHAR_CFG_UUID);
                req.type.uuid = HI_UINT16(GATT_CLIENT_CHAR_CFG_UUID);

                GATT_ReadUsingCharUUID(centralConnList.connHandle, &req, centralTaskId);
            }

      }
      else if(centralConnList.discState == BLE_DISC_STATE_CCCD)
      {
            // Characteristic found, store handle
            if(pMsg->method == ATT_READ_BY_TYPE_RSP &&
            pMsg->msg.readByTypeRsp.numPairs > 0)
            {
                centralConnList.cccHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList,
                                          pMsg->msg.readByTypeRsp.pDataList);

                centralConnList.procedureInProgress = FALSE;

                // Start do write CCCD
                tmos_start_task(centralConnList.taskID, START_WRITE_CCCD_EVT, DEFAULT_WRITE_CCCD_DELAY);

                // Display Characteristic 1 handle
                PRINT("Found client characteristic configuration handle : %x \n", centralConnList.cccHdl);
            }
            centralConnList.discState = BLE_DISC_STATE_IDLE;
      }
    }
    //连接1的枚举
    else if(connItem == CONNECT1_ITEM)
    {
      if(centralConnList.discState == BLE_DISC_STATE_SVC)
      {
            // Service found, store handles
            if(pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
               pMsg->msg.findByTypeValueRsp.numInfo > 0)
            {
                centralConnList.svcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
                centralConnList.svcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);

                // Display Profile Service handle range
                PRINT("Found Profile Service handle : %x ~ %x \n", centralConnList.svcStartHdl, centralConnList.svcEndHdl);
            }
            // If procedure complete
            if((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
                pMsg->hdr.status == bleProcedureComplete) ||
               (pMsg->method == ATT_ERROR_RSP))
            {
                if(centralConnList.svcStartHdl != 0)
                {
                  // Discover characteristic
                  centralConnList.discState = BLE_DISC_STATE_CHAR;
                  req.startHandle = centralConnList.svcStartHdl;
                  req.endHandle = centralConnList.svcEndHdl;
                  req.type.len = ATT_BT_UUID_SIZE;
                  req.type.uuid = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);
                  req.type.uuid = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);

                  GATT_ReadUsingCharUUID(centralConnList.connHandle, &req, centralTaskId);
                }
            }
      }
      else if(centralConnList.discState == BLE_DISC_STATE_CHAR)
      {
            // Characteristic found, store handle
            if(pMsg->method == ATT_READ_BY_TYPE_RSP &&
               pMsg->msg.readByTypeRsp.numPairs > 0)
            {
                centralConnList.charHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList,
                                                               pMsg->msg.readByTypeRsp.pDataList);
                centralConnList.procedureInProgress = FALSE;

                // Start do read or write
                tmos_start_task(centralConnList.taskID, START_READ_OR_WRITE_EVT, DEFAULT_READ_OR_WRITE_DELAY);

                // Display Characteristic 1 handle
                PRINT("Found Characteristic 1 handle : %x \n", centralConnList.charHdl);
            }

            if((pMsg->method == ATT_READ_BY_TYPE_RSP &&
                pMsg->hdr.status == bleProcedureComplete) ||
                (pMsg->method == ATT_ERROR_RSP))
            {
                // Discover characteristic
                centralConnList.discState = BLE_DISC_STATE_CCCD;
                req.startHandle = centralConnList.svcStartHdl;
                req.endHandle = centralConnList.svcEndHdl;
                req.type.len = ATT_BT_UUID_SIZE;
                req.type.uuid = LO_UINT16(GATT_CLIENT_CHAR_CFG_UUID);
                req.type.uuid = HI_UINT16(GATT_CLIENT_CHAR_CFG_UUID);

                GATT_ReadUsingCharUUID(centralConnList.connHandle, &req, centralTaskId);
            }

      }
      else if(centralConnList.discState == BLE_DISC_STATE_CCCD)
      {
            // Characteristic found, store handle
            if(pMsg->method == ATT_READ_BY_TYPE_RSP &&
            pMsg->msg.readByTypeRsp.numPairs > 0)
            {
                centralConnList.cccHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList,
                                          pMsg->msg.readByTypeRsp.pDataList);

                centralConnList.procedureInProgress = FALSE;

                // Start do write CCCD
                tmos_start_task(centralConnList.taskID, START_WRITE_CCCD_EVT, DEFAULT_WRITE_CCCD_DELAY);

                // Display Characteristic 1 handle
                PRINT("Found client characteristic configuration handle : %x \n", centralConnList.cccHdl);
            }
            centralConnList.discState = BLE_DISC_STATE_IDLE;
      }
    }
    //连接2的枚举
    else if(connItem == CONNECT2_ITEM)
    {
    }
}要在这个函数里面实现连接1的枚举。

最后可以看到multicentrl是有接收两个设备的noti


zhuotuzi 发表于 2023-3-19 17:37

学习了,受教了。

tpgf 发表于 2023-4-10 10:22

TMOS是沁恒微电子针对蓝牙协议栈开发的“操作系统”,是简化版的OSAL

qcliu 发表于 2023-4-10 11:14

要实现一个BLE应用,首先需要一个支持BLE射频的芯片,然后还需要提供一个与此芯片配套的BLE协议栈,最后在协议栈上开发自己的应用

drer 发表于 2023-4-10 11:24

TMOS是通过时间片轮询的方式实现多任务调度运行,实际上每次只有一个任务运行

coshi 发表于 2023-4-10 12:00

TMOS循环查询任务链表,根据任务ID确定优先级,越小越高

kxsi 发表于 2023-4-10 12:18

如果使用了ble,建议不要在单个任务中执行超过连接间隔一半时长的任务,否则将影响蓝牙通讯

wiba 发表于 2023-4-10 13:25

请注意 ,TMOS禁止在中断中调用任务调度函数
页: [1]
查看完整版本: 分享CH582的MultiCentral一主二从实现接收两蓝牙从机noti