SER Async Loopback Timed
Edit this on GitLab
SER Async Loopback Timed Sample Application (SSK 2.x)
Overview
The SER Async Loopback Timed sample application demonstrates how to configure a serial channel for asynchronous timed transmission using the NAI Software Support Kit (SSK 2.x). The application is exclusive to the SCA module and allows the user to enable or disable timed transmission mode, configure timed-mode parameters (frame count, sub-bytes, and gap time), adjust the baud rate, and transmit data through an internal loopback path. A menu-driven interface lets you experiment with different timing configurations before initiating a transmission.
This sample supports only the SCA module type. It serves as a practical reference for the naibrd_SER_SetTimedSerialEnable(), naibrd_SER_SetTimedSerialConfig(), and naibrd_SER_GetTimedSerialConfig() APIs, which control the timed serial transmission feature unique to the SCA.
Prerequisites
Before running this sample, make sure you have:
-
An NAI board with an SCA module installed.
-
SSK 2.x installed on your development host.
-
The sample applications built. Refer to the SSK 2.x Software Development Guide for platform-specific build instructions.
How to Run
Launch the ser_async_loopback_timed executable from your build output directory. On startup the application looks for a configuration file (default_SerAsync_TIMED.txt). On the first run, this file will not exist — the application will present an interactive board menu where you configure a board connection, card index, and module slot. You can save this configuration so that subsequent runs skip the menu and connect automatically. Once connected, the application verifies that the selected module is an SCA, queries for a channel, and displays a five-option menu for configuring and testing timed serial mode.
Board Connection and Module Selection
|
Note
|
This startup sequence is common to all NAI sample applications. The board connection and module selection code shown here is not specific to SER. |
The main() function follows a standard SSK 2.x startup flow:
-
Call
naiapp_RunBoardMenu()to load a saved configuration file (if one exists) or present the interactive board menu. The configuration file (default_SerAsync_TIMED.txt) is not included with the SSK — it is created when the user saves their connection settings from the board menu. On the first run, the menu will always appear. -
Query the user for a card index with
naiapp_query_CardIndex(). -
Query for a module slot with
naiapp_query_ModuleNumber(). -
Retrieve the module ID with
naibrd_GetModuleName()and verify it isNAIBRD_MODULE_ID_SCA. If it is not, the application prints an error message and returns. -
Call
Run_SER_ASYNC_TIMED(cardIndex, module, moduleID)to present the timed-mode menu.
#if defined (__VXWORKS__)
int32_t SER_ASYNC_Sample(void)
#else
int32_t main(void)
#endif
{
bool_t stop = NAI_FALSE;
int32_t cardIndex;
int32_t moduleCnt;
int32_t module;
uint32_t moduleID = 0;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
if (naiapp_RunBoardMenu(CONFIG_FILE) == NAI_TRUE)
{
while (stop != NAI_TRUE)
{
/* Query user for the card index */
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
if (stop != NAI_TRUE)
{
check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
/* Query the user for the module number */
stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
if (stop != NAI_TRUE)
{
check_status(naibrd_GetModuleName(cardIndex, module, &moduleID));
if (moduleID == NAIBRD_MODULE_ID_SCA)
{
Run_SER_ASYNC_TIMED(cardIndex, module, moduleID);
}
else
{
naiif_printf("This application can only be run on an SCA\r\n");
}
}
}
naiif_printf("\r\nType Q to quit or Enter key to restart application:\r\n");
stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
}
}
naiif_printf("\r\nType the Enter key to exit the program: ");
naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
naiapp_access_CloseAllOpenCards();
return 0;
}
Note the SSK 2.x differences from SSK 1.x in this startup sequence:
-
The module identifier is retrieved with
naibrd_GetModuleName()(SSK 1.x usesnaibrd_GetModuleID()). -
Boolean constants are
NAI_TRUE/NAI_FALSE(SSK 1.x usesTRUE/FALSE). -
Console output uses
naiif_printf()from the platform abstraction layer (SSK 1.x usesprintf()directly).
|
Important
|
Common connection errors you may encounter at this stage:
|
Program Structure
Application Flow
After establishing a board connection and verifying the module is an SCA, the application:
-
Queries the user for a channel number.
-
Clears the receive and transmit FIFOs and polls until the clear completes (with a 1-second timeout).
-
Configures the channel for async loopback (9600 baud, 8N1, no parity, loopback interface, receiver enabled).
-
Displays a five-option menu.
FIFO Clearing with Timeout
Before configuring the channel, the application clears both FIFOs and polls the channel control register to confirm the clear operation has completed:
naibrd_SER_ClearRxFifo(cardIndex, module, chanNum);
naibrd_SER_ClearTxFifo(cardIndex, module, chanNum);
nCntlValueLo = NAIBRD_SER_CTRL_CLEAR_RX_FIFO | NAIBRD_SER_CTRL_CLEAR_TX_FIFO;
for (i = 0; i < CLEAR_FIFO_TIMEOUT && (nCntlValueLo & (NAIBRD_SER_CTRL_CLEAR_RX_FIFO | NAIBRD_SER_CTRL_CLEAR_TX_FIFO)); i++)
{
naibrd_ser_chanctrl chanCtrlRaw;
naibrd_SER_GetChannelControlRaw(cardIndex, module, chanNum, &chanCtrlRaw);
nCntlValueLo = chanCtrlRaw & 0x0000FFFF;
naibrd_msDelay(1);
}
-
CLEAR_FIFO_TIMEOUTis set to 1000 (1 second). The loop polls every 1 ms. -
naibrd_SER_GetChannelControlRaw()reads the raw channel control register. The clear bits remain set until the hardware completes the operation. -
If the timeout expires, the application prints an error and returns without proceeding.
Channel Configuration
The application configures the channel with standard async parameters:
check_status(naibrd_SER_SetCommProtocol(cardIndex, module, chanNum, NAIBRD_SER_PROTOCOL_ASYNC));
check_status(naibrd_SER_SetInterface(cardIndex, module, chanNum, NAIBRD_SER_INTF_LOOPBACK));
check_status(naibrd_SER_SetParityType(cardIndex, module, chanNum, NAIBRD_SER_PARITY_NONE));
check_status(naibrd_SER_SetNumDataBits(cardIndex, module, chanNum, NAIBRD_SER_DATA_BITS_8));
check_status(naibrd_SER_SetNumStopBits(cardIndex, module, chanNum, NAIBRD_SER_STOP_BITS_1));
check_status(naibrd_SER_SetBaudrate(cardIndex, module, chanNum, 9600));
check_status(naibrd_SER_SetReceiverEnable(cardIndex, module, chanNum, 1));
These settings match the standard async loopback configuration. The interface is set to loopback so transmitted data is received back on the same channel.
Timed Mode Menu
The application presents a five-option menu:
-
Enable/Disable Timed Mode — calls
naibrd_SER_SetTimedSerialEnable()to toggle timed serial mode on or off. -
Change Timed Mode Configuration — calls
naibrd_SER_SetTimedSerialConfig()with user-supplied values for enable, frame count, sub-bytes, and gap time. -
View Timed Mode Configuration — calls
naibrd_SER_GetTimedSerialConfig()and displays the current timed-mode settings. -
Change Baud Rate — calls
naibrd_SER_SetBaudrate()with a user-supplied baud rate. -
Initiate Transmit — builds a test payload, loads it into the FIFO, transmits, and reads back the loopback data.
Timed Mode Configuration
When configuring timed mode (option 2), the user provides three parameters in hexadecimal:
-
Frame count (n) — the number of frames to transmit per timed cycle.
-
Sub bytes (m) — the number of bytes per sub-frame.
-
Gap time (k) — the inter-frame gap time in byte-times.
check_status(naibrd_SER_SetTimedSerialConfig(cardIndex, module, chanNum, (uint32_t)option, outgapTime, outSubBytes, outFrameCnt));
Data Transmission and Reception
When the user selects option 5, the application builds a payload of 8 words with incremental values starting at 0x00, loads the FIFO, initiates transmission, and reads back the loopback data:
for (i = 0; i < NUM_DATA_TX; i++)
{
SendData[i] = (uint8_t)i;
}
nNumWordsSend = i;
check_status(naibrd_SER_LoadBufferWithTimeOut32(cardIndex, module, chanNum, SendData, nNumWordsSend, NUM_DATA_TX, NAIBRD_FIFO_TIMEOUT_NONE, &nNumWordsSend));
check_status(naibrd_SER_TransmitInitiate(cardIndex, module, chanNum));
After transmission, the application prompts the user to press Enter, then reads back the data using naibrd_SER_GetRxBufferCnt() and naibrd_SER_ReceiveBufferWithTimeOut32(), displaying both the sent and received data side by side with per-word status bytes.
Troubleshooting Reference
| Error / Symptom | Possible Causes | Suggested Resolution |
|---|---|---|
Module not SCA |
A non-SCA module is installed at the selected slot |
This application only supports the SCA module. Verify your hardware configuration. |
Unable to clear FIFOs (timeout) |
Hardware is not responding to FIFO clear commands |
Check that the module is powered and the board connection is valid. Try a board reset. |
No board found or connection timeout |
Board not powered, incorrect configuration file, network issue |
Verify hardware is powered and connected. Check |
No data received after transmit |
Receiver not enabled, timed mode misconfigured, transmit not initiated |
Verify receiver is enabled. Check timed-mode configuration with option 3 before transmitting. |
Received count does not match sent count |
Timed mode gap time or frame configuration incorrect |
Review timed-mode settings. Ensure gap time and frame count match the expected transmission pattern. |
Status byte indicates error |
Parity or framing error detected during reception |
In loopback mode this typically indicates a configuration issue. Try resetting the channel. |
Invalid baud rate |
Module does not support the requested baud rate |
Consult the SC3 Manual for supported baud rates. |
Full Source
The complete source for this sample is provided below for reference. The sections above explain each part in detail.
Full Source — ser_async_loopback_timed.c (SSK 2.x)
/* Common Sample Program include files */
#include "nai_sample_apps/naiapp_common/include/naiapp_boardaccess_menu.h"
#include "nai_sample_apps/naiapp_common/include/naiapp_boardaccess_query.h"
#include "nai_sample_apps/naiapp_common/include/naiapp_boardaccess_access.h"
#include "nai_sample_apps/naiapp_common/include/naiapp_boardaccess_display.h"
#include "nai_sample_apps/naiapp_common/include/naiapp_boardaccess_utils.h"
/* nailib include files */
#include "nai_libs/nailib/include/naitypes.h"
#include "nai_libs/nailib/include/nailib.h"
#include "nai_libs/nailib/include/nailib_utils.h"
/* naibrd include files */
#include "nai_libs/naibrd/include/naibrd.h"
#include "nai_libs/naibrd/include/functions/naibrd_ser.h"
/* naiif include files */
#include "nai_libs/naiif/include/naiif_stdio.h"
static const int8_t* CONFIG_FILE = (const int8_t*)"default_SerAsync_TIMED.txt";
/* Function prototypes */
void Run_SER_ASYNC_TIMED(int32_t cardIndex, int32_t module, uint32_t moduleID);
#define NUM_DATA_TX 8
#define MAX_DATA_RX 20
#define CLEAR_FIFO_TIMEOUT 1000 /* 1 second */
/**************************************************************************************************************/
/** \defgroup SERAsyncLoopbackTimed
\brief This sample application demonstrates how to configure and use a SCA module for asynchronous timed loopback transmission.
The Serial Asynchronous Loopback Timed sample application illustrates the methods to call in the naibrd library to configure an SCA module
serial channel for timed transmission. The application will query the user on changing some of the timing parameters.
The main steps include:
- Querying the user for the card index and module number.
- Configuring the serial channel for asynchronous communication with timed loopback.
- Setting and changing the timed mode configuration.
- Transmitting data through the serial channel.
- Receiving the transmitted data and displaying it.
*/
/**************************************************************************************************************/
#if defined (__VXWORKS__)
int32_t SER_ASYNC_Sample(void)
#else
int32_t main(void)
#endif
{
bool_t stop = NAI_FALSE;
int32_t cardIndex;
int32_t moduleCnt;
int32_t module;
uint32_t moduleID = 0;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
if (naiapp_RunBoardMenu(CONFIG_FILE) == NAI_TRUE)
{
while (stop != NAI_TRUE)
{
/* Query user for the card index */
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
if (stop != NAI_TRUE)
{
check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
/* Query the user for the module number */
stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
if (stop != NAI_TRUE)
{
check_status(naibrd_GetModuleName(cardIndex, module, &moduleID));
if (moduleID == NAIBRD_MODULE_ID_SCA)
{
Run_SER_ASYNC_TIMED(cardIndex, module, moduleID);
}
else
{
naiif_printf("This application can only be run on an SCA\r\n");
}
}
}
naiif_printf("\r\nType Q to quit or Enter key to restart application:\r\n");
stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
}
}
naiif_printf("\r\nType the Enter key to exit the program: ");
naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
naiapp_access_CloseAllOpenCards();
return 0;
}
/**************************************************************************************************************/
/** \ingroup SERAsyncLoopbackTimed
Configures a serial module for asynchronous timed transmission. The user is queried for the
serial channel to perform this transmission on. User is then given a menu to choose between enabling/disabling the timed mode.
Once the timed configuration is set, user can read back to ensure the channel's registers have been properly written to.
User also has the option to enable the timed mode if they wanted to test out different timing configurations.
\param cardIndex (Input) Logical Card Index assigned to connection with the NAI_BOARD (0 - NAI_MAX_CARDS-1).
\param module (Input) Module Number of the module to access (1 - [max modules for board]).
\param moduleID (Input) The ID of the module.
*/
/**************************************************************************************************************/
void Run_SER_ASYNC_TIMED(int32_t cardIndex, int32_t module, uint32_t moduleID)
{
int32_t i;
int32_t channelCount;
int32_t chanNum;
int32_t nNumWordsSend = 0;
int32_t nNumWordsRecv = 0;
uint32_t baudRate;
uint32_t SendData[NUM_DATA_TX], RecvDataStatus[MAX_DATA_RX];
int32_t nCntlValueLo;
bool_t bQuit = NAI_FALSE;
int32_t option;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
uint32_t outEnable = 0x0u, outgapTime = 0x0u, outSubBytes = 0x0u, outFrameCnt = 0x0u;
char* enable;
channelCount = naibrd_SER_GetChannelCount(moduleID);
naiapp_query_ChannelNumber(channelCount, 1, &chanNum);
naibrd_SER_ClearRxFifo(cardIndex, module, chanNum);
naibrd_SER_ClearTxFifo(cardIndex, module, chanNum);
nCntlValueLo = NAIBRD_SER_CTRL_CLEAR_RX_FIFO | NAIBRD_SER_CTRL_CLEAR_TX_FIFO;
for (i = 0; i < CLEAR_FIFO_TIMEOUT && (nCntlValueLo & (NAIBRD_SER_CTRL_CLEAR_RX_FIFO | NAIBRD_SER_CTRL_CLEAR_TX_FIFO)); i++)
{
naibrd_ser_chanctrl chanCtrlRaw;
naibrd_SER_GetChannelControlRaw(cardIndex, module, chanNum, &chanCtrlRaw);
nCntlValueLo = chanCtrlRaw & 0x0000FFFF;
naibrd_msDelay(1);
}
if (i == CLEAR_FIFO_TIMEOUT)
{
naiif_printf("Unable to clear FIFOs %d\r\n", chanNum);
naiif_printf("Please press Enter to exit...");
while (naiapp_query_ForQuitResponse(sizeof(inputBuffer), 0x0A, inputBuffer, &inputResponseCnt));
return;
}
naiif_printf("\r\nSerial Channel # %d\r\n", chanNum);
/* Module Configuration */
check_status(naibrd_SER_SetCommProtocol(cardIndex, module, chanNum, NAIBRD_SER_PROTOCOL_ASYNC)); /* Async mode */
check_status(naibrd_SER_SetInterface(cardIndex, module, chanNum, NAIBRD_SER_INTF_LOOPBACK)); /* RS422 */
check_status(naibrd_SER_SetParityType(cardIndex, module, chanNum, NAIBRD_SER_PARITY_NONE)); /* No Parity */
check_status(naibrd_SER_SetNumDataBits(cardIndex, module, chanNum, NAIBRD_SER_DATA_BITS_8)); /* 8 Data Bits */
check_status(naibrd_SER_SetNumStopBits(cardIndex, module, chanNum, NAIBRD_SER_STOP_BITS_1)); /* 1 Stop Bit */
check_status(naibrd_SER_SetBaudrate(cardIndex, module, chanNum, 9600)); /* 9600 baud */
check_status(naibrd_SER_SetReceiverEnable(cardIndex, module, chanNum, 1)); /* Enable Receiver */
naiif_printf("\r\n\r\n*******************************\r\n");
naiif_printf("\tSCA Menu\r\n");
naiif_printf("*******************************\r\n\r\n");
naiif_printf("Select Option then ENTER key.\r\n\r\n");
naiif_printf("1. Enable/Disable Timed Mode\r\n");
naiif_printf("2. Change Timed Mode Configuration\r\n");
naiif_printf("3. View Timed Mode Configuration\r\n");
naiif_printf("4. Change Baud Rate\r\n");
naiif_printf("5. Initiate Transmit\r\n");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
naiapp_query_NumberFromResponse(&option, inputBuffer, inputResponseCnt);
switch (option)
{
case 1:
naiif_printf("Do you wish to enable or disable timed mode? [disable (0) / enable (1)]");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
naiapp_query_NumberFromResponse(&option, inputBuffer, inputResponseCnt);
check_status(naibrd_SER_SetTimedSerialEnable(cardIndex, module, chanNum, (bool_t)option));
}
break;
case 2:
naiif_printf("Do you wish to enable or disable timed mode? [disable (0) / enable (1)]");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
naiapp_query_NumberFromResponse(&option, inputBuffer, inputResponseCnt);
}
naiif_printf("Enter desired frame count (n) [use 0x]");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
outFrameCnt = naiapp_utils_HexStrToDecUInt32(inputBuffer);
}
naiif_printf("Enter desired sub bytes (m) [use 0x]");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
outSubBytes = naiapp_utils_HexStrToDecUInt32(inputBuffer);
}
naiif_printf("Enter desired gap time (k) [use 0x]");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
outgapTime = naiapp_utils_HexStrToDecUInt32(inputBuffer);
}
check_status(naibrd_SER_SetTimedSerialConfig(cardIndex, module, chanNum, (uint32_t)option, outgapTime, outSubBytes, outFrameCnt));
break;
case 3:
check_status(naibrd_SER_GetTimedSerialConfig(cardIndex, module, chanNum, &outEnable, &outgapTime, &outSubBytes, &outFrameCnt));
enable = (outEnable) ? "yes" : "no";
naiif_printf("Is chan %d's timed mode enabled? %s\r\n", chanNum, enable);
naiif_printf("Current frame count: %d\r\n", outFrameCnt);
naiif_printf("Current number of bytes to send: %d\r\n", outSubBytes);
naiif_printf("Current number of bytes to wait between transmission: %d\r\n\r\n", outgapTime);
break;
case 4:
naiif_printf("Enter your desired baud rate (bits/sec)");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
naiapp_query_UnsignedNumberFromResponse(&baudRate, inputBuffer, inputResponseCnt);
check_status(naibrd_SER_SetBaudrate(cardIndex, module, chanNum, baudRate));
}
break;
case 5:
for (i = 0; i < NUM_DATA_TX; i++)
{
SendData[i] = (uint8_t)i; /* incremental data */
}
/* Send data */
nNumWordsSend = i;
naiif_printf("\r\nSending %d words ...", nNumWordsSend);
check_status(naibrd_SER_LoadBufferWithTimeOut32(cardIndex, module, chanNum, SendData, nNumWordsSend, NUM_DATA_TX, NAIBRD_FIFO_TIMEOUT_NONE, &nNumWordsSend));
naiif_printf(" %d words sent\r\n", nNumWordsSend);
check_status(naibrd_SER_TransmitInitiate(cardIndex, module, chanNum));
naiif_printf("Please press Enter to read back data...");
while (naiapp_query_ForQuitResponse(sizeof(inputBuffer), 0x0A, inputBuffer, &inputResponseCnt));
/* Read back data */
naibrd_SER_GetRxBufferCnt(cardIndex, module, chanNum, (uint32_t*)&nNumWordsRecv);
naiif_printf("\r\nReading back %d words ...", nNumWordsRecv);
check_status(naibrd_SER_ReceiveBufferWithTimeOut32(cardIndex, module, chanNum, RecvDataStatus, MAX_DATA_RX, nNumWordsRecv, NAIBRD_FIFO_TIMEOUT_NONE,
&nNumWordsRecv));
naiif_printf(" %d words read\r\n", nNumWordsRecv);
for (i = 0; i < nNumWordsRecv; i++)
{
naiif_printf("Sent 0x%02X; Recd 0x%02X, Status= %02X\r\n",
SendData[i], (RecvDataStatus[i] & 0x00FF), (RecvDataStatus[i] >> 8) & 0x00FF);
}
break;
}
}
}