Logo
UNICENS V2.1.0-3491
User Manual and API Reference
Application Message Service

Introduction

The Application Message Service (AMS) provides a central interface to transmit and receive application messages via the Control Channel of the MOST network. The so called application messages can have a payload size of up to 65535 bytes. Since the payload size of a single MOST control message is limited to 45 bytes (MOST150), an application messages with larger payload is transmitted via segmented transfer. The Application Message Service provides a common interface for single and segmented transfer and automatically performs segmentation.

 See also API Reference, section Application Message Service.

Configuration

Any message which is received or transmitted via the Application Message Service requires a certain amount of memory. The UNICENS Library reserves an amount of memory which can be adjusted by defining the number und payload of AMS Tx and Rx messages. The configuration file ucs_cfg.h can be modified to your specific needs. A possible configuration might be:

  • 20 Rx Message Objects, each object has a reserved payload size of 400 bytes
  • 20 Tx Message Objects, each object has a reserved payload size of 100 bytes

The corresponding configuration will look like this:

/* File: ucs_cfg.h */
#define UCS_AMS_NUM_RX_MSGS 20U
#define UCS_AMS_SIZE_RX_MSG 400U
#define UCS_AMS_NUM_TX_MSGS 20U
#define UCS_AMS_SIZE_TX_MSG 100U

Initialization

Prior to the call of Ucs_Init(), the application is able to configure the AMS-related part of the initialization structure. The following table provides a brief overview of all parameters.

Parameter Default Mandatory Description
rx.message_received_fptr NULL No Callback function that is invoked on message reception
tx.message_freed_fptr NULL No Callback function that is invoked if memory dedicated to a Tx message is freed after a prior allocation has failed
tx.default_llrbc 10 No Default low-level retry block count for Tx messages

The code below shows a possible initialization sequence of the Application Message Service.

void App_Initialize(void)
{
Ucs_InitData_t init_data;
Ucs_SetDefaultConfig(&init_data);
init_data.ams.tx.default_llrbc = 4U;
init_data.ams.tx.message_freed_fptr = &App_OnAmsTxMessageFreed;
init_data.ams.rx.message_received_fptr = &App_OnAmsRxComplete;
/* ... further initialization ... */
Ucs_Init(&init_data, &App_OnInitResult);
}
 See also Getting Started, section Initialization.

Transmission of Messages

In order to transmit an application message you haves to process the following steps.

  1. Retrieve a message object by calling Ucs_AmsTx_AllocMsg() and specifying the needed amount of payload.
  2. Modify the attributes of the given message object.
  3. Take care not to modify data_ptr[] beyond the specified amount of payload.
  4. Transmit the message object by calling Ucs_AmsTx_SendMsg().
  5. Optionally check the transmission result after the transmission has finished.

The following example explains how to retrieve a message object with a payload size of 3 bytes. Be aware of the function Ucs_AmsTx_AllocMsg() might return NULL if it is not possible to allocate the required amount of memory for the message.

void App_SendMessage(void)
{
if (tx_ptr != NULL)
{
tx_ptr->destination_address = 0x202U;
tx_ptr->msg_id = 0x1234U;
/* it is possible to set a smaller data_size than */
tx_ptr->data_size = 3U; /* earlier defined when calling Ucs_AmsTx_AllocMsg() */
tx_ptr->data_ptr[0] = 0xAAU;
tx_ptr->data_ptr[1] = 0xBBU;
tx_ptr->data_ptr[2] = 0xCCU;
ret = Ucs_AmsTx_SendMsg(tx_ptr, &App_OnTxMessageComplete);
(void)printf("App_SendMessage(): called, tx_ptr=0x%04p, ret=%02X\n", tx_ptr, ret);
if (ret != UCS_RET_SUCCESS)
{
Ucs_AmsTx_FreeUnusedMsg(running_inst_ptr, tx_ptr);
}
}
else
{
/* not able to allocate enough memory for the message - wait until App_OnAmsTxMessageFreed() */
}
}

As soon as the transmission has been completed, the Application Message Service will invoke the callback function that was passed to Ucs_AmsTx_SendMsg(). The application's callback function must have the function signature of Ucs_AmsTx_CompleteCb_t. Within the callback function, the application is allowed to access the message object (read only). After the callback function has returned, the application is no longer allowed to access the message object.

An example implementation of such a callback function is shown below.

void App_OnTxMessageComplete(Ucs_AmsTx_Msg_t* msg_ptr, Ucs_AmsTx_Result_t result, Ucs_AmsTx_Info_t info)
{
if (result == UCS_AMSTX_RES_SUCCESS)
{
/* the transmission was finished successfully */
}
else
{
/* the application has to retry later ... */
}
/* the application is not allowed to reuse this message object */
}

Transmission of External Payload

An application may also transmit an application message with "external" payload. This might be useful to avoid copy operations if the message payload is already available in a continuous memory chunk. Process the following steps in order to transmit an application message with external payload.

  1. Prepare the external payload and take care that it is not modified during the transmission.
  2. Retrieve a message object by calling Ucs_AmsTx_AllocMsg(). It is sufficient to pass "0" as data_size. So the Application Message Service will not allocate a separate memory chunk for the payload.
  3. Modify the attributes of the given message object.
  4. Set data_ptr to the external payload reference. Set data_size to define the desired amount of payload to transmit.
  5. Transmit the message object by calling Ucs_AmsTx_SendMsg().
  6. Check the transmission result after the transmission has finished. The external payload is no longer used by the Application Message Service. I.e., the external payload may now be freed or reused for further transmission.

The following example explains how to retrieve a message object and assign a predefined string as external payload. It is important to pass a callback function to Ucs_AmsTx_SendMsg(). The Application Message Service calls this function after the transmission is finished.

/* provide fixed string "Hello" */
static uint8_t my_string[14] = {0x01,'H','e','l','l','o',' ','W','o','r','l','d','!',0x00};
static bool string_in_use = false; /* avoid concurrent access */
void App_SendMessageExt(void)
{
if (!string_in_use)
{
Ucs_AmsTx_Msg_t *tx_ptr = Ucs_AmsTx_AllocMsg(0U); /* define data_size "0" */
if (tx_ptr != NULL)
{
string_in_use = true; /* lock string against concurrent access */
tx_ptr->destination_address = 0x202U;
tx_ptr->msg_id = 0x1234U;
tx_ptr->data_ptr = my_string; /* now set external data and size */
tx_ptr->data_size = 14U;
ret = Ucs_AmsTx_SendMsg(tx_ptr, &App_OnTxStringComplete);
(void)printf("App_SendMessage(): called, tx_ptr=0x%04p, ret=%02X\n", tx_ptr, ret);
if (ret != UCS_RET_SUCCESS)
{ /* free memory if transmission is not possible */
Ucs_AmsTx_FreeUnusedMsg(running_inst_ptr, tx_ptr);
string_in_use = false;
}
}
else
{
/* not able to allocate enough memory for the message - wait until App_OnAmsTxMessageFreed() */
}
}
}

After completed transmission, the Application Message Service will invoke the callback function that was passed to Ucs_AmsTx_SendMsg(). The application is responsible to free or reuse the external payload. The Application Message Service will no longer access it.

static void App_OnTxStringComplete(Ucs_AmsTx_Msg_t* msg_ptr, Ucs_AmsTx_Result_t result, Ucs_AmsTx_Info_t info)
{
uint8_t* payload_ptr;
/* ... handle transmission result ... */
if (msg_ptr->data_ptr == my_string) /* message object has shared string resource ? */
{
string_in_use = false; /* unlock string usage */
}
}

Reception of Messages

By calling ams.rx.message_received_fptr() the AMS notifies that a new Rx message was received and has been added to the Rx queue. It is recommended that the application processes the Rx queue asynchronously, i.e., not within the ams.rx.message_received_fptr(). Therefore, the AMS provides the functions Ucs_AmsRx_PeekMsg(), Ucs_AmsRx_ReleaseMsg() and Ucs_AmsRx_GetMsgCnt(). The example shows that the callback function is implemented to set the variable _process_rx to true. The following main loop will process Rx messages if the variable is set. Instead of implementing the callback function it is also possible to use Ucs_AmsRx_GetMsgCnt() to retrieve the number of available messages in the Rx queue.

void App_OnAmsRxComplete(void)
{
/* A new application message was received. */
/* Trigger an event to process the Rx queue asynchronously. */
}
int main(void)
{
/* initialization */
while (_running)
{
/* ... */ /* driving UNICENS and low-level driver */
if (Ucs_AmsRx_GetMsgCnt() > 0U) /* process all Rx messages in AMS queue */
{
Ucs_AmsRx_Msg_t *msg_ptr;
for (msg_ptr = Ucs_AmsRx_PeekMsg(); msg_ptr != NULL; msg_ptr = Ucs_AmsRx_PeekMsg())
{
uint16_t cnt;
(void)printf("AMS RX: from %04X -> ID-%04X [ ", msg_ptr->source_address, msg_ptr->msg_id);
for (cnt = 0U; cnt < msg_ptr->data_size; cnt++)
{
(void)printf("%02X ", msg_ptr->data_ptr[cnt]);
}
(void)printf("]\n");
/* ... */ /* process message now */
Ucs_AmsRx_ReleaseMsg(); /* free message and remove it from the queue */
}
}
}
return 0;
}

The asynchronous processing of the AMS Rx queue provides a comfortable way to process messages at the best moment for the application. During high message load it is possible that the application is not able to respond to a message immediately. In that case the application shall not call Ucs_AmsRx_ReleaseMsg(). Later, when it is able to process the message it can retrieve the same Rx message object by calling Ucs_AmsRx_PeekMsg().

Note
It is possible that Ucs_Service() flushes the AMS Rx queue. Hence, a reference returned by Ucs_AmsRx_PeekMsg() may become invalid during the call of Ucs_Service(). It is recommended to call Ucs_AmsRx_PeekMsg(), do the Rx message processing and call Ucs_AmsRx_ReleaseMsg() in one go. If the application does not call Ucs_AmsRx_ReleaseMsg() because it cannot finish the message processing, it is recommended to retry Ucs_AmsRx_PeekMsg() when message processing is possible again.
 See also API Reference, section Application Message Service.