2945|1

1382

帖子

2

TA的资源

五彩晶圆(初级)

楼主
 

【CC1352P测评】BLE程序的框架 [复制链接]

  从 SDK 例子的目录树结构上看,CC1352 的 BLE 程序是必须要用到 TI-RTOS 的,而且 SDK 的例子里面没有 BLE 的 GCC 工程。于是我不得已只能用慢吞吞的 CCS 来编译一个试试。尽管 "Simple peripheral" 编译和下载都成功了,手机也能发现和连接板子生成的 BLE 设备,我遇到了一个大问题——CCS中无法设置断点调试,只能单步指令执行(还不如GDB呢,但是CCS生成的ELF和GCC不一样),所以跟踪执行过程来分析代码就行不通了。
  我阅读了 BLE SDK 的文档,其中并未提到支持GCC, 那我就不要试图用 GCC 去处理这个工程了。有些遗憾,下面只能直接看代码分析。

  和已经分析过的例子比起来,Simple_periphral 的主函数多了一些东西:

int main()
{
  /* Register Application callback to trap asserts raised in the Stack */
  RegisterAssertCback(AssertHandler);
  Board_initGeneral();
  // Enable iCache prefetching
  VIMSConfigure(VIMS_BASE, TRUE, TRUE);
  // Enable cache
  VIMSModeSet(VIMS_BASE, VIMS_MODE_ENABLED);
  /* Update User Configuration of the stack */
  user0Cfg.appServiceInfo->timerTickPeriod = Clock_tickPeriod;
  user0Cfg.appServiceInfo->timerMaxMillisecond  = ICall_getMaxMSecs();
  /* Initialize ICall module */
  ICall_init();
  /* Start tasks of external images - Priority 5 */
  ICall_createRemoteTasks();
  SimplePeripheral_createTask();
  /* enable interrupts and start SYS/BIOS */
  BIOS_start();
  return 0;
}

  主要是多了 ICall_init() 以及类似的其它 ICall_... 的东西。ICall 是个啥只有看文档才知道,这里先不管。后面 SimplePeripheral_createTask() 才是要创建这个 BLE 应用的任务,它在另外一个源文件中。

void SimplePeripheral_createTask(void)
{
  Task_Params taskParams;
  // Configure task
  Task_Params_init(&taskParams);
  taskParams.stack = spTaskStack;
  taskParams.stackSize = SP_TASK_STACK_SIZE;
  taskParams.priority = SP_TASK_PRIORITY;
  Task_construct(&spTask, SimplePeripheral_taskFxn, &taskParams, NULL);
}

  显然,任务的主函数是 SimplePeripheral_taskFxn(), 还好写得并不长:

static void SimplePeripheral_taskFxn(UArg a0, UArg a1)
{
  // Initialize application
  SimplePeripheral_init();
  // Application main loop
  for (;;)
  {
    uint32_t events;
    // Waits for an event to be posted associated with the calling thread.
    // Note that an event associated with a thread is posted when a
    // message is queued to the message receive queue of the thread
    events = Event_pend(syncEvent, Event_Id_NONE, SP_ALL_EVENTS,
                        ICALL_TIMEOUT_FOREVER);
    if (events)
    {
      ICall_EntityID dest;
      ICall_ServiceEnum src;
      ICall_HciExtEvt *pMsg = NULL;
      // Fetch any available messages that might have been sent from the stack
      if (ICall_fetchServiceMsg(&src, &dest,
                                (void **)&pMsg) == ICALL_ERRNO_SUCCESS)
      {
        uint8 safeToDealloc = TRUE;
        if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
        {
          ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg;
          // Check for BLE stack events first
          if (pEvt->signature != 0xffff)
          {
            // Process inter-task message
            safeToDealloc = SimplePeripheral_processStackMsg((ICall_Hdr *)pMsg);
          }
        }
        if (pMsg && safeToDealloc)
        {
          ICall_freeMsg(pMsg);
        }
      }
      // If RTOS queue is not empty, process app message.
      if (events & SP_QUEUE_EVT)
      {
        while (!Queue_empty(appMsgQueueHandle))
        {
          spEvt_t *pMsg = (spEvt_t *)Util_dequeueMsg(appMsgQueueHandle);
          if (pMsg)
          {
            // Process message.
            SimplePeripheral_processAppMsg(pMsg);
            // Free the space from the message.
            ICall_free(pMsg);
          }
        }
      }
    }
  }
}

  先是执行一个初始化函数,后面就进入循环,等待事件并处理事件。不究细节的话,这个程序的结构看起来还是简单的。实际上,把另外一个例子 "Simple central" 拿来比较,它们的 main 函数和任务主函数写法是相同的。在这里又遇到 ICall_... 的东西了,等等再看吧。先看下初始化做了哪些工作。
  SimplePeripheral_init() 这个函数比较长,没有必要贴出来。在当中首先是执行了
  ICall_registerApp(&selfEntity, &syncEvent);
  两个参数都是指针,分别指向两个全局变量,调用后它们即被初始化,在后面程序中会用到。下一步是
  appMsgQueueHandle = Util_constructQueue(&appMsgQueue);
  字面意思是创建消息队列。后一个操作是
  Util_constructClock(&clkPeriodic, SimplePeripheral_clockHandler, SP_PERIODIC_EVT_PERIOD, 0, false, (UArg)&argPeriodic);
  这里创建了一个软件时钟。
  再后面就开始 GAP, GATT 服务的配置了,有很多个调用。来看一个
  GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
  的具体实现:它在 BLE stack 的源代码 icall_api.c 中定义:

bStatus_t GGS_SetParameter(uint8 param, uint8 len, void *value)
{
  return profileSetParameter(param, len, value, DISPATCH_GAP_GATT_SERV,
                             DISPATCH_PROFILE_SET_PARAM, matchGGSSetParamCS);
}

static bStatus_t profileSetParameter(uint8 param, uint8 len, void *pValue,
                                     uint8_t subgrp, uint8_t cmdId,
                                     ICall_MsgMatchFn matchCSFn)
{
  // Allocate message buffer space
  ICall_ProfileSetParam *msg =
    (ICall_ProfileSetParam *)ICall_allocMsg(sizeof(ICall_ProfileSetParam));
  if (msg)
  {
    setDispatchCmdEvtHdr(&msg->hdr, subgrp, cmdId);
    // copy param ID, len, value
    msg->paramIdLenVal.paramId = param;
    msg->paramIdLenVal.len = len;
    msg->paramIdLenVal.pValue = pValue;
    // Send the message
    return sendWaitMatchCS(ICall_getEntityId(), msg, matchCSFn);
  }
  return MSG_BUFFER_NOT_AVAIL;
}

  它是通过 ICall 先分配了一个消息,再按参数填写消息内容,最后发送消息。其中用的 sendWaitMatchCS() 函数里面调用了 ICall_sendServiceMsg() 函数。这个函数定义为

static ICall_Errno
ICall_sendServiceMsg(ICall_EntityID src,
                     ICall_ServiceEnum dest,
                     ICall_MSGFormat format, void *msg)
{
  ICall_SendArgs args;
  args.hdr.service = ICALL_SERVICE_CLASS_PRIMITIVE;
  args.hdr.func = ICALL_PRIMITIVE_FUNC_SEND_SERV_MSG;
  args.src = src;
  args.dest.servId = dest;
  args.format = format;
  args.msg = msg;
  return ICall_dispatcher((ICall_FuncArgsHdr *)&args);
}

  也就是最终将BLE服务请求通过 ICall_dispatcher() 发送。那么,消息的接收者是谁呢?ICall, 这到底是个啥?在 BLE stack 源码的 icall.c 中,可以找到很多相关函数的实现。例如在 main() 中就用到的 ICall_init() 

void ICall_init(void)
{
  size_t i;
  for (i = 0; i < ICALL_MAX_NUM_TASKS; i++)
  {
    ICall_tasks[i].task = NULL;
    ICall_tasks[i].queue = NULL;
  }
  for (i = 0; i < ICALL_MAX_NUM_ENTITIES; i++)
  {
    ICall_entities[i].service = ICALL_SERVICE_CLASS_INVALID_ENTRY;
  }
#ifndef ICALL_JT
  /* Initialize primitive service */
  ICall_initPrim();
#else
  /* Initialize heap */
  ICall_heapInit();
#endif
}

  这暗示了,ICall 背后是若干个 TI-RTOS 的任务。在 SDK BLE5 stack 文档中有一个图举了这样的例子:

ICall 是一个中间层,应用程序对 BLE stack 的访问都通过 ICall 来实现。因为有 TI-RTOS 作为基础,应用程序就成了消息(事件)驱动型的。协议栈的部分也由 ICall 管理了,我们不用去管它需要创建多少 RTOS 任务,不用直接和那些任务通信,更不用管底层的两个CPU之间的信息交互(无线功能有一个Cortex-m0核在负责)。

 

  因为有 ICall 在,基于 CC1352 (也包括CC26xx等其它MCU)进行 BLE 开发的时候,只要按照 SDK 例子做一个程序框架,再编写 BLE 事件处理程序就可以了,整个工程只需要少数几个源文件(其余都是库)。SDK 提供的几个工程例子代码读起来不难,虽然内容太多了不能熟练到从零开始自己写,TI 的 BLE stack 文档还是很详细的。看懂一两个例子之后再改写一个自己的 BLE 设备难度不会很大。

 

此帖出自无线连接论坛
点赞 关注
 

回复
举报

1382

帖子

2

TA的资源

五彩晶圆(初级)

沙发
 

【CC1352P测评】BLE程序的框架

图片重新贴一下。

此帖出自无线连接论坛
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
快速回复 返回顶部 返回列表