一.概述

1.工作逻辑

蓝牙从机例程(Peripheral)的大致工作逻辑是:

1.初始化开启广播进入广播态等待蓝牙主机的发起连接;;

2.被蓝牙主机连接后,开启了三个tmos任务,第一个任务是周期性的给主机上报数据,第二个任务是交互连接间隔,第三个任务是周期性的打印输出信号强度;

3.被主机断开连接后,关闭两个周期性的任务,开启广播等待再次被连接。

二.详细讲解

1.蓝牙初始化讲解

void Peripheral_Init()
{
    Peripheral_TaskID = TMOS_ProcessEventRegister(Peripheral_ProcessEvent);

    {
        uint8_t  initial_advertising_enable = TRUE;
        uint16_t desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
        uint16_t desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;

        GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);  //开启广播
        GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);               //设置扫描应答包内容
        GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);                   //设置广播包内容
        GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &desired_min_interval);    //设置连接间隔最小值
        GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &desired_max_interval);    //设置连接间隔最大值
    }

    {
        uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;

        GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, advInt);       //设置广播间隔最小值
        GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, advInt);       //设置广播间隔最小值

        GAP_SetParamValue(TGAP_ADV_SCAN_REQ_NOTIFY, ENABLE);    //开启扫描请求通知
    }

    {
        uint32_t passkey = 0;
        uint8_t  pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
        uint8_t  mitm = TRUE;
        uint8_t  bonding = TRUE;
        uint8_t  ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
        GAPBondMgr_SetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey);    //设置初始配对密码,只可以是6个纯数字,若前面有0则设置为1234对应密码001234,默认000000
        GAPBondMgr_SetParameter(GAPBOND_PERI_PAIRING_MODE, sizeof(uint8_t), &pairMode);        //设置配对模式为等待对方请求
        GAPBondMgr_SetParameter(GAPBOND_PERI_MITM_PROTECTION, sizeof(uint8_t), &mitm);         //开启mitm
        GAPBondMgr_SetParameter(GAPBOND_PERI_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);        //设置I/O能力为仅显示设备
        GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(uint8_t), &bonding);      //开启绑定
    }


    GGS_AddService(GATT_ALL_SERVICES);                //先添加 GAP 和 GATT 服务
    GATTServApp_AddService(GATT_ALL_SERVICES);        //添加 GATT 服务器应用服务
    DevInfo_AddService();                             //添加设备信息服务
    SimpleProfile_AddService(GATT_ALL_SERVICES);      //添加用户自定义服务


    GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);  //设置设备名称(非广播名称),将以与广播名称保持一直,ios设备连接一次之后下次搜索会使用该名称

    {
        uint8_t charValue1[SIMPLEPROFILE_CHAR1_LEN] = {1};
        uint8_t charValue2[SIMPLEPROFILE_CHAR2_LEN] = {2};
        uint8_t charValue3[SIMPLEPROFILE_CHAR3_LEN] = {3};
        uint8_t charValue4[SIMPLEPROFILE_CHAR4_LEN] = {4};
        uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = {1, 2, 3, 4, 5};

        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, SIMPLEPROFILE_CHAR1_LEN, charValue1);    //设置char1的值,使用READ功能时读取此函数设置的值
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, SIMPLEPROFILE_CHAR2_LEN, charValue2);    //设置char2的值,使用READ功能时读取此函数设置的值
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, SIMPLEPROFILE_CHAR3_LEN, charValue3);    //设置char3的值,使用READ功能时读取此函数设置的值
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, SIMPLEPROFILE_CHAR4_LEN, charValue4);    //设置char4的值,使用READ功能时读取此函数设置的值
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5);    //设置char5的值,使用READ功能时读取此函数设置的值
    }

    peripheralInitConnItem(&peripheralConnList);                      //初始化连接参数

    SimpleProfile_RegisterAppCBs(&Peripheral_SimpleProfileCBs);       //注册GATT回调函数用于接收write数据

    GAPRole_BroadcasterSetCB(&Broadcaster_BroadcasterCBs);            //注册广播回调-用于接收扫描请求的回调

    tmos_set_event(Peripheral_TaskID, SBP_START_DEVICE_EVT);          //开启设备
}

 2.蓝牙状态回调讲解

static void simpleProfileChangeCB(uint8_t paramID, uint8_t *pValue, uint16_t len)
{
    switch(paramID)
    {
        case SIMPLEPROFILE_CHAR1:                        //收到char1发送的数据
        {
            uint8_t newValue[SIMPLEPROFILE_CHAR1_LEN];
            tmos_memcpy(newValue, pValue, len);
            PRINT("profile ChangeCB CHAR1.. \n");
            break;
        }

        case SIMPLEPROFILE_CHAR3:                        //收到char3发送的数据
        {
            uint8_t newValue[SIMPLEPROFILE_CHAR3_LEN];
            tmos_memcpy(newValue, pValue, len);
            PRINT("profile ChangeCB CHAR3..\n");
            break;
        }
/*可以在此处添加notify使能和关闭的case分支
1.首先需添加宏:
    #define CHAR4_NOTIFY_ENABLE         5           //CHAR4通知开启
    #define CHAR4_NOTIFY_DISENABLE      6           //CHAR4通知关闭
2.其次写回调中case GATT_CLIENT_CHAR_CFG_UUID做如下改动:
    case GATT_CLIENT_CHAR_CFG_UUID:{
    uint16_t charCfg = BUILD_UINT16(pValue[0], pValue[1]);
    status = GATTServApp_ProcessCCCWriteReq(connHandle, pAttr, pValue, len,
                                        offset, GATT_CLIENT_CFG_NOTIFY);
    notifyApp = (charCfg == GATT_CFG_NO_OPERATION) ? CHAR4_NOTIFY_DISENABLE : CHAR4_NOTIFY_ENABLE;
    break;
}*/
        case CHAR4_NOTIFY_ENABLE:                    //CHAR4通知开启
        {
            PRINT("CHAR4_NOTIFY_ENABLE.. \n");
            break;
        }

        case CHAR4_NOTIFY_DISENABLE:                 //CHAR4通知关闭        
        {
            PRINT("CHAR4_NOTIFY_DISENABLE..\n");
            break;
        }


        default:
            // should not reach here!
            break;
    }
}

3.蓝牙连接处理讲解

static void Peripheral_LinkEstablished(gapRoleEvent_t *pEvent)
{
    gapEstLinkReqEvent_t *event = (gapEstLinkReqEvent_t *)pEvent;


    if(peripheralConnList.connHandle != GAP_CONNHANDLE_INIT)         //通过连接句柄查看当前是否已被连接
    {
        GAPRole_TerminateLink(pEvent->linkCmpl.connectionHandle);    //若已连接断开此连接
        PRINT("Connection max...\n");
    }
    else
    {
        peripheralConnList.connHandle = event->connectionHandle;     //保存连接句柄值
        peripheralConnList.connInterval = event->connInterval;       //保存连接间隔值
        peripheralConnList.connSlaveLatency = event->connLatency;    //保存连接接收延迟值
        peripheralConnList.connTimeout = event->connTimeout;         //保存连接超时时间值

        /*可以新增一些例程中未展示的参数 ,如主机设备的mac地址和地址类型
        typedef struct
{
    tmos_event_hdr_t hdr;        //AP_MSG_EVENT and status
    uint8_t opcode;              //操作码,固定为 GAP_LINK_ESTABLISHED_EVENT(值为 0x05),标识连接已建立。
    uint8_t devAddrType;         //设备地址类型: @ref GAP_ADDR_TYPE_DEFINES
    uint8_t devAddr[B_ADDR_LEN]; //设备地址
    uint16_t connectionHandle;   //连接句柄
    uint8_t connRole;            //本地设备在此连接中的角色:- GAP_CENTRAL_ROLE(0x00):中央设备(主动发起连接)- GAP_PERIPHERAL_ROLE(0x01):外围设备(被动接受连接)
    uint16_t connInterval;       //连接间隔
    uint16_t connLatency;        //连接接收延迟
    uint16_t connTimeout;        //连接超时时间
    uint8_t clockAccuracy;       //时钟精度,反映设备时钟的准确性,单位为百万分之一(ppm)。用于同步连接双方的通信时机。
} gapEstLinkReqEvent_t;
       */

        peripheralMTU = ATT_MTU_SIZE;                                //保存mtu初始值
        // Set timer for periodic event
        tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD);      //开启任务给主机发送数据

        // Set timer for param update event
        tmos_start_task(Peripheral_TaskID, SBP_PARAM_UPDATE_EVT, SBP_PARAM_UPDATE_DELAY);   //开启期任务和主机交互连接参数

        // Start read rssi
        tmos_start_task(Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD);    //开启任务获取信号强度

        PRINT("Conn %x - Int %x \n", event->connectionHandle, event->connInterval);         //打印输出连接句柄和连接间隔
    }
}

4.蓝牙断开连接处理讲解

static void Peripheral_LinkTerminated(gapRoleEvent_t *pEvent)
{
    gapTerminateLinkEvent_t *event = (gapTerminateLinkEvent_t *)pEvent;

    if(event->connectionHandle == peripheralConnList.connHandle)    //查询是否是当前连接句柄
    {
        peripheralConnList.connHandle = GAP_CONNHANDLE_INIT;        //将连接句柄设置为默认值
        peripheralConnList.connInterval = 0;                        //将连接句柄设置为0
        peripheralConnList.connSlaveLatency = 0;                    //将连接句柄设置为0
        peripheralConnList.connTimeout = 0;                         //将连接句柄设置为0
        tmos_stop_task(Peripheral_TaskID, SBP_PERIODIC_EVT);        //停止周期发送事件
        tmos_stop_task(Peripheral_TaskID, SBP_READ_RSSI_EVT);       //停止周期获取信号强度事件

        {
            uint8_t advertising_enable = TRUE;
            GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertising_enable);   //重新开启广播
        }
    }
    else
    {
        PRINT("ERR..\n");                                           //句柄不正确
    }
}

5.蓝牙发送数据介绍讲解

1.
    if(events & SBP_PERIODIC_EVT)
    {
        if(SBP_PERIODIC_EVT_PERIOD)
        {
            tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD);   //在事件执行中再次调用该事件形成周期循环
        }
        performPeriodicTask();      //执行周期通知
        return (events ^ SBP_PERIODIC_EVT);
    }
2.
static void performPeriodicTask(void)
{
    uint8_t notiData[SIMPLEPROFILE_CHAR4_LEN] = {0x88};           //设置发送数据
    peripheralChar4Notify(notiData, SIMPLEPROFILE_CHAR4_LEN);     //参入数据和长度,调用发送函数
}
3.
static void peripheralChar4Notify(uint8_t *pValue, uint16_t len)
{
    attHandleValueNoti_t noti;
    if(len > (peripheralMTU - 3))       //判断长度是否大于mtu
    {
        PRINT("Too large noti\n");
        return;
    }
    noti.len = len;
    noti.pValue = GATT_bm_alloc(peripheralConnList.connHandle, ATT_HANDLE_VALUE_NOTI, noti.len, NULL, 0);   //申请内存
    if(noti.pValue)
    {
        tmos_memcpy(noti.pValue, pValue, noti.len);                                                         //拷贝数据到申请的内存
        if(simpleProfile_Notify(peripheralConnList.connHandle, &noti) != SUCCESS)                           //判断是否发送成功,成功后底层自动释放申请的内存
        {
            GATT_bm_free((gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI);                                        //发送失败收到释放内存
        }
    }
}
4.
bStatus_t simpleProfile_Notify(uint16_t connHandle, attHandleValueNoti_t *pNoti)
{
    uint16_t value = GATTServApp_ReadCharCfg(connHandle, simpleProfileChar4Config);

    if(value & GATT_CLIENT_CFG_NOTIFY)       //判断notifications是都被使能
    {
        pNoti->handle = simpleProfileAttrTbl[SIMPLEPROFILE_CHAR4_VALUE_POS].handle;  //传入notify属性表value对应的handle值
        return GATT_Notification(connHandle, pNoti, FALSE);                          //返回发送函数的值
    }
    return bleIncorrectMode;                                                         //notifications未使能返回失败
}

6.蓝牙接收数据介绍讲解

static void simpleProfileChangeCB(uint8_t paramID, uint8_t *pValue, uint16_t len)
{
    switch(paramID)
    {
        case SIMPLEPROFILE_CHAR1:                        //收到char1发送的数据
        {
            uint8_t newValue[SIMPLEPROFILE_CHAR1_LEN];
            tmos_memcpy(newValue, pValue, len);
            PRINT("profile ChangeCB CHAR1.. \n");
            break;
        }

        case SIMPLEPROFILE_CHAR3:                        //收到char3发送的数据
        {
            uint8_t newValue[SIMPLEPROFILE_CHAR3_LEN];
            tmos_memcpy(newValue, pValue, len);
            PRINT("profile ChangeCB CHAR3..\n");
            break;
        }
/*可以在此处添加notify使能和关闭的case分支
1.首先需添加宏:
    #define CHAR4_NOTIFY_ENABLE         5           //CHAR4通知开启
    #define CHAR4_NOTIFY_DISENABLE      6           //CHAR4通知关闭
2.其次写回调中case GATT_CLIENT_CHAR_CFG_UUID做如下改动:
    case GATT_CLIENT_CHAR_CFG_UUID:{
    uint16_t charCfg = BUILD_UINT16(pValue[0], pValue[1]);
    status = GATTServApp_ProcessCCCWriteReq(connHandle, pAttr, pValue, len,
                                        offset, GATT_CLIENT_CFG_NOTIFY);
    notifyApp = (charCfg == GATT_CFG_NO_OPERATION) ? CHAR4_NOTIFY_DISENABLE : CHAR4_NOTIFY_ENABLE;
    break;
}*/
        case CHAR4_NOTIFY_ENABLE:                    //CHAR4通知开启
        {
            PRINT("CHAR4_NOTIFY_ENABLE.. \n");
            break;
        }

        case CHAR4_NOTIFY_DISENABLE:                 //CHAR4通知关闭        
        {
            PRINT("CHAR4_NOTIFY_DISENABLE..\n");
            break;
        }


        default:
            // should not reach here!
            break;
    }
}

 

 

posted on 2025-05-24 11:38  WCH蓝牙应用分享  阅读(165)  评论(0)    收藏  举报