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

CAN Interrupt Ethernet

CAN Interrupt Ethernet Sample Application (SSK 1.x)

Overview

The CAN Interrupt Ethernet sample application demonstrates how to receive CAN bus interrupt notifications over an Ethernet network using the NAI Software Support Kit (SSK 1.x) Interrupt Driven Response (IDR) mechanism. When a CAN message arrives on a configured receive 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, FIFO status, FIFO data, and any additional embedded read/write command results you configured. Your application runs an IDR server that listens for these packets, decodes the CAN frame data, and displays the results.

This is the Ethernet IDR-only variant of the CAN interrupt handling samples. It operates on a single channel and delivers all interrupt information — including the receive FIFO contents — 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 CAN Interrupt sample application guide. For a simplified ISR-only starting point with no Ethernet involvement, see the CAN Interrupt Basic sample application guide.

This sample supports the following CAN module types: CB1, CB2, CB3, CB4, CB5, and CB6.

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, the FIFO status, and the FIFO data payload, 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.

CAN Status Types

The CAN module exposes two latched status types that can trigger Ethernet IDR notifications:

  • NAI_CAN_STATUS_RECV_MSG_LATCHED (RECV_MSG) — set when a CAN message is received on a channel. This is the status type used by this sample.

  • NAI_CAN_STATUS_SWT_LATCHED (SWT) — set when a Scheduled Watchdog Timer event occurs on a channel.

This sample monitors the RECV_MSG latched status. To monitor SWT events instead, you would substitute NAI_CAN_STATUS_SWT_LATCHED in the interrupt configuration calls and target the corresponding latched status register address in the IDR commands.

Prerequisites

Before running this sample, make sure you have:

  • An NAI board with a CAN module installed (CB1, CB2, CB3, CB4, CB5, or CB6).

  • 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 CAN_Interrupt_Ethernet executable from your build output directory. On startup the application looks for a configuration file (default_CAN_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, interrupt processing mode, and 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 CAN. For details on board connection configuration, see the First Time Setup Guide.

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

  1. Initialize CAN, interrupt, and IDR configuration structures with defaults using initializeCANConfigurations(), initializeInterruptConfigurations(), and initializeIDRConfigurations(). The IDR initialization sets the default response IP address (192.168.1.100), UDP port (52802), and protocol (ETHER_GEN4_UDP_PROTOCOL).

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

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

  4. Retrieve the module ID with naibrd_GetModuleID() to verify a CAN module is installed at the selected slot.

#if defined (__VXWORKS__)
int32_t CAN_Interrupt_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;

   initializeCANConfigurations(0,0,0,0,0,0, MAX_ETHER_CAN_FRAME_PAYLOAD,1,NAI_CAN_500K_BAUD);
   initializeInterruptConfigurations(FALSE,FALSE,FALSE,0,NAIBRD_INT_STEERING_ON_BOARD_1,0,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_CAN_IDR_ID);

   if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
   {
      while (stop != TRUE)
      {
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         inputCANConfig.cardIndex = cardIndex;
         if (stop != TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
            stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
            inputCANConfig.module = module;
            if (stop != TRUE)
            {
               inputCANConfig.modid = naibrd_GetModuleID(cardIndex, module);
               if ((inputCANConfig.modid != 0))
               {
                  Run_CAN_Interrupt_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. initializeCANConfigurations() sets the default baud rate to NAI_CAN_500K_BAUD and the maximum payload size to MAX_ETHER_CAN_FRAME_PAYLOAD (derived from the Ethernet message size limit). 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:

  • 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 CAN module. Use the board menu to verify which slots are populated.

Program Structure

The CAN Interrupt Ethernet sample is split across multiple source files:

  • CAN_Interrupt_Ethernet.c — the application entry point. Contains main() and Run_CAN_Interrupt_Ethernet(), which orchestrates the Gen4 check, user prompts, IDR setup, interrupt configuration, IDR server execution, and cleanup.

  • nai_can_int_ether.c — Ethernet IDR-specific code shared with the CAN_Interrupt full variant. Contains setupIDRConfiguration(), IDR command construction functions (InitCANRxIDRCommands(), MakeCANRxReadFIFOCommand(), MakeCANRxReadRegsFIFOStatus(), MakeCANRxReadRegsCommand(), MakeCANRxWriteRegsCommand()), and HandleCANEtherInterrupt() which decodes incoming IDR packets.

  • nai_can_int.c — shared interrupt configuration logic. Contains configureCANToInterruptOnRx(), enableCANInterrupts(), GetCANLatchStatusTriggerMode(), ClearInterrupt_CAN(), and handleCANInterrupt().

  • nai_can_cfg.h / nai_can_cfg.c — shared CAN configuration. Contains Cfg_Rx_CAN(), initializeCANConfigurations(), timing parameter definitions, and FIFO data structures.

Shared Data Structures

The sample uses three key structures to track configuration state:

/* From nai_can_cfg.h */
typedef struct
{
   int32_t cardIndex;
   int32_t module;
   uint32_t modid;
   int32_t channel;
   int32_t maxChannel;
   int32_t minChannel;
   int32_t maxPayload;
   int32_t minPayload;
   nai_can_baud_rate_type_t baudRate;
} CanConfig;

CanConfig captures the CAN module identification and channel settings. In the Ethernet variant, channel specifies the single channel to monitor for interrupts. maxPayload controls the maximum number of 32-bit words the IDR FIFO read command will retrieve (set to MAX_ETHER_CAN_FRAME_PAYLOAD by default).

/* 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.

CAN FIFO Data Structures

The CAN Ethernet IDR sample also uses FIFO data structures to hold received CAN frame data:

/* From nai_can_cfg.h */
typedef struct CanDataFrame {
   int32_t maxLength;
   uint32_t* data;
   uint32_t length;
} CanDataFrame;

typedef struct FIFO {
   CanDataFrame* buffer;
   int32_t numOfFramesOnFifo;
   uint32_t fifoStatus;
   int32_t maxFramesOnFifo;
} FIFO;

Each FIFO structure holds an array of CanDataFrame entries. Each frame contains the raw 32-bit data words from a single received CAN message. The fifoStatus field captures the channel’s FIFO status register value (overflow, threshold, etc.) at the time of the interrupt.

Gen4 Ethernet Requirement Check

The first thing Run_CAN_Interrupt_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.

int32_t maxChannel = naibrd_CAN_GetChannelCount(inputCANConfig.modid);
int32_t minChannel = 1;

/* check if CAN module supports GEN 4 Ethernet */
bGen4CanIDRCommands = SupportsGen4Ether(inputCANConfig.cardIndex);

if (!bGen4CanIDRCommands)
{
   printf("CAN 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

  • "Generation 4 Ethernet commands currently not supported" — the connected board does not support IDR. Verify you are using a Gen4 or later board. Check the board’s firmware version if you believe the hardware should support Gen4.

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(maxChannel, minChannel, &inputCANConfig.channel);
inputCANConfig.maxChannel = inputCANConfig.channel;
inputCANConfig.minChannel = inputCANConfig.channel;

bQuit = QueryIDRConfigInformation(&inputIDRConfig);

bQuit = QueryUserForOnboardOffboardInterrupts(&inputInterruptConfig.bProcessOnboardInterrupts);

bQuit = QueryUserForEtherIDRMsgDisplay(&bDisplayEtherUPR);

bQuit = QueryUserForDisplayingData(&inputInterruptConfig.displayData);

The prompts collect:

  • Channel number — which receive channel to monitor for interrupts (1 through the module’s maximum channel count). Both minChannel and maxChannel are set to this value since the Ethernet variant monitors a single channel.

  • IDR configuration — QueryIDRConfigInformation() prompts for the destination IP address, port, and protocol. Defaults are 192.168.1.100, port 52802, UDP protocol.

  • Onboard/offboard processing — determines the board interface and interrupt steering. Onboard uses NAI_INTF_ONBOARD with NAIBRD_INT_STEERING_ON_BOARD_1 steering. Offboard uses NAI_INTF_PCI with NAIBRD_INT_STEERING_CPCI_APP steering.

  • IDR message display — whether to show the raw Ethernet IDR message on the console.

  • Data display — whether to print received FIFO data to the console (stdout) or log it to a file (CAN_Interrupt_FIFO_Data.txt).

Note that unlike the CAN Interrupt full variant, this sample does not prompt for trigger mode — the CAN interrupt configuration in configureCANToInterruptOnRx() uses the default edge-triggered mode set during initializeInterruptConfigurations().

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 CAN Ethernet IDR is more complex than simpler module IDRs (such as ARINC) because it embeds three commands: a FIFO read for the CAN frame data, a register read for the FIFO status, and a register read for the interrupt status. This means each IDR response packet contains the complete picture of what happened on the channel.

Setting Up the IDR

After the user selects onboard or offboard processing, the sample configures the board interface and steering, then calls setupIDRConfiguration() to build and install the IDR command set:

if (inputInterruptConfig.bProcessOnboardInterrupts == TRUE)
{
   inputIDRConfig.cardIndex = inputCANConfig.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;
}
setupIDRConfiguration(inputCANConfig, &inputIDRConfig, bGen4CanIDRCommands);
check_status(naibrd_Ether_StartIDR(inputIDRConfig.cardIndex, (uint16_t)DEF_ETHERNET_CAN_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. This is the standard choice for Ethernet IDR — the onboard processor intercepts the interrupt and executes the IDR commands. The offboard path (NAIBRD_INT_STEERING_CPCI_APP) is used when the board is accessed over a backplane and the IDR engine is reached through that path.

Building IDR Commands

setupIDRConfiguration() in nai_can_int_ether.c clears any previous IDR configuration and then constructs the command buffer:

void setupIDRConfiguration(CanConfig inputCANConfig, IDRConfig* inputIDRConfig, bool_t bGen4CanIDRCommands)
{
   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_CAN_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_CAN_IDR_ID));
   InitCANRxIDRCommands(inputCANConfig, inputIDRConfig, bGen4CanIDRCommands);
   check_status(naibrd_Ether_SetIDRConfig(cardIndex, (uint16_t)DEF_ETHERNET_CAN_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.

  • InitCANRxIDRCommands() — builds the embedded Ethernet commands: a FIFO read for CAN frame data, a register read for FIFO status, and a register read for interrupt status. These are the commands whose results appear in the IDR response packet.

  • 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. After this call, the board knows where to send notifications and what register operations to embed in each packet.

IDR Command Construction

InitCANRxIDRCommands() builds three Gen4 Ethernet commands and appends them to the IDR command buffer. The commands are constructed in sequence, with each function updating the cmdlength and cmdcount fields:

void InitCANRxIDRCommands(CanConfig inputCANConfig, IDRConfig* inputIDRConfig, bool_t bGen4CanIDRCommands)
{
   nai_status_t status = NAI_SUCCESS;
   uint16_t msgIndex = 0;
   uint32_t boardAddress;
   uint32_t moduleOffset;
   boardAddress = 0;

   status = check_status(naibrd_GetModuleOffset(inputCANConfig.cardIndex, inputCANConfig.module, &moduleOffset));
   if (status == NAI_SUCCESS)
      status = check_status(naibrd_GetAddress(inputCANConfig.cardIndex, &boardAddress));

   if (status == NAI_SUCCESS)
   {
      if (bGen4CanIDRCommands)
      {
         MakeCANRxReadFIFOCommand(inputCANConfig, inputIDRConfig, boardAddress, moduleOffset,
            bGen4CanIDRCommands, msgIndex);
         msgIndex = inputIDRConfig->cmdlength;

         MakeCANRxReadRegsFIFOStatus(inputCANConfig, inputIDRConfig, boardAddress, moduleOffset,
            bGen4CanIDRCommands, msgIndex);
         msgIndex = inputIDRConfig->cmdlength;

         MakeCANRxReadRegsCommand(inputIDRConfig, boardAddress, moduleOffset,
            bGen4CanIDRCommands, msgIndex);
         msgIndex = inputIDRConfig->cmdlength;
      }
   }
}

The three commands are:

  1. FIFO Read (MakeCANRxReadFIFOCommand()) — reads the CAN receive FIFO buffer for the selected channel. This retrieves the actual CAN frame data (message ID, DLC, and payload bytes). The command uses nai_ether_MakeFifoReadMessage() which constructs a special FIFO read that also references the buffer count register so the board knows how many entries to return.

  2. FIFO Status Read (MakeCANRxReadRegsFIFOStatus()) — reads the channel’s FIFO status register. This indicates conditions such as FIFO overflow, FIFO almost full, or FIFO empty. The global variable command_index_fifo_status records the command index so the response handler knows which response belongs to the FIFO status.

  3. Interrupt Status Read (MakeCANRxReadRegsCommand()) — reads the module’s latched receive status register (NAI_CAN_GEN5_REG_RECV_STATUS_LATCHED_ADD). This tells you which channels have pending receive interrupts. The global variable command_index_interrupt_status records the command index.

Each command computes the absolute register address by adding the board base address (naibrd_GetAddress()), the module offset (naibrd_GetModuleOffset()), and the per-channel or per-module register offset. The addresses must be computed at runtime because they vary by board configuration.

FIFO Read Command Detail

The FIFO read command is unique to the CAN Ethernet IDR — unlike simpler modules, the CAN IDR embeds the FIFO data directly in the response packet:

void MakeCANRxReadFIFOCommand(CanConfig inputCANConfig, IDRConfig* inputIDRConfig,
   uint32_t boardAddress, int32_t moduleOffset, bool_t bGen4Ether, uint16_t startIndex)
{
   uint16_t seqno;
   uint32_t bufferAddr;
   uint32_t bufferCountAddr;
   uint32_t CAN_GEN5_RxbufferCountAddr[] = NAI_CAN_GEN5_REG_RX_BUFFER_COUNT_ADD;
   uint32_t CAN_GEN5_RxBufferAddr[] = NAI_CAN_GEN5_REG_RX_BUFFER_ADD;
   int32_t count;
   uint16_t msgIndex;
   int32_t channel = inputCANConfig.channel;
   msgIndex = startIndex;

   if (bGen4Ether)
   {
      seqno = 0;
      bufferAddr = CAN_GEN5_RxBufferAddr[channel-1] + moduleOffset + boardAddress;
      bufferCountAddr = CAN_GEN5_RxbufferCountAddr[channel-1] + moduleOffset + boardAddress;
      count = inputCANConfig.maxPayload;

      msgIndex = (uint16_t)nai_ether_MakeFifoReadMessage(
         &inputIDRConfig->commands[startIndex], seqno, NAI_ETHER_GEN4,
         (nai_intf_t)inputIDRConfig->boardInterface,
         bufferAddr, bufferCountAddr, count, NAI_REG32, 10);

      inputIDRConfig->cmdlength = inputIDRConfig->cmdlength + msgIndex;
      inputIDRConfig->cmdcount++;
   }
}

nai_ether_MakeFifoReadMessage() constructs a FIFO read message that reads up to count 32-bit words from the FIFO buffer register. The bufferCountAddr parameter tells the board which register holds the current FIFO count, so the board can read exactly the number of entries available (up to the maximum). The last parameter (10) specifies the timeout in board clock ticks for waiting for FIFO data.

Write Command for Re-arming (Optional)

The MakeCANRxWriteRegsCommand() function is available but not called by default in InitCANRxIDRCommands(). You can add it to the IDR command sequence to have the board automatically clear the latched status register after each interrupt, which re-arms the interrupt without requiring your application to issue a separate API call:

void MakeCANRxWriteRegsCommand(CanConfig inputCANConfig, IDRConfig* inputIDRConfig,
   uint32_t boardAddress, int32_t moduleOffset, bool_t bGen4Ether, uint16_t startIndex)
{
   uint32_t data = 0x1 << (inputCANConfig.channel-1);   /* Clear Rx interrupt bits */

   if (bGen4Ether)
   {
      uint16_t seqno = 0;
      uint32_t regaddr = moduleOffset + boardAddress + NAI_CAN_GEN5_REG_RECV_STATUS_LATCHED_ADD;
      uint32_t count = 1;
      uint32_t stride = 4;
      uint16_t msgIndex;

      msgIndex = (uint16_t)nai_ether_BeginWriteMessage(
         &inputIDRConfig->commands[startIndex], seqno, NAI_ETHER_GEN4,
         (nai_intf_t)inputIDRConfig->boardInterface, regaddr, stride, count, NAI_REG32);
      msgIndex = (uint16_t)nai_ether_WriteMessageData(
         &inputIDRConfig->commands[startIndex], msgIndex, NAI_REG32, &data, NAI_REG32, count);
      msgIndex = (uint16_t)nai_ether_FinishMessage(
         &inputIDRConfig->commands[startIndex], msgIndex, NAI_ETHER_GEN4);

      inputIDRConfig->cmdlength = inputIDRConfig->cmdlength + msgIndex;
      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 with the channel’s bit set to clear only that channel’s status), and nai_ether_FinishMessage() finalizes the message. To include this command, add a call to MakeCANRxWriteRegsCommand() at the end of InitCANRxIDRCommands().

Important

Common Errors

  • naibrd_Ether_SetIDRConfig() fails — verify the IDR ID is valid and that no other IDR is already configured with the same ID. Always call naibrd_Ether_ClearIDRConfig() first.

  • Incorrect register address — the board address and module offset must be queried at runtime with naibrd_GetAddress() and naibrd_GetModuleOffset(). Hardcoded addresses will fail on different board configurations.

  • Wrong board interface — NAI_INTF_ONBOARD is for onboard processing; NAI_INTF_PCI is for offboard. Using the wrong interface causes the IDR read/write commands to target the wrong bus.

Interrupt Configuration

After the IDR is configured and started, the sample configures the CAN module to generate interrupts on receive. This uses the same interrupt configuration functions as the standard (ISR-based) variant.

Module Interrupt Setup

configureCANToInterruptOnRx() configures the module-level interrupt settings:

void configureCANToInterruptOnRx(InterruptConfig inputInterruptConfig, CanConfig inputCANConfig)
{
   int32_t cardIndex = inputCANConfig.cardIndex;
   int32_t module = inputCANConfig.module;
   int32_t vector = NAI_CAN_INTERRUPT_VECTOR;
   int32_t interrupt_Edge_Trigger = inputInterruptConfig.interrupt_Edge_Trigger;
   int32_t steering = inputInterruptConfig.steering;

   nai_can_status_type_t type = NAI_CAN_STATUS_RECV_MSG_LATCHED;

   /* clear interruptConfigs of previous channels */
   check_status(naibrd_CAN_SetIntEnableRaw(cardIndex, module, 0));
   check_status(naibrd_CAN_ClearStatusRaw(cardIndex, module, type, 0xFFFF));

   check_status(naibrd_CAN_SetIntVector(cardIndex, module, 1, type, vector));
   check_status(naibrd_CAN_SetInterruptEdgeLevel(cardIndex, module, type, interrupt_Edge_Trigger));
   check_status(naibrd_CAN_SetInterruptSteering(cardIndex, module, type, steering));
}

The steps are:

  1. Disable and clear — naibrd_CAN_SetIntEnableRaw() with 0 disables all interrupt sources. naibrd_CAN_ClearStatusRaw() with 0xFFFF clears all latched status bits for NAI_CAN_STATUS_RECV_MSG_LATCHED. This prevents stale status from triggering an immediate interrupt.

  2. Set vector — naibrd_CAN_SetIntVector() assigns NAI_CAN_INTERRUPT_VECTOR to the module for the RECV_MSG status type. This vector must match the vector specified in the naibrd_Ether_SetIDRConfig() call so the IDR engine knows which interrupts to service.

  3. Set trigger mode — naibrd_CAN_SetInterruptEdgeLevel() configures edge (0) or level (1) triggering for the RECV_MSG latched status type. The default is edge-triggered.

  4. Set steering — naibrd_CAN_SetInterruptSteering() routes the interrupt to the appropriate destination. For Ethernet IDR, this is NAIBRD_INT_STEERING_ON_BOARD_1 (onboard) so the interrupt reaches the IDR engine.

Enable Interrupts

After configuration, enableCANInterrupts() enables the receive interrupt on the selected channel:

enableCANInterrupts(inputCANConfig, TRUE);

This function builds a bitmask from the minChannel to maxChannel range (in the Ethernet variant, both are set to the single selected channel) and calls naibrd_CAN_SetIntEnableRaw() with the resulting bitmask. From this point forward, any CAN message received on the channel will trigger an interrupt, which the IDR engine intercepts and sends as a UDP packet to the configured host.

CAN Receive Channel Configuration

Cfg_Rx_CAN() configures the physical CAN receive channel so that data actually arrives:

Cfg_Rx_CAN(inputCANConfig);

This shared function sets the CAN baud rate using naibrd_CAN_SetBitTiming() with the timing parameters corresponding to the configured baud rate (default: 500 kbit/s for CAN J1939, or 1 Mbit/s for CAN AB), then enables the receiver on the channel with naibrd_CAN_SetRxEnable(). The baud rate timing parameters (prescaler, SJW, TSEG1, TSEG2) are defined in nai_can_cfg.h.

Important

Common Errors

  • No interrupts received — verify that naibrd_CAN_SetInterruptSteering() used the correct steering constant for Ethernet IDR (NAIBRD_INT_STEERING_ON_BOARD_1 for onboard). Using the wrong steering routes the interrupt to a different destination.

  • Interrupt vector mismatch — the vector passed to naibrd_CAN_SetIntVector() must match the vector in naibrd_Ether_SetIDRConfig(). A mismatch means the IDR engine will not recognize the interrupt.

  • Stale interrupts fire immediately — always clear status with naibrd_CAN_ClearStatusRaw() before enabling interrupts. Latched status from a previous run will trigger immediately if not cleared.

  • Baud rate mismatch — both the transmitting and receiving CAN nodes must use the same baud rate. A mismatch will result in no messages being received.

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:

CAN_ClearInterrupt = ClearInterrupt_CAN;
canEtherIntFunc = HandleCANEtherInterrupt;
bQuit = runIDRServer(inputIDRConfig);

The two function pointer assignments register the callback functions used by the IDR server framework:

  • CAN_ClearInterrupt — points to ClearInterrupt_CAN(), which reads and clears the latched status register. This is called to re-arm interrupts after each notification is processed.

  • canEtherIntFunc — points to HandleCANEtherInterrupt(), 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

HandleCANEtherInterrupt() in nai_can_int_ether.c processes each incoming IDR packet. Because the CAN IDR contains three embedded commands (FIFO read, FIFO status read, interrupt status read), the handler is called once per command result and accumulates the data across calls:

void HandleCANEtherInterrupt(uint16_t msglen, uint8_t msg[], uint16_t tdr_idr_id)
{
   static uint8_t status;
   static FIFO* fifo[NAI_GEN5_CAN_MAX_CHANNEL_COUNT];

   int32_t channel;
   uint16_t seq;
   nai_ether_typecode_t typeCode;
   nai_ether_gen_t gen = NAI_ETHER_GEN4;
   int32_t size;
   int32_t commandIndex;
   int32_t idrID;

   nai_ether_DecodeMessageHeader(msg, msglen, &seq, &typeCode, gen, &size);
   channel = inputCANConfig.channel;

   if (fifo[channel-1] == NULL)
   {
      fifo[channel-1] = allocateSpaceForFIFO(MAX_FIFO_COUNT, inputCANConfig.maxPayload);
   }

   commandIndex = (seq & (0x0F << 6)) >> 6;
   idrID = tdr_idr_id;

   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_fifo_status == commandIndex)
         {
            fifo[channel-1]->fifoStatus = (fifo[channel-1]->fifoStatus | msg[11]);
            fifo[channel-1]->fifoStatus = (fifo[channel-1]->fifoStatus | (msg[10] << 8));
         }
         else if (command_index_interrupt_status == commandIndex)
            status = msg[11];
         break;
      /* Read FIFO */
      case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_FIFO_4:
         getInterruptDataEthernet(msg, msglen, fifo[channel-1]);
         break;
   }

   if (commandIndex == inputIDRConfig.cmdcount - 1)
   {
      printf("\nInterrupt Occurred\n");
      printInterruptInformation_CAN(inputCANConfig, idrID, status, fifo, TRUE, fifoDataFile);
      if (fifo[channel-1] != NULL)
      {
         deallocSpaceForFIFO(fifo[channel-1]);
         fifo[channel-1] = NULL;
      }
   }
}

The handler performs the following steps:

  1. Decode the message header — nai_ether_DecodeMessageHeader() extracts the sequence number, type code, and payload size from the raw UDP packet. The commandIndex is extracted from the sequence number bits to determine which IDR command this response corresponds to.

  2. Allocate FIFO storage — on the first call for a channel, allocateSpaceForFIFO() creates space for up to MAX_FIFO_COUNT CAN frames with the configured payload size.

  3. Process by type code — write command completions are acknowledged silently. Register read completions are matched against the known command indices:

    • If commandIndex matches command_index_fifo_status, the response contains the FIFO status register value.

    • If commandIndex matches command_index_interrupt_status, the response contains the latched interrupt status.

    • FIFO read completions (NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_FIFO_4) are passed to getInterruptDataEthernet() for CAN frame extraction.

  4. Display results — when the last command response arrives (commandIndex == inputIDRConfig.cmdcount - 1), the handler calls printInterruptInformation_CAN() to display the IDR ID, interrupt status, FIFO status, and all received CAN frame data.

  5. Free FIFO storage — the FIFO buffer is deallocated after display and will be reallocated on the next interrupt.

CAN Frame Extraction from UDP Payload

getInterruptDataEthernet() parses the raw FIFO read response to extract individual CAN frames. CAN frames in the FIFO are delimited by start (NAI_CAN_MSG_START) and end (NAI_CAN_MSG_END) markers, with a header containing the DLC and data words between them:

void getInterruptDataEthernet(uint8_t msg[], uint16_t msglen, FIFO* fifo)
{
   /* ... variable declarations ... */

   for (msgIndex = startOfPayLoad + NAI_REG32 - 1; msgIndex <= msglen; msgIndex++)
   {
      startOfCanFrame = msgIndex;
      if ((msg[msgIndex-1] * 256) == NAI_CAN_MSG_START && fifo->numOfFramesOnFifo < fifo->maxFramesOnFifo)
      {
         startOfDataInCanFrame = startOfCanFrame + canHeaderSize;
         /* ... extract DLC and data words ... */
         endOfCanFrame = msg[endOfHeader + dataSectionSize - 1] * 256;

         if (endOfCanFrame == NAI_CAN_MSG_END)
         {
            for (dataIndex = startOfDataInCanFrame;
                 dataIndex < startOfDataInCanFrame + dataSectionSize;
                 dataIndex = dataIndex + NAI_REG32)
            {
               if (amountInDataBuffer < bufferCapacity)
               {
                  dataBuffer[amountInDataBuffer] = msg[dataIndex];
                  amountInDataBuffer++;
               }
            }
            fifo->buffer[frameBufferIndex].length = amountInDataBuffer;
            frameBufferIndex++;
            frameCount++;
            fifo->numOfFramesOnFifo++;
         }
      }
   }
}

The function scans the raw message bytes looking for CAN frame start markers, validates each frame by checking for the end marker, and copies the data words into the FIFO buffer structure. The global frameCount variable tracks the total number of CAN frames received across all interrupts.

IDR Response Display

printInterruptInformation_CAN() formats the interrupt data for display or logging:

void printInterruptInformation_CAN(CanConfig inputCANConfig, int32_t interruptID,
   uint8_t status, FIFO* fifoData[NAI_GEN5_CAN_MAX_CHANNEL_COUNT], bool_t isEther, FILE* stream)
{
   fprintf(stream, "Interrupt Information\n");
   fprintf(stream, "-----------------------------------\n");
   fprintf(stream, "\nIDR ID = %#x \n", interruptID);
   fprintf(stream, "Status = %#x \n", status);

   for (channel = inputCANConfig.minChannel; channel <= inputCANConfig.maxChannel; ++channel)
   {
      statusBitSet = status & (1 << (channel - 1));
      if (statusBitSet && fifoData[channel - 1] != NULL)
      {
         printFIFOStatusRx(fifoData[channel-1]->fifoStatus, stream);
         printFIFOChannelData(channel, fifoData[channel-1]->buffer,
            fifoData[channel-1]->numOfFramesOnFifo, stream);
      }
   }
   fprintf(stream, "\nFrames Rx = %d", frameCount);
   fprintf(stream, "\n-----------------------------------\n");
}

The output stream is either stdout (if the user chose to display data on the console) or a file handle for CAN_Interrupt_FIFO_Data.txt (if the user chose to log data). Each interrupt event prints the IDR ID, the raw status register value, the FIFO status, the received CAN frame data for the channel, and a running total of received frames.

Important

Common Errors

  • No IDR packets received — verify the destination IP address matches the host running the application. Check that no firewall is blocking UDP port 52802. Confirm the board has network connectivity.

  • IDR packets arrive but show status 0 — the interrupt may have already been cleared, or the channel may not be receiving CAN data. Verify that a transmitter is actively sending on the bus.

  • FIFO data shows no frames — the CAN frame extraction depends on correct start/end markers (NAI_CAN_MSG_START / NAI_CAN_MSG_END). If the FIFO read returned no data, verify the transmitter is sending valid CAN frames and the baud rate matches.

  • Frame count keeps increasing across interrupts — the global frameCount variable accumulates across interrupts for the lifetime of the application. This is expected behavior.

Cleanup

When the user quits the IDR server loop, the sample performs orderly cleanup of both module and board configurations:

/***** 7. Clear Module Configurations *****/
check_status(naibrd_CAN_SetRxEnable(inputCANConfig.cardIndex, inputCANConfig.module,
   inputCANConfig.channel, FALSE));
enableCANInterrupts(inputCANConfig, FALSE);

/***** 8. Clear Board Configurations *****/
check_status(naibrd_Ether_StopIDR(inputIDRConfig.cardIndex, (uint16_t)DEF_ETHERNET_CAN_IDR_ID));

The cleanup sequence is:

  1. Disable the receiver — naibrd_CAN_SetRxEnable() with FALSE stops the channel from receiving CAN messages.

  2. Disable interrupts — enableCANInterrupts() with FALSE disables the interrupt enable bitmask for the channel.

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

Always stop the IDR before exiting the application. Leaving the IDR running after the application exits can result in the board continuing to send UDP packets to a host that is no longer listening.

Configuration File

The sample uses the configuration file default_CAN_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 naibrd_Ether_StartIDR() was called after naibrd_Ether_SetIDRConfig().

IDR packets arrive but show status 0x0

Interrupt already cleared. Channel not receiving CAN data. Wrong channel selected.

Verify a transmitter is sending CAN data on the monitored channel. Check that the IDR read command targets the correct register address.

No CAN frames in FIFO data

Baud rate mismatch between transmitter and receiver. Receiver not enabled. No transmitter on the bus.

Verify both nodes use the same baud rate. Confirm naibrd_CAN_SetRxEnable() was called with TRUE. Connect a CAN transmitter to the bus.

naibrd_Ether_SetIDRConfig() returns error

Stale IDR configuration not cleared. Invalid IDR ID. Board does not support IDR.

Call naibrd_Ether_ClearIDRConfig() before naibrd_Ether_SetIDRConfig(). Verify the IDR ID is within the valid range for your board.

Interrupts fire once but do not re-arm

Latched status not cleared after processing. Missing ClearInterrupt_CAN call or missing write command in IDR.

Ensure the handler clears status after processing each interrupt. Alternatively, add MakeCANRxWriteRegsCommand() to the IDR to clear the status register automatically.

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

CAN module not installed in the selected slot.

Use the board menu to verify which slots are populated with CB1-CB6 modules.

Full Source

CAN_Interrupt_Ethernet.c

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

The CAN_Interrupt_Ethernet program demonstrates how to perform an interrupt when a single channel receives
a can message.

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

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

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

/*Common Module Specific Sample Program include files*/
#include "nai_can_int_ether.h"
#include "nai_can_int.h"
#include "nai_can_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_can.h"

IDRConfig inputIDRConfig;
etherIntFuncDef canEtherIntFunc;
ClearInterrupt CAN_ClearInterrupt;
bool_t bDisplayEtherUPR;

/* Extern Functions or Variables*/
extern InterruptConfig inputInterruptConfig;
extern CanConfig inputCANConfig;
extern FILE* fifoDataFile;

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

/********************/
/* Application Name */
/********************/

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

/********************************/
/* Internal Function Prototypes */
/********************************/
static bool_t Run_CAN_Interrupt_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 CAN_Interrupt_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;

   initializeCANConfigurations(0,0,0,0,0,0, MAX_ETHER_CAN_FRAME_PAYLOAD,1,NAI_CAN_500K_BAUD);
   initializeInterruptConfigurations(FALSE,FALSE,FALSE,0,NAIBRD_INT_STEERING_ON_BOARD_1,0,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_CAN_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);
         inputCANConfig.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);
            inputCANConfig.module = module;
            if (stop != TRUE)
            {
               inputCANConfig.modid = naibrd_GetModuleID(cardIndex, module);
               if ((inputCANConfig.modid != 0))
               {
                  Run_CAN_Interrupt_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 channel receives CAN message.

   API CALLS - naibrd_CAN_SetInterruptEdgeLevel, naibrd_CAN_SetIntVector, naibrd_CAN_SetInterruptSteering, naibrd_CAN_SetIntEnable

4. Configure Module to Cause Rx Interrupt - sets the BAUD Rate to 1mbit/s (CAN AB) or 500k bits/s (CAN J1939) as definted in nai_can_cfg.h
   It also enables the particular channel on the module to receive can messages

   API CALLS - naibrd_CAN_SetBitTiming , naibrd_CAN_SetRxEnable

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_CAN_SetRxEnable, naibrd_CAN_ClearStatus

8. Clear Board Configurations

   API CALLS - naibrd_Ether_StopIDR, naibrd_Ether_ClearIDRConfig

</summary>
*/
/**************************************************************************************************************/
static bool_t Run_CAN_Interrupt_Ethernet()
{
   bool_t bQuit = FALSE;
	bool_t bGen4CanIDRCommands;
	int32_t maxChannel = naibrd_CAN_GetChannelCount(inputCANConfig.modid);
	int32_t minChannel = 1;

	/* check if CAN module supports GEN 4 Ethernet */

    bGen4CanIDRCommands = SupportsGen4Ether(inputCANConfig.cardIndex);

    if(!bGen4CanIDRCommands)
	{
         printf("CAN Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported\n");
         bQuit = TRUE;
	}
	if(!bQuit){
		bQuit = naiapp_query_ChannelNumber(maxChannel,minChannel,&inputCANConfig.channel);
		inputCANConfig.maxChannel = inputCANConfig.channel;
		inputCANConfig.minChannel = inputCANConfig.channel;
	}
	if(!bQuit){
		bQuit = QueryIDRConfigInformation(&inputIDRConfig);
	}
	if(!bQuit){
		bQuit = QueryUserForOnboardOffboardInterrupts(&inputInterruptConfig.bProcessOnboardInterrupts);
	}
	if(!bQuit)
	{
		bQuit = QueryUserForEtherIDRMsgDisplay(&bDisplayEtherUPR);
	}
	if(!bQuit)
	{
		bQuit = QueryUserForDisplayingData(&inputInterruptConfig.displayData);
	}

	if (!bQuit)
	{
		if(inputInterruptConfig.displayData)
			fifoDataFile = stdout;
		else
			fifoDataFile = fopen("CAN_Interrupt_FIFO_Data.txt","w+");

		/****2. Setup IDR to Handle Interrupt (also contains step 6) ****/
		if(inputInterruptConfig.bProcessOnboardInterrupts == TRUE)
		{
			inputIDRConfig.cardIndex = inputCANConfig.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;
		}
		setupIDRConfiguration(inputCANConfig,&inputIDRConfig,bGen4CanIDRCommands);
		check_status(naibrd_Ether_StartIDR(inputIDRConfig.cardIndex, (uint16_t)DEF_ETHERNET_CAN_IDR_ID));

		/****3. configure module To Interrupt****/
		configureCANToInterruptOnRx(inputInterruptConfig,inputCANConfig);
		enableCANInterrupts(inputCANConfig,TRUE);

		/****4. Configure CAN to cause Interrupts ****/

		Cfg_Rx_CAN(inputCANConfig);

		/****5. Show Interrupt Handling****/
		CAN_ClearInterrupt = ClearInterrupt_CAN;
		canEtherIntFunc = HandleCANEtherInterrupt;
		bQuit = runIDRServer(inputIDRConfig);

		/***** 7. Clear Module Configurations *****/

		check_status(naibrd_CAN_SetRxEnable(inputCANConfig.cardIndex, inputCANConfig.module, inputCANConfig.channel, FALSE));
		enableCANInterrupts(inputCANConfig,FALSE);

		/*****8. Clear Board Configurations *****/
		check_status(naibrd_Ether_StopIDR(inputIDRConfig.cardIndex, (uint16_t)DEF_ETHERNET_CAN_IDR_ID));

		if(!inputInterruptConfig.displayData)
		{
			fclose(fifoDataFile);
		}
	}
	return bQuit;
}

nai_can_int_ether.c

Full Source — nai_can_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 CAN Sample Program include files */
#include "nai_can_int_ether.h"
#include "nai_can_cfg.h"
/* naibrd include files */
#include "functions/naibrd_can.h"
#include "maps/nai_map_can.h"
#include "advanced/nai_ether_adv.h"

int32_t command_index_fifo_status;
int32_t command_index_interrupt_status;

/* Extern Functions or Variables*/
extern CanConfig inputCANConfig;
extern IDRConfig inputIDRConfig;
extern FILE* fifoDataFile;
extern int32_t frameCount;

/**************************************************************************************************************/
/**
<summary>
Constructs the ethernet commands that are part of the IDR. Configures the IDR on the board.
</summary>
*/
/**************************************************************************************************************/
void setupIDRConfiguration(CanConfig inputCANConfig,IDRConfig* inputIDRConfig,bool_t bGen4CanIDRCommands){

	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_CAN_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_CAN_IDR_ID));
	InitCANRxIDRCommands(inputCANConfig,inputIDRConfig,bGen4CanIDRCommands);

	check_status(naibrd_Ether_SetIDRConfig(cardIndex,(uint16_t)DEF_ETHERNET_CAN_IDR_ID,protocol,ipLength,ipAddress,port,vector,*cmdcount,*cmdlength,commands));

}

/**************************************************************************************************************/
/**
<summary>
This function configures the IDR (Interrupt Driven Response) commands when a CAN Rx interrupt occurs.
There are four Ethernet commands that will be processed by the board when a CAN Rx interrupt occurs.

</summary>
*/
/**************************************************************************************************************/
void InitCANRxIDRCommands(CanConfig inputCANConfig,IDRConfig* inputIDRConfig,bool_t bGen4CanIDRCommands)
{
   nai_status_t status = NAI_SUCCESS;
   uint16_t msgIndex = 0;
   uint32_t boardAddress;
   uint32_t moduleOffset;
   boardAddress = 0;

   status = check_status(naibrd_GetModuleOffset(inputCANConfig.cardIndex, inputCANConfig.module, &moduleOffset));
   if(status == NAI_SUCCESS)
		status = check_status(naibrd_GetAddress(inputCANConfig.cardIndex,&boardAddress));

   if (status == NAI_SUCCESS)
   {
      if (bGen4CanIDRCommands)
      {

			MakeCANRxReadFIFOCommand(inputCANConfig,inputIDRConfig,boardAddress,moduleOffset,bGen4CanIDRCommands,msgIndex);
			msgIndex = inputIDRConfig->cmdlength;
			MakeCANRxReadRegsFIFOStatus(inputCANConfig,inputIDRConfig,boardAddress,moduleOffset,bGen4CanIDRCommands,msgIndex);
			msgIndex = inputIDRConfig->cmdlength;
			MakeCANRxReadRegsCommand(inputIDRConfig,boardAddress,moduleOffset,bGen4CanIDRCommands,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 modules latched status interrupt register.
</summary>
*/
/**************************************************************************************************************/
void MakeCANRxReadRegsCommand(IDRConfig* inputIDRConfig,uint32_t boardAddress,int32_t moduleOffset,bool_t bGen4Ether, uint16_t startIndex)
{
   uint16_t msgIndex = startIndex;
   uint16_t seqno;
   uint32_t count, stride;
   uint32_t regaddr;
   if (bGen4Ether)
   {

      seqno = 0;
      regaddr = boardAddress + moduleOffset + NAI_CAN_GEN5_REG_RECV_STATUS_LATCHED_ADD;
      count = 1;
      stride = 4;

	  msgIndex = (uint16_t)nai_ether_MakeReadMessage(&inputIDRConfig->commands[startIndex],seqno,NAI_ETHER_GEN4,(nai_intf_t)inputIDRConfig->boardInterface,regaddr,stride,count,NAI_REG32);

	  inputIDRConfig->cmdlength = inputIDRConfig->cmdlength + msgIndex;
	  command_index_interrupt_status = inputIDRConfig->cmdcount;
	  inputIDRConfig->cmdcount++;
   }
}

/**************************************************************************************************************/
/**
<summary>
This function constructs an ethernet read reg command and stores it in the IDR Configuration. The read will be performed
on the channels FIFO status register. The channel is set in inputCANConfig.
</summary>
*/
/**************************************************************************************************************/
void MakeCANRxReadRegsFIFOStatus(CanConfig inputCANConfig, IDRConfig* inputIDRConfig,uint32_t boardAddress,int32_t moduleOffset,bool_t bGen4Ether, uint16_t startIndex)
{

   uint16_t msgIndex = startIndex;
   uint16_t seqno;
   uint32_t count, stride;
   uint32_t regaddr;
   uint32_t CAN_GEN5_RxFifoStatus[] = NAI_CAN_GEN5_REG_FIFO_STATUS_ADD;

   if (bGen4Ether)
   {
      seqno = 0;
      regaddr = boardAddress + moduleOffset + CAN_GEN5_RxFifoStatus[inputCANConfig.channel-1];
      count = 1;
      stride = 4;

	  msgIndex = (uint16_t)nai_ether_MakeReadMessage(&inputIDRConfig->commands[startIndex],seqno,NAI_ETHER_GEN4,(nai_intf_t)inputIDRConfig->boardInterface,regaddr,stride,count,NAI_REG32);

	  inputIDRConfig->cmdlength = inputIDRConfig->cmdlength + msgIndex;
	  command_index_fifo_status = inputIDRConfig->cmdcount;
	  inputIDRConfig->cmdcount++;

   }
}

/**************************************************************************************************************/
/**
<summary>
This function constructs an ethernet read FIFO command and stores it in the IDR Configuration. The read will be performed
on the channels FIFO buffer register.
</summary>
*/
/**************************************************************************************************************/
void MakeCANRxReadFIFOCommand(CanConfig inputCANConfig, IDRConfig* inputIDRConfig,uint32_t boardAddress,int32_t moduleOffset,bool_t bGen4Ether, uint16_t startIndex)
{
   uint16_t seqno;
   uint32_t bufferAddr;
   uint32_t bufferCountAddr;
   uint32_t CAN_GEN5_RxbufferCountAddr[] = NAI_CAN_GEN5_REG_RX_BUFFER_COUNT_ADD;
   uint32_t CAN_GEN5_RxBufferAddr[] = NAI_CAN_GEN5_REG_RX_BUFFER_ADD;
   int32_t count;
   uint16_t msgIndex;

   int32_t channel;
   channel = inputCANConfig.channel;
   msgIndex = startIndex;

   if (bGen4Ether)
   {
		seqno = 0;

		bufferAddr = CAN_GEN5_RxBufferAddr[channel-1] + moduleOffset + boardAddress;
		bufferCountAddr =  CAN_GEN5_RxbufferCountAddr[channel-1] + moduleOffset + boardAddress;

		count = inputCANConfig.maxPayload;
		msgIndex = (uint16_t)nai_ether_MakeFifoReadMessage(&inputIDRConfig->commands[startIndex],seqno,NAI_ETHER_GEN4,(nai_intf_t)inputIDRConfig->boardInterface,bufferAddr,bufferCountAddr,count,NAI_REG32,10);

		inputIDRConfig->cmdlength = inputIDRConfig->cmdlength + msgIndex;
		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 modules latched status interrupt register. The purpose of this write is to clear the interrupt status register and re-arm the interrupts
after one has occurred.
</summary>
*/
/**************************************************************************************************************/
void MakeCANRxWriteRegsCommand(CanConfig inputCANConfig, IDRConfig* inputIDRConfig,uint32_t boardAddress,int32_t moduleOffset,bool_t bGen4Ether, uint16_t startIndex)
{

   uint16_t msgIndex = startIndex;
   uint16_t seqno;
   uint32_t count, stride;
   uint32_t regaddr;
   uint32_t data = 0x1 << (inputCANConfig.channel-1);   /* Clear Rx interrupt bits */

	if (bGen4Ether)
	{

		seqno = 0;
		regaddr = moduleOffset + boardAddress + NAI_CAN_GEN5_REG_RECV_STATUS_LATCHED_ADD;
		count = 1;
		stride = 4;

		msgIndex = (uint16_t)nai_ether_BeginWriteMessage(&inputIDRConfig->commands[startIndex],seqno,NAI_ETHER_GEN4,(nai_intf_t)inputIDRConfig->boardInterface,regaddr,stride,count,NAI_REG32);
		if (msgIndex >= 0)
		{
			msgIndex = (uint16_t)nai_ether_WriteMessageData(&inputIDRConfig->commands[startIndex],msgIndex,NAI_REG32,&data,NAI_REG32,count);

			if (msgIndex >= 0)
			{
			msgIndex = (uint16_t)nai_ether_FinishMessage(&inputIDRConfig->commands[startIndex],msgIndex,NAI_ETHER_GEN4);
			}
		}
		inputIDRConfig->cmdlength = inputIDRConfig->cmdlength + msgIndex;
		inputIDRConfig->cmdcount++;
	}
}

/**************************************************************************************************************/
/**
<summary>
Handles interpreting the idr response msgs received for can data.
displays the IDR ID and status. Also displays the fifo data from the interrupting channel.
Expects to be parsing a message for a SINGLE channel interrupt only.
</summary>
*/
/**************************************************************************************************************/

void HandleCANEtherInterrupt(uint16_t msglen, uint8_t msg[],uint16_t tdr_idr_id)
{
	static uint8_t status;
	static FIFO* fifo[NAI_GEN5_CAN_MAX_CHANNEL_COUNT];

	int32_t channel;
	uint16_t seq;
	nai_ether_typecode_t typeCode;
	nai_ether_gen_t gen = NAI_ETHER_GEN4;
	int32_t size;
	int32_t commandIndex;
	int32_t idrID;
	nai_ether_DecodeMessageHeader(msg, msglen, &seq, &typeCode, gen, &size);
	channel = inputCANConfig.channel;

	if(fifo[channel-1] == NULL)
	{
		fifo[channel-1] = allocateSpaceForFIFO(MAX_FIFO_COUNT,inputCANConfig.maxPayload);
	}

	commandIndex = (seq & (0x0F << 6)) >> 6;
	idrID = tdr_idr_id;
	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_fifo_status == commandIndex)
			{
				fifo[channel-1]->fifoStatus = (fifo[channel-1]->fifoStatus | msg[11]);
				fifo[channel-1]->fifoStatus = (fifo[channel-1]->fifoStatus | (msg[10] << 8));
			}
			else if(command_index_interrupt_status == commandIndex)
				status = msg[11];
			break;
		/* Read FIFO */
		case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_FIFO_4:
			getInterruptDataEthernet(msg, msglen,fifo[channel-1]);
			break;
	}

	if(commandIndex == inputIDRConfig.cmdcount-1)
	{
		printf("\nInterrupt Occurred\n");

		printInterruptInformation_CAN(inputCANConfig,idrID,status,fifo,TRUE,fifoDataFile);
		if(fifo[channel-1] != NULL)
		{
			deallocSpaceForFIFO(fifo[channel-1]);
			fifo[channel-1] = NULL;
		}
	}
}

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

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

void getInterruptDataEthernet(uint8_t msg[], uint16_t msglen,FIFO* fifo)
{
	int32_t msgIndex;
	int32_t startOfCanFrame;
	int32_t endOfCanFrame;
	int32_t endOfHeader;
	int32_t dataSectionSize;
	int32_t canHeaderSize;
	int32_t startOfDataInCanFrame;
	int32_t frameBufferIndex;
	int32_t amountInDataBuffer;
	int32_t dataIndex;
	int32_t sizeOfPostamble;
	int32_t startOfPayLoad;
	int32_t amountOfDataInFrame;

	int32_t bufferCapacity;
	uint32_t* dataBuffer;

	frameBufferIndex = 0;
	sizeOfPostamble = 2;
	startOfPayLoad = ETHERNET_OVERHEAD_FIFO_COMMAND - sizeOfPostamble;
	canHeaderSize = NAI_CAN_FRAME_SIZE*NAI_REG32;

	for(msgIndex = startOfPayLoad + NAI_REG32 - 1; msgIndex <= msglen; msgIndex++)
	{
		startOfCanFrame = msgIndex;
		if((msg[msgIndex-1] * 256) == NAI_CAN_MSG_START && fifo->numOfFramesOnFifo < fifo->maxFramesOnFifo)
		{
			startOfDataInCanFrame = startOfCanFrame + canHeaderSize;
			amountInDataBuffer = 0;
			dataBuffer = fifo->buffer[frameBufferIndex].data;
			bufferCapacity = fifo->buffer[frameBufferIndex].maxLength;
			endOfHeader = startOfDataInCanFrame - NAI_REG32;

			if(endOfHeader < msglen)
			{
				amountOfDataInFrame = ((msg[endOfHeader-1]* 256) | msg[endOfHeader]) & 0x7FF;
				dataSectionSize = amountOfDataInFrame*NAI_REG32;
				endOfCanFrame = msg[endOfHeader + dataSectionSize - 1] * 256;

				if(endOfCanFrame == NAI_CAN_MSG_END)
				{
					for(dataIndex = startOfDataInCanFrame; dataIndex < startOfDataInCanFrame + dataSectionSize;dataIndex = dataIndex + NAI_REG32)
					{
						if((amountInDataBuffer < bufferCapacity))
						{
							dataBuffer[amountInDataBuffer] = msg[dataIndex];
							amountInDataBuffer++;
						}
					}
					fifo->buffer[frameBufferIndex].length = amountInDataBuffer;
					frameBufferIndex++;
					frameCount++;
					fifo->numOfFramesOnFifo++;
				}
			}
		}
	}
}

Help Bot

X