RLY Interrupt Basic
Edit this on GitLab
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:
-
Initialize the RLY and interrupt configuration structures with defaults by calling
initializeRLYConfigurations()andinitializeInterruptConfigurations(). -
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. -
Query the user for card index and module number.
-
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:
|
Program Structure
The RLY Interrupt Basic sample is split across two source files:
-
RLY_Interrupt_Basic.c— provides themain()entry point and theRun_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:
-
Determine the channel count for the installed relay module.
-
Query the user for interrupt steering type (onboard or offboard).
-
Install the ISR with
naibrd_InstallISR(). -
Configure the module for BIT interrupts with
configureRLYToInterruptOnRx(). -
Enable BIT interrupts on all channels with
enableRLYInterrupts(). -
Enter an interactive loop waiting for interrupts with
checkForRLYInterrupt(). -
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:
-
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). -
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.
-
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.
-
When a BIT fault latches, the module sets the corresponding bit in the
NAI_RLY_BIT_STATUS_LATCHEDregister. 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:
-
Disable interrupts — call
enableRLYInterrupts()withFALSEto prevent spurious interrupts during reconfiguration. -
Set the interrupt vector — assign a vector number to the BIT status type so the ISR can identify the interrupt source.
-
Clear status and set trigger mode — for each channel, clear any stale BIT status and configure edge or level triggering.
-
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:
|
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:
-
Sets the
interruptOccuredflag toTRUEso the polling loop (checkForRLYInterrupt()) knows an interrupt has arrived. -
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. -
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:
-
Reads the group BIT latched status with
naibrd_RLY_GetGroupRaw(). The returnedrawstatusis a bitmask where each set bit indicates a channel with a BIT fault. For example, a value of0x05means channels 1 and 3 have BIT faults. -
Prints the interrupt vector and status bitmask so you can see which vector fired and which channels are faulted.
-
Resets the
interruptOccuredflag to prepare for the next interrupt. -
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:
|
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:
-
Check whether the BIT status register is changing. Call
naibrd_RLY_GetGroupRaw()withNAI_RLY_RAW_GROUP_BIT_LATCHED_STATUSto 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). -
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. |
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 |
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: |
Interrupts stop after first event |
Status register not cleared in handler (edge-triggered mode) |
Clear the BIT latched status after servicing each interrupt using |
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");
}