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 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> DA14580BLE接收流程分析 -> 正文阅读

[网络协议]DA14580BLE接收流程分析

当BLE设备收到配对设备发来的更新特征值请求时会产生一个GATTC_WRITE_CMD_IND消息,这是GATT Task messages里的一种。

/// GATT Task messages
enum gattc_msg_id
{
    /* Default event */
    /// Command Complete event
    GATTC_CMP_EVT = KE_FIRST_MSG(TASK_GATTC),

    /* ATTRIBUTE CLIENT */
    /// Server configuration request
    GATTC_EXC_MTU_CMD,

    /*Discover All Services */
    /*Discover Services by Service UUID*/
    /*Find Included Services*/
    /*Discover Characteristics by UUID*/
    /*Discover All Characteristics of a Service*/
    /*Discover All Characteristic Descriptors*/
    /// Discovery command
    GATTC_DISC_CMD,
    /* GATT -> HL: Events to Upper layer */
    /*Discover All Services*/
    /// Discovery services indication
    GATTC_DISC_SVC_IND,
    /*Find Included Services*/
    /// Discover included services indication
    GATTC_DISC_SVC_INCL_IND,
    /*Discover All Characteristics of a Service*/
    /// Discover characteristic indication
    GATTC_DISC_CHAR_IND,
    /*Discover All Characteristic Descriptors*/
    /// Discovery characteristic descriptor indication
    GATTC_DISC_CHAR_DESC_IND,

    /*Read Value*/
    /*Read Using UUID*/
    /*Read Long Value*/
    /*Read Multiple Values*/
    /// Read command
    GATTC_READ_CMD,
    /// Read response
    GATTC_READ_IND,

    /*Write without response*/
    /*Write without response with Authentication*/
    /*Write Characteristic Value*/
    /*Signed Write Characteristic Value*/
    /*Write Long Characteristic Value*/
    /*Characteristic Value Reliable Write*/
    /*Write Characteristic Descriptors*/
    /*Write Long Characteristic Descriptors*/
    /*Characteristic Value Reliable Write*/
    /// Write command request
    GATTC_WRITE_CMD,

    /* Cancel / Execute pending write operations */
    /// Execute write characteristic request
    GATTC_EXECUTE_WRITE_CMD,

    /* Reception of an indication or notification from peer device. */
    /// peer device triggers an event (indication or notification)
    GATTC_EVENT_IND,

    /// Registration to peer device events (Indication/Notification).
    GATTC_REG_TO_PEER_EVT_CMD,

    /* ATTRIBUTE SERVER */
    /*Notify Characteristic*/
    /*Indicate Characteristic*/
    /// send an event to peer device
    GATTC_SEND_EVT_CMD,

    /* Indicate that write operation is requested. */
    /// Write command indicated to upper layers.
    GATTC_WRITE_CMD_IND,

    /* Service Changed Characteristic Indication */
    /**
     * Send a Service Changed indication to a device
     * (message structure is struct gattm_svc_changed_ind_req)
     */
    GATTC_SEND_SVC_CHANGED_CMD,
    /**
     * Inform the application when sending of Service Changed indications has been
     * enabled or disabled
     */
    GATTC_SVC_CHANGED_CFG_IND,

    /* Confirm write command execution. */
    /// Write command confirmation from upper layers.
    GATTC_WRITE_CMD_CFM,
    
    /* Indicate that a read operation is requested. */
    /// Read command indicated to upper layers.
    GATTC_READ_CMD_IND,
};

GATTC_WRITE_CMD_IND消息会触发回调函数gattc_write_cmd_ind_handler()。该函数位于custs1_task.c文件。

/// Default State handlers definition
const struct ke_msg_handler custs1_connected[] =
{
    {GATTC_WRITE_CMD_IND,           (ke_msg_func_t)gattc_write_cmd_ind_handler},
    {GATTC_CMP_EVT,                 (ke_msg_func_t)gattc_cmp_evt_handler},
    {CUSTS1_VAL_NTF_REQ,            (ke_msg_func_t)custs1_val_ntf_req_handler},
    {CUSTS1_VAL_SET_REQ,            (ke_msg_func_t)custs1_val_set_req_handler},
    {CUSTS1_VAL_IND_REQ,            (ke_msg_func_t)custs1_val_ind_req_handler},
};

gattc_write_cmd_ind_handler()把消息从GAP层传递给app_entry_point_handler()APP接入点函数,该函数负责把消息再次传递给用户APP应用函数。

/**
 ****************************************************************************************
 * @brief Handles reception of the @ref GATT_WRITE_CMD_IND message.
 * @param[in] msgid Id of the message received (probably unused).
 * @param[in] param Pointer to the parameters of the message.
 * @param[in] dest_id ID of the receiving task instance (probably unused).
 * @param[in] src_id ID of the sending task instance.
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
static int gattc_write_cmd_ind_handler(ke_msg_id_t const msgid,
                                       struct gattc_write_cmd_ind const *param,
                                       ke_task_id_t const dest_id,
                                       ke_task_id_t const src_id)
{
    uint16_t att_idx, value_hdl;
    uint8_t status = PRF_ERR_OK;
    uint8_t uuid[GATT_UUID_128_LEN];
    uint8_t uuid_len;
    att_size_t len;
    uint8_t *value;
    uint16_t perm;

    if (KE_IDX_GET(src_id) == custs1_env.con_info.conidx)
    {
        att_idx = param->handle - custs1_env.shdl;//uint16_t handle:struct gattc_write_cmd_ind结构体成员;custs1_env.shdl:struct custs1_env_tagd结构体成员,服务启动句柄
//handle - custs1_env.shdl是求出索引值,后面用于判断是哪个特征
        if( att_idx < custs1_env.max_nb_att )//判断handle是否合法
        {
            // Retrieve UUID    检索 UUID
            attmdb_att_get_uuid(param->handle, &uuid_len, &(uuid[0]));

            // In case of Client Characteristic Configuration, check validity and set value
			//在客户端特征配置的情况下,检查有效性和设置值
            if ((uint16_t)*(uint16_t *)&uuid[0] == ATT_DESC_CLIENT_CHAR_CFG)
            {
                // Find the handle of the Characteristic Value
                value_hdl = get_value_handle( param->handle );
                if ( !value_hdl ) ASSERT_ERR(0);

                // Get permissions to identify if it is NTF or IND.
				//获取权限以识别它是 NTF(通告) 还是 IND(指示)。
                attmdb_att_get_permission(value_hdl, &perm);
                status = check_client_char_cfg(PERM_IS_SET(perm, NTF, ENABLE), param);

                if (status == PRF_ERR_OK)
                {
                    // Set Client Characteristic Configuration value
					//设置客户端特征配置值
                    status = attmdb_att_set_value(param->handle, param->length, (uint8_t*)&(param->value[0]));
                }
            }
            else
            {
                // Call the application function to validate the value before it is written to database
				//在将值写入数据库之前调用应用程序函数以验证值
                uint8_t i = 0;

                status = PRF_ERR_OK;
                while( cust_prf_funcs[i].task_id != TASK_NONE )
                {
                    if( cust_prf_funcs[i].task_id == dest_id)
                    {
                        if ( cust_prf_funcs[i].value_wr_validation_func != NULL)//如果用户定义了验证回调函数
                        {
                            status = cust_prf_funcs[i].value_wr_validation_func(att_idx, param->last, param->offset, param->length, (uint8_t *)&param->value[0]);
                            break;
                        } else i++;
                    } else i++;
                }

                if (status == PRF_ERR_OK)//验证通过
                {
                    if (param->offset == 0)//gattc_write_cmd_ind结构体下的成员,该结构体用于通知一个数据库修改操作已经被配对设备请求
											//offset,offset at which the data has to be written,要修改的数组的偏移值
                    {
                        // Set value in the database  如果偏移值是0,把值全部写入数据库
						//param->length: uint16_t length;  Data length to be written,  将要写入的数据长度;
						//(uint8_t *)&param->value[0]: uint8_t  value[__ARRAY_EMPTY],  将要写入属性数据库的数据;
                        status = attmdb_att_set_value(param->handle, param->length, (uint8_t *)&param->value[0]);
                    }
                    else
                    {
                        // Update value in the database,如果偏移值不是0,则只更新部分数据
                        status = attmdb_att_update_value(param->handle, param->length, param->offset,
                                                            (uint8_t *)&param->value[0]);
                    }
                }
            }

            if( (param->last) && (status == PRF_ERR_OK) )//bool last,通知它是多准备写入请求的最后一个请求,意即本次数据全部写完
            {
                // Get the value size and data. Can not use param->value, it might be a long value
				//获取值大小和数据。 不能使用 param->value,它可能是一个长值
                if( attmdb_att_get_value(param->handle, &len, &value) != ATT_ERR_NO_ERROR )
                {
                    ASSERT_ERR(0);
                }

                // Inform APP
				//新建一个CUSTS1_VAL_WRITE_IND 消息的结构体,然后填充数据
                struct custs1_val_write_ind *req_id = KE_MSG_ALLOC_DYN(CUSTS1_VAL_WRITE_IND,
                                                        custs1_env.con_info.appid, custs1_env.con_info.prf_id,
                                                        custs1_val_write_ind,
                                                        len);
                memcpy(req_id->value, (uint8_t*)&value[0], len);
                req_id->conhdl = gapc_get_conhdl(custs1_env.con_info.conidx);
                req_id->handle = att_idx;
                req_id->length = len;

                ke_msg_send(req_id);//把消息发给APP,该消息内含数据长度和全部数据
            }
        }
        else//Inexistent handle for sending a read/write characteristic request,不存在的handle用于发送读/写特征请求
        {
            status = PRF_ERR_INEXISTENT_HDL;
        }

        // Send Write Response only if client requests for RSP (ignored when 'Write Without Response' is used)
        if (param->response == 1)
        {
            // Send Write Response
            atts_write_rsp_send(custs1_env.con_info.conidx, param->handle, status);
        }
    }

    return (KE_MSG_CONSUMED);
}

函数通过 if( (param->last) && (status == PRF_ERR_OK) )判断数据全部接收完后,调用attmdb_att_get_value(param->handle, &len, &value)获取数据和数据长度。

然后新建一个CUSTS1_VAL_WRITE_IND 消息的结构体,并填充刚才接收的数据,最后通过调用ke_msg_send()函数把这个消息发给用户应用APP处理,该消息内含数据长度和全部数据。这里 Inform APP,很重要,把消息从GAP层发给了用户应用APP处理。

收到并处理该消息的是app_entry_point_handler()函数,该函数在sdk_app_api目录下的app_entry_point.c文件里。sdk_app_api目录的文件是负责连接底层SDK和用户APP的接口。

app_entry_point_handler()是用户应用程序入口点处理函数。


/**
 ****************************************************************************************
 * @brief Application entry point handler.
 * @param[in] msgid       Message Id
 * @param[in] param       Pointer to message
 * @param[in] dest_id     Destination task Id
 * @param[in] src_id      Source task Id
 * @return Message status
 ****************************************************************************************
 */
int app_entry_point_handler(ke_msg_id_t const msgid,
                            void const *param,
                            ke_task_id_t const dest_id,
                            ke_task_id_t const src_id)
{
    int i = 0;
    enum ke_msg_status_tag process_msg_handling_result; 定义一个枚举类型,处理消息时返回的状态。Status returned by a task when handling a message

    while (i < (sizeof(app_process_handlers) / sizeof(process_event_func_t)))//回调函数数组挨个轮询检查是否已经处理过
    {
        ASSERT_ERR(app_process_handlers[i]);
         if (app_process_handlers[i](msgid, param, dest_id, src_id, &process_msg_handling_result) == PR_EVENT_HANDLED)//如果已经处理过则消息到此终止。
         {
             return (process_msg_handling_result);
         }
         i++;
    }
	
	//如果消息未处理过,则调用回调函数app_process_catch_rest_cb(),该函数在user_callback_config.h文件里定义为 = user_catch_rest_hndl()函数
    //user cannot do anything else than consume the message
    if (app_process_catch_rest_cb != NULL)
    {
        app_process_catch_rest_cb(msgid,param, dest_id, src_id);
    }

    return (KE_MSG_CONSUMED);
};

app_entry_point_handler()函数里判断消息是否已经处理,如果处理了则退出终止消息传递流程。如果没有处理,则调用app_process_catch_rest_cb()回调函数进行处理。处理完毕后,return (KE_MSG_CONSUMED),告知该消息已经处理完毕。

文件user_callback_config.h里定义了回调函数app_process_catch_rest_cb = user_catch_rest_hndl();

static const catch_rest_event_func_t app_process_catch_rest_cb = (catch_rest_event_func_t)user_catch_rest_hndl;

也就是说GAP层发来的消息通过app_entry_point_handler()接入点处理函数后调用了用户定义的回调函数user_catch_rest_hndl来处理。该函数简介里说明,是处理未被SDK内部机制处理的消息。下面看一下user_catch_rest_hndl这个回调函数都做了啥:

/**
 ****************************************************************************************
 * @brief Handles the messages that are not handled by the SDK internal mechanisms.
 * //处理未被SDK内部机制处理的消息
 * @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.
 * @param[in] src_id  ID of the sending task instance.
 * @return void
 ****************************************************************************************
*/
void user_catch_rest_hndl(ke_msg_id_t const msgid,
                        void const *param,
                        ke_task_id_t const dest_id,
                        ke_task_id_t const src_id)
{
    switch(msgid)
    {
        case CUSTS1_VAL_WRITE_IND:
        {
            struct custs1_val_write_ind const *msg_param = (struct custs1_val_write_ind const *)(param);

            switch (msg_param->handle)
            {
                case CUST1_IDX_CONTROL_POINT_VAL:
                   user_custs1_ctrl_wr_ind_handler(msgid, msg_param, dest_id, src_id);
                   break;

                case CUST1_IDX_LED_STATE_VAL:
                    user_custs1_led_wr_ind_handler(msgid, msg_param, dest_id, src_id);
                    break;

                case CUST1_IDX_ADC_VAL_1_NTF_CFG:
                    user_custs1_adc_val_1_cfg_ind_handler(msgid, msg_param, dest_id, src_id);
                    break;

                case CUST1_IDX_BUTTON_STATE_NTF_CFG:
                    user_custs1_button_cfg_ind_handler(msgid, msg_param, dest_id, src_id);
                    break;

                case CUST1_IDX_INDICATEABLE_IND_CFG:
                    user_custs1_long_val_cfg_ind_handler(msgid, msg_param, dest_id, src_id);
                    break;

                case CUST1_IDX_LONG_VALUE_NTF_CFG:
                    user_custs1_long_val_cfg_ind_handler(msgid, msg_param, dest_id, src_id);
                    break;

                case CUST1_IDX_LONG_VALUE_VAL:
                    user_custs1_long_val_wr_ind_handler(msgid, msg_param, dest_id, src_id);
                    break;

                default:
                    break;
            }
        } break;

        case CUSTS1_VAL_NTF_CFM:
        {
            struct custs1_val_ntf_cfm const *msg_param = (struct custs1_val_ntf_cfm const *)(param);

            switch (msg_param->handle)
            {
                case CUST1_IDX_ADC_VAL_1_VAL:
                    break;

                case CUST1_IDX_BUTTON_STATE_VAL:
                    break;

                case CUST1_IDX_LONG_VALUE_VAL:
                    break;

                default:
                    break;
            }
        } break;

        case CUSTS1_VAL_IND_CFM:
        {
            struct custs1_val_ind_cfm const *msg_param = (struct custs1_val_ind_cfm const *)(param);

            switch (msg_param->handle)
            {
                case CUST1_IDX_INDICATEABLE_VAL:
                    break;

                default:
                    break;
             }
         } break;

        case GAPC_PARAM_UPDATED_IND:
        {
            // Cast the void pointer to the appropriate message structure
            struct gapc_param_updated_ind const *msg_param = (struct gapc_param_updated_ind const *)(param);

            // Check if updated Conn Params filled to preffered ones
            if ((msg_param->con_interval >= user_connection_param_conf.intv_min) &&
                (msg_param->con_interval <= user_connection_param_conf.intv_max) &&
                (msg_param->con_latency == user_connection_param_conf.latency) &&
                (msg_param->sup_to == user_connection_param_conf.time_out))
            {
            }
        } break;

        default:
            break;
    }
}

根据msgid,选择对应分支进行处理。现在是一个特征值改变的指示消息CUSTS1_VAL_WRITE_IND (Indicate that the characteristic value has been written)。
在这里插入图片描述
进入CUSTS1_VAL_WRITE_IND分支后,根据消息具体内容的handle选择对应的服务回调函数来处理。这里是user_custs1_led_wr_ind_handler()函数。
在这里插入图片描述下面看一下这个回调函数user_custs1_led_wr_ind_handler()。

void user_custs1_led_wr_ind_handler(ke_msg_id_t const msgid,
                                     struct custs1_val_write_ind const *param,
                                     ke_task_id_t const dest_id,
                                     ke_task_id_t const src_id)
{
    uint8_t val = 0;
    memcpy(&val, &param->value[0], param->length);

    if (val == CUSTS1_LED_ON)
        GPIO_SetActive(GPIO_LED_PORT, GPIO_LED_PIN);
    else if (val == CUSTS1_LED_OFF)
        GPIO_SetInactive(GPIO_LED_PORT, GPIO_LED_PIN);
}


/// Parameters of the @ref CUSTS1_VAL_WRITE_IND massage
struct custs1_val_write_ind
{
    /// Connection handle
    uint16_t conhdl;
    /// Handle of the attribute that has to be written
    uint16_t handle;
    /// Data length to be written
    uint16_t length;
    /// Data to be written in attribute database
    uint8_t  value[__ARRAY_EMPTY];
};

把传递来的消息具体数据内容*param结构体里的元素uint8_t value[__ARRAY_EMPTY]复制到val变量里。当然这里应该是param->length = 1,也就是数组只有一个元素。
复制完毕后,根据这个值来决定GPIO口输出状态。这里是点亮或熄灭LED灯,当然也可以是去做其他事情。

以上是整个BLE设备收到其它配对设备比如手机等发来的消息后的处理流程。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-04-22 19:15:05  更:2022-04-22 19:15:59 
 
开发: 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:55:37-

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