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

背景:
之前有使用过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_t  pairMode = DEFAULT_PAIRING_MODE;

        uint8_t  mitm = DEFAULT_MITM_MODE;

        uint8_t  ioCap = DEFAULT_IO_CAPABILITIES;

        uint8_t  bonding = 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_EVT

uint16_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[CENTRAL_MAX_CONNECTION] = {

    {0x3d, 0x61, 0x85, 0x26, 0x3b, 0x38 },

    {0x34, 0x53, 0x24, 0x7b, 0x54, 0x50 }

};


GAP_LINK_ESTABLISHED_EVENT是建立连接的事件,在这里wch已经实现了连接0的了,如下:

                    //  连接0

                    if(connItem == CONNECT0_ITEM)

                    {

                        centralConnList[connItem].procedureInProgress = TRUE;



                        // Initiate service discovery

                        tmos_start_task(centralConnList[connItem].taskID, START_SVC_DISCOVERY_EVT, DEFAULT_SVC_DISCOVERY_DELAY);



                        // Initiate connect parameter update

                        tmos_start_task(centralConnList[connItem].taskID, START_PARAM_UPDATE_EVT, DEFAULT_PARAM_UPDATE_DELAY);



                        // Start RSSI polling

                        tmos_start_task(centralConnList[connItem].taskID, START_READ_RSSI_EVT, DEFAULT_RSSI_PERIOD);

                    }

这里直接复制连接0的即可:

                    //  连接1

                    else if(connItem == CONNECT1_ITEM)

                    {

                        centralConnList[connItem].procedureInProgress = TRUE;



                        // Initiate service discovery

                        tmos_start_task(centralConnList[connItem].taskID, START_SVC_DISCOVERY_EVT, DEFAULT_SVC_DISCOVERY_DELAY);



                        // Initiate connect parameter update

                        tmos_start_task(centralConnList[connItem].taskID, START_PARAM_UPDATE_EVT, DEFAULT_PARAM_UPDATE_DELAY);



                        // Start RSSI polling

                        tmos_start_task(centralConnList[connItem].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[CONNECT1_ITEM].procedureInProgress == FALSE)

        {

            if(centralDoWrite)

            {

                // Do a write

                attWriteReq_t req;



                req.cmd = FALSE;

                req.sig = FALSE;

                req.handle = centralConnList[CONNECT1_ITEM].charHdl;

                req.len = 1;

                req.pValue = GATT_bm_alloc(centralConnList[CONNECT1_ITEM].connHandle, ATT_WRITE_REQ, req.len, NULL, 0);

                if(req.pValue != NULL)

                {

                    *req.pValue = centralCharVal;



                    if(GATT_WriteCharValue(centralConnList[CONNECT1_ITEM].connHandle, &req, centralTaskId) == SUCCESS)

                    {

                        centralConnList[CONNECT1_ITEM].procedureInProgress = TRUE;

                        centralDoWrite = !centralDoWrite;

                        tmos_start_task(centralConnList[CONNECT1_ITEM].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[CONNECT1_ITEM].charHdl;

                if(GATT_ReadCharValue(centralConnList[CONNECT1_ITEM].connHandle, &req, centralTaskId) == SUCCESS)

                {

                    centralConnList[CONNECT1_ITEM].procedureInProgress = TRUE;

                    centralDoWrite = !centralDoWrite;

                }

            }

        }

        return (events ^ START_READ_OR_WRITE_EVT);

    }



    if(events & START_PARAM_UPDATE_EVT)

    {

        // start connect parameter update

        GAPRole_UpdateLink(centralConnList[CONNECT1_ITEM].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[CONNECT1_ITEM].procedureInProgress == FALSE)

        {

            // Do a write

            attWriteReq_t req;



            req.cmd = FALSE;

            req.sig = FALSE;

            req.handle = centralConnList[CONNECT1_ITEM].cccHdl;

            req.len = 2;

            req.pValue = GATT_bm_alloc(centralConnList[CONNECT1_ITEM].connHandle, ATT_WRITE_REQ, req.len, NULL, 0);

            if(req.pValue != NULL)

            {

                req.pValue[0] = 1;

                req.pValue[1] = 0;



                if(GATT_WriteCharValue(centralConnList[CONNECT1_ITEM].connHandle, &req, centralTaskId) == SUCCESS)

                {

                    centralConnList[CONNECT1_ITEM].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[CONNECT1_ITEM].connHandle);

        tmos_start_task(centralConnList[CONNECT1_ITEM].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[ATT_BT_UUID_SIZE] = {LO_UINT16(SIMPLEPROFILE_SERV_UUID),

                                      HI_UINT16(SIMPLEPROFILE_SERV_UUID)};



    // Initialize cached handles

    centralConnList[CONNECT1_ITEM].svcStartHdl = centralConnList[CONNECT1_ITEM].svcEndHdl = centralConnList[CONNECT1_ITEM].charHdl = 0;



    centralConnList[CONNECT1_ITEM].discState = BLE_DISC_STATE_SVC;



    // Discovery simple BLE service

    GATT_DiscPrimaryServiceByUUID(centralConnList[CONNECT1_ITEM].connHandle,

                                  uuid,

                                  ATT_BT_UUID_SIZE,

                                  centralTaskId);

}

然后回到,Central_ProcessEvent添加调用

    // 连接1的任务处理

    else if(task_id == centralConnList[CONNECT1_ITEM].taskID)

    {

        return connect1_ProcessEvent(task_id, events);

    }


如果直接编译下载的话,估计跟我第一次修改实现的一样,还是只能接收先连接的,只有一个外设的noti,
因为我还差了一步:

/*********************************************************************

 * @fn      centralGATTDiscoveryEvent

 *

 * [url=home.php?mod=space&uid=247401]@brief[/url]   Process GATT discovery event

 *

 * [url=home.php?mod=space&uid=266161]@return[/url]  none

 */

static void centralGATTDiscoveryEvent(uint8_t connItem, gattMsgEvent_t *pMsg)

{

    attReadByTypeReq_t req;

    //  连接0的枚举

    if(connItem == CONNECT0_ITEM)

    {

        if(centralConnList[connItem].discState == BLE_DISC_STATE_SVC)

        {

            // Service found, store handles

            if(pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&

               pMsg->msg.findByTypeValueRsp.numInfo > 0)

            {

                centralConnList[connItem].svcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);

                centralConnList[connItem].svcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);



                // Display Profile Service handle range

                PRINT("Found Profile Service handle : %x ~ %x \n", centralConnList[connItem].svcStartHdl, centralConnList[connItem].svcEndHdl);

            }

            // If procedure complete

            if((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&

                pMsg->hdr.status == bleProcedureComplete) ||

               (pMsg->method == ATT_ERROR_RSP))

            {

                if(centralConnList[connItem].svcStartHdl != 0)

                {

                    // Discover characteristic

                    centralConnList[connItem].discState = BLE_DISC_STATE_CHAR;

                    req.startHandle = centralConnList[connItem].svcStartHdl;

                    req.endHandle = centralConnList[connItem].svcEndHdl;

                    req.type.len = ATT_BT_UUID_SIZE;

                    req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);

                    req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);



                    GATT_ReadUsingCharUUID(centralConnList[connItem].connHandle, &req, centralTaskId);

                }

            }

        }

        else if(centralConnList[connItem].discState == BLE_DISC_STATE_CHAR)

        {

            // Characteristic found, store handle

            if(pMsg->method == ATT_READ_BY_TYPE_RSP &&

               pMsg->msg.readByTypeRsp.numPairs > 0)

            {

                centralConnList[connItem].charHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],

                                                                 pMsg->msg.readByTypeRsp.pDataList[1]);

                centralConnList[connItem].procedureInProgress = FALSE;



                // Start do read or write

                tmos_start_task(centralConnList[connItem].taskID, START_READ_OR_WRITE_EVT, DEFAULT_READ_OR_WRITE_DELAY);



                // Display Characteristic 1 handle

                PRINT("Found Characteristic 1 handle : %x \n", centralConnList[0].charHdl);

            }



            if((pMsg->method == ATT_READ_BY_TYPE_RSP &&

                pMsg->hdr.status == bleProcedureComplete) ||

                (pMsg->method == ATT_ERROR_RSP))

            {

                // Discover characteristic

                centralConnList[connItem].discState = BLE_DISC_STATE_CCCD;

                req.startHandle = centralConnList[connItem].svcStartHdl;

                req.endHandle = centralConnList[connItem].svcEndHdl;

                req.type.len = ATT_BT_UUID_SIZE;

                req.type.uuid[0] = LO_UINT16(GATT_CLIENT_CHAR_CFG_UUID);

                req.type.uuid[1] = HI_UINT16(GATT_CLIENT_CHAR_CFG_UUID);



                GATT_ReadUsingCharUUID(centralConnList[connItem].connHandle, &req, centralTaskId);

            }



        }

        else if(centralConnList[connItem].discState == BLE_DISC_STATE_CCCD)

        {

            // Characteristic found, store handle

            if(pMsg->method == ATT_READ_BY_TYPE_RSP &&

            pMsg->msg.readByTypeRsp.numPairs > 0)

            {

                centralConnList[connItem].cccHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],

                                            pMsg->msg.readByTypeRsp.pDataList[1]);



                centralConnList[connItem].procedureInProgress = FALSE;



                // Start do write CCCD

                tmos_start_task(centralConnList[connItem].taskID, START_WRITE_CCCD_EVT, DEFAULT_WRITE_CCCD_DELAY);



                // Display Characteristic 1 handle

                PRINT("Found client characteristic configuration handle : %x \n", centralConnList[connItem].cccHdl);

            }

            centralConnList[connItem].discState = BLE_DISC_STATE_IDLE;

        }

    }

    //  连接1的枚举

    else if(connItem == CONNECT1_ITEM)

    {

        if(centralConnList[connItem].discState == BLE_DISC_STATE_SVC)

        {

            // Service found, store handles

            if(pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&

               pMsg->msg.findByTypeValueRsp.numInfo > 0)

            {

                centralConnList[connItem].svcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);

                centralConnList[connItem].svcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);



                // Display Profile Service handle range

                PRINT("Found Profile Service handle : %x ~ %x \n", centralConnList[connItem].svcStartHdl, centralConnList[connItem].svcEndHdl);

            }

            // If procedure complete

            if((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&

                pMsg->hdr.status == bleProcedureComplete) ||

               (pMsg->method == ATT_ERROR_RSP))

            {

                if(centralConnList[connItem].svcStartHdl != 0)

                {

                    // Discover characteristic

                    centralConnList[connItem].discState = BLE_DISC_STATE_CHAR;

                    req.startHandle = centralConnList[connItem].svcStartHdl;

                    req.endHandle = centralConnList[connItem].svcEndHdl;

                    req.type.len = ATT_BT_UUID_SIZE;

                    req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);

                    req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);



                    GATT_ReadUsingCharUUID(centralConnList[connItem].connHandle, &req, centralTaskId);

                }

            }

        }

        else if(centralConnList[connItem].discState == BLE_DISC_STATE_CHAR)

        {

            // Characteristic found, store handle

            if(pMsg->method == ATT_READ_BY_TYPE_RSP &&

               pMsg->msg.readByTypeRsp.numPairs > 0)

            {

                centralConnList[connItem].charHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],

                                                                 pMsg->msg.readByTypeRsp.pDataList[1]);

                centralConnList[connItem].procedureInProgress = FALSE;



                // Start do read or write

                tmos_start_task(centralConnList[connItem].taskID, START_READ_OR_WRITE_EVT, DEFAULT_READ_OR_WRITE_DELAY);



                // Display Characteristic 1 handle

                PRINT("Found Characteristic 1 handle : %x \n", centralConnList[0].charHdl);

            }



            if((pMsg->method == ATT_READ_BY_TYPE_RSP &&

                pMsg->hdr.status == bleProcedureComplete) ||

                (pMsg->method == ATT_ERROR_RSP))

            {

                // Discover characteristic

                centralConnList[connItem].discState = BLE_DISC_STATE_CCCD;

                req.startHandle = centralConnList[connItem].svcStartHdl;

                req.endHandle = centralConnList[connItem].svcEndHdl;

                req.type.len = ATT_BT_UUID_SIZE;

                req.type.uuid[0] = LO_UINT16(GATT_CLIENT_CHAR_CFG_UUID);

                req.type.uuid[1] = HI_UINT16(GATT_CLIENT_CHAR_CFG_UUID);



                GATT_ReadUsingCharUUID(centralConnList[connItem].connHandle, &req, centralTaskId);

            }



        }

        else if(centralConnList[connItem].discState == BLE_DISC_STATE_CCCD)

        {

            // Characteristic found, store handle

            if(pMsg->method == ATT_READ_BY_TYPE_RSP &&

            pMsg->msg.readByTypeRsp.numPairs > 0)

            {

                centralConnList[connItem].cccHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],

                                            pMsg->msg.readByTypeRsp.pDataList[1]);



                centralConnList[connItem].procedureInProgress = FALSE;



                // Start do write CCCD

                tmos_start_task(centralConnList[connItem].taskID, START_WRITE_CCCD_EVT, DEFAULT_WRITE_CCCD_DELAY);



                // Display Characteristic 1 handle

                PRINT("Found client characteristic configuration handle : %x \n", centralConnList[connItem].cccHdl);

            }

            centralConnList[connItem].discState = BLE_DISC_STATE_IDLE;

        }

    }

    //  连接2的枚举

    else if(connItem == CONNECT2_ITEM)

    {

    }

}

要在这个函数里面实现连接1的枚举。

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

---------------------
作者:lilijin1995
链接:https://bbs.21ic.com/icview-3290008-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值