cruelfox 发表于 2021-6-13 19:01

关于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 />
&nbsp;</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-&gt;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>&nbsp;</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 &lt;Msg_Handler&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3328, &nbsp; &nbsp;func = 0x100d31 &lt;GAPM_CmpEvt&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3356, &nbsp; &nbsp;func = 0x100ced &lt;GAPM_ProfileAddedInd&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3585, &nbsp; &nbsp;func = 0x100fb1 &lt;GAPC_ConnectionReqInd&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3584, &nbsp; &nbsp;func = 0x100e51 &lt;GAPC_CmpEvt&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3587, &nbsp; &nbsp;func = 0x100f41 &lt;GAPC_DisconnectInd&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3594, &nbsp; &nbsp;func = 0x100dd9 &lt;GAPC_GetDevInfoReqInd&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3601, &nbsp; &nbsp;func = 0x100e55 &lt;GAPC_ParamUpdatedInd&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3599, &nbsp; &nbsp;func = 0x100e81 &lt;GAPC_ParamUpdateReqInd&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 9217, &nbsp; &nbsp;func = 0x100605 &lt;Batt_EnableRsp_Server&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 9220, &nbsp; &nbsp;func = 0x1005e5 &lt;Batt_LevelNtfCfgInd&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3091, &nbsp; &nbsp;func = 0x100715 &lt;GATTC_ReadReqInd&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3093, &nbsp; &nbsp;func = 0x100861 &lt;GATTC_WriteReqInd&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 2817, &nbsp; &nbsp;func = 0x1006e5 &lt;GATTM_AddSvcRsp&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3072, &nbsp; &nbsp;func = 0x1009a9 &lt;GATTC_CmpEvt&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3095, &nbsp; &nbsp;func = 0x1009e1 &lt;GATTC_AttInfoReqInd&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3841, &nbsp; &nbsp;func = 0x1003d5 &lt;APP_Timer&gt;}</span><br />
<span style="background-color:#dddddd;">{id = 3842, func = 0x100469 &lt;LED_Timer&gt;}}</span></span><br />
&nbsp;</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-&gt;operation)
    {
      /* A reset has occurred, configure the device and
         * start a kernel timer for the application */
      case (GAPM_RESET):
      {
            if (param-&gt;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 &lt; 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 />
&nbsp;</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>&nbsp;</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 &lt; 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>&nbsp;</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 &lt; NUM_MASTERS; i++)
    {
      cs_env.start_hdl = param-&gt;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 &lt; NUM_MASTERS; i++)
      {
            ble_env.state = APPM_READY;
      }
      Advertising_Start();
    }

    return (KE_MSG_CONSUMED);
}</code></pre>

<p>&nbsp;</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 状态,无线广播开始。在应用程序看来这是&ldquo;后台&rdquo;在进行的。只有特定事件产生了消息,才会执行回调函数。例如,当有 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() &lt; NUM_MASTERS)
    {
      uint8_t device_indx;

      /* Search for the first disconnected link */
      for (device_indx = 0; device_indx &lt; 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-&gt;conhdl;

            Send_Connection_Confirmation(device_indx);
            BLE_SetServiceState(true, device_indx);
      }
    }

    return (KE_MSG_CONSUMED);
}</code></pre>

<p>&nbsp;</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-&gt;ltk_present = false;

    cfm-&gt;svc_changed_ind_enable = 0;
    cfm-&gt;pairing_lvl = GAP_PAIRING_BOND_UNAUTH;

    /* Send the message */
    ke_msg_send(cfm);
}</code></pre>

<p>&nbsp;</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>

Jacktang 发表于 2021-6-13 21:23

<p>楼主用了一个例(peripheral_server)大家讲了BLE 是怎么工作的了</p>

<p>不错</p>

爱吃土豆 发表于 2021-7-2 18:15

<p>请问解释各种命令的图片是来自那个资料</p>

cruelfox 发表于 2021-7-2 18:52

爱吃土豆 发表于 2021-7-2 18:15
请问解释各种命令的图片是来自那个资料

<p>文档压缩包里面,CEVA目录下。</p>

justd0 发表于 2021-7-3 00:12

<p>楼主追踪的两贴,我看完 了 ,于是 我看&nbsp; &nbsp;完了。。已经绕混了。。</p>
页: [1]
查看完整版本: 关于RSL10 SDK中Event Kernel部分的代码分析(下)