Integrator Resources

The official home for NAI Support

Not sure where to start? Try Quick Start Guide or ask a question below!

Toggle Components with Visual Button
JavaScript Form Processing

SER ASync GPIO

SER ASync GPIO Sample Application (SSK 1.x)

Overview

The SER ASync GPIO sample application demonstrates how to use NAI serial module channels as general-purpose digital I/O (GPIO) rather than as serial communication interfaces. Using the SSK 1.x API, you can repurpose a serial channel’s physical pins to drive a digital output (GPO) and read a digital input (GPI), which is useful when your system needs a small number of discrete digital signals without dedicating a separate digital I/O module.

In normal serial mode, a channel’s pins carry asynchronous or synchronous serial data (RS-232, RS-422, RS-485, etc.). In GPIO mode, those same pins become simple high/low digital lines. The sample configures a channel for GPIO mode with loopback enabled so the GPO line feeds directly back to the GPI line, letting you verify GPIO operation without external wiring.

This sample supports the SC3 serial module. Combination modules that include serial functionality (such as CMH) may also support GPIO mode — consult your module’s manual to confirm GPIO availability on your specific hardware.

Prerequisites

Before running this sample, make sure you have:

  • An NAI board with an SC3 serial module installed.

  • SSK 1.x installed on your development host.

  • The sample applications built. Refer to the SSK 1.x build instructions for your platform if you have not already compiled them.

How to Run

Launch the SER_ASync_GPIO executable from your build output directory. On startup the application looks for a configuration file (default_SerASync_GPIO.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 prompts you to select a serial channel, configures it for GPIO mode, and lets you toggle the output line interactively.

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 serial GPIO. For details on board connection configuration, see the First Time Setup Guide.

The main() function follows a standard SSK 1.x startup flow:

  1. Call naiapp_RunBoardMenu() to load a saved configuration file (if one exists) or present the interactive board menu. The configuration file (default_SerASync_GPIO.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.

  2. Query the user for a card index with naiapp_query_CardIndex().

  3. Query for a module slot with naiapp_query_ModuleNumber().

  4. Retrieve the module ID with naibrd_GetModuleID() and verify it is an SC3 module before proceeding.

if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
   while (stop != TRUE)
   {
      stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
      if (stop != TRUE)
      {
         check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
         stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
         if (stop != TRUE)
         {
            moduleID = naibrd_GetModuleID(cardIndex, module);
            if ((moduleID != 0) && (moduleID == NAI_MODULE_ID_SC3))
            {
               Run_SER_ASync_GPIO(cardIndex, module, moduleID);
            }
            else
            {
               printf("\nThe GPIO feature is only available on the NAI SC3 module.\n\n");
            }
         }
      }
   }
}

The application explicitly checks for NAI_MODULE_ID_SC3 because GPIO mode is an SC3-specific feature. If you select a module that does not support GPIO, the application prints a message and returns to the selection prompt.

Important

Common connection errors you may encounter at this stage:

  • No board found — verify that the board is powered on and physically connected. Check that the configuration file lists the correct interface and address.

  • Connection timeout — confirm network settings (for Ethernet connections) or bus configuration (for PCI/PCIe). Firewalls and IP mismatches are frequent causes.

  • Invalid card or module index — indices are zero-based for cards and one-based for modules. Ensure the values you pass match your hardware setup.

  • Module not present at selected slot — the slot you selected does not contain a serial module. Use the board menu to verify which slots are populated.

  • "The GPIO feature is only available on the NAI SC3 module" — you selected a non-SC3 serial module. GPIO mode requires SC3 hardware.

Program Structure

Entry Point

On standard platforms the entry point is main(). On VxWorks the entry point is SER_ASync_GPIO_Sample() — the SSK 1.x build system selects the correct variant via a preprocessor guard:

#if defined (__VXWORKS__)
int32_t SER_ASync_GPIO_Sample(void)
#else
int32_t main(void)
#endif

The startup flow is the same in both cases:

  1. Attempt to load the saved configuration file via naiapp_RunBoardMenu(CONFIG_FILE). If the file does not yet exist, the interactive board menu is presented instead.

  2. Enter a loop that queries for card index and module slot.

  3. Validate the module is SC3, then call Run_SER_ASync_GPIO() to enter the GPIO configuration and toggle loop.

  4. On exit, close all open board connections with naiapp_access_CloseAllOpenCards().

Application Flow

Unlike the menu-driven BasicOps samples, this application follows a linear flow: connect to board, select a channel, configure GPIO mode, then enter an interactive loop that toggles the output and reads the input. The key values your application needs to track are:

  • cardIndex — identifies which board in a multi-board system.

  • module — the slot number where the serial module is installed.

  • chanNum — the serial channel to configure for GPIO mode.

  • control — the raw channel control register value, used to set and clear the GPO bit.

The menu system is a sample convenience — in your own code, call these API functions directly.

Channel Reset and FIFO Clear

Before configuring a serial channel for GPIO mode, you must reset it and clear its transmit and receive FIFOs. This ensures the channel starts in a known state with no stale data.

To reset a channel and clear its FIFOs, call naibrd_SER_ChannelReset(), naibrd_SER_ClearRxFifo(), and naibrd_SER_ClearTxFifo():

naibrd_SER_ChannelReset(cardIndex, module, chanNum);
naibrd_SER_ClearRxFifo(cardIndex, module, chanNum);
naibrd_SER_ClearTxFifo(cardIndex, module, chanNum);

After issuing the clear commands, you must poll the channel control register to confirm the FIFO clear operations have completed. The hardware clears the NAI_SER_CTRLLO_CLEAR_RX_FIFO and NAI_SER_CTRLLO_CLEAR_TX_FIFO bits when the operation finishes:

nCntlValueLo = NAI_SER_CTRLLO_CLEAR_RX_FIFO | NAI_SER_CTRLLO_CLEAR_TX_FIFO;
for (i = 0; i < CLEAR_FIFO_TIMEOUT && (nCntlValueLo & (NAI_SER_CTRLLO_CLEAR_RX_FIFO | NAI_SER_CTRLLO_CLEAR_TX_FIFO)); i++)
{
   nai_ser_chanctrl chanCtrlRaw;
   naibrd_SER_GetChannelControlRaw(cardIndex, module, chanNum, &chanCtrlRaw);
   nCntlValueLo = chanCtrlRaw & 0x0000FFFF;
   nai_msDelay(1);
}
if (i == CLEAR_FIFO_TIMEOUT)
{
   printf("Unable to clear FIFOs %d\n", chanNum);
   return;
}

The CLEAR_FIFO_TIMEOUT constant is set to 1000 (1 second). Each iteration polls the control register via naibrd_SER_GetChannelControlRaw() and waits 1 ms with nai_msDelay(). If the bits have not cleared within the timeout, the FIFO clear has failed and you should not proceed with GPIO configuration.

  • cardIndex — logical card index (0-based).

  • module — module slot number (1-based).

  • chanNum — serial channel number (1-based).

Important

Common Errors

  • FIFO clear timeout ("Unable to clear FIFOs") — the hardware did not acknowledge the FIFO clear within 1 second. This can indicate a hardware fault or that the module is not responding. Power-cycle the board and retry. If the problem persists, consult your module’s manual for hardware diagnostics.

  • NAI_ERROR_INVALID_CHANNEL — the channel number is out of range for this module. Use naibrd_SER_GetChannelCount() to determine the valid range.

GPIO Mode Configuration

This is the core of the sample: switching a serial channel from its normal serial mode into GPIO mode. In GPIO mode, the channel’s physical pins function as a digital output (GPO) and digital input (GPI) rather than carrying serial data.

Setting the Interface Level for GPIO

To configure a channel for GPIO mode, call naibrd_SER_SetInterfaceLevel() with the NAI_SER_GEN5_INTF_GP_OUT flag. The sample also combines this with NAI_SER_INTF_LOOPBACK to internally connect GPO to GPI for testing:

check_status(naibrd_SER_SetInterfaceLevel(cardIndex, module, chanNum,
   NAI_SER_INTF_LOOPBACK | NAI_SER_GEN5_INTF_GP_OUT));
  • NAI_SER_GEN5_INTF_GP_OUT — enables GPIO mode on the channel. The channel’s transmit pin becomes a general-purpose output (GPO) and the receive pin becomes a general-purpose input (GPI).

  • NAI_SER_INTF_LOOPBACK — enables internal loopback, connecting the GPO line back to the GPI line. This is used for verification without external wiring. In your production application, omit this flag and connect external signals to the GPI pin instead.

In your own application, if you only need GPIO output without loopback, pass NAI_SER_GEN5_INTF_GP_OUT alone. If you need to read external signals on GPI, wire your signal source to the channel’s receive pin and omit NAI_SER_INTF_LOOPBACK.

Enabling the Receiver

After setting the interface level, enable the channel’s receiver so GPI status can be read:

check_status(naibrd_SER_SetReceiverEnable(cardIndex, module, chanNum, TRUE));

Pass TRUE to enable the receiver. Without this step, GPI reads will not reflect the actual pin state.

Configuration Settling Delay

The sample inserts a 500 ms delay after configuration to allow the hardware to stabilize:

nai_msDelay(500);

This delay ensures the interface level change and receiver enable have taken effect before you begin reading or writing GPIO state. Consult your module’s manual for the recommended settling time for your specific hardware revision.

Initializing GPO to a Known State

Before entering the toggle loop, the sample reads the current channel control register and explicitly clears the GPO bit to ensure the output starts low:

check_status(naibrd_SER_GetChannelControlRaw(cardIndex, module, chanNum, &control));
control &= ~NAI_SER_GEN5_CTRL_GPO1;
check_status(naibrd_SER_SetChannelControlRaw(cardIndex, module, chanNum, control));

The NAI_SER_GEN5_CTRL_GPO1 constant represents the GPO bit within the channel control register. Use a read-modify-write pattern: read the current register value, clear or set the GPO bit, then write the value back. This preserves any other control bits that may be set.

Important

Common Errors

  • NAI_ERROR_NOT_SUPPORTED — the selected module does not support GPIO mode. GPIO is an SC3-specific feature. Verify you have selected an SC3 module.

  • GPI always reads 0 after configuration — the receiver may not be enabled. Ensure you called naibrd_SER_SetReceiverEnable() with TRUE. Also verify the settling delay has elapsed before reading GPI.

  • GPO has no effect on external signals — if loopback is enabled, the GPO is routed internally. Remove NAI_SER_INTF_LOOPBACK from the interface level to drive the external pin.

Toggling GPO and Reading GPI

Once the channel is configured for GPIO mode, you can drive the output high or low and read the corresponding input state. The sample demonstrates this in an interactive loop.

Setting GPO High

To drive the general-purpose output high, set the NAI_SER_GEN5_CTRL_GPO1 bit in the channel control register:

control |= NAI_SER_GEN5_CTRL_GPO1;
check_status(naibrd_SER_SetChannelControlRaw(cardIndex, module, chanNum, control));

Setting GPO Low

To drive the output low, clear the NAI_SER_GEN5_CTRL_GPO1 bit:

control &= ~NAI_SER_GEN5_CTRL_GPO1;
check_status(naibrd_SER_SetChannelControlRaw(cardIndex, module, chanNum, control));

Reading GPI State

After changing GPO, read the GPI state from the channel’s real-time status register using naibrd_SER_GetChannelStatusEx():

uint32_t chanStatus = 0;
check_status(naibrd_SER_GetChannelStatusEx(cardIndex, module, chanNum,
   NAI_SER_STATUS_REALTIME, &chanStatus));
printf("GPI bit: %u\n", (chanStatus & NAI_SER_GEN5_INT_GPI1) >> 15);
  • NAI_SER_STATUS_REALTIME — reads the real-time (live) status register rather than a latched status. This gives you the current pin state at the moment of the read.

  • NAI_SER_GEN5_INT_GPI1 — the bitmask for the GPI bit within the status register. The shift >> 15 isolates it as a 0 or 1 value.

With loopback enabled, setting GPO high causes GPI to read 1, and setting GPO low causes GPI to read 0. In your production application without loopback, GPI reflects whatever external signal is connected to the channel’s receive pin.

The Toggle Loop

The sample runs a simple interactive loop that alternates between pulling GPO high and low:

do
{
   printf("Press ENTER to pull GPO high, or %c to exit program...\n", NAI_QUIT_CHAR);
   bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);

   if (TRUE != bQuit)
   {
      control |= NAI_SER_GEN5_CTRL_GPO1;
      check_status(naibrd_SER_SetChannelControlRaw(cardIndex, module, chanNum, control));

      check_status(naibrd_SER_GetChannelStatusEx(cardIndex, module, chanNum, NAI_SER_STATUS_REALTIME, &chanStatus));
      printf("GPI bit: %u\n", (chanStatus & NAI_SER_GEN5_INT_GPI1) >> 15);

      printf("Press ENTER to pull GPO low, or %c to exit program...\n", NAI_QUIT_CHAR);
      bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
   }

   if (TRUE != bQuit)
   {
      control &= ~NAI_SER_GEN5_CTRL_GPO1;
      check_status(naibrd_SER_SetChannelControlRaw(cardIndex, module, chanNum, control));

      check_status(naibrd_SER_GetChannelStatusEx(cardIndex, module, chanNum, NAI_SER_STATUS_REALTIME, &chanStatus));
      printf("GPI bit: %u\n", (chanStatus & NAI_SER_GEN5_INT_GPI1) >> 15);
   }
} while (TRUE != bQuit);

In your own application, replace the interactive prompts with your application logic. The key API pattern is the same: use naibrd_SER_SetChannelControlRaw() to drive GPO and naibrd_SER_GetChannelStatusEx() to read GPI.

Important

Common Errors

  • GPI does not change after toggling GPO — with loopback enabled, GPI should always follow GPO. If it does not, verify the channel is configured with both NAI_SER_INTF_LOOPBACK and NAI_SER_GEN5_INTF_GP_OUT. Also confirm the receiver is enabled.

  • GPI reads an unexpected value without loopback — the GPI pin reflects the external signal. Check your wiring and signal levels. Consult your module’s manual for input voltage thresholds.

  • NAI_ERROR_INVALID_CHANNEL — the channel number exceeds the module’s channel count. Query the count with naibrd_SER_GetChannelCount() before selecting a channel.

Troubleshooting Reference

The following table summarizes common errors and symptoms you may encounter when working with serial GPIO mode. Consult your module’s manual for hardware-specific diagnostics and specifications.

Error / Symptom Possible Causes Suggested Resolution

FIFO clear timeout

Hardware not responding; module fault.

Power-cycle the board. If persistent, check module seating and consult the module manual.

"The GPIO feature is only available on the NAI SC3 module"

Selected module is not SC3; GPIO mode is SC3-specific.

Select the correct module slot containing an SC3. Use the board menu to identify installed modules.

NAI_ERROR_NOT_SUPPORTED

API call targeting a feature the current module does not support.

Verify the module ID is NAI_MODULE_ID_SC3. Check the module manual for supported features.

NAI_ERROR_INVALID_CHANNEL

Channel number out of range for the module.

Call naibrd_SER_GetChannelCount() to determine valid channel numbers.

GPI always reads 0

Receiver not enabled; settling delay too short; GPIO mode not set.

Call naibrd_SER_SetReceiverEnable() with TRUE. Ensure interface level includes NAI_SER_GEN5_INTF_GP_OUT. Wait for the configuration settling delay.

GPI does not follow GPO in loopback

Loopback flag not included in interface level; receiver not enabled.

Verify the interface level was set with both NAI_SER_INTF_LOOPBACK and NAI_SER_GEN5_INTF_GP_OUT.

GPO does not drive external pin

Loopback mode routes GPO internally, not to the external connector.

Remove NAI_SER_INTF_LOOPBACK from the interface level setting.

No board found / connection timeout

Board not powered; incorrect interface or address; firewall blocking connection.

Verify power and physical connection. Check the configuration file for correct interface settings.

Full Source

Full Source — SER_ASync_GPIO.c (SSK 1.x)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

/* Common Sample Program include files */
#include "include/naiapp_boardaccess_menu.h"
#include "include/naiapp_boardaccess_query.h"
#include "include/naiapp_boardaccess_access.h"
#include "include/naiapp_boardaccess_display.h"
#include "include/naiapp_boardaccess_utils.h"

/* naibrd include files */
#include "nai.h"
#include "naibrd.h"
#include "functions/naibrd_ser.h"
#include "advanced/nai_ether_adv.h"

static const int8_t *CONFIG_FILE = (const int8_t *)"default_SerASync_GPIO.txt";

/* Function prototypes */
void Run_SER_ASync_GPIO(int32_t cardIndex, int32_t module, uint32_t moduleID);

#define CLEAR_FIFO_TIMEOUT 1000  /* 1 second */

/**************************************************************************************************************/
/** \defgroup SERGPIO Serial General Purpose I/O
The purpose of the Serial General Purpose I/O sample application is to illustrate the methods to call in the
naibrd library to configure a given serial channel for GPIO mode and toggle the output. In loopback, the general
purpose input and general purpose output lines are connected together.
*/
/**************************************************************************************************************/
#if defined (__VXWORKS__)
int32_t SER_ASync_GPIO_Sample(void)
#else
int32_t main(void)
#endif
{
   bool_t stop = 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) == TRUE)
   {
      while (stop != TRUE)
      {
         /* Query user for the card index */
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         if (stop != TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));

            /* Query the user for the module number */
            stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
            if (stop != TRUE)
            {
               moduleID = naibrd_GetModuleID(cardIndex, module);
               if ((moduleID != 0) && (moduleID == NAI_MODULE_ID_SC3))
               {
                  Run_SER_ASync_GPIO(cardIndex, module, moduleID);
               }
               else
               {
                  printf("\nThe GPIO feature is only available on the NAI SC3 module.\n\n");
               }
            }
         }

         printf("\nType Q to quit or Enter key to restart application:\n");
         stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
      }
   }

   printf("\nType the Enter key to exit the program: ");
   naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
   naiapp_access_CloseAllOpenCards();

   return 0;
}

/**************************************************************************************************************/
/** \ingroup SERGPIO
Configures a serial module for GPIO mode. The user can than the toggle the general purpose output
and monitor the general purpose input.
\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_GPIO(int32_t cardIndex, int32_t module, uint32_t moduleID)
{
   int32_t ch, i;
   int32_t channelCount;
   int32_t chanNum;
   int32_t nCntlValueLo;
   bool_t bQuit = FALSE;
   uint32_t control = 0;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   channelCount = naibrd_SER_GetChannelCount(moduleID);
   naiapp_query_ChannelNumber(channelCount, 1, &chanNum);

   naibrd_SER_ChannelReset(cardIndex, module, chanNum);
   naibrd_SER_ClearRxFifo(cardIndex, module, chanNum);
   naibrd_SER_ClearTxFifo(cardIndex, module, chanNum);
   nCntlValueLo = NAI_SER_CTRLLO_CLEAR_RX_FIFO | NAI_SER_CTRLLO_CLEAR_TX_FIFO;
   for (i = 0; i < CLEAR_FIFO_TIMEOUT && (nCntlValueLo & (NAI_SER_CTRLLO_CLEAR_RX_FIFO | NAI_SER_CTRLLO_CLEAR_TX_FIFO)); i++)
   {
      nai_ser_chanctrl chanCtrlRaw;
      naibrd_SER_GetChannelControlRaw(cardIndex, module, chanNum, &chanCtrlRaw);
      nCntlValueLo = chanCtrlRaw & 0x0000FFFF;
      nai_msDelay(1);
   }
   if (i == CLEAR_FIFO_TIMEOUT)
   {
      printf("Unable to clear FIFOs %d\n", chanNum);
      printf("Please press Enter to exit...");
      while ((ch = getchar()) != 0x0A);
      return;
   }

   printf("\nSerial Channel # %d\n", chanNum);

   /* Configure GPIO + Loopback on the channel selected */
   check_status(naibrd_SER_SetInterfaceLevel(cardIndex, module, chanNum, NAI_SER_INTF_LOOPBACK | NAI_SER_GEN5_INTF_GP_OUT));  /* LoopBack with GPIO */
   /* Enable receiver */
   check_status(naibrd_SER_SetReceiverEnable(cardIndex, module, chanNum, TRUE));

   /* Add a delay here for the configuration to be ready */
   nai_msDelay(500);

   /* Ensure GPO is low */
   check_status(naibrd_SER_GetChannelControlRaw(cardIndex, module, chanNum, &control));
   control &= ~NAI_SER_GEN5_CTRL_GPO1;
   check_status(naibrd_SER_SetChannelControlRaw(cardIndex, module, chanNum, control));

   do
   {
      printf("Press ENTER to pull GPO high, or %c to exit program...\n", NAI_QUIT_CHAR);
      bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);

      if (TRUE != bQuit)
      {
         uint32_t chanStatus = 0;

         /* Set the GPO bit to 1 (high state) */
         control |= NAI_SER_GEN5_CTRL_GPO1;
         check_status(naibrd_SER_SetChannelControlRaw(cardIndex, module, chanNum, control));

         /* Since LOOPBACK mode ties GPO to GPI, GPI will be in a high state */
         check_status(naibrd_SER_GetChannelStatusEx(cardIndex, module, chanNum, NAI_SER_STATUS_REALTIME, &chanStatus));
         printf("GPI bit: %u\n", (chanStatus & NAI_SER_GEN5_INT_GPI1) >> 15);

         printf("Press ENTER to pull GPO low, or %c to exit program...\n", NAI_QUIT_CHAR);
         bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
      }

      if (TRUE != bQuit)
      {
         uint32_t chanStatus = 0;

         /* Set the GPO bit to 0 (low state) */
         control &= ~NAI_SER_GEN5_CTRL_GPO1;
         check_status(naibrd_SER_SetChannelControlRaw(cardIndex, module, chanNum, control));

         /* Since LOOPBACK mode ties GPO to GPI, GPI will be in a low state */
         check_status(naibrd_SER_GetChannelStatusEx(cardIndex, module, chanNum, NAI_SER_STATUS_REALTIME, &chanStatus));
         printf("GPI bit: %u\n", (chanStatus & NAI_SER_GEN5_INT_GPI1) >> 15);
      }
   } while (TRUE != bQuit);

   return;
}

Help Bot

X