Bluetooth—-ICall module and TI RTOS in CC2640

TIRTOS—-ICall module in CC2640

  • 1.Introduction to ICall module
  • 2.Initialization of ICall module
  • 3.ICall thread synchronization
  • 4.ICall message flow analysis
  • 5.ICall calling process

I have been using TI’s CC2640 Bluetooth for some time. I usually do application layer development and don’t pay much attention to the ICall module. But after reading this article, I gained a lot. I combined it with some other learning materials to make some summaries.

1.Introduction to ICall module

ICall is an integral part of the application and protocol stack that implements the functionality of TI’s Bluetooth chip.

Block diagram of TI Bluetooth chip
The ICALL module implementation includes communication between application and BLE protocol stack, realizing message passing between multiple tasks, the sender applying for memory, and the receiver Release memoryThe above sentence generally summarizes the role of the ICall module:
1. Message passing and thread synchronization
1.1 A client entity and a server entity realize the message passing of the protocol stack through ICall
1.2 ICall completes thread synchronization by sending a blocking message to another task in the message queue.
2. Heap allocation and management
ICall provides applications with a global heap management API for dynamic memory allocation.

2.ICall module initialization

ICall is initialized by calling ICall_init() in the main function. This function initializes the ICall primary service, such as the stack manager, and calls ICall_createRemoteTasks() to create but not start the Bluetooth protocol stack task. Before using ICall, you must first register ICall.

 ICall_init(); /* Initialize ICall module */
  ICall _createRemoteTasks ();/* Create the protocol stack operation as a task */

Before using the ICall protocol service, the server and client complete registration and registration respectively.

 / * ICALL server registration * /
/* The BLE protocol uses ICALL_SERVICE_CLASS_BLE as the identifier for message interaction of the Bluetooth protocol stack ICALL */
ICall _enrollService (ICALL _SERVICE_CLASS_BLE , NULL , &entity , &syncHandle );
 /*ICALL client registration*/
/*ICALL server passes messages through two variables.
The syncEvent parameter represents the event identifier.
selfEntity represents the purpose of processing the message and is also the source address of the client entity for future communications.
Clients registered for ICALL use unique syncEvent and selfEntity . */
   ICall _registerApp (& selfEntity, & syncEvent);

3.ICall thread synchronization

ICALL uses TI-RTOS events for thread synchronization.

The ICall message queue retains specific event flags
#define ICALL_MSG_EVENT_ID Event_Id_31

The client or server remains blocked until the message is received. ICall uses the blocking API to keep the task blocked until the associated semaphore is Posted

The Event_pend function means blocking the current task and waiting for an event flag to occur.

UInt Event_pend(Event_Handle handle, UInt andMask, UInt orMask, UInt32 timeout);
handle is a constructed Event_Handle event handle (identifier).
andMask and orMask select event flags for the user to block/suspend on.
timeout is the timeout period in milliseconds. If the event has not been posted within this timeout, the function will return.

Event_post is used by the client/server to activate a blocking task into a running state.

void Event_post(Event_Handle handle, UInt eventMask);

The above event handle handle is obtained by calling ICall _enrollService() and ICall _registerApp() on the server side.

4.ICall message flow analysis

1.ICall server creation

Regarding protocol stack task entities: distinctions between TI software architectures
The independent mirroring method uses tools to automatically calculate the first address as the entry address of the protocol stack task.
The static link library method is to directly link the entry address of the protocol stack as the task entity of the protocol stack task.

2. Server registration

ICALL_SERVICE_CLASS_BLE_MSG as the source address of the server
ICall_enrollService(ICALL_SERVICE_CLASS_BLE_MSG,ICall_ServiceFunc) osal_service_entry,
& amp;osal_entity, & amp;osal_syncHandle)

3. Client registration,

selfEntity as the source address of client Icall communication
ICall_registerApp( & amp;selfEntity, & amp;syncEvent);

4. Application sends message

All application Icall message sending is encapsulated in the icall_directAPI function.
4.1icall_direct encapsulates the message to be delivered

4.1 Call ICall_sendServiceMsg

Call_sendServiceMsg(ICall_getEntityId(),service,ICALL_MSG_FORMAT_DIRECT_API_ID, & amp;(liteMsg.msg));
? ICall_getEntityId(): Task ID of the application calling the API
? service: for ICALL_SERVICE_CLASS_BLE_MSG
? Function prototype of ICall_sendServiceMsg
○ ICall_sendServiceMsg(ICall_EntityID src, ICall_ServiceEnum dest,ICall_MSGFormat format, void *msg)
○ ICall_EntityID client address, application calling API
○ ICall_ServiceEnum server-side address, protocol stack

4.2 Call ICall_send
ICall_send directly puts the message into the message queue of the destination Icall message entity, and triggers an event to notify the task to wake up to parse and process the message

?ICall_msgEnqueue( & amp;ICall_entities[dest].task->queue, msg);
?ICALL_SYNC_HANDLE_POST(ICall_entities[dest].task->syncHandle);# `

5.ICall calling process

In summary, the protocol stack triggers applications: Callback functions, message enqueuing, asynchronous events, and blocking stops
The following takes event acceptance at the GATT layer as an example.
1. Callback function definition

static void SimplePeripheral_charValueChangeCB(uint8_t paramId)
{<!-- -->
  uint8_t *pValue = ICall_malloc(sizeof(uint8_t));
  if(pValue)
  {<!-- -->
    *pValue = paramId;
    if (SimplePeripheral_enqueueMsg(SP_CHAR_CHANGE_EVT, pValue) != SUCCESS)
    {<!-- -->
      ICall_free(pValue);
    }
  }
}
static simpleProfileCBs_t SimplePeripheral_simpleProfileCBs =
{<!-- -->
  SimplePeripheral_charValueChangeCB // Simple GATT Characteristic value change callback
};

2. Bind callback function

 SimpleProfile_RegisterAppCBs( & amp;SimplePeripheral_simpleProfileCBs);

3. When the GATT layer receives the Bluetooth data, SP_CHAR_CHANGE_EVT joins the queue and is executed after the app task wakes up.

if (events)
    {<!-- -->
      ICall_EntityID dest;
      ICall_ServiceEnum src;
      ICall_HciExtEvt *pMsg = NULL;
      //Prioritize checking protocol stack messages
      if (ICall_fetchServiceMsg( & amp;src, & amp;dest,
                                (void **) & amp;pMsg) == ICALL_ERRNO_SUCCESS)
      {<!-- -->
        uint8 safeToDealloc = TRUE;
        if ((src == ICALL_SERVICE_CLASS_BLE) & amp; & amp; (dest == selfEntity))
        {<!-- -->
          ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg;
          if (pEvt->signature != 0xffff)
          {<!-- -->
            safeToDealloc = SimplePeripheral_processStackMsg((ICall_Hdr *)pMsg);
          }
        }
        if (pMsg & amp; & amp; safeToDealloc)
        {<!-- -->
          ICall_freeMsg(pMsg);
        }
      }

      // Determine APP message
      if (events & amp; SP_QUEUE_EVT)
      {<!-- -->
        while (!Queue_empty(appMsgQueueHandle))
        {<!-- -->
          spEvt_t *pMsg = (spEvt_t *)Util_dequeueMsg(appMsgQueueHandle);
          if(pMsg)
          {<!-- -->
            SimplePeripheral_processAppMsg(pMsg);
            ICall_free(pMsg);
          }
        }
      }
    }

4. Determine which layer of the Bluetooth layer the task in the protocol stack is

static uint8_t SimplePeripheral_processStackMsg(ICall_Hdr *pMsg)
{<!-- -->
  switch (pMsg->event)
  {<!-- -->
    case GAP_MSG_EVENT:
      SimplePeripheral_processGapMessage((gapEventHdr_t*) pMsg);
      break;

    case GATT_MSG_EVENT:
      // Process GATT message
      safeToDealloc = SimplePeripheral_processGATTMsg((gattMsgEvent_t *)pMsg);
      break;

    case HCI_GAP_EVENT_EVENT:
    {<!-- -->
      // Process HCI message
     ************
           break;
    }

    default:
      // do nothing
      break;
  }

5. Complete the processing of the final event in the GATT layer task

static uint8_t SimplePeripheral_processGATTMsg(gattMsgEvent_t *pMsg)
{<!-- -->
  if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT)
  {<!-- -->
    // Display the opcode of the message that caused the violation.
    Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode);
  }
  else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
  {<!-- -->
    // MTU size updated
    Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "MTU Size: %d", pMsg->msg.mtuEvt.MTU);
  }

  // Free message payload. Needed only for ATT Protocol messages
  GATT_bm_free( & amp;pMsg->msg, pMsg->method);

  // It's safe to free the incoming message
  return (TRUE);
}