RTD Interrupt Ethernet
Edit this on GitLab
RTD Interrupt Ethernet Sample Application (SSK 1.x)
Overview
The RTD Interrupt Ethernet sample application demonstrates how to receive RTD (Resistance Temperature Detector) interrupt notifications over an Ethernet network using the NAI Software Support Kit (SSK 1.x) Interrupt Driven Response (IDR) mechanism. When an open-circuit condition is detected on an RTD channel, the board generates an interrupt and — instead of invoking a local ISR — sends a UDP packet to a remote host containing the interrupt status register and any embedded read/write commands you configured. Your application runs an IDR server that listens for these packets and decodes the open status.
This is the Ethernet IDR-only variant of the RTD interrupt handling samples. It operates on a single channel and delivers all interrupt information in a single UDP message to a remote host. For the combined ISR + IDR version that supports multi-channel monitoring and both onboard/offboard ISR delivery alongside Ethernet IDR, see the RTD Interrupt sample application guide. For a simplified ISR-only starting point with no Ethernet involvement, see the RTD Interrupt Basic sample application guide.
This sample supports the following RTD module types: G4, RT1, TR1, and TC1.
RTD modules support seven latched status types that can generate interrupts: BIT, Open, Alert-Lo, Alarm-Lo, Alert-Hi, Alarm-Hi, and Summary. This sample monitors the Open latched status type (NAI_RTD_STATUS_OPEN_LATCHED), which indicates a broken or disconnected RTD sensor. The IDR read command reads the open latched status register in a single read operation.
Why Ethernet IDR?
Standard interrupt delivery (ISR) requires your application to run on the same board or be connected via a backplane bus (PCI/PCIe/cPCI). Ethernet IDR removes that constraint entirely:
-
Remote monitoring — your application can run on any host reachable over the network. There is no need for a physical bus connection to the board.
-
All status in one message — the IDR packet contains the interrupt status register value and any read/write command results you configured, eliminating the need for separate register reads after the interrupt fires.
-
Automatic re-arming — you can include a write command in the IDR configuration that clears the latched status register as part of the response, so interrupts re-arm without a separate API call from the host.
-
Reduced host-side complexity — no ISR installation, no IRQ management, no thread synchronization. Your application simply listens on a UDP socket.
The tradeoff is that Ethernet IDR is only available on Gen4 and later boards, and network latency is inherently higher than a local ISR. For latency-critical applications where the host is directly connected to the board, standard ISR delivery may be more appropriate.
Prerequisites
Before running this sample, make sure you have:
-
An NAI board with an RTD module installed (G4, RT1, TR1, or TC1).
-
A Gen4 or later board with Ethernet connectivity. Earlier board generations do not support the Ethernet IDR protocol.
-
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.
-
Network connectivity between the host running this sample and the board. The host must be reachable at the IP address configured in the IDR settings (default:
192.168.1.100).
How to Run
Launch the RTD_Interrupt_Ethernet executable from your build output directory. On startup the application looks for a configuration file (default_RTD_Interrupt_Ethernet.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 channel selection, IDR network settings, and interrupt display options, then begins listening for IDR packets from the board.
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 RTD. For details on board connection configuration, see the First Time Setup Guide. |
The main() function follows a standard SSK 1.x startup flow:
-
Initialize RTD, interrupt, and IDR configuration structures with defaults using
initializeRTDConfigurations(),initializeInterruptConfigurations(), andinitializeIDRConfigurations(). The IDR initialization sets the default response IP address (192.168.1.100), UDP port (52802), and protocol (ETHER_GEN4_UDP_PROTOCOL). -
Call
naiapp_RunBoardMenu()to load a saved configuration file (if one exists) or present the interactive board menu. The configuration file (default_RTD_Interrupt_Ethernet.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 with
naibrd_GetModuleID()to verify an RTD module is installed at the selected slot.
#if defined (__VXWORKS__)
int32_t RTD_Interrupt_Basic_Ethernet(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;
initializeRTDConfigurations(0, 0, 0, 0, 0, 0);
initializeInterruptConfigurations(FALSE, FALSE, FALSE, 0, NAIBRD_INT_STEERING_ON_BOARD_1, -1, 0);
initializeIDRConfigurations(0, 0, DEF_RX_RESPONSE_PROTOCOL, DEF_RX_RESPONSE_PORT,
DEF_RX_RESPONSE_IPv4_ADDR, DEF_RX_RESPONSE_IPv4_LENGTH,
COMMANDS, 0, 0, DEF_ETHERNET_RTD_IDR_ID);
if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
while (stop != TRUE)
{
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
inputRTDConfig.cardIndex = cardIndex;
if (stop != TRUE)
{
check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
inputRTDConfig.module = module;
if (stop != TRUE)
{
inputRTDConfig.modid = naibrd_GetModuleID(cardIndex, module);
if ((inputRTDConfig.modid != 0))
{
Run_RTD_Interrupt_Basic_Ethernet();
}
}
}
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;
}
Note the three initialization calls before the board menu. initializeIDRConfigurations() is unique to the Ethernet variant — it populates the IDRConfig structure with default network settings that are used later when configuring the board’s IDR engine.
|
Important
|
Common connection errors you may encounter at this stage:
|
Program Structure
The RTD Interrupt Ethernet sample is split across multiple source files:
-
RTD_Interrupt_Ethernet.c— the application entry point. Containsmain()andRun_RTD_Interrupt_Basic_Ethernet(), which orchestrates the Gen4 check, user prompts, IDR setup, interrupt configuration, IDR server execution, and cleanup. -
nai_rtd_int_ether.c— Ethernet IDR-specific code shared with theRTD_Interruptfull variant. ContainssetupRTDIDRConfiguration(), IDR command construction functions (InitRTDRxIDRCommands(),MakeRTDRxReadRegsCommand(),MakeRTDRxWriteRegsCommand()), andHandleRTDEtherInterrupt()which decodes incoming IDR packets. -
nai_rtd_int.c— shared interrupt configuration logic. ContainsconfigureRTDToInterruptOnRx(),enableRTDInterrupts(),GetRTDLatchStatusTriggerMode(),handleRTDInterrupt(), andClearInterrupt_RTD(). -
nai_rtd_cfg.c— shared RTD configuration. ContainsinitializeRTDConfigurations().
Shared Data Structures
The sample uses three key structures to track configuration state:
/* From nai_rtd_cfg.h */
typedef struct
{
int32_t cardIndex;
int32_t module;
uint32_t modid;
int32_t channel;
int32_t maxChannel;
int32_t minChannel;
} RtdConfig;
RtdConfig captures the RTD module identification and channel settings. In the Ethernet variant, channel specifies the single channel to monitor for interrupts.
/* From naiapp_interrupt.h */
typedef struct _InterruptConfig
{
bool_t bPromptForInterruptClear;
bool_t bProcessOnboardInterrupts;
bool_t displayData;
int32_t interrupt_Edge_Trigger;
int32_t steering;
int32_t irq;
int32_t cardIndex;
} InterruptConfig;
InterruptConfig captures the interrupt delivery preferences: trigger mode (edge vs. level), steering destination, and whether data should be displayed to the console or logged to a file. The bProcessOnboardInterrupts flag determines whether the IDR commands target the board’s onboard interface or an offboard (PCI) interface.
/* From naiapp_interrupt_ether.h */
typedef struct _IDRConfig
{
int32_t cardIndex;
nai_intf_t boardInterface;
uint16_t protocol;
uint16_t port;
uint8_t *ipAddress;
uint8_t ipLength;
uint8_t *commands;
uint16_t cmdcount;
uint16_t cmdlength;
uint16_t idrId;
} IDRConfig;
IDRConfig captures the Ethernet IDR network settings: the UDP protocol, destination IP address and port where interrupt notifications are sent, and the command buffer that stores the read/write operations the board executes when an interrupt fires. The cmdcount and cmdlength fields track the number and total byte length of the embedded commands.
Gen4 Ethernet Requirement Check
The first thing Run_RTD_Interrupt_Basic_Ethernet() does after determining the channel count is verify that the connected board supports Gen4 Ethernet commands. This is a hard requirement — earlier board generations do not have the IDR engine.
inputRTDConfig.maxChannel = naibrd_RTD_GetChannelCount(inputRTDConfig.modid);
inputRTDConfig.minChannel = 1;
/* check if RTD module supports GEN 4 Ethernet */
bGen4RtdIDRCommands = SupportsGen4Ether(inputRTDConfig.cardIndex);
if (!bGen4RtdIDRCommands)
{
printf("RTD Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported\n");
bQuit = TRUE;
}
SupportsGen4Ether() is a shared utility function that queries the board’s Ethernet generation capability. If the board does not support Gen4, the application exits the interrupt flow immediately with an error message. There is no fallback to an older protocol — you must use a Gen4 or later board for Ethernet IDR.
In your own application, always call SupportsGen4Ether() before attempting any IDR configuration. Calling naibrd_Ether_SetIDRConfig() on a pre-Gen4 board will fail.
|
Important
|
Common Errors
|
User Configuration Prompts
After the Gen4 check passes, the sample prompts the user for several configuration parameters before setting up the IDR and interrupt system. In your own application, you would set these values programmatically rather than prompting interactively.
bQuit = naiapp_query_ChannelNumber(inputRTDConfig.maxChannel, inputRTDConfig.minChannel,
&inputRTDConfig.channel);
bQuit = QueryIDRConfigInformation(&inputIDRConfig);
bQuit = QueryUserForOnboardOffboardInterrupts(&inputInterruptConfig.bProcessOnboardInterrupts);
bQuit = QueryUserForEtherIDRMsgDisplay(&bDisplayEtherUPR);
The prompts collect:
-
Channel number — which RTD channel to monitor for interrupts (1 through the module’s maximum channel count).
-
IDR configuration —
QueryIDRConfigInformation()prompts for the destination IP address, port, and protocol. Defaults are192.168.1.100, port52802, UDP protocol. -
Onboard/offboard processing — determines the board interface and interrupt steering. Onboard uses
NAI_INTF_ONBOARDwithNAIBRD_INT_STEERING_ON_BOARD_1steering. Offboard usesNAI_INTF_PCIwithNAIBRD_INT_STEERING_CPCI_APPsteering. -
IDR message display — whether to show the raw Ethernet IDR message on the console.
IDR Configuration
The IDR (Interrupt Driven Response) is the core mechanism that makes Ethernet-based interrupt delivery work. When an interrupt fires, the board’s IDR engine executes a preconfigured sequence of register read/write commands, packages the results into a UDP packet, and sends it to the specified IP address and port. Your application never needs to issue separate register read commands after the interrupt — all the data arrives in a single network message.
The RTD sample configures a single IDR ID (DEF_ETHERNET_RTD_IDR_ID) bound to the open interrupt vector (NAI_RTD_INTERRUPT_VECTOR). Before configuring the IDR, the sample clears any pre-existing open latched status to prevent stale interrupts.
Setting Up the IDR
After the user selects onboard or offboard processing, the sample configures the board interface and steering, clears status, then calls setupRTDIDRConfiguration() to build and install the IDR command set:
if (inputInterruptConfig.bProcessOnboardInterrupts == TRUE)
{
inputIDRConfig.cardIndex = inputRTDConfig.cardIndex;
inputIDRConfig.boardInterface = NAI_INTF_ONBOARD;
inputInterruptConfig.steering = NAIBRD_INT_STEERING_ON_BOARD_1;
}
else /* OffBoard Interrupt */
{
inputIDRConfig.cardIndex = 0;
inputIDRConfig.boardInterface = NAI_INTF_PCI;
inputInterruptConfig.steering = NAIBRD_INT_STEERING_CPCI_APP;
}
check_status(naibrd_RTD_ClearStatusRaw(inputRTDConfig.cardIndex, inputRTDConfig.module,
NAI_RTD_STATUS_OPEN_LATCHED, 0x0));
setupRTDIDRConfiguration(inputRTDConfig, &inputIDRConfig, bGen4RtdIDRCommands);
check_status(naibrd_Ether_StartIDR(inputIDRConfig.cardIndex, (uint16_t)DEF_ETHERNET_RTD_IDR_ID));
The steering value NAIBRD_INT_STEERING_ON_BOARD_1 tells the module to route the interrupt to the board’s onboard processor, which then triggers the IDR engine. Note that naibrd_RTD_ClearStatusRaw() is called before IDR setup to clear any pre-existing open latched status.
Building IDR Commands
setupRTDIDRConfiguration() in nai_rtd_int_ether.c clears any previous IDR configuration and then constructs the command buffer:
void setupRTDIDRConfiguration(RtdConfig inputRTDConfig, IDRConfig* inputIDRConfig,
bool_t bGen4RtdIDRCommands)
{
int32_t cardIndex = inputIDRConfig->cardIndex;
uint16_t protocol = inputIDRConfig->protocol;
uint16_t port = inputIDRConfig->port;
uint8_t* ipAddress = inputIDRConfig->ipAddress;
uint8_t ipLength = inputIDRConfig->ipLength;
int32_t vector = NAI_RTD_INTERRUPT_VECTOR;
uint8_t *commands = inputIDRConfig->commands;
uint16_t *cmdcount = &inputIDRConfig->cmdcount;
uint16_t *cmdlength = &inputIDRConfig->cmdlength;
check_status(naibrd_Ether_ClearIDRConfig(cardIndex, (uint16_t)DEF_ETHERNET_RTD_IDR_ID));
InitRTDRxIDRCommands(inputRTDConfig, inputIDRConfig, bGen4RtdIDRCommands);
check_status(naibrd_Ether_SetIDRConfig(cardIndex, (uint16_t)DEF_ETHERNET_RTD_IDR_ID,
protocol, ipLength, ipAddress, port, vector, *cmdcount, *cmdlength, commands));
}
The key API calls are:
-
naibrd_Ether_ClearIDRConfig()— clears any existing IDR configuration for the specified IDR ID. Always call this before reconfiguring an IDR to avoid stale command data. -
InitRTDRxIDRCommands()— builds the embedded Ethernet read command that reads the open latched status register when the interrupt fires. -
naibrd_Ether_SetIDRConfig()— installs the complete IDR configuration on the board: destination protocol, IP address, port, interrupt vector to match, command count, command byte length, and the command buffer itself.
IDR Read Command Construction
InitRTDRxIDRCommands() calls MakeRTDRxReadRegsCommand() to construct a Gen4 Ethernet read command targeting the open latched status register:
void MakeRTDRxReadRegsCommand(IDRConfig* inputIDRConfig, uint32_t boardAddress,
int32_t moduleOffset, bool_t bGen4Ether, uint16_t startIndex)
{
uint16_t msgIndexOpen = startIndex;
uint16_t seqno;
uint32_t count, stride;
uint32_t regaddrOpen;
if (bGen4Ether)
{
seqno = 0;
regaddrOpen = boardAddress + moduleOffset + NAI_RTD_GEN5_REG_OPEN_STATUS_LATCHED_ADD;
count = 1;
stride = 4;
msgIndexOpen = (uint16_t)nai_ether_MakeReadMessage(
&inputIDRConfig->commands[startIndex], seqno, NAI_ETHER_GEN4,
(nai_intf_t)inputIDRConfig->boardInterface, regaddrOpen, stride, count, NAI_REG32);
inputIDRConfig->cmdlength = inputIDRConfig->cmdlength + msgIndexOpen;
command_index_interrupt_status = inputIDRConfig->cmdcount;
inputIDRConfig->cmdcount++;
}
}
The function computes the absolute register address by adding the board base address, the module offset, and the open latched status register offset (NAI_RTD_GEN5_REG_OPEN_STATUS_LATCHED_ADD). A single 32-bit register is read (count = 1, stride = 4). The command_index_interrupt_status variable records which command index in the IDR response contains the status register value.
You can also add write commands to the IDR buffer using MakeRTDRxWriteRegsCommand(). This constructs a write command that clears the open latched status register to re-arm interrupts automatically as part of the IDR response:
void MakeRTDRxWriteRegsCommand(RtdConfig inputRTDConfig, IDRConfig* inputIDRConfig,
uint32_t boardAddress, int32_t moduleOffset, bool_t bGen4Ether, uint16_t startIndex)
{
uint16_t msgIndexOpen = startIndex;
uint16_t seqno;
uint32_t count, stride;
uint32_t regaddrOpen;
uint32_t dataOpen = 0x1 << (inputRTDConfig.channel - 1);
if (bGen4Ether)
{
seqno = 0;
regaddrOpen = moduleOffset + boardAddress + NAI_RTD_GEN5_REG_OPEN_STATUS_LATCHED_ADD;
count = 1;
stride = 4;
msgIndexOpen = (uint16_t)nai_ether_BeginWriteMessage(
&inputIDRConfig->commands[startIndex], seqno, NAI_ETHER_GEN4,
(nai_intf_t)inputIDRConfig->boardInterface, regaddrOpen, stride, count, NAI_REG32);
msgIndexOpen = (uint16_t)nai_ether_WriteMessageData(
&inputIDRConfig->commands[startIndex], msgIndexOpen, NAI_REG32,
&dataOpen, NAI_REG32, count);
msgIndexOpen = (uint16_t)nai_ether_FinishMessage(
&inputIDRConfig->commands[startIndex], msgIndexOpen, NAI_ETHER_GEN4);
inputIDRConfig->cmdlength = inputIDRConfig->cmdlength + msgIndexOpen;
inputIDRConfig->cmdcount++;
}
}
The write command uses the three-step message construction pattern: nai_ether_BeginWriteMessage() writes the header, nai_ether_WriteMessageData() appends the data payload (a bitmask for the selected channel), and nai_ether_FinishMessage() finalizes the message. Including this write in the IDR means the board clears its own status register immediately after sending the notification, so interrupts re-arm without requiring a separate API call from the host.
|
Important
|
Common Errors
|
Interrupt Configuration
After the IDR is configured and started, the sample configures the RTD module to generate interrupts on open-circuit detection. This uses the same interrupt configuration functions as the standard (ISR-based) variant.
Module Interrupt Setup
configureRTDToInterruptOnRx() performs the following steps:
void configureRTDToInterruptOnRx(InterruptConfig inputInterruptConfig, RtdConfig inputRTDConfig)
{
int32_t channel;
int32_t maxChan;
int32_t cardIndex = inputRTDConfig.cardIndex;
int32_t module = inputRTDConfig.module;
int32_t vector = NAI_RTD_INTERRUPT_VECTOR;
nai_rtd_interrupt_t interrupt_Level_Trigger = NAI_RTD_LEVEL_INTERRUPT;
int32_t steering = inputInterruptConfig.steering;
nai_rtd_status_type_t typeOpen = NAI_RTD_STATUS_OPEN_LATCHED;
check_status(naibrd_RTD_SetInterruptEnableRaw(cardIndex, module, typeOpen, 0));
check_status(naibrd_RTD_ClearStatusRaw(cardIndex, module, typeOpen, 0xFFFF));
check_status(naibrd_RTD_SetInterruptVector(cardIndex, module, typeOpen, vector));
maxChan = naibrd_RTD_GetChannelCount(inputRTDConfig.modid);
for (channel = 1; channel <= maxChan; channel++)
{
check_status(naibrd_RTD_SetInterruptEdgeLevel(cardIndex, module, channel,
typeOpen, interrupt_Level_Trigger));
}
check_status(naibrd_RTD_SetInterruptSteering(cardIndex, module, typeOpen, steering));
}
The steps are:
-
Disable and clear —
naibrd_RTD_SetInterruptEnableRaw()with0disables all interrupt sources.naibrd_RTD_ClearStatusRaw()with0xFFFFclears all latched open status bits. This prevents stale status from triggering an immediate interrupt. -
Set vector —
naibrd_RTD_SetInterruptVector()assignsNAI_RTD_INTERRUPT_VECTORto the open latched status type. This vector must match the vector specified in thenaibrd_Ether_SetIDRConfig()call so the IDR engine knows which interrupts to service. -
Set trigger mode —
naibrd_RTD_SetInterruptEdgeLevel()configures level triggering (NAI_RTD_LEVEL_INTERRUPT) for each channel. Level-triggered is the default and recommended choice for RTD open detection because the open condition may persist until the sensor is reconnected. -
Set steering —
naibrd_RTD_SetInterruptSteering()routes the interrupt to the appropriate destination. For Ethernet IDR, this isNAIBRD_INT_STEERING_ON_BOARD_1(onboard) so the interrupt reaches the IDR engine.
Enable Interrupts
After configuration, enableRTDInterrupts() enables the open interrupt on all channels:
enableRTDInterrupts(inputRTDConfig, TRUE);
This function builds a bitmask with one bit set for each channel from minChannel to maxChannel, then calls naibrd_RTD_SetInterruptEnableRaw() to enable interrupts for all channels at once. From this point forward, any open-circuit condition detected on any channel will trigger an interrupt, which the IDR engine intercepts and sends as a UDP packet to the configured host.
|
Important
|
Common Errors
|
IDR Server and UDP Message Handling
Once the IDR is started and interrupts are enabled, the sample launches the IDR server to listen for incoming UDP packets from the board:
RTD_ClearInterrupt = ClearInterrupt_RTD;
rtdEtherIntFunc = HandleRTDEtherInterrupt;
bQuit = runIDRServer(inputIDRConfig);
The two function pointer assignments register the callback functions used by the IDR server framework:
-
RTD_ClearInterrupt— points toClearInterrupt_RTD(), which reads and clears the open latched status register. This is called to re-arm interrupts after each notification is processed. -
rtdEtherIntFunc— points toHandleRTDEtherInterrupt(), the main handler that decodes each incoming IDR UDP packet.
runIDRServer() is a shared utility that opens a UDP socket on the configured port and enters a receive loop. Each time a packet arrives, it dispatches to the registered handler function.
Decoding IDR Packets
HandleRTDEtherInterrupt() in nai_rtd_int_ether.c processes each incoming IDR packet. The IDR response contains one or more embedded command results corresponding to the read/write commands configured during IDR setup:
void HandleRTDEtherInterrupt(uint16_t msglen, uint8_t msg[], uint16_t tdr_idr_id)
{
static uint8_t status;
uint16_t seq;
nai_ether_typecode_t typeCode;
nai_ether_gen_t gen = NAI_ETHER_GEN4;
int32_t size;
int32_t commandIndex;
nai_ether_DecodeMessageHeader(msg, msglen, &seq, &typeCode, gen, &size);
commandIndex = (seq & (0x0F << 6)) >> 6;
switch (typeCode)
{
case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_4:
break;
case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_4:
if (command_index_interrupt_status == commandIndex)
status = msg[11];
break;
}
if (commandIndex == inputIDRConfig.cmdcount - 1)
{
printf("\nInterrupt Occurred\n");
printInterruptInformation_RTD(tdr_idr_id, status, TRUE);
printInterruptInformationToFile(tdr_idr_id, status, TRUE, fifoDataFile);
}
}
The handler performs the following steps:
-
Decode the message header —
nai_ether_DecodeMessageHeader()extracts the sequence number, type code, and payload size from the raw UDP packet. ThecommandIndexis extracted from the sequence number to determine which IDR command this response corresponds to. -
Process by type code — write command completions (
NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_4) are acknowledged silently. Read command completions contain the register data. -
Extract status — for the read command matching the open latched status register (identified by
command_index_interrupt_status == commandIndex), the handler extracts the status byte from the message payload. -
Display results — once all commands have been processed (
commandIndex == inputIDRConfig.cmdcount - 1),printInterruptInformation_RTD()prints the IDR ID and open status to the console, andprintInterruptInformationToFile()logs the same data to a file.
The open status bitmask encodes which channels have an open-circuit condition. Each bit position corresponds to a channel number (bit 0 = channel 1, bit 1 = channel 2, etc.).
|
Important
|
Common Errors
|
Cleanup
When the user quits the IDR server loop, the sample performs orderly cleanup of both module and board configurations:
/***** 7. Clear Module Configurations *****/
enableRTDInterrupts(inputRTDConfig, FALSE);
/***** 8. Clear Board Configurations *****/
check_status(naibrd_Ether_StopIDR(inputIDRConfig.cardIndex,
(uint16_t)DEF_ETHERNET_RTD_IDR_ID));
check_status(naibrd_Ether_ClearIDRConfig(inputIDRConfig.cardIndex,
(uint16_t)DEF_ETHERNET_RTD_IDR_ID));
The cleanup sequence is:
-
Disable interrupts —
enableRTDInterrupts()withFALSEdisables the open interrupt enable for all channels. -
Stop the IDR —
naibrd_Ether_StopIDR()halts the IDR engine on the board. After this call, the board will no longer send UDP notifications for this IDR ID. -
Clear the IDR configuration —
naibrd_Ether_ClearIDRConfig()removes the IDR command buffer and network settings from the board.
Always stop the IDR before clearing its configuration. Clearing the configuration while the IDR is still running can result in undefined behavior.
Configuration File
The sample uses the configuration file default_RTD_Interrupt_Ethernet.txt to store board connection settings. This file is not included in the SSK — it is created when you save your connection settings from the board menu during the first run. The file stores the board interface type, IP address or bus address, and other connection parameters so that subsequent runs can skip the interactive menu and connect automatically.
The default IDR network settings (IP address 192.168.1.100, port 52802, UDP protocol) are hardcoded in the source as compile-time defaults. To change them, either modify the source constants or respond to the interactive prompts when the application runs.
static uint8_t DEF_RX_RESPONSE_IPv4_ADDR[] = {192,168,1,100};
#define DEF_RX_RESPONSE_PORT 52802
#define DEF_RX_RESPONSE_PROTOCOL ETHER_GEN4_UDP_PROTOCOL
#define DEF_RX_RESPONSE_IPv4_LENGTH ETHER_GEN4_IPv4_ADDR_LEN
Troubleshooting Reference
|
Note
|
This section summarizes errors covered in the preceding sections and provides additional network-specific diagnostics. Consult your module’s manual for hardware-specific diagnostics. |
| Error / Symptom | Possible Causes | Suggested Resolution |
|---|---|---|
"Generation 4 Ethernet commands currently not supported" |
Board does not support Gen4 Ethernet protocol. |
Use a Gen4 or later board. Check firmware version with your NAI support contact. |
No IDR packets received |
Destination IP address does not match the host. Firewall blocking UDP port. Board has no network connectivity. IDR not started. |
Verify IP address matches the host running the application. Open UDP port 52802 in the firewall. Ping the board to confirm connectivity. Confirm |
IDR packets arrive but show status 0x0 |
Interrupt already cleared. No open-circuit condition present on any channel. |
Verify an RTD sensor is physically disconnected or broken. Open detection requires an actual hardware fault. |
Interrupts fire continuously without stopping |
Open-circuit condition persists on the hardware. Status is cleared but the fault re-triggers immediately. |
Disable interrupts for the faulted channel with |
|
Stale IDR configuration not cleared. Invalid IDR ID. Board does not support IDR. |
Call |
Interrupts fire once but do not re-arm |
Latched status not cleared after processing. Missing |
Ensure the handler clears the open latched status after processing each interrupt. Verify |
No board found / Connection timeout |
Board not powered or connected. Incorrect interface or address in configuration file. |
Verify physical connection. Delete the configuration file and re-run to enter settings interactively. |
Invalid card or module index |
Zero-based card index or one-based module index mismatch. |
Cards are zero-indexed, modules are one-indexed. Verify values match your hardware setup. |
Module not present at selected slot |
RTD module not installed in the selected slot. |
Use the board menu to verify which slots are populated with G4, RT1, TR1, or TC1 modules. |
Full Source
RTD_Interrupt_Ethernet.c
Full Source — RTD_Interrupt_Ethernet.c (SSK 1.x)
/**************************************************************************************************************/
/**
<summary>
The RTD_Interrupt_Ethernet program demonstrates how to check for RTD Open interrupts via Ethernet connection
to the board. 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_rtd_int_ether.h"
#include "nai_rtd_int.h"
#include "nai_rtd_cfg.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_rtd.h"
#include "maps/nai_map_rtd.h"
static uint8_t DEF_RX_RESPONSE_IPv4_ADDR[] = { 192,168,1,100 };
static uint8_t COMMANDS[MAX_ETHER_IDR_CMD_CNT*MAX_ETHER_BLOCK_REG_CNT];
/*********************************************/
/* Program Configurations */
/*********************************************/
InterruptConfig inputInterruptConfig;
IDRConfig inputIDRConfig;
ClearInterrupt RTD_ClearInterrupt;
etherIntFuncDef rtdEtherIntFunc;
bool_t bDisplayEtherUPR;
/* Extern Functions or Variables*/
extern RtdConfig inputRTDConfig;
extern FILE* fifoDataFile;
/*********************************************/
/* Application Name and Revision Declaration */
/*********************************************/
static const int8_t *CONFIG_FILE = (int8_t *)"default_RTD_Interrupt_Ethernet.txt";
/********************************/
/* Internal Function Prototypes */
/********************************/
static bool_t Run_RTD_Interrupt_Basic_Ethernet();
/**************************************************************************************************************/
/***** 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 RTD_Interrupt_Basic_Ethernet(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;
initializeRTDConfigurations(0, 0, 0, 0, 0, 0);
initializeInterruptConfigurations(FALSE, FALSE, FALSE, 0, NAIBRD_INT_STEERING_ON_BOARD_1, -1, 0);
initializeIDRConfigurations(0, 0, DEF_RX_RESPONSE_PROTOCOL, DEF_RX_RESPONSE_PORT, DEF_RX_RESPONSE_IPv4_ADDR, DEF_RX_RESPONSE_IPv4_LENGTH, COMMANDS, 0, 0, DEF_ETHERNET_RTD_IDR_ID);
if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
while (stop != TRUE)
{
/* Query the user for the card index */
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
inputRTDConfig.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);
inputRTDConfig.module = module;
if (stop != TRUE)
{
inputRTDConfig.modid = naibrd_GetModuleID(cardIndex, module);
if ((inputRTDConfig.modid != 0))
{
Run_RTD_Interrupt_Basic_Ethernet();
}
}
}
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.
2a. Ethernet Interrupt Handling - Setup IDR to handle interrupt
API CALLS - naibrd_Ether_SetIDRConfig, naibrd_Ether_StartIDR,naibrd_Ether_ClearIDRConfig
3. Enable Module Interrupts- Configures module to interrupt when RTD open latched status of channel is set to 1.
API CALLS - naibrd_RTD_SetInterruptEdgeLevel, naibrd_RTD_SetInterruptVector, naibrd_RTD_SetInterruptSteering,
naibrd_RTD_SetInterruptEnableRaw, naibrd_RTD_ClearStatusRaw
4. Nothing needs to be done for the RTD module
5. Show Interrupt Handling - The IDR server will listen on the boards ports for IDRs indicating an interrupt.
These results will be decoded and displayed to the user.
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, the write command is included in the IDR.
API CALLS - nai_ether_BeginWriteMessage, nai_ether_WriteMessageData, nai_ether_FinishMessage
7. Clear Module Configurations
API CALLS - naibrd_RTD_SetInterruptEnableRaw
8. Clear Board Configurations
API CALLS - naibrd_Ether_StopIDR, naibrd_Ether_ClearIDRConfig
</summary>
*/
/**************************************************************************************************************/
static bool_t Run_RTD_Interrupt_Basic_Ethernet()
{
bool_t bQuit = FALSE;
bool_t bGen4RtdIDRCommands;
inputRTDConfig.maxChannel = naibrd_RTD_GetChannelCount(inputRTDConfig.modid);
inputRTDConfig.minChannel = 1;
/* check if RTD module supports GEN 4 Ethernet */
bGen4RtdIDRCommands = SupportsGen4Ether(inputRTDConfig.cardIndex);
if (!bGen4RtdIDRCommands)
{
printf("RTD Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported\n");
bQuit = TRUE;
}
if (!bQuit)
{
bQuit = naiapp_query_ChannelNumber(inputRTDConfig.maxChannel, inputRTDConfig.minChannel, &inputRTDConfig.channel);
}
if (!bQuit)
{
bQuit = QueryIDRConfigInformation(&inputIDRConfig);
}
if (!bQuit)
{
bQuit = QueryUserForOnboardOffboardInterrupts(&inputInterruptConfig.bProcessOnboardInterrupts);
}
if (!bQuit)
{
bQuit = QueryUserForEtherIDRMsgDisplay(&bDisplayEtherUPR);
}
if (!bQuit)
{
if (inputInterruptConfig.displayData)
fifoDataFile = stdout;
else
fifoDataFile = fopen("RTD_Interrupt_FIFO_Data.txt", "w+");
/**** 2. Setup IDR to Handle Interrupt (also contains step 6) ****/
if (inputInterruptConfig.bProcessOnboardInterrupts == TRUE)
{
inputIDRConfig.cardIndex = inputRTDConfig.cardIndex;
inputIDRConfig.boardInterface = NAI_INTF_ONBOARD;
inputInterruptConfig.steering = NAIBRD_INT_STEERING_ON_BOARD_1;
}
else /* OffBoard Interrupt */
{
inputIDRConfig.cardIndex = 0;
inputIDRConfig.boardInterface = NAI_INTF_PCI;
inputInterruptConfig.steering = NAIBRD_INT_STEERING_CPCI_APP;
}
check_status(naibrd_RTD_ClearStatusRaw(inputRTDConfig.cardIndex, inputRTDConfig.module, NAI_RTD_STATUS_OPEN_LATCHED, 0x0));
setupRTDIDRConfiguration(inputRTDConfig, &inputIDRConfig, bGen4RtdIDRCommands);
check_status(naibrd_Ether_StartIDR(inputIDRConfig.cardIndex, (uint16_t)DEF_ETHERNET_RTD_IDR_ID));
/**** 3. configure module To Interrupt ****/
configureRTDToInterruptOnRx(inputInterruptConfig, inputRTDConfig);
enableRTDInterrupts(inputRTDConfig, TRUE);
/**** 4. Configure RTD to cause Interrupts ****/
/* Nothing needs to be done for the RTD module */
/**** 5. Show Interrupt Handling ****/
RTD_ClearInterrupt = ClearInterrupt_RTD;
rtdEtherIntFunc = HandleRTDEtherInterrupt;
bQuit = runIDRServer(inputIDRConfig);
/**** 6. Prompt User To Clear Interrupt Statuses ****/
/* Taken care of in step 2 */
/***** 7. Clear Module Configurations *****/
enableRTDInterrupts(inputRTDConfig, FALSE);
/***** 8. Clear Board Configurations *****/
check_status(naibrd_Ether_StopIDR(inputIDRConfig.cardIndex, (uint16_t)DEF_ETHERNET_RTD_IDR_ID));
check_status(naibrd_Ether_ClearIDRConfig(inputIDRConfig.cardIndex, (uint16_t)DEF_ETHERNET_RTD_IDR_ID));
}
return bQuit;
}
nai_rtd_int_ether.c
Full Source — nai_rtd_int_ether.c (SSK 1.x)
/* 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"
/* Common RTD Sample Program include files */
#include "nai_rtd_int_ether.h"
#include "nai_rtd_cfg.h"
/* naibrd include files */
#include "functions/naibrd_rtd.h"
#include "maps/nai_map_rtd.h"
#include "advanced/nai_ether_adv.h"
int32_t command_index_interrupt_status;
/* Extern Functions or Variables*/
extern IDRConfig inputIDRConfig;
extern FILE* fifoDataFile;
/**************************************************************************************************************/
/**
<summary>
Constructs the ethernet commands that are part of the IDR. Configures the IDR on the board.
</summary>
*/
/**************************************************************************************************************/
void setupRTDIDRConfiguration( RtdConfig inputRTDConfig, IDRConfig* inputIDRConfig, bool_t bGen4RtdIDRCommands )
{
int32_t cardIndex = inputIDRConfig->cardIndex;
uint16_t protocol = inputIDRConfig->protocol;
uint16_t port = inputIDRConfig->port;
uint8_t* ipAddress = inputIDRConfig->ipAddress;
uint8_t ipLength = inputIDRConfig->ipLength;
int32_t vector = NAI_RTD_INTERRUPT_VECTOR;
uint8_t *commands = inputIDRConfig->commands; /* Stores the ethernet commands that are going to be executed as part of the IDR */
uint16_t *cmdcount = &inputIDRConfig->cmdcount;
uint16_t *cmdlength = &inputIDRConfig->cmdlength;
check_status( naibrd_Ether_ClearIDRConfig( cardIndex, (uint16_t)DEF_ETHERNET_RTD_IDR_ID ) ); /* clear IDR config */
InitRTDRxIDRCommands( inputRTDConfig, inputIDRConfig, bGen4RtdIDRCommands );
check_status( naibrd_Ether_SetIDRConfig( cardIndex, (uint16_t)DEF_ETHERNET_RTD_IDR_ID, protocol, ipLength, ipAddress, port, vector, *cmdcount, *cmdlength, commands ) );
}
/**************************************************************************************************************/
/**
<summary>
This function configures the IDR (Interrupt Driven Response) commands when an RTD open interrupt occurs.
There are two Ethernet commands that will be processed by the board when an RTD open interrupt occurs.
</summary>
*/
/**************************************************************************************************************/
void InitRTDRxIDRCommands( RtdConfig inputRTDConfig, IDRConfig* inputIDRConfig, bool_t bGen4RtdIDRCommands )
{
nai_status_t status = NAI_SUCCESS;
uint16_t msgIndex = 0;
uint32_t boardAddress;
uint32_t moduleOffset;
boardAddress = 0;
status = check_status( naibrd_GetModuleOffset( inputRTDConfig.cardIndex, inputRTDConfig.module, &moduleOffset ) );
if ( status == NAI_SUCCESS )
status = check_status( naibrd_GetAddress( inputRTDConfig.cardIndex, &boardAddress ) );
if ( status == NAI_SUCCESS )
{
if (bGen4RtdIDRCommands)
{
MakeRTDRxReadRegsCommand( inputIDRConfig, boardAddress, moduleOffset, bGen4RtdIDRCommands, msgIndex );
msgIndex = inputIDRConfig->cmdlength;
}
}
}
/**************************************************************************************************************/
/**
<summary>
This function constructs an ethernet read reg command and stores it in the IDR Configuration. The read will be performed
on the module's open latched status register.
</summary>
*/
/**************************************************************************************************************/
void MakeRTDRxReadRegsCommand( IDRConfig* inputIDRConfig, uint32_t boardAddress, int32_t moduleOffset, bool_t bGen4Ether, uint16_t startIndex )
{
uint16_t msgIndexOpen = startIndex;
uint16_t seqno;
uint32_t count, stride;
uint32_t regaddrOpen;
if (bGen4Ether)
{
seqno = 0;
regaddrOpen = boardAddress + moduleOffset + NAI_RTD_GEN5_REG_OPEN_STATUS_LATCHED_ADD;
count = 1;
stride = 4;
msgIndexOpen = (uint16_t)nai_ether_MakeReadMessage( &inputIDRConfig->commands[startIndex], seqno, NAI_ETHER_GEN4, (nai_intf_t)inputIDRConfig->boardInterface, regaddrOpen, stride, count, NAI_REG32 );
inputIDRConfig->cmdlength = inputIDRConfig->cmdlength + msgIndexOpen;
command_index_interrupt_status = inputIDRConfig->cmdcount;
inputIDRConfig->cmdcount++;
}
}
/**************************************************************************************************************/
/**
<summary>
This function constructs an ethernet write reg command and stores it in the IDR Configuration. The write will be performed
on the module's open latched status register. The purpose of this write is to clear the open latched status register and
re-arm the interrupts after one has occurred.
</summary>
*/
/**************************************************************************************************************/
void MakeRTDRxWriteRegsCommand( RtdConfig inputRTDConfig, IDRConfig* inputIDRConfig, uint32_t boardAddress, int32_t moduleOffset, bool_t bGen4Ether, uint16_t startIndex )
{
uint16_t msgIndexOpen = startIndex;
uint16_t seqno;
uint32_t count, stride;
uint32_t regaddrOpen;
uint32_t dataOpen = 0x1 << ( inputRTDConfig.channel-1 );
if (bGen4Ether)
{
seqno = 0;
regaddrOpen = moduleOffset + boardAddress + NAI_RTD_GEN5_REG_OPEN_STATUS_LATCHED_ADD;
count = 1;
stride = 4;
msgIndexOpen = (uint16_t)nai_ether_BeginWriteMessage( &inputIDRConfig->commands[startIndex], seqno, NAI_ETHER_GEN4, (nai_intf_t)inputIDRConfig->boardInterface, regaddrOpen, stride, count, NAI_REG32 );
if ( msgIndexOpen >= 0 )
{
msgIndexOpen = (uint16_t)nai_ether_WriteMessageData( &inputIDRConfig->commands[startIndex], msgIndexOpen, NAI_REG32, &dataOpen, NAI_REG32, count );
if ( msgIndexOpen >= 0 )
{
msgIndexOpen = (uint16_t)nai_ether_FinishMessage( &inputIDRConfig->commands[startIndex], msgIndexOpen, NAI_ETHER_GEN4 );
}
}
inputIDRConfig->cmdlength = inputIDRConfig->cmdlength + msgIndexOpen;
inputIDRConfig->cmdcount++;
}
}
/**************************************************************************************************************/
/**
<summary>
Handles interpreting the idr response msgs received for rtd data. displays the IDR ID and status. Expects
to be parsing a message for a SINGLE channel interrupt only.
</summary>
*/
/**************************************************************************************************************/
void HandleRTDEtherInterrupt( uint16_t msglen, uint8_t msg[], uint16_t tdr_idr_id )
{
static uint8_t status;
uint16_t seq;
nai_ether_typecode_t typeCode;
nai_ether_gen_t gen = NAI_ETHER_GEN4;
int32_t size;
int32_t commandIndex;
nai_ether_DecodeMessageHeader(msg, msglen, &seq, &typeCode, gen, &size);
commandIndex = ( seq & ( 0x0F << 6 ) ) >> 6;
switch (typeCode)
{
/* Write REG */
case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_4:
break;
/* READ REG */
case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_4:
if ( command_index_interrupt_status == commandIndex )
status = msg[11];
break;
}
if ( commandIndex == inputIDRConfig.cmdcount - 1 ) /* Print once all commands have been executed */
{
printf( "\nInterrupt Occurred\n" );
printInterruptInformation_RTD( tdr_idr_id, status, TRUE );
printInterruptInformationToFile( tdr_idr_id, status, TRUE, fifoDataFile );
}
}