首先,把上篇写的重点再回顾一下:
(1) 底层 Event Kernel 是通过消息驱动的,消息的接收者称为 task, 但并非是 RTOS 中的任务概念。可以把一个 task 看作是一组消息处理函数(回调函数)的集合。每当执行 Kernel_Schedule() 函数的时候,消息逐个以传递给匹配的回调函数的方式被处理。
(2) 一个BLE应用需要创建一个 type 为 TASK_APP 的 task, 和系统内部的 task 进行消息交换,以操作BLE stack.
(3) 消息ID是从接收方 task 的默认消息处理函数集合中选择哪一个的依据。消息ID(包含了task index和消息序号),在SDK中定义。用户可以自行定义包含 TASK_ID_APP 的消息。
(4) 除了消息ID,消息处理函数可以从参数中的发送方 task id、接收方 task id 获取额外的信息。消息包含的数据长度和格式是自由的,由发送和接收双方约定。
这篇再用一个例子(peripheral_server)看看 BLE 是怎么工作的。
在 BLE_Initialize() 函数里面,发送了一个 GAPM_RESET_CMD 消息:
/* Reset the stack */
cmd = KE_MSG_ALLOC(GAPM_RESET_CMD, TASK_GAPM, TASK_APP, gapm_reset_cmd);
cmd->operation = GAPM_RESET;
/* Send the message */
ke_msg_send(cmd);
这个消息由 TASK_APP 发送给 TASK_GAPM (task type),带有 struct gapm_reset_cmd 结构类型的数据。手册中能找到这个命令的说明:
此处发送 GAPM_RESET_CMD 消息产生软复位。当完成以后,GAPM 任务会回应一个 GAPM_CMP_EVT 消息。
在 BLE_Initialize() 中调用 ke_msg_send() 时,消息只是放入了队列。要待后面 Kernel_Schedule() 执行时才会处理消息。况且此时 ke_create_task(TASK_APP) 都还没有调用,消息通路建立不起来。
当 main() 中 Kernel_Schedule() 得到执行之后,GAPM 软复位执行。若 GAPM_CMP_EVT 消息回应(给TASK_APP接收),则对应的回调函数是?
用 GDB 查看一下 appm_default_state[] 这个表就清楚了:
(gdb) print appm_default_state
$1 = {{id = 65535, func = 0x1003d1 <Msg_Handler>}
{id = 3328, func = 0x100d31 <GAPM_CmpEvt>}
{id = 3356, func = 0x100ced <GAPM_ProfileAddedInd>}
{id = 3585, func = 0x100fb1 <GAPC_ConnectionReqInd>}
{id = 3584, func = 0x100e51 <GAPC_CmpEvt>}
{id = 3587, func = 0x100f41 <GAPC_DisconnectInd>}
{id = 3594, func = 0x100dd9 <GAPC_GetDevInfoReqInd>}
{id = 3601, func = 0x100e55 <GAPC_ParamUpdatedInd>}
{id = 3599, func = 0x100e81 <GAPC_ParamUpdateReqInd>}
{id = 9217, func = 0x100605 <Batt_EnableRsp_Server>}
{id = 9220, func = 0x1005e5 <Batt_LevelNtfCfgInd>}
{id = 3091, func = 0x100715 <GATTC_ReadReqInd>}
{id = 3093, func = 0x100861 <GATTC_WriteReqInd>}
{id = 2817, func = 0x1006e5 <GATTM_AddSvcRsp>}
{id = 3072, func = 0x1009a9 <GATTC_CmpEvt>}
{id = 3095, func = 0x1009e1 <GATTC_AttInfoReqInd>}
{id = 3841, func = 0x1003d5 <APP_Timer>}
{id = 3842, func = 0x100469 <LED_Timer>}}
从定义查找 GAPM_CMP_EVT 的值, 算得: 3328
enum gapm_msg_id
{
/* Default event */
/// Command Complete event
GAPM_CMP_EVT = TASK_FIRST_MSG(TASK_ID_GAPM),
因此当 GAPM_CMP_EVT 消息回应给 TASK_APP 时,GAPM_CmpEvt() 函数就会被调用。
int GAPM_CmpEvt(ke_msg_id_t const msg_id,
struct gapm_cmp_evt const *param,
ke_task_id_t const dest_id, ke_task_id_t const src_id)
{
struct gapm_set_dev_config_cmd *cmd;
switch (param->operation)
{
/* A reset has occurred, configure the device and
* start a kernel timer for the application */
case (GAPM_RESET):
{
if (param->status == GAP_ERR_NO_ERROR)
{
/* Set the device configuration */
cmd = KE_MSG_ALLOC(GAPM_SET_DEV_CONFIG_CMD, TASK_GAPM, TASK_APP,
gapm_set_dev_config_cmd);
memcpy(cmd, gapmConfigCmd,
sizeof(struct gapm_set_dev_config_cmd));
free(gapmConfigCmd);
/* Send message */
ke_msg_send(cmd);
/* Start a timer to be used as a periodic tick timer for
* application */
ke_timer_set(APP_TEST_TIMER, TASK_APP, TIMER_200MS_SETTING);
/* Start LED timer */
ke_timer_set(LED_TIMER, TASK_APP, TIMER_200MS_SETTING);
}
}
break;
/* Device configuration updated */
case (GAPM_SET_DEV_CONFIG):
{
/* Start creating the GATT database */
ble_env[0].state = APPM_CREATE_DB;
/* Add the first required service in the database */
if (!Service_Add())
{
for (unsigned int i = 0; i < NUM_MASTERS; i++)
{
/* If there are no more services to add, go to the ready
* state */
ble_env[i].state = APPM_READY;
}
/* Start advertising since there are no services to add
* to the attribute database */
Advertising_Start();
}
}
break;
default:
{
/* No action required for other operations */
}
break;
}
return (KE_MSG_CONSUMED);
}
它根据消息的数据进行选择,那么看一下手册对 GAPM_CMP_EVT 的说明
这个消息是回应操作完成的意思,通过消息数据中的 operation 字段表明是什么操作完成。因为前面执行的是复位,所以这次当取 GAPM_RESET 分支。
程序又判断了一下状态,如果没有错误,就发送 GAPM_SET_DEV_CONFIG_CMD 消息,对 GAPM 进行配置。这个消息的数据很多,在先前初始化过的 gapmConfigCmd 全局变量中。
接着,程序设置了两个定时器,各自分别将在 200ms 之后产生消息 APP_TEST_TIMER, 和 LED_TIMER. 用定时器是在 Event Kernel 环境下产生延迟操作的标准做法。
根据手册说明,当 GAPM_SET_DEV_CONFIG_CMD 消息处理后,也会回应 GAPM_CMP_EVT 消息,此时,上面 GAPM_CmpEvt() 函数中的另一个条件分支得以执行:调用了 Service_Add() 函数。
bool Service_Add(void)
{
/* Check if another should be added in the database */
if (appm_add_svc_func_list[ble_env[0].next_svc] != NULL)
{
/* Call the function used to add the required service */
appm_add_svc_func_list[ble_env[0].next_svc] ();
/* Select the next service to add */
ble_env[0].next_svc++;
return (true);
}
return (false);
}
这个 Service_Add() 函数的写法比较特别,经分析,在第一次调用它的时候,将调用 Batt_ServiceAdd_Server() 函数。省略一些细节列出其代码如下:
void Batt_ServiceAdd_Server(void)
{
struct bass_db_cfg *db_cfg;
struct gapm_profile_task_add_cmd *req =
KE_MSG_ALLOC_DYN(GAPM_PROFILE_TASK_ADD_CMD,
TASK_GAPM,
TASK_APP,
gapm_profile_task_add_cmd,
sizeof(struct bass_db_cfg));
/* Fill message */
......
/* Send the message */
ke_msg_send(req);
}
可见,添加 Battery Service 是通过发送 GAPM_PROFILE_TASK_ADD_CMD 消息实现的。
这个消息会产生两个回应,第一个回应将引起回调 GAPM_ProfileAddedInd() 函数。
int GAPM_ProfileAddedInd(ke_msg_id_t const msg_id,
struct gapm_profile_added_ind const *param,
ke_task_id_t const dest_id,
ke_task_id_t const src_id)
{
/* If the application is creating its attribute database, continue to add
* services; otherwise do nothing. */
if (ble_env[0].state == APPM_CREATE_DB)
{
PRINTF("__GAPM_PROFILE_ADDED_IND\n");
/* Add the next requested service */
if (!Service_Add())
{
for (unsigned int i = 0; i < NUM_MASTERS; i++)
{
/* If there are no more services to add, go to the ready state
* */
ble_env[i].state = APPM_READY;
}
/* No more services to add, start advertising */
Advertising_Start();
}
}
return (KE_MSG_CONSUMED);
}
其中再次调用 Service_Add() 函数,这是第二次,将会引起调用 CustomService_ServiceAdd() 函数。在这个函数中通过向 TASK_GATTM 发送 GATTM_ADD_SVC_REQ 消息添加自定义的服务。
GATTM 处理消息后将会回应 GATTM_ADD_SVC_RSP, 再由 GATTC_AddSvcRsp() 函数接收。
int GATTM_AddSvcRsp(ke_msg_id_t const msg_id,
struct gattm_add_svc_rsp const *param,
ke_task_id_t const dest_id,
ke_task_id_t const src_id)
{
for (unsigned int i = 0; i < NUM_MASTERS; i++)
{
cs_env[i].start_hdl = param->start_hdl;
}
PRINTF("__CUSTOM SERVICE ADDED\n");
/* Add the next requested service */
if (!Service_Add())
{
/* All services have been added, go to the ready state
* and start advertising */
for (unsigned int i = 0; i < NUM_MASTERS; i++)
{
ble_env[i].state = APPM_READY;
}
Advertising_Start();
}
return (KE_MSG_CONSUMED);
}
第三次调用 Service_Add() 的时候,已经没有可添加的服务了,于是 Advertising_Start() 函数得到执行。
可以看 Advertising_Start() 的实现,里面向 GAPM 发送了 GAPM_START_ADVERTISE_CMD 消息。
这样 BLE 进入 advertising 状态,无线广播开始。在应用程序看来这是“后台”在进行的。只有特定事件产生了消息,才会执行回调函数。例如,当有 BLE central 设备发起连接之后,GAPM 将回应 GAPC_CONNECTIEN_REQ_IND 消息,被 GAPC_ConnectionReqInd() 函数处理。
int GAPC_ConnectionReqInd(ke_msg_id_t const msg_id,
struct gapc_connection_req_ind const *param,
ke_task_id_t const dest_id,
ke_task_id_t const src_id)
{
if (Connected_Peer_Num() < NUM_MASTERS)
{
uint8_t device_indx;
/* Search for the first disconnected link */
for (device_indx = 0; device_indx < NUM_MASTERS; device_indx++)
{
if (ble_env[device_indx].state != APPM_CONNECTED)
{
break;
}
}
PRINTF("__GAPC_CONNECTION_REQ_IND\n");
/* Set connection index */
ble_env[device_indx].conidx = KE_IDX_GET(src_id);
/* Check if the received connection handle was valid */
if (ble_env[device_indx].conidx != GAP_INVALID_CONIDX)
{
/* Change peer state in the app environment structure */
ble_env[device_indx].state = APPM_CONNECTED;
/* Retrieve the connection info from the parameters */
ble_env[device_indx].conhdl = param->conhdl;
Send_Connection_Confirmation(device_indx);
BLE_SetServiceState(true, device_indx);
}
}
return (KE_MSG_CONSUMED);
}
在其中调用的 Send_Connection_Confirmation() 函数中,通过发送 GAPC_CONNECTION_CFM 消息,确认建立 BLE 连接。
void Send_Connection_Confirmation(uint8_t device_indx)
{
struct gapc_connection_cfm *cfm;
/* Allocate connection confirmation message */
cfm = KE_MSG_ALLOC(GAPC_CONNECTION_CFM,
KE_BUILD_ID(TASK_GAPC, ble_env[device_indx].conidx),
KE_BUILD_ID(TASK_APP, device_indx), gapc_connection_cfm);
cfm->ltk_present = false;
cfm->svc_changed_ind_enable = 0;
cfm->pairing_lvl = GAP_PAIRING_BOND_UNAUTH;
/* Send the message */
ke_msg_send(cfm);
}
还有许多 BLE 服务的访问,也是类似的,通过发送命令消息、接收应答消息实现。这里就不继续列举了。
如此这般,就将步骤拆散到单独的回调函数里去了。你们觉得绕吗?
引用: 爱吃土豆 发表于 2021-7-2 18:15 请问解释各种命令的图片是来自那个资料
文档压缩包里面,CEVA目录下。