工程 D:\git\dialog\6.0.10.511\projects\target_apps\ble_examples\ble_app_peripheral\Keil_5
直接debug
手机APP连接蓝牙 DLG-PRPH
最后连续三个unknown service
如下的位置 是LED的 写01可以看到黄色LED亮起
开始分析
开始倒退 谁点灯的
是 user_catch_rest_hndl 中收到消息 msgid=CUSTS1_VAL_WRITE_IND msg_param->handle=SVC1_IDX_LED_STATE_VAL
执行的user_svc1_led_wr_ind_handler
这两个参数 可以说是一个大 一个小 前面是写 大类 后面是参数 写哪一个
void user_svc1_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, ¶m->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);
}
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 SVC1_IDX_CONTROL_POINT_VAL:
user_svc1_ctrl_wr_ind_handler(msgid, msg_param, dest_id, src_id);
break;
case SVC1_IDX_LED_STATE_VAL:
user_svc1_led_wr_ind_handler(msgid, msg_param, dest_id, src_id);
break;
那么谁调用的 user_catch_rest_hndl
/// Format of a catch rest event handler function
typedef void(*catch_rest_event_func_t)(ke_msg_id_t const msgid,
void const *param,
ke_task_id_t const dest_id,
ke_task_id_t const src_id);
static const catch_rest_event_func_t app_process_catch_rest_cb = (catch_rest_event_func_t)user_catch_rest_hndl;
他其实是给了一个钩子 那么谁调用的狗子 app_process_catch_rest_cb
看到很多
if (app_process_catch_rest_cb != NULL)
{
app_process_catch_rest_cb(msgid, param, dest_id, src_id);
}
我们看看是哪里的
断点如图 流程很明白了
也就是app_entry_point_handler --- app_process_catch_rest_cb user_catch_rest_hndl ---
那么谁来触发 app_entry_point_handler 这个函数 只有
const struct ke_msg_handler app_default_state[] =
{
{KE_MSG_DEFAULT_HANDLER, (ke_msg_func_t)app_entry_point_handler},
};
它是一个事件驱动型的 只管发消息即可 TX KE_MSG_DEFAULT_HANDLER 后面就是挨个去消费的!
重点关注第一个参数 ke_msg_id_t const msgid 它是U16 SVC1_IDX_LED_STATE_VAL
这个ID是一些枚举 和任务有绑定关系的
app_entry_point_handler --- app_process_catch_rest_cb user_catch_rest_hndl ---
那么谁TX消息的
// Inform APP
struct custs1_val_write_ind *req_id = KE_MSG_ALLOC_DYN(CUSTS1_VAL_WRITE_IND,
prf_dst_task_get(&(custs1_env->prf_env), KE_IDX_GET(src_id)),
dest_id, custs1_val_write_ind,
param->length);
memcpy(req_id->value, param->value, param->length);
//req_id->conhdl = gapc_get_conhdl(custs1_env.con_info.conidx);
req_id->handle = att_idx;
req_id->length = param->length;
ke_msg_send(req_id);
是这个函数
static int gattc_write_req_ind_handler(ke_msg_id_t const msgid,
注意这里到了生产-消费模型 任意的地方 都可以生产 也就是TX 然后死循环是消费的
它做的是柔性数组 TX的时候malloc 在消费发时候free
struct fake_custs1_val_write_ind
{
/// Connection index
uint8_t conidx;
/// 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[10];
};
struct fake_custs1_val_write_ind fake_custs1_val_write={0};
static int gattc_write_req_ind_handler(ke_msg_id_t const msgid,
const struct gattc_write_req_ind *param,
ke_task_id_t const dest_id,
ke_task_id_t const src_id)
{
struct custs1_env_tag *custs1_env = PRF_ENV_GET(CUSTS1, custs1);
struct gattc_write_cfm * cfm;
uint8_t att_idx = 0;
uint8_t conidx = KE_IDX_GET(src_id);
// retrieve handle information
uint8_t status = custs1_get_att_idx(param->handle, &att_idx);
att_perm_type perm;
ASSERT_ERROR(param->offset == 0);
// If the attribute has been found, status is ATT_ERR_NO_ERROR
if (status == ATT_ERR_NO_ERROR)
{
const struct cust_prf_func_callbacks *callbacks = custs_get_func_callbacks(TASK_ID_CUSTS1);
if (callbacks->att_db[att_idx].uuid_size == ATT_UUID_16_LEN &&
*(uint16_t *)callbacks->att_db[att_idx].uuid == ATT_DESC_CLIENT_CHAR_CFG)
{
struct attm_elmt elem = {0};
// Find the handle of the Characteristic Value
uint16_t value_hdl = get_value_handle(param->handle);
ASSERT_ERROR(value_hdl);
// Get permissions to identify if it is NTF or IND.
attmdb_att_get_permission(value_hdl, &perm, 0, &elem);
status = check_client_char_cfg(PERM_IS_SET(perm, NTF, ENABLE), param);
if (status == ATT_ERR_NO_ERROR)
{
custs1_set_ccc_value(conidx, att_idx, *(uint16_t *)param->value);
}
}
else
{
if (callbacks != NULL && callbacks->value_wr_validation_func != NULL)
status = callbacks->value_wr_validation_func(att_idx, param->offset, param->length, (uint8_t *)¶m->value[0]);
if (status == ATT_ERR_NO_ERROR)
{
// Set value in the database
status = attmdb_att_set_value(param->handle, param->length, param->offset, (uint8_t *)¶m->value[0]);
}
}
if (status == ATT_ERR_NO_ERROR)
{
// Inform APP
struct custs1_val_write_ind *req_id = KE_MSG_ALLOC_DYN(CUSTS1_VAL_WRITE_IND,
prf_dst_task_get(&(custs1_env->prf_env), KE_IDX_GET(src_id)),
dest_id, custs1_val_write_ind,
param->length);
memcpy(req_id->value, param->value, param->length);
//req_id->conhdl = gapc_get_conhdl(custs1_env.con_info.conidx);
req_id->handle = att_idx;
req_id->length = param->length;
memcpy(&fake_custs1_val_write,req_id,(sizeof(struct custs1_val_write_ind) + param->length));
ke_msg_send(req_id);
}
}
可以测试 00 --- 01 控制LED
这里把参数2找到了 前面的大参数1 呢
在malloc的时候出现了!!!!!!!
真相大白了 所以 这个大参数是RW内核自己控制的 在malloc的就指定了
(怀疑它不是一个数组再做生产消费 它有多个数组 用哪个数组生产呢 就是这个malloc约定的)
https://blog.csdn.net/adr5970/article/details/101306927
谁来调 gattc_write_req_ind_handler 只有
/// Default State handlers definition
const struct ke_msg_handler custs1_default_state[] =
{
{GATTC_READ_REQ_IND, (ke_msg_func_t)gattc_read_req_ind_handler},
{GATTC_WRITE_REQ_IND, (ke_msg_func_t)gattc_write_req_ind_handler},
{GATTC_ATT_INFO_REQ_IND,
const struct ke_state_handler custs1_default_handler = KE_STATE_HANDLER(custs1_default_state);
这个东西 放在一个接口 就不动了
static uint8_t custs1_init(struct prf_task_env *env, uint16_t *start_hdl, uint16_t app_task, uint8_t sec_lvl, struct custs1_db_cfg *params)
{
for (uint8_t i = 0; i < custs1_services_size; i++)
{
// Create the service
status = attm_svc_create_db_128(custs1_services[i],
start_hdl,
NULL,
custs1_services[i+1],
NULL,
env->task,
custs1_att_db,
(sec_lvl & PERM_MASK_SVC_AUTH) | (sec_lvl & PERM_MASK_SVC_EKS) | PERM(SVC_PRIMARY, ENABLE));
}
if (status == ATT_ERR_NO_ERROR)
{
env->desc.default_handler = &custs1_default_handler;
ke_state_set(env->task, CUSTS1_IDLE);
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
直接开始自己完成一个LED 手机APP去写
#define DEF_SVC4_USER_DESC "0000-off 0001-ON"
SVC3_IDX_READ_3_CHAR,
SVC3_IDX_READ_3_VAL,
SVC3_IDX_READ_3_IND_CFG,
SVC3_IDX_READ_3_USER_DESC,
SVC4_IDX_SVC,
SVC4_IDX_WRITE_CHAR,
SVC4_IDX_WRITE_VAL,
SVC4_IDX_WRITE_USER_DESC,
CUSTS1_IDX_NB
};
const uint8_t custs1_services[] = {SVC1_IDX_SVC, SVC2_IDX_SVC, SVC3_IDX_SVC, SVC4_IDX_SVC, CUSTS1_IDX_NB};
const uint8_t custs1_services_size = ARRAY_LEN(custs1_services) - 1;
const uint16_t custs1_att_max_nb = CUSTS1_IDX_NB;
static const att_svc_desc128_t custs1_svc4 = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x0B, 0x10, 0x99, 0x2E, 0xC6, 0xFE, 0xED};
static const uint8_t DEF_SVC4_CTRL_POINT_UUID_128[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x10, 0x99, 0x2E, 0xC6, 0xFE, 0xED};
// Service 1 Declaration
[SVC4_IDX_SVC] = {(uint8_t*)&att_decl_svc, ATT_UUID_128_LEN, PERM(RD, ENABLE) | PERM(WR, ENABLE),
sizeof(custs1_svc4), sizeof(custs1_svc4), (uint8_t*)&custs1_svc4},
// Control Point Characteristic Declaration
[SVC4_IDX_WRITE_CHAR] = {(uint8_t*)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE),
0, 0, NULL},
// Control Point Characteristic Value
[SVC4_IDX_WRITE_VAL] = {DEF_SVC4_CTRL_POINT_UUID_128, ATT_UUID_128_LEN, PERM(WR, ENABLE) | PERM(WRITE_REQ, ENABLE),
2*DEF_SVC1_CTRL_POINT_CHAR_LEN, 0, NULL},
// Control Point Characteristic User Description
[SVC4_IDX_WRITE_USER_DESC] = {(uint8_t*)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE),
sizeof(DEF_SVC4_USER_DESC) - 1, sizeof(DEF_SVC4_USER_DESC) - 1, DEF_SVC4_USER_DESC},
此时看到APP已经出现这个服务了
发送oooo 0001 看到handel就是我们的枚举
ADD一个LED函数
case SVC4_IDX_WRITE_VAL:
user_svc4_wr_ind_handler(msgid, msg_param, dest_id, src_id);
break;
测试OK
发送0000关灯 0001开灯
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
换个角度看问题
全局 SVC1_IDX_LED_STATE_VAL 关注额这个线索
第一个是枚举 每一个都是一个数组的成员 每一个数组成员都是一个服务
enum
{
// Custom Service 1
SVC1_IDX_SVC = 0,
SVC1_IDX_CONTROL_POINT_CHAR,
SVC1_IDX_CONTROL_POINT_VAL,
SVC1_IDX_CONTROL_POINT_USER_DESC,
SVC1_IDX_LED_STATE_CHAR,
SVC1_IDX_LED_STATE_VAL,
SVC1_IDX_LED_STATE_USER_DESC,
第二个是数组 这里面全部是蓝牙的服务 =一个profile 多个service
uint8_t interesting=10;
/// Full CUSTS1 Database Description - Used to add attributes into the database
const struct attm_desc_128 custs1_att_db[CUSTS1_IDX_NB] =
{
/*************************
* Service 1 configuration
*************************
*/
//ͳһ Ò»¸öserver
// Service 1 Declaration 2800
[SVC1_IDX_SVC] = {(uint8_t*)&att_decl_svc,
ATT_UUID_128_LEN, PERM(RD, ENABLE),
sizeof(custs1_svc1),
sizeof(custs1_svc1),
(uint8_t*)&custs1_svc1},
//DEF_SVC1_UUID_128
//ûÓÐʲôÒâÒå
// Control Point Characteristic Declaration 2803
[SVC1_IDX_CONTROL_POINT_CHAR] = {(uint8_t*)&att_decl_char,
ATT_UUID_16_LEN, PERM(RD, ENABLE),
0,
0,
NULL},
//Ò»¸ö126bitµÄUUIDÃèÊöµÄ ÌØÕ÷Öµ
// Control Point Characteristic Value //UUID ´ó
[SVC1_IDX_CONTROL_POINT_VAL] = {SVC1_CTRL_POINT_UUID_128,
ATT_UUID_128_LEN, PERM(RD, ENABLE),
DEF_SVC1_CTRL_POINT_CHAR_LEN,
0,
(uint8_t *)&interesting},
//Ò»¸ö16bieµÄUUIDÃèÊöµÄ ÌØÕ÷ÖµÃèÊö
// Control Point Characteristic User Description 2901
[SVC1_IDX_CONTROL_POINT_USER_DESC] = {(uint8_t*)&att_desc_user_desc,
ATT_UUID_16_LEN, PERM(RD, ENABLE),
sizeof(DEF_SVC1_CONTROL_POINT_USER_DESC) - 1,
sizeof(DEF_SVC1_CONTROL_POINT_USER_DESC) - 1,
(uint8_t *) DEF_SVC1_CONTROL_POINT_USER_DESC},
// LED State Characteristic Declaration
[SVC1_IDX_LED_STATE_CHAR] = {(uint8_t*)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE),
0, 0, NULL},
// LED State Characteristic Value
[SVC1_IDX_LED_STATE_VAL] = {SVC1_LED_STATE_UUID_128, ATT_UUID_128_LEN, PERM(WR, ENABLE) | PERM(WRITE_COMMAND, ENABLE),
PERM(RI, ENABLE) | DEF_SVC1_LED_STATE_CHAR_LEN, 0, NULL},
// LED State Characteristic User Description
[SVC1_IDX_LED_STATE_USER_DESC] = {(uint8_t*)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE),
sizeof(DEF_SVC1_LED_STATE_USER_DESC) - 1, sizeof(DEF_SVC1_LED_STATE_USER_DESC) - 1,
(uint8_t *) DEF_SVC1_LED_STATE_USER_DESC},
第三个是这个任务回来的处理
case SVC1_IDX_LED_STATE_VAL:
user_svc1_led_wr_ind_handler(msgid, msg_param, dest_id, src_id);
break;
case SVC1_IDX_ADC_VAL_1_NTF_CFG://µã»÷Çл»ÐÇÐÇ¿ª¹Ø
user_svc1_adc_val_1_cfg_ind_handler(msgid, msg_param, dest_id, src_id);
break;
可以输入 1 0 控制LED开关
另外一个 没有使用 我们研究一下
{GATTC_WRITE_REQ_IND, (ke_msg_func_t)gattc_write_req_ind_handler},
手机写的数据 马上到这里来
gattc_write_req_ind_handler
从 gattc_write_req_ind_handler 再到 user_catch_rest_hndl 是通过 app_entry_point_handler 过来的
user_catch_rest_hndl
那么user_catch_rest_hndl 是怎么调用 app_entry_point_handler 的?
看下面
/* Default State handlers definition. */
const struct ke_msg_handler app_default_state[] =
{
{KE_MSG_DEFAULT_HANDLER, (ke_msg_func_t)app_entry_point_handler},
// {APP_TIMER_API_MES2, (ke_msg_func_t)app_second_timer_handler},
};
只需要发消息即可!右边就会去消费的!
并且 SDK只有这样一个消息!
一个似曾相识燕归来的函数
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;
while (i < (sizeof(app_process_handlers) / sizeof(process_event_func_t)))
{
ASSERT_ERROR(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++;
}
//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);
};
消息ID都过了 消费 这个数组里面全部是函数 每个函数都把msgid拿去看看 不是我的就不管 类似
前面的各位大佬完成消费以后 再给最后的小弟 用户自己完成 app_process_catch_rest_cb(msgid,param, dest_id, src_id);
C基础知识 没有问题的
struct gattc_test
{
uint16_t handle;
uint8_t value[3];
};
struct gattc_test A={0X1234,{3,4,5}};
struct gattc_test B={0};
void my_test(void)
{
memcpy(&B,&A,sizeof(struct gattc_test));
}
但是这个兄弟 用的是 柔性数组啊
struct gattc_write_req_ind
{
/// Handle of the attribute that has to be written
uint16_t handle;
/// offset at which the data has to be written
uint16_t offset;
/// Data length to be written
uint16_t length;
/// Data to be written in attribute database
uint8_t value[__ARRAY_EMPTY];
};
最后是 uint8_t value[]
啥意思
它的sizeof------6 也就是2+2+2 最后的这个value 不参加!!!!!!
那怎么办?再次表示一下我的问题
struct gattc_write_req_ind AAAA={0};
static int gattc_write_req_ind_handler(ke_msg_id_t const msgid,
struct gattc_write_req_ind const *param,
ke_task_id_t const dest_id,
ke_task_id_t const src_id)
{
int msg_status = KE_MSG_CONSUMED;
memcpy(&AAAA,param,sizeof(struct gattc_write_req_ind));---这里AAAA看不到数组!!
}
就是一个数字 这么看呢? 仔细看上面我测试C的小程序
我虽然memcpy了 但是 地址 不能修改的 !!!
那就这样做
uint32_t AAAADDR=0;
uint8_t *AAAADDRP=0;
AAAADDR = (uint32_t)¶m->value[0];
AAAADDRP= (uint8_t *)param->value;
+++++++++++++++++++++++++++++
所以这个是比较符合我们公司这样的业务的 工程对比
可以看到
D:\A_CODE\6.0.10.511\projects\target_apps\ble_examples\mCube_mc36xx_data_notifcation\keil_v5
增加服务 类似外设LED 它是我们追加的 不是官方的SDK
我增加一个服务 XYZG 我是S 点击以后板子不停的notify ++到手机APP