关于RSL10 SDK中Event Kernel部分的代码分析(下)
<p> 首先,把上篇写的重点再回顾一下:<br />(1) 底层 Event Kernel 是通过消息驱动的,消息的接收者称为 task, 但并非是 RTOS 中的任务概念。可以把一个 task 看作是一组消息处理函数(回调函数)的集合。每当执行 <strong>Kernel_Schedule()</strong> 函数的时候,消息逐个以传递给匹配的回调函数的方式被处理。<br />
(2) 一个BLE应用需要创建一个 type 为 TASK_APP 的 task, 和系统内部的 task 进行消息交换,以操作BLE stack.<br />
(3) 消息ID是从接收方 task 的默认消息处理函数集合中选择哪一个的依据。消息ID(包含了task index和消息序号),在SDK中定义。用户可以自行定义包含 TASK_ID_APP 的消息。<br />
(4) 除了消息ID,消息处理函数可以从参数中的发送方 task id、接收方 task id 获取额外的信息。消息包含的数据长度和格式是自由的,由发送和接收双方约定。<br />
</p>
<p> 这篇再用一个例子(peripheral_server)看看 BLE 是怎么工作的。</p>
<p> 在 <strong>BLE_Initialize()</strong> 函数里面,发送了一个 <span style="color:#c0392b;"><strong>GAPM_RESET_CMD</strong></span> 消息:</p>
<pre>
<code class="language-cpp"> /* 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);</code></pre>
<p> 这个消息由 TASK_APP 发送给 TASK_GAPM (task type),带有 struct gapm_reset_cmd 结构类型的数据。手册中能找到这个命令的说明:</p>
<p></p>
<p> 此处发送 <span style="color:#c0392b;"><strong>GAPM_RESET_CMD</strong></span> 消息产生软复位。当完成以后,GAPM 任务会回应一个 <span style="color:#c0392b;"><strong>GAPM_CMP_EVT</strong></span> 消息。</p>
<p> </p>
<p> 在 <strong>BLE_Initialize()</strong> 中调用 <strong>ke_msg_send()</strong> 时,消息只是放入了队列。要待后面 <strong>Kernel_Schedule()</strong> 执行时才会处理消息。况且此时 <strong>ke_create_task(</strong>TASK_APP<strong>)</strong> 都还没有调用,消息通路建立不起来。</p>
<p> 当 <strong>main()</strong> 中 <strong>Kernel_Schedule()</strong> 得到执行之后,GAPM 软复位执行。若 <span style="color:#c0392b;"><strong>GAPM_CMP_EVT</strong></span> 消息回应(给TASK_APP接收),则对应的回调函数是?<br />
用 GDB 查看一下 appm_default_state[] 这个表就清楚了:</p>
<p><span style="font-family:Courier;">(gdb) print appm_default_state<br />
<span style="background-color:#dddddd;">$1 = {{id = 65535, func = 0x1003d1 <Msg_Handler>}</span><br />
<span style="background-color:#dddddd;">{id = 3328, func = 0x100d31 <GAPM_CmpEvt>}</span><br />
<span style="background-color:#dddddd;">{id = 3356, func = 0x100ced <GAPM_ProfileAddedInd>}</span><br />
<span style="background-color:#dddddd;">{id = 3585, func = 0x100fb1 <GAPC_ConnectionReqInd>}</span><br />
<span style="background-color:#dddddd;">{id = 3584, func = 0x100e51 <GAPC_CmpEvt>}</span><br />
<span style="background-color:#dddddd;">{id = 3587, func = 0x100f41 <GAPC_DisconnectInd>}</span><br />
<span style="background-color:#dddddd;">{id = 3594, func = 0x100dd9 <GAPC_GetDevInfoReqInd>}</span><br />
<span style="background-color:#dddddd;">{id = 3601, func = 0x100e55 <GAPC_ParamUpdatedInd>}</span><br />
<span style="background-color:#dddddd;">{id = 3599, func = 0x100e81 <GAPC_ParamUpdateReqInd>}</span><br />
<span style="background-color:#dddddd;">{id = 9217, func = 0x100605 <Batt_EnableRsp_Server>}</span><br />
<span style="background-color:#dddddd;">{id = 9220, func = 0x1005e5 <Batt_LevelNtfCfgInd>}</span><br />
<span style="background-color:#dddddd;">{id = 3091, func = 0x100715 <GATTC_ReadReqInd>}</span><br />
<span style="background-color:#dddddd;">{id = 3093, func = 0x100861 <GATTC_WriteReqInd>}</span><br />
<span style="background-color:#dddddd;">{id = 2817, func = 0x1006e5 <GATTM_AddSvcRsp>}</span><br />
<span style="background-color:#dddddd;">{id = 3072, func = 0x1009a9 <GATTC_CmpEvt>}</span><br />
<span style="background-color:#dddddd;">{id = 3095, func = 0x1009e1 <GATTC_AttInfoReqInd>}</span><br />
<span style="background-color:#dddddd;">{id = 3841, func = 0x1003d5 <APP_Timer>}</span><br />
<span style="background-color:#dddddd;">{id = 3842, func = 0x100469 <LED_Timer>}}</span></span><br />
</p>
<p> 从定义查找 <span style="color:#c0392b;"><strong>GAPM_CMP_EVT</strong></span> 的值, 算得: 3328</p>
<pre>
<code class="language-cpp">enum gapm_msg_id
{
/* Default event */
/// Command Complete event
GAPM_CMP_EVT = TASK_FIRST_MSG(TASK_ID_GAPM),</code></pre>
<p>因此当 <span style="color:#c0392b;"><strong>GAPM_CMP_EVT</strong></span> 消息回应给 TASK_APP 时,<strong>GAPM_CmpEvt()</strong> 函数就会被调用。</p>
<pre>
<code class="language-cpp">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.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.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);
}</code></pre>
<p>它根据消息的数据进行选择,那么看一下手册对 <span style="color:#c0392b;"><strong>GAPM_CMP_EVT</strong></span> 的说明</p>
<p> 这个消息是回应操作完成的意思,通过消息数据中的 operation 字段表明是什么操作完成。因为前面执行的是复位,所以这次当取 GAPM_RESET 分支。</p>
<p><br />
程序又判断了一下状态,如果没有错误,就发送 <span style="color:#c0392b;"><strong>GAPM_SET_DEV_CONFIG_CMD</strong></span> 消息,对 GAPM 进行配置。这个消息的数据很多,在先前初始化过的 gapmConfigCmd 全局变量中。<br />
接着,程序设置了两个定时器,各自分别将在 200ms 之后产生消息 APP_TEST_TIMER, 和 LED_TIMER. 用定时器是在 Event Kernel 环境下产生延迟操作的标准做法。<br />
</p>
<p> 根据手册说明,当 <strong><span style="color:#c0392b;">GAPM_SET_DEV_CONFIG_CMD</span></strong> 消息处理后,也会回应 <span style="color:#c0392b;"><strong>GAPM_CMP_EVT</strong></span> 消息,此时,上面 <strong>GAPM_CmpEvt()</strong> 函数中的另一个条件分支得以执行:调用了 <strong>Service_Add()</strong> 函数。</p>
<pre>
<code class="language-cpp">bool Service_Add(void)
{
/* Check if another should be added in the database */
if (appm_add_svc_func_list.next_svc] != NULL)
{
/* Call the function used to add the required service */
appm_add_svc_func_list.next_svc] ();
/* Select the next service to add */
ble_env.next_svc++;
return (true);
}
return (false);
}</code></pre>
<p> 这个 <strong>Service_Add()</strong> 函数的写法比较特别,经分析,在第一次调用它的时候,将调用 <strong>Batt_ServiceAdd_Server()</strong> 函数。省略一些细节列出其代码如下:</p>
<pre>
<code class="language-cpp">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);
}</code></pre>
<p> </p>
<p> 可见,添加 Battery Service 是通过发送 <span style="color:#c0392b;"><strong>GAPM_PROFILE_TASK_ADD_CMD</strong></span> 消息实现的。</p>
<p> 这个消息会产生两个回应,第一个回应将引起回调 <strong>GAPM_ProfileAddedInd()</strong> 函数。</p>
<pre>
<code class="language-cpp">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.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.state = APPM_READY;
}
/* No more services to add, start advertising */
Advertising_Start();
}
}
return (KE_MSG_CONSUMED);
}</code></pre>
<p> </p>
<p> 其中再次调用 <strong>Service_Add()</strong> 函数,这是第二次,将会引起调用 <strong>CustomService_ServiceAdd()</strong> 函数。在这个函数中通过向 TASK_GATTM 发送 <span style="color:#c0392b;"><strong>GATTM_ADD_SVC_REQ</strong></span> 消息添加自定义的服务。</p>
<p> GATTM 处理消息后将会回应 <span style="color:#c0392b;"><strong>GATTM_ADD_SVC_RSP</strong></span>, 再由 <strong>GATTC_AddSvcRsp()</strong> 函数接收。</p>
<pre>
<code class="language-cpp">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.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.state = APPM_READY;
}
Advertising_Start();
}
return (KE_MSG_CONSUMED);
}</code></pre>
<p> </p>
<p> 第三次调用 <strong>Service_Add()</strong> 的时候,已经没有可添加的服务了,于是 <strong>Advertising_Start()</strong> 函数得到执行。</p>
<p> 可以看 <strong>Advertising_Start()</strong> 的实现,里面向 GAPM 发送了 <span style="color:#c0392b;"><strong>GAPM_START_ADVERTISE_CMD</strong></span> 消息。<br />
这样 BLE 进入 advertising 状态,无线广播开始。在应用程序看来这是“后台”在进行的。只有特定事件产生了消息,才会执行回调函数。例如,当有 BLE central 设备发起连接之后,GAPM 将回应 <span style="color:#c0392b;"><strong>GAPC_CONNECTIEN_REQ_IND</strong></span> 消息,被 <strong>GAPC_ConnectionReqInd()</strong> 函数处理。</p>
<pre>
<code class="language-cpp">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.state != APPM_CONNECTED)
{
break;
}
}
PRINTF("__GAPC_CONNECTION_REQ_IND\n");
/* Set connection index */
ble_env.conidx = KE_IDX_GET(src_id);
/* Check if the received connection handle was valid */
if (ble_env.conidx != GAP_INVALID_CONIDX)
{
/* Change peer state in the app environment structure */
ble_env.state = APPM_CONNECTED;
/* Retrieve the connection info from the parameters */
ble_env.conhdl = param->conhdl;
Send_Connection_Confirmation(device_indx);
BLE_SetServiceState(true, device_indx);
}
}
return (KE_MSG_CONSUMED);
}</code></pre>
<p> </p>
<p> 在其中调用的 <strong>Send_Connection_Confirmation()</strong> 函数中,通过发送 <span style="color:#c0392b;"><strong>GAPC_CONNECTION_CFM</strong></span> 消息,确认建立 BLE 连接。</p>
<pre>
<code class="language-cpp">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.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);
}</code></pre>
<p> </p>
<p> 还有许多 BLE 服务的访问,也是类似的,通过发送命令消息、接收应答消息实现。这里就不继续列举了。</p>
<p> 如此这般,就将步骤拆散到单独的回调函数里去了。你们觉得绕吗?<img height="50" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan57.gif" width="49" /></p>
<p>楼主用了一个例(peripheral_server)大家讲了BLE 是怎么工作的了</p>
<p>不错</p>
<p>请问解释各种命令的图片是来自那个资料</p>
爱吃土豆 发表于 2021-7-2 18:15
请问解释各种命令的图片是来自那个资料
<p>文档压缩包里面,CEVA目录下。</p>
<p>楼主追踪的两贴,我看完 了 ,于是 我看 完了。。已经绕混了。。</p>
页:
[1]