IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> DA14580 主机从扫描到建立连接全过程分析(含代码) -> 正文阅读

[嵌入式]DA14580 主机从扫描到建立连接全过程分析(含代码)

主机扫描到广播事件,回调user_on_adv_report_ind( )函数。该函数里先使用memcmp()函数对收到的广播数据和本地预存的数据进行对比,这里是对比服务的128bit UUID。
在主机的user_config.h文件下,预定义了USER_ADVERTISE_DATA:

#define USER_ADVERTISE_DATA    "\x11\x07\xb7\x5c\x49\xd2\x04\xa3\x40\x71\xa0\xb5\x35\x85\x3e\xb0\x83\x07"

把这个128bit数据和从广播收到的数据比对,如果匹配则说明找到了需要连接的从机。

/**
 *********************主机手收到广播数据后的回调函数*********************************************
 * @brief Handles advertise reports
 * @param[in]   param Parameters of disconnect message
 * @return void
 ****************************************************************************************
 */
void user_on_adv_report_ind(struct gapm_adv_report_ind const * param)
{
    if(!memcmp(&param->report.data[3], USER_ADVERTISE_DATA, USER_ADVERTISE_DATA_LEN))
    {
    //打印即将连接到的从机的地址,该地址在广播数据里
        arch_printf("Connect with %02x %02x %02x %02x %02x %02x",
            param->report.adv_addr.addr[5],
            param->report.adv_addr.addr[4],
            param->report.adv_addr.addr[3],
            param->report.adv_addr.addr[2],
            param->report.adv_addr.addr[1],
            param->report.adv_addr.addr[0]);
            
    //设置连接参数
    app_easy_gap_start_connection_to_set(param->report.adv_addr_type, (uint8_t *)&param->report.adv_addr.addr, MS_TO_DOUBLESLOTS(USER_CON_INTV));
    user_gapm_cancel();//取消当前GAP任务,也就是取消正在进行的扫描
    }
}

我们看一下server端定义的service,SPS_SERVICE_UUID是128bit的,{0xb7, 0x5c, 0x49, 0xd2, 0x04, 0xa3, 0x40, 0x71, 0xa0, 0xb5, 0x35, 0x85, 0x3e, 0xb0, 0x83, 0x07}

/*
 * SPSS PROFILE ATTRIBUTES DEFINITION
 *******************这是server端定义的服务和特征UUID***********************************************
 */
#define SPS_SERVICE_UUID    {0xb7, 0x5c, 0x49, 0xd2, 0x04, 0xa3, 0x40, 0x71, 0xa0, 0xb5, 0x35, 0x85, 0x3e, 0xb0, 0x83, 0x07}

#define SPS_SERVER_TX_UUID  {0xb8, 0x5c, 0x49, 0xd2, 0x04, 0xa3, 0x40, 0x71, 0xa0, 0xb5, 0x35, 0x85, 0x3e, 0xb0, 0x83, 0x07}
#define SPS_SERVER_RX_UUID  {0xba, 0x5c, 0x49, 0xd2, 0x04, 0xa3, 0x40, 0x71, 0xa0, 0xb5, 0x35, 0x85, 0x3e, 0xb0, 0x83, 0x07}
#define SPS_FLOW_CTRL_UUID  {0xb9, 0x5c, 0x49, 0xd2, 0x04, 0xa3, 0x40, 0x71, 0xa0, 0xb5, 0x35, 0x85, 0x3e, 0xb0, 0x83, 0x07}

#define SPS_SERVER_TX_CHAR_LEN      160
#define SPS_SERVER_RX_CHAR_LEN      160
#define SPS_FLOW_CTRL_CHAR_LEN      1

上面在回调函数user_on_adv_report_ind()的末尾调用了 user_gapm_cancel(),取消当前GAP任务,也就是正进行的扫描任务。该函数是向ke内核发送一个取消的消息。

static void user_gapm_cancel(void)
{
    // Disable Advertising
    struct gapm_cancel_cmd *cmd = app_gapm_cancel_msg_create();
    // Send the message
    app_gapm_cancel_msg_send(cmd);
}

内核收到该消息后,rwip_schedule()会发出GAPM_CMP_EVT事件,该事件触发回调函数gapm_cmp_evt_handler( )。
程序进一步进入gapm_cmp_evt_handler( )函数下的 case GAPM_SCAN_ACTIVE:或 case GAPM_SCAN_PASSIVE:语句,执行
EXECUTE_CALLBACK_PARAM(app_on_scanning_completed, param->status);

/**
 ****************************************************************************************
 * @brief Handles GAP manager command complete events.
 *
 * @param[in] msgid     Id of the message received.
 * @param[in] param     Pointer to the parameters of the message.
 * @param[in] dest_id   ID of the receiving task instance (TASK_GAP).
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
int gapm_cmp_evt_handler(ke_msg_id_t const msgid,
                                struct gapm_cmp_evt const *param,
                                ke_task_id_t const dest_id,
                                ke_task_id_t const src_id)
{
    switch(param->operation)
    {
        // reset completed
        case GAPM_RESET:
        {
            if(param->status != GAP_ERR_NO_ERROR)
            {
                ASSERT_ERR(0); // unexpected error
            }
            else
            {
                // set device configuration
                app_easy_gap_dev_configure ();
            }
        }
        break;

        // device configuration updated
        case GAPM_SET_DEV_CONFIG:
        {
            if(param->status != GAP_ERR_NO_ERROR)
            {
                ASSERT_ERR(0); // unexpected error
            }
            else
            {
                EXECUTE_CALLBACK_VOID(app_on_set_dev_config_complete);
            }
        }
        break;

        // Advertising finished
        case GAPM_ADV_UNDIRECT:
        {
           EXECUTE_CALLBACK_PARAM(app_on_adv_undirect_complete, param->status); 
        }
        break;
        
        // Directed advertising finished
        case GAPM_ADV_DIRECT:
        {
            EXECUTE_CALLBACK_PARAM(app_on_adv_direct_complete, param->status);
        }
        break;

        case GAPM_SCAN_ACTIVE:
        case GAPM_SCAN_PASSIVE:
        {
            EXECUTE_CALLBACK_PARAM(app_on_scanning_completed, param->status);
        }
        break;
        
        case GAPM_CONNECTION_DIRECT:
            if (param->status == GAP_ERR_CANCELED)
            {
                EXECUTE_CALLBACK_VOID(app_on_connect_failed);
            }
        break;

        case GAPM_CANCEL:
        {
            if(param->status != GAP_ERR_NO_ERROR)
            {
                ASSERT_ERR(0); // unexpected error
            }
            if (app_process_catch_rest_cb!=NULL)
            {
                app_process_catch_rest_cb(msgid,param,dest_id,src_id);
            }
         }
        break;
         
        default:
            if (app_process_catch_rest_cb!=NULL)
            {
                app_process_catch_rest_cb(msgid,param,dest_id,src_id);
            }    
        break;
    }

    return (KE_MSG_CONSUMED);
}

其中app_on_scanning_completed定义等于user_on_scanning_completed( )。也就是说,会回调user_on_scanning_completed( )函数处理。

该函先判断GAP的任务状态是否是GAP_ERR_CANCELED,如果是则说明找到一个符合条件的广播者,并且GAP已经取消了扫描任务,此时需要设置连接参数并发出连接消息 ,然后设置连接超时计时器。如果状态不是GAP_ERR_CANCELED,说明未有找到广播者,重新启动新的扫描。

/**
 ****************************************************************************************
 * @brief Called upon scan completion
 * @return void
 ****************************************************************************************
 */
#define USER_CON_TIMEOUT     700 //7 sec
void user_on_scanning_completed (uint8_t status)
{
    if(status == GAP_ERR_CANCELED)
    {
        app_easy_gap_start_connection_to();
        connection_timer = app_easy_timer(USER_CON_TIMEOUT, user_gapm_cancel);//设置连接超时时间,这里是7s。7s后如果未能连接上,则取消当前连接任务。
    }
    else
    {
        user_scan_start();
    }
    return;
}

连接消息发出后,会有两种结果一是收到来自 GAPC 的连接请求 (GAPC_CONNECTION_REQ_IND) 指示事件,二是定时器超时,取消连接任务。

当收到连接请求指示事件(GAPC_CONNECTION_REQ_IND) 的时候,会触发gapc_connection_req_ind_handler( )函数。该函数会读取当前TASK_APP任务状态,并把状态从APP_CONNECTABLE设置为APP_CONNECTED。
该函数最后通过EXECUTE_CALLBACK_PARAM1_PARAM2(app_on_connection, connection_idx, param),调用了user_on_connection()函数。

/**
 ****************************************************************************************
 * @brief Handles connection complete event from the GAP. Will enable profile.
 *
 * @param[in] msgid     Id of the message received.
 * @param[in] param     Pointer to the parameters of the message.
 * @param[in] dest_id   ID of the receiving task instance (TASK_GAP).
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
int gapc_connection_req_ind_handler(ke_msg_id_t const msgid,
                                           struct gapc_connection_req_ind const *param,
                                           ke_task_id_t const dest_id,
                                           ke_task_id_t const src_id)
{
    // Connection Index
    if (ke_state_get(dest_id) == APP_CONNECTABLE)
    {
        uint8_t connection_idx=KE_IDX_GET(src_id);
        ASSERT_WARNING(connection_idx<APP_EASY_MAX_ACTIVE_CONNECTION);
        app_env[connection_idx].conidx = connection_idx;
        
        if (connection_idx != GAP_INVALID_CONIDX)
        {
            app_env[connection_idx].connection_active=true;
            ke_state_set(TASK_APP, APP_CONNECTED);
            // Retrieve the connection info from the parameters
            app_env[connection_idx].conhdl = param->conhdl;
            app_env[connection_idx].peer_addr_type = param->peer_addr_type;
            memcpy(app_env[connection_idx].peer_addr.addr, param->peer_addr.addr, BD_ADDR_LEN);
            #if (BLE_APP_SEC)
            // send connection confirmation
                app_easy_gap_confirm(connection_idx, (enum gap_auth) app_sec_env[connection_idx].auth, GAP_AUTHZ_NOT_SET);
            #else // (BLE_APP_SEC)
                app_easy_gap_confirm(connection_idx, GAP_AUTH_REQ_NO_MITM_NO_BOND, GAP_AUTHZ_NOT_SET);  
            #endif
        }
        EXECUTE_CALLBACK_PARAM1_PARAM2(app_on_connection, connection_idx, param);
    }
    else
    {
        // APP_CONNECTABLE state is used to wait the GAP_LE_CREATE_CONN_REQ_CMP_EVT message
        ASSERT_ERR(0);
    }

    return (KE_MSG_CONSUMED);
}

在user_on_connection( )函数里,主机(client)调用app_easy_timer_cancel( )取消连接超时计时器,调用app_prf_enable( )启用配置文件和服务并初始化, 调用user_gattc_exc_mtu_cmd( ) 函数发出交换MTU。

/**
 *******主机(client)*************主机(client)**************主机(client)***************
 * @brief Handles connection event
 * @param[in]   connection_idx Connection index
 * @param[in]   param Parameters of connection
 * @return void
 ****************************************************************************************
 */
void user_on_connection(uint8_t connection_idx, struct gapc_connection_req_ind const *param)
{
    if (app_env[connection_idx].conidx != GAP_INVALID_CONIDX)
    {
        app_easy_timer_cancel(connection_timer);//取消定时器任务
        app_prf_enable (param->conhdl);//启用配置文件和服务并初始化
        user_gattc_exc_mtu_cmd(connection_idx);//发出交换MTU指令
        if ((user_default_hnd_conf.security_request_scenario==DEF_SEC_REQ_ON_CONNECT) && (BLE_APP_SEC))
        {//如果启用了安全选项,则发出启用安全的请求消息
             app_easy_security_request(connection_idx);
        }
    arch_printf("Device connected\r\n");//打印消息,设备已经连接
    }
    else
    {
        // No connection has been established, restart scanning  无连接建立,重新发出消息启动扫描
        user_scan_start();
    }    
}

以上分析了主机从扫描到广播者发出的广播数据开始到建立连接的全过程:扫描到广播数据---->通过匹配服务的UUID找到连接对象---->发出建立连接请求(向从机发出)---->收到连接请求消息指示(从机同意了)---->把状态从可连接切换为已连接,启用配置文件和服务并初始化、交换MTU,---->连接建立成功。
至此,设备建立连接完毕,主机(client)可以修改从机(server)的服务特征值(可写的情况下),从机也可以发出NOTIFY或INDICATION来和主机交互数据。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-05-05 11:35:52  更:2022-05-05 11:39:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 3:56:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码