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

RLY Interrupt Basic

RLY Interrupt Basic Sample Application (SSK 1.x)

Overview

The RLY Interrupt Basic sample application demonstrates how to configure and handle hardware interrupts on relay (RLY) modules using the NAI Software Support Kit (SSK 1.x). The sample configures interrupts to detect BIT (Built-In Test) status changes — the relay module’s mechanism for verifying that the physical position of each relay matches the software-commanded state.

This is the simplest interrupt sample in the SSK: it monitors a single status type (BIT) using a straightforward ISR-based handler. If you are new to NAI interrupt programming, this sample is a good starting point before moving to more complex interrupt samples such as DT Interrupts, which handles four status types simultaneously.

This sample supports the following relay module types:

  • Gen3: KN, KL

  • Gen5: RY1, RY2

For background on relay operations — commanding relays, reading state, and basic module configuration — see the RLY BasicOps guide. For interrupt concepts including edge vs. level triggering, interrupt vector numbering, steering architecture, and latency measurement, see the Interrupts API Guide. This guide focuses on how those concepts apply specifically to relay BIT interrupts and walks through the practical implementation using the SSK 1.x API.

What BIT Detects

Relay modules include a secondary set of Form-C contacts that mirror the primary switching contacts. The BIT circuitry continuously compares the state of these secondary contacts against the software-commanded relay position. When the secondary contacts indicate a state that does not match the commanded position — for example, a relay commanded closed but physically stuck open, or a relay commanded open but mechanically failed closed — the BIT circuitry latches a fault for that channel.

This secondary contact monitoring is entirely transparent to normal relay operation. The primary contacts switch loads as commanded regardless of BIT status. BIT is a diagnostic overlay that runs in parallel, providing real-time health monitoring without affecting relay function.

Common causes of BIT faults include:

  • Mechanical wear — relay contacts have a finite mechanical lifetime. After enough switching cycles, a relay may fail to actuate reliably.

  • Stuck contacts — contamination, welding from overcurrent events, or mechanical damage can cause contacts to stick in one position.

  • Wiring faults — if the secondary Form-C contacts are not properly connected (on modules that require external wiring for the secondary set), BIT cannot detect contact position and may report spurious faults.

For detailed relay hardware specifications including contact ratings, switching lifetime, and Form-C contact wiring diagrams, consult the RY1-RY2 Manual.

Prerequisites

Before running this sample, make sure you have:

  • An NAI board with a relay module installed (KN, KL, RY1, or RY2).

  • 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 RLY_Interrupt_Basic executable from your build output directory. On startup the application looks for a configuration file (default_RLY_Interrupt_Basic.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 for interrupt steering and begins waiting for interrupt events.

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 RLY.

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

  1. Initialize the RLY and interrupt configuration structures with defaults by calling initializeRLYConfigurations() and initializeInterruptConfigurations().

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

  3. Query the user for card index and module number.

  4. Retrieve the module ID to confirm a valid relay module is installed at the selected slot.

#if defined (__VXWORKS__)
int32_t RLY_Interrupt_Basic(void)
#else
int32_t main(void)
#endif
{
   bool_t stop = FALSE;
   int32_t cardIndex;
   int32_t moduleCnt;
   int32_t module;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   initializeRLYConfigurations(0, 0, 0, 0, 0, 0);
   initializeInterruptConfigurations(FALSE, FALSE, FALSE, 0, 0, 0, 0);

   if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
   {
      while (stop != TRUE)
      {
         /* Query the user for the card index */
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         inputRLYConfig.cardIndex = cardIndex;
         if (stop != TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));

            /* Query the user for the module number */
            stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
            inputRLYConfig.module = module;
            if (stop != TRUE)
            {
               inputRLYConfig.modid = naibrd_GetModuleID(cardIndex, module);
               if ((inputRLYConfig.modid != 0))
               {
                  Run_RLY_Interrupt_Basic();
               }
            }
         }

         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;
}

The outer while loop allows the user to reconfigure and re-run without restarting the application. After each run, typing Q exits the application; pressing Enter restarts the card/module selection flow. naiapp_access_CloseAllOpenCards() is called on exit to release all board resources.

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 relay module. Use the board menu to verify which slots are populated. naibrd_GetModuleID() returning 0 indicates no recognized module at that slot.

Program Structure

The RLY Interrupt Basic sample is split across two source files:

  • RLY_Interrupt_Basic.c — provides the main() entry point and the Run_RLY_Interrupt_Basic() function that orchestrates the interrupt workflow.

  • nai_rly_int.c — shared interrupt configuration, ISR, and status checking utilities used by all RLY interrupt samples.

The Run_RLY_Interrupt_Basic() function follows a linear sequence:

  1. Determine the channel count for the installed relay module.

  2. Query the user for interrupt steering type (onboard or offboard).

  3. Install the ISR with naibrd_InstallISR().

  4. Configure the module for BIT interrupts with configureRLYToInterruptOnRx().

  5. Enable BIT interrupts on all channels with enableRLYInterrupts().

  6. Enter an interactive loop waiting for interrupts with checkForRLYInterrupt().

  7. On exit, disable interrupts and uninstall the ISR.

static bool_t Run_RLY_Interrupt_Basic()
{
   bool_t bQuit = FALSE;
   inputRLYConfig.maxChannel = naibrd_RLY_GetChannelCount(inputRLYConfig.modid);
   inputRLYConfig.minChannel = 1;

   if (!bQuit)
   {
      bQuit = GetIntSteeringTypeFromUser(&inputInterruptConfig.steering);
   }
   if (!bQuit)
   {
      /**** Install ISR ****/
      setIRQ(inputInterruptConfig.steering, &inputInterruptConfig.irq);
      naibrd_InstallISR(inputRLYConfig.cardIndex, inputInterruptConfig.irq, basic_ISR_RLY, NULL);

      /**** Configure module to perform interrupts ****/
      configureRLYToInterruptOnRx(inputInterruptConfig, inputRLYConfig);
      enableRLYInterrupts(inputRLYConfig, TRUE);

      /**** Wait for interrupts ****/
      bQuit = checkForRLYInterrupt(inputRLYConfig);

      /**** Cleanup ****/
      enableRLYInterrupts(inputRLYConfig, FALSE);
      check_status(naibrd_UninstallISR(inputRLYConfig.cardIndex));
   }
   return bQuit;
}

Compared to the DT Interrupts sample — which manages four interrupt status types (BIT, low-to-high, high-to-low, overcurrent), a threaded state machine, and both onboard/Ethernet delivery paths — this relay sample is intentionally minimal. There is one status type (BIT), one ISR, and a simple polling loop. The menu system is a sample convenience — in your own code, call these API functions directly.

Interrupt Status Types

The relay module exposes a single interrupt status type: BIT (Built-In Test), accessed through the NAI_RLY_BIT_STATUS_LATCHED constant. This is in contrast to modules like DT, which expose four separate status types. The simplicity of the relay interrupt model reflects the nature of relay hardware — the primary diagnostic question is whether the relay is in the position you commanded.

BIT Status — Hardware-Level Operation

The BIT monitoring circuitry operates as follows:

  1. You command a relay position using the SSK API (e.g., naibrd_RLY_SetState()). This drives the primary Form-C contacts to the commanded position (open or closed).

  2. The secondary Form-C contacts actuate in parallel. These contacts are mechanically coupled to the same armature as the primary contacts but are electrically isolated. They carry no load current — they exist solely for BIT monitoring.

  3. The BIT comparator reads the secondary contacts' position and compares it to the commanded state stored in the module’s registers. If the secondary contacts indicate a position that matches the commanded state, BIT reports healthy (no fault). If there is a mismatch, BIT latches a fault for that channel.

  4. When a BIT fault latches, the module sets the corresponding bit in the NAI_RLY_BIT_STATUS_LATCHED register. If BIT interrupts are enabled for that channel, the module asserts an interrupt.

The BIT status register is a bitmask where each bit corresponds to a relay channel. Bit 0 represents channel 1, bit 1 represents channel 2, and so on. A set bit indicates a BIT fault on that channel. The register is latched — once set, it remains set until software explicitly clears it, even if the relay subsequently returns to the correct position.

Both real-time and latched BIT status are available. The real-time status shows the current state of the BIT comparator at the moment you read it. The latched status captures any fault that has occurred since the last clear, ensuring you do not miss transient faults. This sample uses the latched status (NAI_RLY_BIT_STATUS_LATCHED) for interrupt generation because latched status guarantees that a brief mismatch during relay switching is captured and reported.

For relay hardware specifications, contact ratings, and Form-C wiring details, consult the RY1-RY2 Manual.

Interrupt Configuration

The configureRLYToInterruptOnRx() function in nai_rly_int.c performs the complete interrupt configuration sequence. Because the relay module has only one interrupt status type, the configuration is straightforward compared to multi-status modules like DT.

Configuration Flow

The function performs four steps in sequence:

  1. Disable interrupts — call enableRLYInterrupts() with FALSE to prevent spurious interrupts during reconfiguration.

  2. Set the interrupt vector — assign a vector number to the BIT status type so the ISR can identify the interrupt source.

  3. Clear status and set trigger mode — for each channel, clear any stale BIT status and configure edge or level triggering.

  4. Set interrupt steering — route the interrupt signal to the appropriate delivery path (onboard or offboard).

void configureRLYToInterruptOnRx(InterruptConfig inputInterruptConfig, RlyConfig inputRLYConfig)
{
   int32_t cardIndex = inputRLYConfig.cardIndex;
   int32_t module = inputRLYConfig.module;
   int32_t vector = NAI_RLY_INTERRUPT_VECTOR;
   int32_t interrupt_Edge_Trigger = inputInterruptConfig.interrupt_Edge_Trigger;
   int32_t steering = inputInterruptConfig.steering;
   int32_t chan = 1;

   enableRLYInterrupts(inputRLYConfig, FALSE);

   /* Setup the Interrupt Vector - map to the same vector */
   check_status(naibrd_RLY_SetGroupInterruptVector(cardIndex, module, 1, NAI_RLY_BIT_STATUS_LATCHED, vector));

   for (chan = 1; chan <= inputRLYConfig.maxChannel; chan++)
   {
      /* Clear the Interrupt Status */
      check_status(naibrd_RLY_ClearStatus(cardIndex, module, chan, NAI_RLY_BIT_STATUS_LATCHED));
      /* Setup the Latched Status Mode */
      check_status(naibrd_RLY_SetEdgeLevelInterrupt(cardIndex, module, chan, NAI_RLY_BIT_STATUS_LATCHED,
                   (nai_rly_interrupt_t)interrupt_Edge_Trigger));
   }
   check_status(naibrd_RLY_SetGroupInterruptSteering(cardIndex, module, 1, NAI_RLY_BIT_STATUS_LATCHED, steering));
}

Step-by-Step Explanation

Disable interrupts first. Always disable interrupts before changing configuration to avoid spurious triggers during setup. The enableRLYInterrupts() call iterates over all channels and disables BIT interrupt generation.

Set the interrupt vector. naibrd_RLY_SetGroupInterruptVector() assigns NAI_RLY_INTERRUPT_VECTOR to the BIT status type for the entire group (all channels). The vector identifies which interrupt source generated the event. Since the relay module has only one status type, a single vector assignment is sufficient. In your own application, you can use any vector value — just ensure your ISR checks for the same value.

Clear stale status. naibrd_RLY_ClearStatus() clears any latched BIT faults from a previous run. If you skip this step, stale status bits will trigger an immediate interrupt as soon as you enable — and with edge triggering, that stale interrupt may be the only one you ever see, because no new edge will be generated when the actual fault occurs.

Set the trigger mode. naibrd_RLY_SetEdgeLevelInterrupt() configures whether each channel uses edge-triggered (0) or level-triggered (1) interrupts. Edge-triggered fires once when the BIT fault first latches. Level-triggered continues to assert as long as the BIT fault condition remains active. For relay BIT monitoring, edge-triggered is typically preferred — you want to know when a fault occurs, then investigate, rather than receiving continuous interrupts for an ongoing mechanical failure.

Set interrupt steering. naibrd_RLY_SetGroupInterruptSteering() routes the BIT interrupt to the correct delivery path. The steering value is selected by the user at runtime through GetIntSteeringTypeFromUser(). The available steering options are:

  • Onboard (NAIBRD_INT_STEERING_ON_BOARD_0) — handled locally on the board’s processor. Use this when your application runs directly on the NAI board.

  • cPCI offboard (NAIBRD_INT_STEERING_CPCI_APP) — routes the interrupt to the CompactPCI backplane host.

  • PCIe offboard (NAIBRD_INT_STEERING_PCIE_APP) — routes the interrupt to the PCIe host. This is the standard choice for desktop or rack-mount systems with a PCIe connection.

For detailed descriptions of each steering mode and the hardware paths involved, see the Interrupts API Guide and the DT Interrupts guide which covers steering in depth.

Enabling Interrupts

After configuration, enableRLYInterrupts() turns on interrupt generation for all channels:

void enableRLYInterrupts(RlyConfig inputRLYConfig, bool_t enable) {
   int32_t channel;
   for (channel = inputRLYConfig.minChannel; channel <= inputRLYConfig.maxChannel; channel++)
   {
      check_status(naibrd_RLY_SetInterruptEnable(inputRLYConfig.cardIndex, inputRLYConfig.module,
                   channel, NAI_RLY_BIT_STATUS_LATCHED, enable));
   }
}

The function loops from channel 1 through maxChannel and calls naibrd_RLY_SetInterruptEnable() for the BIT status type on each channel. Pass TRUE to enable or FALSE to disable. In your own application, you can selectively enable only the channels you need rather than enabling all channels.

Important

Common interrupt configuration errors:

  • ISR installation failure — naibrd_InstallISR() returns an error when the steering mode does not match your hardware configuration or bus type. For example, selecting cPCI steering on a PCIe-only system will fail because no CompactPCI interrupt path exists. Verify that your steering selection matches the physical bus connection.

  • Interrupts not firing after enable — stale latched status registers from a previous run prevent new edges from being generated. Always clear status before enabling interrupts. This is the most common cause of silent failures.

  • Wrong steering mode — selecting onboard steering when your application runs on an external host (or vice versa) routes the interrupt to a path with no handler. Double-check that the steering mode matches where your application executes.

Interrupt Handler

The basic_ISR_RLY() function is the ISR (Interrupt Service Routine) that fires each time the module asserts a BIT interrupt. It is deliberately minimal — ISRs should execute as quickly as possible and defer detailed processing to non-interrupt context.

ISR Implementation

#if defined (__VXWORKS__)
void basic_ISR_RLY(uint32_t param)
#else
void basic_ISR_RLY(void *param, uint32_t vector)
#endif
{
   interruptOccured = TRUE;

#if defined (__VXWORKS__)
   interruptVector = nai_Onboard_GetInterruptVector();
   nai_Onboard_ClearInterrupt();
#elif defined (WIN32)
   UNREFERENCED_PARAMETER(param);
   interruptVector = vector;
#else
   interruptVector = vector;
#endif
}

The ISR does three things:

  1. Sets the interruptOccured flag to TRUE so the polling loop (checkForRLYInterrupt()) knows an interrupt has arrived.

  2. Captures the interrupt vector for later identification. On VxWorks, the vector is read from the onboard interrupt controller via nai_Onboard_GetInterruptVector(). On Windows and Linux, the vector is passed directly as a parameter by the library.

  3. On VxWorks, clears the board-level interrupt with nai_Onboard_ClearInterrupt(). This is required on VxWorks to prevent the ISR from being called again immediately. On other platforms, the library handles this automatically.

This ISR is simpler than the DT interrupt handler, which must iterate over four status types and conditionally clear each one. Because the relay module has only BIT status, the ISR just captures the event and defers status reading to the polling loop.

Interrupt Polling and Status Reading

The checkForRLYInterrupt() function provides the interactive loop where the user checks for and responds to BIT interrupts:

bool_t checkForRLYInterrupt(RlyConfig inputRLYConfig)
{
   bool_t bQuit;
   int32_t cardIndex;
   int32_t module;
   int32_t channel;
   uint32_t rawstatus = 0;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   cardIndex = inputRLYConfig.cardIndex;
   module = inputRLYConfig.module;
   channel = inputRLYConfig.channel;
   bQuit = FALSE;
   interruptOccured = FALSE;
   while (!bQuit) {
      printf("\nPress enter to check if RLY interrupt Occurred (press Q to quit):");
      bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
      if (!bQuit) {
         if (interruptOccured) {

            check_status(naibrd_RLY_GetGroupRaw(cardIndex, module, 1,
                         NAI_RLY_RAW_GROUP_BIT_LATCHED_STATUS, &rawstatus));
            printf("\nVector = %#x \n", interruptVector);
            printf("Status = %#x \n", rawstatus);
            interruptOccured = FALSE;
         }
         else {
            printf("\nNo Interrupt Occurred");
         }
         printf("\n\nWould you like to clear the status register? (default:N):");

         bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
         if (inputBuffer[0] == 'y' || inputBuffer[0] == 'Y')
         {
            for (channel = 1; channel <= inputRLYConfig.maxChannel; channel++)
            {
               check_status(naibrd_RLY_ClearStatus(cardIndex, module, channel, NAI_RLY_BIT_STATUS_LATCHED));
            }
         }
      }
   }
   return bQuit;
}

When the interruptOccured flag is set by the ISR, the function:

  1. Reads the group BIT latched status with naibrd_RLY_GetGroupRaw(). The returned rawstatus is a bitmask where each set bit indicates a channel with a BIT fault. For example, a value of 0x05 means channels 1 and 3 have BIT faults.

  2. Prints the interrupt vector and status bitmask so you can see which vector fired and which channels are faulted.

  3. Resets the interruptOccured flag to prepare for the next interrupt.

  4. Optionally clears the status registers. The user is prompted to clear — pressing Y clears the latched BIT status for all channels by calling naibrd_RLY_ClearStatus() per channel. Clearing the status re-arms the interrupt so it can fire again on the next BIT fault. If you do not clear, edge-triggered interrupts will not fire again because no new edge will be generated.

In your own application, you would typically clear the status automatically rather than prompting the user. The prompt-based approach in this sample is designed for learning and debugging — it lets you observe the latched status before clearing.

Important

Common interrupt handling errors:

  • Interrupts stop after first event (edge-triggered) — the status register was not cleared after the interrupt was serviced. With edge triggering, the interrupt fires on the transition into the latched state. If the status is never cleared, no new edge occurs and the interrupt controller will not fire again. Always call naibrd_RLY_ClearStatus() in your handler.

  • Interrupts fire continuously (level-triggered) — this is expected behavior. In level-triggered mode, the interrupt re-asserts as long as the BIT fault condition persists. If you do not want continuous interrupts, switch to edge-triggered mode or resolve the underlying relay fault.

  • Status always reads zero — the relay has not been commanded to a position. BIT compares the secondary contacts against the commanded state. If no state has been commanded, there is no reference for comparison. Use naibrd_RLY_SetState() to command a relay position before expecting BIT to detect faults.

Troubleshooting Reference

This table summarizes common errors and symptoms covered in the sections above. For detailed context, refer to the relevant section. Consult your module’s manual for hardware-specific diagnostic procedures.

Debugging Relay Interrupts That Are Not Firing

When relay interrupts are not being delivered, use this two-step approach to isolate the problem:

  1. Check whether the BIT status register is changing. Call naibrd_RLY_GetGroupRaw() with NAI_RLY_RAW_GROUP_BIT_LATCHED_STATUS to read the latched BIT status. If the status bits change when you physically disturb a relay (or command a relay to a position it cannot reach), the module is detecting the event correctly — the issue is in your interrupt delivery path (steering, ISR installation, or IRQ selection).

  2. If the BIT status register is NOT changing, the issue is at the hardware or configuration level. First, verify that a relay position has been commanded — BIT requires a commanded state to compare against. Then verify the relay module type is supported and the channel count is nonzero. You can also read the BIT status registers directly at their register addresses to bypass the API layer entirely. Consult your module’s manual for the specific register addresses.

This approach quickly narrows down whether the problem is on the hardware/configuration side or the interrupt delivery side, saving significant debugging time.

Error / Symptom Possible Causes Suggested Resolution

No board found or connection timeout

Board not powered, incorrect or missing configuration file, network issue

Verify hardware is powered and connected. If the configuration file exists, check that it lists the correct interface and address. If it does not exist, the board menu will appear — configure and save your connection settings.

Module not detected at selected slot

No relay module installed at the selected slot, incorrect module number, or a non-relay module is present

Verify hardware configuration. naibrd_GetModuleID() returning 0 indicates no recognized module at the selected slot. Confirm the slot contains a KN, KL, RY1, or RY2 module.

ISR installation failure

Steering mode does not match hardware configuration, driver not loaded, or bus access issue

Verify steering mode matches your bus architecture. On Linux, check that the NAI driver is loaded. On Windows, verify the device appears in Device Manager.

Interrupts not firing after enable

Status registers not cleared before enabling, wrong steering mode, ISR not installed, or relay not commanded to a position

Follow the full setup sequence: disable, clear status, configure vector and trigger mode, set steering, install ISR, enable. Verify a relay state has been commanded — BIT requires a commanded position to compare against. See the debugging technique above.

BIT always shows fault (all channels)

Relay hardware fault, secondary Form-C contacts not wired, or module not properly seated

Check physical relay hardware. If secondary contacts require external wiring on your module, verify the wiring. Reseat the module and check for mechanical damage. Consult the module manual for Form-C contact pinout.

BIT never triggers despite relay faults

BIT interrupts not enabled, status not being read correctly, or secondary contacts not connected

Verify naibrd_RLY_SetInterruptEnable() was called with TRUE for the target channels. Read the BIT status register directly to confirm the module detects faults. On modules where secondary contacts require external wiring, verify the connections.

Wrong steering mode selected

Using onboard steering from an external host, or offboard steering from the onboard processor

Match steering to where your application executes. Onboard: ON_BOARD_0. External PCI: CPCI_APP. External PCIe: PCIE_APP.

Interrupts stop after first event

Status register not cleared in handler (edge-triggered mode)

Clear the BIT latched status after servicing each interrupt using naibrd_RLY_ClearStatus(). Without clearing, no new edge is generated and the interrupt controller will not fire again.

Edge vs. level trigger mismatch

Application expects one-shot interrupt behavior but configured level triggering, or vice versa

Edge triggering (0) fires once per fault event and requires clearing to re-arm. Level triggering (1) fires continuously while the fault persists. Choose the mode that matches your application’s response model.

Full Source

The complete source for this sample is provided below for reference. The sections above explain each part in detail. This sample consists of two source files: the main application and the shared relay interrupt utilities.

Full Source — RLY_Interrupt_Basic.c (SSK 1.x)
/**************************************************************************************************************/
/**
<summary>

The RLY_Interrupt_Basic program demonstrates how to perform an interrupt when a single channel receives
a message. The purpose of this program is to demonstrate the method calls in the naibrd library for performing
the interrupt. More information on this process can be found in the naibrd SSK Quick Guide(Interrupts) file.

</summary>
*/
/**************************************************************************************************************/

/************************/
/************************/
/* Include Declarations */
/************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/*Common Module Specific Sample Program include files*/

#include "nai_rly_cfg.h"
#include "nai_rly_int.h"

/* Common Sample Program include files */
#include "include/naiapp_interrupt.h"
#include "include/naiapp_interrupt_ether.h"
#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"

/* Module Specific NAI Board Library files */
#include "functions/naibrd_rly.h"

/* Extern Functions or Variables*/
extern InterruptConfig inputInterruptConfig;
extern RlyConfig inputRLYConfig;

/*********************************************/
/* Application Name and Revision Declaration */
/*********************************************/
/*
static const int8_t *App_Name = "RLY Interrupt Basic";
static const int8_t *App_Rev = "1.0";
*/

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

/********************************/
/* Internal Function Prototypes */
/********************************/
static bool_t Run_RLY_Interrupt_Basic();

/**************************************************************************************************************/
/*****                                     Main Routine                                                   *****/
/**************************************************************************************************************/
/**************************************************************************************************************/
/**
<summary>

The main routine assists in gaining access to the board.

The following routines from the nai_sys_cfg.c file are
called to assist with accessing and configuring the board.

 - ConfigDevice
 - DisplayDeviceCfg
 - GetBoardSNModCfg
 - CheckModule

</summary>
*/
/*****************************************************************************/

#if defined (__VXWORKS__)
int32_t RLY_Interrupt_Basic(void)
#else
int32_t main(void)
#endif
{
   bool_t stop = FALSE;
   int32_t cardIndex;
   int32_t moduleCnt;
   int32_t module;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   initializeRLYConfigurations(0, 0, 0, 0, 0, 0);
   initializeInterruptConfigurations(FALSE, FALSE, FALSE, 0, 0, 0, 0);

   if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
   {
      while (stop != TRUE)
      {
         /* Query the user for the card index */
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         inputRLYConfig.cardIndex = cardIndex;
         if (stop != TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));

            /* Query the user for the module number */
            stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
            inputRLYConfig.module = module;
            if (stop != TRUE)
            {
               inputRLYConfig.modid = naibrd_GetModuleID(cardIndex, module);
               if ((inputRLYConfig.modid != 0))
               {
                  Run_RLY_Interrupt_Basic();
               }
            }
         }
         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;
}
/**************************************************************************************************************/
/**
<summary>

This function is broken into the following major steps. These steps correspond with the steps provided
in the naibrd SSK Quick Guide(Interrupts) file.

2. Bus Interrupt Handling - Install ISR

   API CALLS - naibrd_InstallISR

3. Enable Module Interrupts- Configures module to interrupter when channel receives RLY message.

   API CALLS - naibrd_RLY_SetInterruptEdgeLevel, naibrd_RLY_SetIntVector, naibrd_RLY_SetInterruptSteering, naibrd_RLY_SetIntEnable

4. Not applicable to DSW module

5. Show Interrupt Handling - Check if any interrupt has occurred and report back the status and vector

6. Re-arming Interrupts - Clear the status register to allow interrupts to occur again. This is done by writing to the status register.
   In this program, we use an API call to do this.

   API CALLS - naibrd_RLY_ClearStatusRaw

7. Clear Module Configurations

8. Clear Board Configurations

   API CALLS - naibrd_UninstallISR

</summary>
*/
/**************************************************************************************************************/
static bool_t Run_RLY_Interrupt_Basic()
{

   bool_t bQuit = FALSE;
   /* Query user for RX channel */
   inputRLYConfig.maxChannel = naibrd_RLY_GetChannelCount(inputRLYConfig.modid);
   inputRLYConfig.minChannel = 1;

   /*if(!bQuit)
   {
      printf("\nWhich channel would you like to Receive on? \n");
      bQuit = GetChannelNumber(inputDSWConfig.maxChannel,inputDSWConfig.minChannel,&inputDSWConfig.channel);
   }
   Query user for location interrupt will be sent out to*/
   if (!bQuit)
   {
      bQuit = GetIntSteeringTypeFromUser(&inputInterruptConfig.steering);
   }
   if (!bQuit)
   {
      /**** 2. Implement Bus Interrupt Handling****/
      setIRQ(inputInterruptConfig.steering, &inputInterruptConfig.irq);
      naibrd_InstallISR(inputRLYConfig.cardIndex, inputInterruptConfig.irq, basic_ISR_RLY, NULL);

      /****3. configure Module to perform interrupts****/
      configureRLYToInterruptOnRx(inputInterruptConfig, inputRLYConfig);
      enableRLYInterrupts(inputRLYConfig, TRUE);

      /****5. Show Interrupt Handling (contains step 6)****/
      bQuit = checkForRLYInterrupt(inputRLYConfig);

      /*****7. Clear Module Configurations*****/
      enableRLYInterrupts(inputRLYConfig, FALSE);

      /*****8. Clear Board Configurations *****/
      check_status(naibrd_UninstallISR(inputRLYConfig.cardIndex));
   }
   return bQuit;
}
Full Source — nai_rly_int.c (SSK 1.x)
#include <stdlib.h>
#include <stdio.h>

/* Common Sample Program include files */
#include "include/naiapp_interrupt.h"
#include "include/naiapp_interrupt_ether.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"

/* Common DSW Sample Program include files */
#include "nai_rly_int.h"
#include "nai_rly_cfg.h"

/* naibrd include files */
#include "nai.h"
#include "naibrd.h"
#include "naibrd_ether.h"
#include "functions/naibrd_rly.h"
#include "maps/nai_map_rly.h"

bool_t interruptOccured; /* used by checkForInterrupt to see if interrupt occurred*/
int32_t frameCount;	/* counter increment when frames are found*/
uint32_t interruptVector; /* used by checkForInterrupt to get vector of the interrupt that occurred in myIsr */
InterruptConfig inputInterruptConfig;

/* Extern Functions or Variables*/
extern RlyConfig inputRLYConfig;

/**************************************************************************************************************/
/**
<summary>
This function configures an interrupt to occur when a message is received on any of the relay channels.
</summary>
*/
/**************************************************************************************************************/
void configureRLYToInterruptOnRx(InterruptConfig inputInterruptConfig, RlyConfig inputRLYConfig)
{
   int32_t cardIndex = inputRLYConfig.cardIndex;
   int32_t module = inputRLYConfig.module;
   int32_t vector = NAI_RLY_INTERRUPT_VECTOR;
   int32_t interrupt_Edge_Trigger = inputInterruptConfig.interrupt_Edge_Trigger;
   int32_t steering = inputInterruptConfig.steering;
   int32_t chan = 1;

   enableRLYInterrupts(inputRLYConfig, FALSE);

   /* Setup the Interrupt Vector - map to the same vector */
   check_status(naibrd_RLY_SetGroupInterruptVector(cardIndex, module, 1, NAI_RLY_BIT_STATUS_LATCHED, vector));

   for (chan = 1; chan <= inputRLYConfig.maxChannel; chan++)
   {
     /* Clear the Interrupt Status (Read the status and write back "1" to statuses which are set to clear the status) */
      check_status(naibrd_RLY_ClearStatus(cardIndex, module, chan, NAI_RLY_BIT_STATUS_LATCHED));
       /* Setup the Latched Status Mode */
      check_status(naibrd_RLY_SetEdgeLevelInterrupt(cardIndex, module, chan, NAI_RLY_BIT_STATUS_LATCHED, (nai_rly_interrupt_t)interrupt_Edge_Trigger));
   }
   check_status(naibrd_RLY_SetGroupInterruptSteering(cardIndex, module, 1, NAI_RLY_BIT_STATUS_LATCHED, steering));

}
/**************************************************************************************************************/
/**
<summary>
Enables the channels within the (minChannel,maxChannel) range to interrupt
if enable is true and disables them otherwise.
</summary>
*/
/**************************************************************************************************************/
void enableRLYInterrupts(RlyConfig inputRLYConfig, bool_t enable) {
   int32_t channel;
   for (channel = inputRLYConfig.minChannel; channel <= inputRLYConfig.maxChannel; channel++)
   {
      check_status(naibrd_RLY_SetInterruptEnable(inputRLYConfig.cardIndex, inputRLYConfig.module, channel, NAI_RLY_BIT_STATUS_LATCHED, enable));
   }
}

/**************************************************************************************************************/
/**
<summary>
The routine is called to handle a interrupt. This function alerts checkForInterrupt that an interrupt has occurred.
In vxWorks you must clear the Interrupt on the board within the ISR.
</summary>
*/
/**************************************************************************************************************/

#if defined (__VXWORKS__)
void basic_ISR_RLY(uint32_t param)
#else
void basic_ISR_RLY(void *param, uint32_t vector)
#endif
{
   interruptOccured = TRUE;

#if defined (__VXWORKS__)
   interruptVector = nai_Onboard_GetInterruptVector();
   nai_Onboard_ClearInterrupt();
#elif defined (WIN32)
   UNREFERENCED_PARAMETER(param);
   interruptVector = vector;
#else
   interruptVector = vector;
#endif
}

/**************************************************************************************************************/
/**
<summary>
Prompts user to check if an interrupt has occurred. MyIsr() should be installed if using this function.
</summary>
*/
/**************************************************************************************************************/
bool_t checkForRLYInterrupt(RlyConfig inputRLYConfig)
{
   bool_t bQuit;
   int32_t cardIndex;
   int32_t module;
   int32_t channel;
   uint32_t rawstatus = 0;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   cardIndex = inputRLYConfig.cardIndex;
   module = inputRLYConfig.module;
   channel = inputRLYConfig.channel;
   bQuit = FALSE;
   interruptOccured = FALSE;
   while (!bQuit) {
      printf("\nPress enter to check if RLY interrupt Occurred (press Q to quit):");
      bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
      if (!bQuit) {
         if (interruptOccured) {

            check_status(naibrd_RLY_GetGroupRaw(cardIndex, module, 1, NAI_RLY_RAW_GROUP_BIT_LATCHED_STATUS, &rawstatus));
            printf("\nVector = %#x \n", interruptVector);
            printf("Status = %#x \n", rawstatus);
            interruptOccured = FALSE;
         }
         else {
            printf("\nNo Interrupt Occurred");
         }
         printf("\n\nWould you like to clear the status register? (default:N):");

         bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
         if (inputBuffer[0] == 'y' || inputBuffer[0] == 'Y')
         {
            for (channel = 1; channel <= inputRLYConfig.maxChannel; channel++)
            {
                 /* Clear the Interrupt Status (Read the status and write back "1" to statuses which are set to clear the status) */
               check_status(naibrd_RLY_ClearStatus(cardIndex, module, channel, NAI_RLY_BIT_STATUS_LATCHED));
            }
         }
      }
   }
   return bQuit;
}

/**************************************************************************************************************/
/**
<summary>
GetDSWLatchStatusTriggerMode handles prompting the user for the trigger mode for the latched status register
(Edge Triggered or Level Triggered).
</summary>
*/
/**************************************************************************************************************/
bool_t GetRLYLatchStatusTriggerMode(int32_t* interrupt_Edge_Trigger)
{
   bool_t bQuit;
   uint32_t temp;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   printf("\nEnter Latched Status Trigger Mode (Edge=0, Level=1) (Default=0): ");
   bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
   if (!bQuit)
   {
      if (inputResponseCnt > 0)
      {
         temp = (int32_t)atol((const char*)inputBuffer);

         if (temp == 0 || temp == 1)
         {
            *interrupt_Edge_Trigger = temp;
         }
         else
         {
            printf("ERROR: Invalid Interrupt Trigger Mode.\n");
         }
      }
      else
         *interrupt_Edge_Trigger = 0;
   }
   return(bQuit);
}

/**************************************************************************************************************/
/**
<summary>
This routine takes in the vector of the RLY RX interrupt that has just occurred. A
</summary>
*/
/**************************************************************************************************************/
void handleRLYInterrupt(uint32_t nVector) {

   uint32_t rawstatus = 0;
   int32_t chan = 1;
   int32_t maxChannel;
   nai_rly_status_type_t rly_status_type;
   rly_status_type = NAI_RLY_BIT_STATUS_LATCHED;
   maxChannel = inputRLYConfig.maxChannel;

   printf("\n\nInterrupt Occurred \n\n");
   if (inputInterruptConfig.bPromptForInterruptClear)
   {
      promptUserToClearInterrupt_RLY();
   }
   check_status(naibrd_RLY_GetGroupRaw(inputRLYConfig.cardIndex, inputRLYConfig.module, 1, rly_status_type, &rawstatus));

   for (chan = 1; chan <= maxChannel; chan++)
   {

      check_status(naibrd_RLY_ClearStatus(inputRLYConfig.cardIndex, inputRLYConfig.module, chan, rly_status_type));
   }
   printInterruptInformation_RLY(nVector, rawstatus, FALSE);

}

void promptUserToClearInterrupt_RLY()
{

   /* Prompt the user to clear the interrupt received */
   SetUserRequestClearInt(FALSE);
   printf("\n");
   DisplayMessage_RLYInterrupt(MSG_USER_CLEAR_RLY_INT);

   /* Wait for the user to respond */
   while (!GetUserRequestClearInt())
   {
      nai_msDelay(10);
   }
}
/**************************************************************************************************************/
/**
<summary>
DisplayMessage_DSWInterrupt handles displaying the messages associated with the msgId passed in.
</summary>
*/
/**************************************************************************************************************/

void DisplayMessage_RLYInterrupt(int32_t msgId)
{
   switch (msgId)
   {
      case (int32_t)MSG_BANNER_RLY_INT:
      {
         printf("\n********************************************************************************");
         printf("\n******                        RLY INTERRUPT                               ******");
         printf("\nAn interrupt will occur when the RLY Module receives a BIT FAult status         ");
         printf("\n********************************************************************************");
      }
      break;

      case (int32_t)MSG_USER_TRIGGER_RLY_INT:
      {
         printf("\nPress \"Q\" to quit the application.\nPlease trigger RLY BIT Fault status interrupt (Recv Msg):");
      }
      break;

      case (int32_t)MSG_USER_CLEAR_RLY_INT:
      {
         printf("Press \"C\" to clear interrupts... ");
      }
      break;

   }
}

/**************************************************************************************************************/
/**
<summary>
Function will print the Data provided for each channel that has been set in the status register.
It will also print the status and idr id or vector.
</summary>
*/
/**************************************************************************************************************/

void printInterruptInformation_RLY(int32_t interruptID, uint32_t status, bool_t isEther)
{
   printf("Interrupt Information\n");
   printf("-----------------------------------\n");
   if (isEther)
      printf("\nIDR ID = %#x \n", interruptID);
   else
      printf("\nVector = %#x \n", interruptID);
   printf("Status = %#x \n", status);

   printf("\nFrames Rx = %d", frameCount);
   printf("\n-----------------------------------\n");

}

Help Bot

X