CAN Interrupt Ethernet
Edit this on GitLab
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:
-
Initialize CAN, interrupt, and IDR configuration structures with defaults using
initializeCANConfigurations(),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_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. -
Query the user for card index and module number.
-
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:
|
Program Structure
The CAN Interrupt Ethernet sample is split across multiple source files:
-
CAN_Interrupt_Ethernet.c— the application entry point. Containsmain()andRun_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 theCAN_Interruptfull variant. ContainssetupIDRConfiguration(), IDR command construction functions (InitCANRxIDRCommands(),MakeCANRxReadFIFOCommand(),MakeCANRxReadRegsFIFOStatus(),MakeCANRxReadRegsCommand(),MakeCANRxWriteRegsCommand()), andHandleCANEtherInterrupt()which decodes incoming IDR packets. -
nai_can_int.c— shared interrupt configuration logic. ContainsconfigureCANToInterruptOnRx(),enableCANInterrupts(),GetCANLatchStatusTriggerMode(),ClearInterrupt_CAN(), andhandleCANInterrupt(). -
nai_can_cfg.h/nai_can_cfg.c— shared CAN configuration. ContainsCfg_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
|
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
minChannelandmaxChannelare 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 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.
-
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:
-
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 usesnai_ether_MakeFifoReadMessage()which constructs a special FIFO read that also references the buffer count register so the board knows how many entries to return. -
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 variablecommand_index_fifo_statusrecords the command index so the response handler knows which response belongs to the FIFO status. -
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 variablecommand_index_interrupt_statusrecords 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
|
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:
-
Disable and clear —
naibrd_CAN_SetIntEnableRaw()with0disables all interrupt sources.naibrd_CAN_ClearStatusRaw()with0xFFFFclears all latched status bits forNAI_CAN_STATUS_RECV_MSG_LATCHED. This prevents stale status from triggering an immediate interrupt. -
Set vector —
naibrd_CAN_SetIntVector()assignsNAI_CAN_INTERRUPT_VECTORto the module for the RECV_MSG 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_CAN_SetInterruptEdgeLevel()configures edge (0) or level (1) triggering for the RECV_MSG latched status type. The default is edge-triggered. -
Set steering —
naibrd_CAN_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, 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
|
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 toClearInterrupt_CAN(), which reads and clears the latched status register. This is called to re-arm interrupts after each notification is processed. -
canEtherIntFunc— points toHandleCANEtherInterrupt(), 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:
-
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 bits to determine which IDR command this response corresponds to. -
Allocate FIFO storage — on the first call for a channel,
allocateSpaceForFIFO()creates space for up toMAX_FIFO_COUNTCAN frames with the configured payload size. -
Process by type code — write command completions are acknowledged silently. Register read completions are matched against the known command indices:
-
If
commandIndexmatchescommand_index_fifo_status, the response contains the FIFO status register value. -
If
commandIndexmatchescommand_index_interrupt_status, the response contains the latched interrupt status. -
FIFO read completions (
NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_FIFO_4) are passed togetInterruptDataEthernet()for CAN frame extraction.
-
-
Display results — when the last command response arrives (
commandIndex == inputIDRConfig.cmdcount - 1), the handler callsprintInterruptInformation_CAN()to display the IDR ID, interrupt status, FIFO status, and all received CAN frame data. -
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
|
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:
-
Disable the receiver —
naibrd_CAN_SetRxEnable()withFALSEstops the channel from receiving CAN messages. -
Disable interrupts —
enableCANInterrupts()withFALSEdisables the interrupt enable bitmask for the channel. -
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 |
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 |
|
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 status after processing each interrupt. Alternatively, add |
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++;
}
}
}
}
}