NXP QN902X蓝牙QPPS 流程的理解
2019-07-12 11:28发布
生成海报
首先烧录程序,完成后用light blue查看服务。在profile中只有一个服务,UUID为128位的UUID,但是可以转换为16位UUID:FEE9,在这个服务下包含有8个特征,其中第一个特征UUID为9600结尾的一个128位UUID,是用于接收APP发送的数据的特征,在APP端显示为可写的一个特征值。后面是7个用于发送数据到APP的特征值,特征值UUID后四位由9601递增至9607,一共有七个特征值。这个特征值的数目实际是可以修改的,可以改为1~7,在usr_config.h中有如下定义,更改QPPS_NOTIFY_NUM的数值就可以改变发送特征值的数目。
-
///Quintic private profile Role
-
#define CFG_PRF_QPPS
-
#define QPPS_NOTIFY_NUM 7
-
//#define CFG_TASK_QPPS TASK_PRF1
复制代码
app向第一个特征值中写入数据,发送,则系统会调用到gatt_write_cmd_ind_handler函数处理APP端的操作命令。
-
static int gatt_write_cmd_ind_handler(ke_msg_id_t const msgid,
-
struct gatt_write_cmd_ind const *param,
-
ke_task_id_t const dest_id,
-
ke_task_id_t const src_id)
-
{
-
uint8_t status = PRF_ERR_OK;
-
-
if (param->conhdl == qpps_env.conhdl)
-
{
-
// Client Char. Configuration
-
uint8_t char_index = param->handle - (qpps_env.shdl + QPPS_IDX_VAL_NTF_CFG);
-
if ((param->handle > (qpps_env.shdl + QPPS_IDX_VAL_CHAR)) && ((char_index % 3) == 0))
-
{
-
uint16_t value = 0x0000;
-
-
//Extract value before check
-
memcpy(&value, &(param->value), sizeof(uint16_t));
-
-
if ((value == PRF_CLI_STOP_NTFIND) || (value == PRF_CLI_START_NTF))
-
{
-
if (value == PRF_CLI_STOP_NTFIND)
-
{
-
qpps_env.features &= ~(QPPS_VALUE_NTF_CFG << (char_index / 3));
-
}
-
else //PRF_CLI_START_NTF
-
{
-
qpps_env.features |= QPPS_VALUE_NTF_CFG << (char_index / 3);
-
}
-
}
-
else
-
{
-
status = PRF_APP_ERROR;
-
}
-
-
if (status == PRF_ERR_OK)
-
{
-
uint8_t *old_value;
-
atts_size_t length;
-
-
attsdb_att_get_value(param->handle, &length, &old_value);
-
if (value != co_read16p(old_value))
-
{
-
//Update the attribute value
-
attsdb_att_set_value(param->handle, sizeof(uint16_t), (uint8_t *)&value);
-
-
if(param->last)
-
{
-
//Inform APP of configuration change
-
struct qpps_cfg_indntf_ind * ind = KE_MSG_ALLOC(QPPS_CFG_INDNTF_IND,
-
qpps_env.appid, TASK_QPPS,
-
qpps_cfg_indntf_ind);
-
-
ind->char_index = (char_index / 3);
-
memcpy(&ind->cfg_val, &value, sizeof(uint16_t));
-
-
ke_msg_send(ind);
-
}
-
}
-
}
-
}
-
else if (param->handle == (qpps_env.shdl + QPPS_IDX_RX_DATA_VAL))
-
{
-
if (param->length <= QPP_DATA_MAX_LEN)
-
{
-
//inform APP of configuration change
-
struct qpps_data_val_ind * ind = KE_MSG_ALLOC_DYN(QPPS_DAVA_VAL_IND,
-
qpps_env.appid,
-
TASK_QPPS,
-
qpps_data_val_ind, param->length);
-
-
memcpy(&ind->conhdl, &(qpps_env.conhdl), sizeof(uint16_t));
-
//Send received data to app value
-
ind->length = param->length;
-
memcpy(ind->data, param->value, param->length);
-
-
ke_msg_send(ind);
-
}
-
else
-
{
-
status = QPPS_ERR_RX_DATA_EXCEED_MAX_LENGTH;
-
}
-
}
-
else
-
{
-
status = QPPS_ERR_INVALID_PARAM;
-
}
-
}
-
-
if (param->response)
-
{
-
//Send write response
-
atts_write_rsp_send(qpps_env.conhdl, param->handle, status);
-
}
-
-
return (KE_MSG_CONSUMED);
-
}
-
复制代码
由于app对特征值9600的写入操作实际上是对特征中QPPS_IDX_RX_DATA_VAL一值进行了写入操作,所以函数中以下代码就是经过了一系列的接收操作后,我们能在程序中看到的第一入口。程序中构建了一条标识为QPPS_DAVA_VAL_IND的消息,在信息中填入了从app处获取到的写入内容。
-
if (param->handle == (qpps_env.shdl + QPPS_IDX_RX_DATA_VAL))
-
{
-
if (param->length <= QPP_DATA_MAX_LEN)
-
{
-
//inform APP of configuration change
-
struct qpps_data_val_ind * ind = KE_MSG_ALLOC_DYN(QPPS_DAVA_VAL_IND,
-
qpps_env.appid,
-
TASK_QPPS,
-
qpps_data_val_ind, param->length);
-
-
memcpy(&ind->conhdl, &(qpps_env.conhdl), sizeof(uint16_t));
-
//Send received data to app value
-
ind->length = param->length;
-
memcpy(ind->data, param->value, param->length);
-
-
ke_msg_send(ind);
-
}
-
else
-
{
-
status = QPPS_ERR_RX_DATA_EXCEED_MAX_LENGTH;
-
}
-
}
-
else
-
{
-
status = QPPS_ERR_INVALID_PARAM;
-
}
-
}
-
复制代码
通过查看QPPS_DAVA_VAL_IND,得知该消息处理函数为app_qpps_data_ind_handler。根据函数描述,app发送数据到开发板后,会在串口中输出数据,打开串口,设置好波特率(9600),可以看到app端发送数据的字节长度和第一个数据的16进制。如果需要取出完整数据,根据param->length把param->data数组的数据都取出来,就是完整的数据了。
-
int app_qpps_data_ind_handler(ke_msg_id_t const msgid,
-
struct qpps_data_val_ind *param,
-
ke_task_id_t const dest_id,
-
ke_task_id_t const src_id)
-
{
-
if (param->length > 0)
-
{
-
QPRINTF("len=%d, I%02X", param->length, param->data[0]);
-
}
-
QPRINTF("
");
-
-
return (KE_MSG_CONSUMED);
-
}
-
复制代码
对于发送数据,昆天科做了一个很有意思的算法,让app在配置完所有的notify后持续发送递增数据到每一个发送特征值中,所以开启这个发送的办法在与打开所有的notify。我们看一下打开notify的操作,这个操作会触发gatt_write_cmd_ind_handler处理函数,代码片如下:
-
uint8_t char_index = param->handle - (qpps_env.shdl + QPPS_IDX_VAL_NTF_CFG);
-
if ((param->handle > (qpps_env.shdl + QPPS_IDX_VAL_CHAR)) && ((char_index % 3) == 0))
-
{
-
uint16_t value = 0x0000;
-
-
//Extract value before check
-
memcpy(&value, &(param->value), sizeof(uint16_t));
-
-
if ((value == PRF_CLI_STOP_NTFIND) || (value == PRF_CLI_START_NTF))
-
{
-
if (value == PRF_CLI_STOP_NTFIND)
-
{
-
qpps_env.features &= ~(QPPS_VALUE_NTF_CFG << (char_index / 3));
-
}
-
else //PRF_CLI_START_NTF
-
{
-
qpps_env.features |= QPPS_VALUE_NTF_CFG << (char_index / 3);
-
}
-
}
-
else
-
{
-
status = PRF_APP_ERROR;
-
}
-
-
if (status == PRF_ERR_OK)
-
{
-
uint8_t *old_value;
-
atts_size_t length;
-
-
attsdb_att_get_value(param->handle, &length, &old_value);
-
if (value != co_read16p(old_value))
-
{
-
//Update the attribute value
-
attsdb_att_set_value(param->handle, sizeof(uint16_t), (uint8_t *)&value);
-
-
if(param->last)
-
{
-
//Inform APP of configuration change
-
struct qpps_cfg_indntf_ind * ind = KE_MSG_ALLOC(QPPS_CFG_INDNTF_IND,
-
qpps_env.appid, TASK_QPPS,
-
qpps_cfg_indntf_ind);
-
-
ind->char_index = (char_index / 3);
-
memcpy(&ind->cfg_val, &value, sizeof(uint16_t));
-
-
ke_msg_send(ind);
-
}
-
}
-
}
-
}
复制代码
当app开启notify的时候,会带来一个value,value代表着是开启还是关闭notify,所以首先要进行判断是哪一个特征中开启或者关闭了notify,然后判断是否需要进行开启和关闭操作(检查之前状态是否与当前操作冲突),如果需要开启或者关闭,则会发出一个标识为QPPS_CFG_INDNTF_IND的消息,消息中填入了操作的特征序号和值。
消息QPPS_CFG_INDNTF_IND的处理函数为app_qpps_cfg_indntf_ind_handler,此消息为指示性消息,notify已经在刚才 的处理中开启,所以本函数主要任务是指示了哪一个消息被开启或关闭,在判断所有的notify被开启后,函数调用了另一个函数app_test_send_data(app_qpps_env->tx_char_num
- 1);,发送数据到9601~9607七个特征值去。
-
int app_qpps_cfg_indntf_ind_handler(ke_msg_id_t const msgid,
-
struct qpps_cfg_indntf_ind *param,
-
ke_task_id_t const dest_id,
-
ke_task_id_t const src_id)
-
{
-
if (app_qpps_env->conhdl == param->conhdl)
-
{
-
if (param->cfg_val == PRF_CLI_START_NTF)
-
{
-
QPRINTF("param->char_index %d
",param->char_index);
-
app_qpps_env->features |= (QPPS_VALUE_NTF_CFG << param->char_index);
-
// App send data if all of characteristic have been configured
-
QPRINTF("app_qpps_env->features 0x%X : 0x%X
",app_qpps_env->features,app_qpps_env->tx_char_num);
-
if (get_bit_num(app_qpps_env->features) == app_qpps_env->tx_char_num)
-
{
-
app_qpps_env->char_status = app_qpps_env->features;
-
app_test_send_data(app_qpps_env->tx_char_num - 1);
-
}
-
}
-
else
-
{
-
app_qpps_env->features &= ~(QPPS_VALUE_NTF_CFG << param->char_index);
-
app_qpps_env->char_status &= ~(QPPS_VALUE_NTF_CFG << param->char_index);
-
}
-
}
-
-
return (KE_MSG_CONSUMED);
-
}
复制代码
函数通过对cnt计数和一些条件判断,对特征值依次发送数据。
-
static void app_test_send_data(uint8_t max)
-
{
-
uint8_t cnt;
-
-
for (cnt = 0; (max != 0) && cnt < app_qpps_env->tx_char_num; cnt++)
-
{
-
if ((app_qpps_env->char_status >> cnt) & QPPS_VALUE_NTF_CFG)
-
{
-
static uint8_t val[] = {0, '0', '1', '2','3','4','5','6','7','8','9','8','7','6','5','4','3','2','1','0'};
-
-
// Increment the first byte for test
-
val[0]++;
-
-
max--;
-
// Allow next notify until confirmation received in this characteristic
-
app_qpps_env->char_status &= ~(QPPS_VALUE_NTF_CFG << cnt);
-
app_qpps_data_send(app_qpps_env->conhdl, cnt, sizeof(val), val);
-
}
-
QPRINTF("send to cnt:%d,max %d
",cnt,max);
-
}
-
QPRINTF("app_qpps_env->char_status: %X
",app_qpps_env->char_status);
-
}
-
复制代码
发送数据完成,系统会发出QPPS_DATA_SEND_CFM消息作为响应,于是在消息对应的处理函数app_qpps_data_send_cfm_handler中,再次发起消息发送。
-
int app_qpps_data_send_cfm_handler(ke_msg_id_t const msgid,
-
struct qpps_data_send_cfm *param,
-
ke_task_id_t const dest_id,
-
ke_task_id_t const src_id)
-
{
-
if (app_qpps_env->conhdl == param->conhdl && param->status == PRF_ERR_OK)
-
{
-
// Allow new notify
-
app_qpps_env->char_status |= (1 << param->char_index);
-
QPRINTF("app_qpps_env->char_status %X
",app_qpps_env->char_status);
-
-
// Send next group data until current data have been sent
-
if (get_bit_num(app_qpps_env->char_status) == (app_qpps_env->tx_char_num - 1))
-
{
-
QPRINTF("t:app_qpps_env->char_status %X
",app_qpps_env->char_status);
-
app_test_send_data(app_qpps_env->tx_char_num - 1);
-
}
-
}
-
else
-
{
-
QPRINTF("QPPS send error %d.
", param->status);
-
}
-
-
return (KE_MSG_CONSUMED);
-
}
-
复制代码
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮