CAN Interrupt
Edit this on GitLab
CAN Interrupt Sample Application (SSK 1.x)
Overview
The CAN Interrupt sample application demonstrates how to configure and handle hardware interrupts on CAN bus modules using the NAI Software Support Kit (SSK 1.x). The sample covers two interrupt delivery mechanisms: standard (onboard/offboard ISR) and Ethernet IDR (Interrupt Driven Response, network-based). Both variants configure and enable the same underlying interrupt type — Receive Message (RECV_MSG) — and share the same module-level configuration code. The difference is only in how the interrupt notification reaches your application.
This sample supports the following CAN module types: CB1, CB2, CB3, CB4, CB5, and CB6.
This is the "full" version of the CAN interrupt sample. It supports multiple channels simultaneously, configurable edge/level triggering, onboard and offboard ISR delivery, Ethernet IDR delivery, optional FIFO data display, and user-controlled interrupt clearing. For a simpler starting point that covers single-channel onboard ISR delivery only, see the CAN Interrupt Basic sample application guide.
For background on interrupt concepts — including edge vs. level triggering, interrupt vector numbering, steering architecture, and latency measurement — see the Interrupts API Guide. This guide focuses on how those concepts apply specifically to CAN modules and walks through the practical implementation using the SSK 1.x API.
Prerequisites
Before running this sample, make sure you have:
-
An NAI board with a CAN module installed (CB1, CB2, CB3, CB4, CB5, or CB6).
-
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.
-
For the Ethernet IDR variant: a Gen4 or later board with an Ethernet connection.
How to Run
This sample ships as two executables that share common configuration code:
-
CAN_Interrupt— standard interrupt delivery (onboard or offboard ISR). Supports a range of channels simultaneously. -
CAN_Interrupt_Ethernet— Ethernet-based interrupt delivery using IDR commands. Operates on a single channel.
Launch either executable from your build output directory. On startup the application looks for a configuration file (default_CAN_Interrupt.txt for the standard variant, default_CAN_Interrupt_Ethernet.txt for the Ethernet variant). On the first run, this file will not exist — the application will present an interactive board menu where you configure a board connection, card index, and module slot. You can save this configuration so that subsequent runs skip the menu and connect automatically. Once connected, the application prompts you for interrupt settings and begins waiting for interrupt events.
Board Connection and Module Selection
|
Note
|
This startup sequence is common to all NAI sample applications. The board connection and module selection code shown here is not specific to CAN. |
The main() function follows a standard SSK 1.x startup flow:
-
Initialize CAN and interrupt configuration structures with defaults using
initializeCANConfigurations()andinitializeInterruptConfigurations(). The Ethernet variant additionally callsinitializeIDRConfigurations()to set up the IDR response address, port, and 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.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(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, NAI_J1939_MAX_DATA_LEN, 1, NAI_CAN_500K_BAUD);
initializeInterruptConfigurations(FALSE, FALSE, FALSE, 0, 0, 0, 0);
if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
while (stop != TRUE)
{
/* Query the user for the card index */
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
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();
}
}
}
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;
}
|
Important
|
Common connection errors you may encounter at this stage:
|
Program Structure
The CAN Interrupt sample is split across five source files. Two variant-specific files provide the main() entry point and interrupt delivery logic, while three shared common files handle module configuration, interrupt handlers, and Ethernet IDR command construction:
-
CAN_Interrupt.c— standard interrupt variant. Uses onboard or offboard ISR delivery vianaibrd_InstallISR(). Supports a range of channels simultaneously. -
CAN_Interrupt_Ethernet.c— Ethernet IDR variant. Delivers interrupt notifications over the network using IDR commands. Operates on a single channel. -
nai_can_int.c— shared interrupt configuration and handling logic used by both variants. ContainsconfigureCANToInterruptOnRx(),enableCANInterrupts(),handleCANInterrupt(), the basic ISR callback, and interrupt clearing functions. -
nai_can_int_ether.c— Ethernet IDR-specific code. ContainssetupIDRConfiguration(), IDR command construction functions (InitCANRxIDRCommands(),MakeCANRxReadRegsCommand(),MakeCANRxReadFIFOCommand(),MakeCANRxWriteRegsCommand()), andHandleCANEtherInterrupt(). -
nai_can_cfg.c— shared CAN configuration utilities. ContainsinitializeCANConfigurations(),Cfg_Rx_CAN(), baud rate and protocol helpers, and FIFO data management functions.
Each variant has its own entry point:
-
Standard:
main()(orCAN_Interrupt()on VxWorks). -
Ethernet IDR:
main()(orCAN_Interrupt_Ethernet()on VxWorks).
After the board connection is established, both variants follow a similar user selection flow:
-
Select a card index and module slot.
-
For the standard variant: select a channel range (min/max). For the Ethernet variant: select a single channel.
-
Choose edge-triggered or level-triggered interrupt mode (standard variant only).
-
Choose whether to be prompted before clearing each interrupt, or clear automatically (standard variant).
-
Choose onboard or offboard interrupt delivery.
-
Optionally enable FIFO data display to stdout (or redirect to a file).
-
For the standard variant: choose the interrupt steering destination. For the Ethernet variant: configure the IDR IP address, port, and protocol.
The difference between the two variants is only in how the interrupt notification reaches your application — the underlying module configuration is identical.
Shared Data Structures
Both variants use two key structures to track user selections and board 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. For the standard variant, minChannel and maxChannel define the range of channels to monitor. For the Ethernet variant, channel specifies the single channel to monitor. The maxPayload field controls the maximum number of data words per CAN frame, and baudRate sets the CAN bus timing (default: NAI_CAN_500K_BAUD).
/* 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, IRQ assignment, and whether the user wants to be prompted before clearing each interrupt. The displayData flag controls whether FIFO data is printed to stdout or written to a log file.
Both structures are declared as global externs so that the variant-specific entry point and the common configuration code can share them.
Interrupt Status Types
CAN modules expose two primary interrupt status types relevant to interrupt-driven applications. Each monitors a different hardware condition at the module level.
Receive Message (RECV_MSG)
NAI_CAN_STATUS_RECV_MSG_LATCHED
The module’s receive FIFO for a channel contains at least one complete CAN message. When a valid CAN frame is received and placed into the FIFO, the module sets the Receive Message status bit for that channel and, if enabled, raises an interrupt. This is the primary interrupt type used by this sample.
Enable RECV_MSG interrupts when you need immediate notification that new CAN data has arrived, rather than polling the FIFO buffer on a timer. This is the standard approach for event-driven CAN receive applications — vehicle bus monitoring, industrial control message processing, or real-time data acquisition.
When the interrupt fires, your handler should read the latched status register to determine which channels received data, then drain the FIFO for each active channel. The sample demonstrates this pattern in both the standard and Ethernet handlers.
SWT (Software Watchdog Timer)
CAN modules also support SWT-related status types that monitor the bus error state. The SWT status escalates through warning, error-passive, and bus-off states based on the transmit and receive error counters defined by the CAN protocol specification.
While this sample focuses on RECV_MSG interrupts, the SWT status types are available for fault monitoring. In safety-critical CAN applications, you may want to enable SWT interrupts alongside RECV_MSG to detect bus error escalation immediately — for example, to take corrective action when a channel enters error-passive state before it reaches bus-off.
Consult your module’s manual for the complete list of CAN interrupt status types and the specific register layout for your module generation.
Interrupt Configuration
This configuration applies to both the standard and Ethernet IDR interrupt mechanisms. Both variants call the same configuration functions to set up the module before enabling interrupts.
The configureCANToInterruptOnRx() function in nai_can_int.c performs five steps in sequence: disable interrupts, clear status registers, set the interrupt vector, configure the trigger mode, and set interrupt steering.
Step 1: Disable and Clear Previous Configuration
Always disable interrupts and clear latched status before changing configuration. Stale latched status from a previous run will trigger an immediate interrupt if not cleared. The function begins by disabling all channel interrupt enables and clearing all status bits:
/* clear interruptConfigs of previous channels */
check_status(naibrd_CAN_SetIntEnableRaw(cardIndex, module, 0));
check_status(naibrd_CAN_ClearStatusRaw(cardIndex, module, type, 0xFFFF));
naibrd_CAN_SetIntEnableRaw() with a value of 0 disables all interrupt sources for the module. naibrd_CAN_ClearStatusRaw() with 0xFFFF clears all latched status bits. This combination ensures you start from a known-clean state.
Step 2: Set Interrupt Vector
The vector identifies which interrupt source generated the event. The sample assigns NAI_CAN_INTERRUPT_VECTOR to channel 1 for the RECV_MSG status type:
check_status(naibrd_CAN_SetIntVector(cardIndex, module, 1, type, vector));
If you need to distinguish channels inside your ISR without reading status registers, you can assign a different vector to each channel. In practice, most applications use a shared vector and read the per-channel status register to determine which channel(s) fired.
Step 3: Set Trigger Mode
The trigger mode determines when the interrupt fires relative to the status change:
check_status(naibrd_CAN_SetInterruptEdgeLevel(cardIndex, module, type, interrupt_Edge_Trigger));
A value of 0 selects edge-triggered mode, where the interrupt fires once when the condition is first detected (the status bit transitions from 0 to 1). A value of 1 selects level-triggered mode, where the interrupt continues to assert as long as the condition remains active.
For CAN receive interrupts, edge-triggered mode (the default in this sample) is a common choice. CAN messages arrive asynchronously and the handler clears the status register after processing, which re-arms the edge for the next event. Level-triggered mode is useful when you want continuous notification as long as data remains in the FIFO — but be aware that the interrupt will keep firing until you drain the FIFO completely. For detailed theory on edge vs. level triggering, see the Interrupts API Guide.
Step 4: Set Interrupt Steering
Interrupt steering determines how the interrupt signal is delivered from the module to your application:
check_status(naibrd_CAN_SetInterruptSteering(cardIndex, module, type, steering));
The steering options are:
-
Onboard (
NAIBRD_INT_STEERING_ON_BOARD_0) — handled locally on the board’s processor. The ISR runs directly on the board without traversing a bus. This is the standard choice for onboard applications. -
Ethernet (
NAIBRD_INT_STEERING_ON_BOARD_1) — triggers an Ethernet IDR response. The board’s onboard processor intercepts the interrupt and sends a notification over Ethernet to the host application. Use this mode with theCAN_Interrupt_Ethernetvariant. -
cPCI offboard (
NAIBRD_INT_STEERING_CPCI_APP) — routes the interrupt to the CompactPCI backplane host. -
PCIe offboard (
NAIBRD_INT_STEERING_PCIE_APP) — routes the interrupt to the PCIe host.
For a full description of each steering mode and the hardware paths involved, refer to your module manual and the Interrupts API Guide.
Complete Configuration Function
The complete configureCANToInterruptOnRx() function applies all four steps:
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));
}
This function is called by both variants before enabling interrupts. The Ethernet variant passes NAIBRD_INT_STEERING_ON_BOARD_1 as the steering mode to route interrupts through the IDR path.
Enable Interrupts
After configuration, the enableCANInterrupts() function enables interrupt generation for the configured channels:
void enableCANInterrupts(CanConfig inputCANConfig, bool_t enable) {
int32_t channel;
int32_t enableRegVal = 0;
int32_t isEnabled = 0;
for (channel = inputCANConfig.minChannel; channel <= inputCANConfig.maxChannel; channel++)
{
isEnabled = 1 << (channel - 1);
if (enable)
enableRegVal = (1 << (channel - 1)) | enableRegVal;
else if (isEnabled & enableRegVal)
enableRegVal = (1 << (channel - 1)) ^ enableRegVal;
}
naibrd_CAN_SetIntEnableRaw(inputCANConfig.cardIndex, inputCANConfig.module, enableRegVal);
}
The function builds a bitmask where each bit corresponds to a channel in the min/max range. When enable is TRUE, the bits for all channels in the range are set. When FALSE, only the bits for those channels are cleared. The bitmask is written to the interrupt enable register with a single naibrd_CAN_SetIntEnableRaw() call. In your own application, you can selectively enable only the channels you need.
|
Important
|
Common interrupt configuration errors:
|
CAN Bus Configuration
Before interrupts can fire, the CAN module must be configured to receive messages. The Cfg_Rx_CAN() function handles this setup by configuring the baud rate and enabling receive on the selected channels.
Cfg_Rx_CAN(inputCANConfig);
This function sets the CAN bit timing parameters (prescaler, SJW, TSEG1, TSEG2) based on the baudRate field in CanConfig using naibrd_CAN_SetBitTiming(), and then enables receive on each channel in the configured range with naibrd_CAN_SetRxEnable(). The default baud rate is 500 Kbit/s for J1939 channels. Timing parameters are defined in nai_can_cfg.h for standard rates (1 Mbit/s, 500 Kbit/s, 250 Kbit/s).
Without this step, the module will not receive any CAN bus traffic and no interrupts will fire even though the interrupt path is fully configured.
Standard Interrupt Handling
This section walks through the standard (non-Ethernet) interrupt path: installing an ISR, starting the interrupt processing thread, handling interrupts as they arrive, and reading the FIFO data from the interrupting channels. All code is from CAN_Interrupt.c and nai_can_int.c.
ISR Installation
The SSK provides two ISR installation patterns depending on whether the interrupt is handled onboard (on the board’s own processor) or offboard (on the host through a PCI/PCIe bus).
Onboard:
check_status(naibrd_InstallISR(inputInterruptConfig.cardIndex, inputInterruptConfig.irq,
(nai_isr_t)IntOnboardIsr, NULL));
Offboard:
check_status(naibrd_InstallISR(inputInterruptConfig.cardIndex, inputInterruptConfig.irq,
(nai_isr_t)IntOffboardIsr, (void*)&inputCANConfig.cardIndex));
For onboard delivery, naibrd_InstallISR() registers IntOnboardIsr with the IRQ determined by the user’s steering selection. For offboard delivery, IntOffboardIsr is installed with the originating card index passed as context so the handler can identify which board generated the interrupt. The IRQ ID is set by setIRQ() based on the steering mode configured in the previous section. A mismatch between the IRQ ID and steering mode will silently prevent interrupts from reaching your ISR.
Thread Setup and Execution Flow
The standard variant follows this sequence after ISR installation:
/**** 3. configure Module to perform interrupts ****/
configureCANToInterruptOnRx(inputInterruptConfig, inputCANConfig);
/**** 4. Configure Module to cause Interrupts ****/
Cfg_Rx_CAN(inputCANConfig);
/**** Initialize Message Queue ****/
InitInterruptAppThread(ONBOARD_INT, 0);
nai_msDelay(10);
UpdateThreadState(RUN);
/**** Enable Interrupts ****/
enableCANInterrupts(inputCANConfig, TRUE);
/*** 5. Show Interrupt Handling (contains step 6) ***/
canIntProcessFunc = handleCANInterrupt;
/*** Request user triggers interrupt ***/
DisplayMessage_CANInterrupt(MSG_USER_TRIGGER_CAN_INT);
Cfg_Rx_CAN() configures the CAN receive parameters (baud rate and receive enable) for the selected channels, making the module ready to receive bus traffic. InitInterruptAppThread() creates the background thread that runs the interrupt state machine. The 10 ms delay ensures the thread’s state machine has completed its initialization cycle before any interrupts can arrive. UpdateThreadState(RUN) transitions the thread from its idle startup state into active processing.
The canIntProcessFunc function pointer is set to handleCANInterrupt so the interrupt thread knows which callback to invoke when an interrupt message arrives. The application then displays a prompt and waits for interrupt events until the user quits.
Interrupt Handler
handleCANInterrupt() is called by the interrupt thread each time the ISR posts a message. The handler reads the latched RECV_MSG status register to determine which channels fired, allocates FIFO storage for each active channel, drains the FIFO data, prints the results, and clears the status to re-arm the interrupt.
void handleCANInterrupt(uint32_t nVector) {
FIFO* fifo[NAI_GEN5_CAN_MAX_CHANNEL_COUNT];
uint8_t status;
int32_t maxChannel;
int32_t channel;
maxChannel = inputCANConfig.maxChannel;
printf("\n\nInterrupt Occurred \n\n");
if (inputInterruptConfig.bPromptForInterruptClear)
{
promptUserToClearInterrupt_CAN();
}
naibrd_CAN_GetStatusRaw(inputCANConfig.cardIndex, inputCANConfig.module, NAI_CAN_STATUS_RECV_MSG_LATCHED, &status);
naibrd_CAN_ClearStatusRaw(inputCANConfig.cardIndex, inputCANConfig.module, NAI_CAN_STATUS_RECV_MSG_LATCHED, status);
for (channel = inputCANConfig.minChannel; channel <= inputCANConfig.maxChannel; channel++)
{
fifo[channel - 1] = allocateSpaceForFIFO(MAX_FIFO_COUNT, inputCANConfig.maxPayload);
}
getFIFODataFromInterruptChannels(status, fifo, inputCANConfig.minChannel, maxChannel);
printInterruptInformation_CAN(inputCANConfig, nVector, status, fifo, FALSE, fifoDataFile);
for (channel = inputCANConfig.minChannel; channel < inputCANConfig.maxChannel; channel++)
{
deallocSpaceForFIFO(fifo[channel - 1]);
fifo[channel - 1] = NULL;
}
}
The handler works as follows:
-
Read and clear status —
naibrd_CAN_GetStatusRaw()reads the latched RECV_MSG status register, returning a bitmask where each bit corresponds to a channel.naibrd_CAN_ClearStatusRaw()writes back the same value to clear exactly the bits that were set, re-arming the interrupt for the next event. If you omit the clear step with edge-triggered interrupts, no new edge will occur and the interrupt will never fire again. -
Allocate FIFO storage — for each channel in the configured range,
allocateSpaceForFIFO()allocates memory for up toMAX_FIFO_COUNTframes withmaxPayloaddata words each. This is a sample convenience — in your own application, you would typically pre-allocate these buffers. -
Drain FIFO per channel —
getFIFODataFromInterruptChannels()iterates over each channel, checks whether its status bit is set, and if so reads the FIFO data usinggetFIFOChannelData()and the FIFO status usingnaibrd_CAN_GetFifoStatusRaw(). Only channels that actually triggered are read, avoiding unnecessary bus traffic. -
Display results —
printInterruptInformation_CAN()formats the interrupt vector, status bitmask, FIFO status, and received CAN frame data to the output stream (stdout or a file, depending on thedisplayDataflag). -
Free FIFO storage — dynamically allocated FIFO buffers are released after each interrupt.
Auto-Clear vs. Prompt-Clear
The sample provides two interrupt clearing modes controlled by the bPromptForInterruptClear flag:
if (inputInterruptConfig.bPromptForInterruptClear)
{
promptUserToClearInterrupt_CAN();
}
naibrd_CAN_GetStatusRaw(inputCANConfig.cardIndex, inputCANConfig.module, NAI_CAN_STATUS_RECV_MSG_LATCHED, &status);
naibrd_CAN_ClearStatusRaw(inputCANConfig.cardIndex, inputCANConfig.module, NAI_CAN_STATUS_RECV_MSG_LATCHED, status);
When prompt-clear is active, the handler pauses and prints Press "C" to clear interrupts…. It polls GetUserRequestClearInt() in a 10 ms loop until the user responds. This mode is useful for learning and debugging because it lets you observe the latched status and FIFO contents before clearing.
When auto-clear is active, the prompt is skipped and the handler proceeds directly to reading and clearing the status register. This is the recommended mode for production use — it minimizes interrupt latency and avoids the risk of missed interrupts while waiting for user input.
Cleanup
When the user quits, the application disables interrupts and uninstalls the ISR:
/***** 7. Clear Module Configurations *****/
enableCANInterrupts(inputCANConfig, FALSE);
/***** 8. Clear Board Configurations *****/
check_status(naibrd_UninstallISR(inputInterruptConfig.cardIndex));
Always disable interrupts and uninstall the ISR before exiting. Leaving an ISR installed after your application exits can cause system instability if a stale interrupt fires.
|
Important
|
Common standard interrupt handling errors:
|
Ethernet IDR Interrupt Handling
The Ethernet IDR (Interrupt Driven Response) variant delivers interrupt notifications over the network instead of through a hardware ISR. Use this approach when your application runs on a remote host connected via Ethernet, when a hardware ISR is not available or practical for your deployment, or when you want the board to automatically read status and FIFO data and package the results into UDP messages.
Prerequisites
Ethernet IDR requires a Generation 4 or later board with Ethernet support. The application verifies this at startup before proceeding with IDR configuration:
bGen4CanIDRCommands = SupportsGen4Ether(inputCANConfig.cardIndex);
if (!bGen4CanIDRCommands)
{
printf("CAN Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported\n");
bQuit = TRUE;
}
If the check fails, the board’s firmware does not support the Gen4 Ethernet protocol and IDR commands will not work. You may need to update the board firmware.
IDR Configuration
The setupIDRConfiguration() function builds and registers the IDR commands that the board will execute each time an interrupt fires. The board then sends the results back to your application as UDP messages.
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 configuration flow proceeds in three steps:
-
Clear any previous IDR configuration —
naibrd_Ether_ClearIDRConfig()removes any existing IDR registered underDEF_ETHERNET_CAN_IDR_IDso the new configuration starts clean. -
Build the IDR commands —
InitCANRxIDRCommands()constructs three Ethernet commands that will be executed by the board when the interrupt fires: a FIFO read command (MakeCANRxReadFIFOCommand()), a FIFO status read (MakeCANRxReadRegsFIFOStatus()), and a latched interrupt status read (MakeCANRxReadRegsCommand()). All three are embedded in the IDR definition so the board automatically reads the FIFO data, FIFO status, and interrupt status registers, packaging all results into the UDP response. -
Register the IDR —
naibrd_Ether_SetIDRConfig()programs the board with the complete IDR definition: the response IP address, port, protocol, interrupt vector, and the command payload to execute when the vector fires.
IDR Command Details
The CAN Ethernet IDR packages three separate read commands into a single IDR definition. This is more comprehensive than the DT or AR Ethernet IDR variants, which typically read only status registers and leave FIFO draining to follow-up API calls.
-
FIFO Read —
MakeCANRxReadFIFOCommand()constructs annai_ether_MakeFifoReadMessage()targeting the channel’s receive FIFO buffer. The board reads up tomaxPayloaddata words from the FIFO in one operation. -
FIFO Status Read —
MakeCANRxReadRegsFIFOStatus()reads the FIFO status register for the configured channel, indicating overflow, underflow, and frame count. -
Interrupt Status Read —
MakeCANRxReadRegsCommand()reads the latched RECV_MSG status register to identify which channels triggered the interrupt.
The command index for each response is tracked in command_index_fifo_status and command_index_interrupt_status so the handler can identify which command a response belongs to when parsing the incoming UDP messages.
Starting and Stopping the IDR
Once the IDR is configured, start it with naibrd_Ether_StartIDR() so the board begins sending UDP notifications when interrupts occur:
check_status(naibrd_Ether_StartIDR(inputIDRConfig.cardIndex, (uint16_t)DEF_ETHERNET_CAN_IDR_ID));
The Ethernet variant then starts the IDR server with runIDRServer(), which listens for incoming UDP messages and dispatches them to the registered handler.
When the application exits, stop the IDR and clean up:
/***** 8. Clear Board Configurations *****/
check_status(naibrd_Ether_StopIDR(inputIDRConfig.cardIndex, (uint16_t)DEF_ETHERNET_CAN_IDR_ID));
Always stop the IDR before closing the connection. If you close the connection without stopping the IDR, the board will continue sending UDP messages to an address that is no longer listening.
Ethernet Interrupt Handler
HandleCANEtherInterrupt() is called when the application’s Ethernet listener receives a UDP message matching the IDR ID. Unlike the standard handler which reads status registers directly, this handler parses the status and FIFO data out of the incoming network messages — the board has already read the registers and packaged the results.
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)
{
case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_4:
break;
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;
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 works as follows:
-
Parse the message header —
nai_ether_DecodeMessageHeader()extracts the sequence number, typecode, and payload offset from the raw UDP message. -
Extract the command index — the command index is encoded in the sequence number field (
(seq & (0x0F << 6)) >> 6). The handler uses this to identify which IDR command response it is processing: FIFO data, FIFO status, or interrupt status. -
Route by typecode — the handler dispatches based on the Ethernet typecode:
-
RSP_COMMAND_COMPLETE_READ_4(register read) — if the command index matchescommand_index_fifo_status, the FIFO status register value is extracted. If it matchescommand_index_interrupt_status, the latched RECV_MSG status is extracted. -
RSP_COMMAND_COMPLETE_READ_FIFO_4(FIFO read) — the CAN frame data is parsed from the response bygetInterruptDataEthernet(), which identifies frame start/end markers and extracts individual CAN data words. -
RSP_COMMAND_COMPLETE_WRITE_4(write acknowledgment) — no action needed.
-
-
Display on final response — when the last command response arrives (
commandIndex == cmdcount-1), all data has been collected. The handler callsprintInterruptInformation_CAN()withisEther=TRUE, which displays the IDR ID instead of an ISR vector. -
Free FIFO storage — the allocated FIFO buffer is freed after display so it can be reallocated cleanly for the next interrupt.
Efficiency Advantage
The key advantage of the CAN Ethernet IDR approach is that the board reads both status registers and FIFO data automatically and includes all results in the UDP response messages. The standard handler must issue separate naibrd_CAN_GetStatusRaw() and getFIFOChannelData() calls — each one a register read over the bus — to retrieve the same information. For remote monitoring scenarios where the host communicates with the board over Ethernet, the IDR approach eliminates multiple network round-trips per interrupt event, reducing both latency and network traffic.
This is more efficient than the AR or DT Ethernet IDR variants, which only read status registers via IDR and still require follow-up API calls to drain FIFO data.
|
Important
|
Common Ethernet IDR errors:
|
Troubleshooting Reference
This table summarizes common errors and symptoms covered in the sections above. For detailed context, refer to the relevant section. Consult your module’s manual for hardware-specific diagnostic procedures.
Debugging Interrupts That Are Not Firing
When interrupts are not being delivered, use this two-step approach to isolate the problem:
-
Check whether the status registers are changing. Call
naibrd_CAN_GetStatusRaw()withNAI_CAN_STATUS_RECV_MSG_LATCHEDto read the latched status for the module. If the status bits are changing when CAN messages are being sent to the channel, the module is detecting the event correctly — the issue is in your interrupt delivery path (steering, ISR installation, or thread setup). -
If the status registers are NOT changing, the issue is at the hardware or channel configuration level. Verify that the correct channel is configured, that
naibrd_CAN_SetRxEnable()has been called, that the baud rate matches the transmitting device, and that a valid CAN signal is present on the bus. You can also read the status registers directly at their register addresses to bypass the API layer entirely. Consult your module’s manual for the specific register addresses for each status type.
This approach quickly narrows down whether the problem is on the hardware/configuration side or the interrupt delivery side, saving significant debugging time.
| Error / Symptom | Possible Causes | Suggested Resolution |
|---|---|---|
No board found or connection timeout |
Board not powered, incorrect or missing configuration file, network issue |
Verify hardware is powered and connected. If the configuration file exists, check that it lists the correct interface and address. If it does not exist, the board menu will appear — configure and save your connection settings. |
Module not detected or not recognized as CAN |
No CAN module installed at the selected slot, incorrect module number, or a non-CAN module is present |
Verify hardware configuration. |
ISR installation failure |
Steering mode does not match hardware configuration, driver not loaded, or bus access issue |
Verify steering mode matches your bus architecture. On Linux, check that the NAI driver is loaded. On Windows, verify the device appears in Device Manager. |
Interrupts not firing after enable |
Status registers not cleared before enabling, wrong steering mode, ISR not installed, or thread not started |
Follow the full setup sequence: disable, clear status, configure, install ISR, enable, start thread. See the debugging technique above. |
Wrong steering mode |
Using onboard steering from an external host, or offboard steering from the onboard processor |
Match steering to where your application executes. Onboard: |
Handler never called (standard) |
Thread not started, ISR not installed for correct IRQ, or steering mismatch |
Verify |
Interrupts stop after first (edge-triggered) |
Status not cleared in handler — edge interrupts require clearing to re-arm |
Ensure |
Interrupts fire continuously (level-triggered) |
The underlying condition persists (data in FIFO) — this is expected for level-triggered interrupts |
Drain the FIFO completely and clear the status in your handler, or switch to edge triggering if continuous firing is not desired. |
FIFO data missing or empty |
CAN receive not enabled, baud rate mismatch, no signal on bus |
Verify |
Gen4 Ethernet not supported |
Board Ethernet firmware is older than Gen4 |
Consult board documentation for Ethernet generation support. Firmware update may be required. |
No IDR messages received (Ethernet) |
IP/port mismatch, firewall blocking UDP, IDR not started |
Verify network configuration matches IDR setup. Check firewall rules. Confirm |
Ethernet FIFO data missing |
Wrong channel or payload size in IDR command construction, module offset retrieval failed |
Verify |
Status registers not changing (debugging) |
Channel not configured correctly, baud rate mismatch, no physical signal present |
Verify channel configuration, baud rate, and that valid CAN traffic is present on the bus. Read registers directly — consult module manual for addresses. |
Full Source
The complete source for this sample is provided below for reference. The sections above explain each part in detail. This sample consists of five source files: the standard interrupt application, the Ethernet IDR variant, shared interrupt configuration code, Ethernet IDR command construction code, and shared CAN configuration utilities.
Full Source — CAN_Interrupt.c (SSK 1.x)
/**************************************************************************************************************/
/**
<summary>
The CAN_Interrupt program demonstrates how to perform an interrupt when a single channel receives
a can message. The purpose of this program is to demonstrate the method calls in the naibrd library for performing
the interrupt. More information on this process can be found in the naibrd SSK Quick Guide(Interrupts) file.
This application differs from CAN_Interrupt_Basic in that it could handle multiple interrupts at once.
It also queries the user for the edge trigger value and whether the user should be prompted to clear an interrupt.
The application also has support for offboard interrupts.
</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.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"
IntProcessFuncDef canIntProcessFunc;
/* Extern Functions or Variables*/
extern CanConfig inputCANConfig;
extern InterruptConfig inputInterruptConfig;
extern FILE* fifoDataFile;
/********************/
/* Application Name */
/********************/
static const int8_t *CONFIG_FILE = (int8_t *)"default_CAN_Interrupt.txt";
/********************************/
/* Internal Function Prototypes */
/********************************/
static bool_t Run_CAN_Interrupt();
/**************************************************************************************************************/
/***** 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(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, NAI_J1939_MAX_DATA_LEN, 1, NAI_CAN_500K_BAUD);
initializeInterruptConfigurations(FALSE, FALSE, FALSE, 0, 0, 0, 0);
if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
while (stop != TRUE)
{
/* Query the user for the card index */
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
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();
}
}
}
printf("\nType Q to quit or Enter key to restart application:\n");
stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
}
}
printf("\nType the Enter key to exit the program: ");
naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
naiapp_access_CloseAllOpenCards();
return 0;
}
/**************************************************************************************************************/
/**
<summary>
This function is broken into the following major steps. These steps correspond with the steps provided
in the naibrd SSK Quick Guide(Interrupts) file.
2. Bus Interrupt Handling - Install ISR
API CALLS - naibrd_InstallISR
3. Enable Module Interrupts- Configures module to 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_utils.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 - Check the mailbox to see if any interrupts occurred.
6. Re-arming Interrupts - Clear the status register to allow interrupts to occur again. This is done by writing to the status register.
In this program, we use an API call to do this.
API CALLS - naibrd_CAN_ClearStatus
7. Clear Module Configurations
API CALLS - naibrd_CAN_SetRxEnable
8. Clear Board Configurations
API CALLS - naibrd_UninstallISR
</summary>
*/
/**************************************************************************************************************/
static bool_t Run_CAN_Interrupt()
{
bool_t bQuit = FALSE;
int32_t minChannel;
int32_t maxChannel;
minChannel = 1;
maxChannel = naibrd_CAN_GetChannelCount(inputCANConfig.modid);
/* Query for Channels to Operate on */
bQuit = naiapp_query_ForChannelRange(&inputCANConfig.minChannel,&inputCANConfig.maxChannel,minChannel,maxChannel);
/* Query for Trigger Status of interrupts */
if(!bQuit)
{
bQuit = GetCANLatchStatusTriggerMode(&inputInterruptConfig.interrupt_Edge_Trigger);
}
/* Query user if they'd like to be prompted for clearing interrupts */
if(!bQuit){
bQuit = QueryUserForClearingInterruptPrompts(&inputInterruptConfig.bPromptForInterruptClear);
}
if(!bQuit){
bQuit = QueryUserForOnboardOffboardInterrupts(&inputInterruptConfig.bProcessOnboardInterrupts);
}
/* Query user if they'd like to display fifo data after interrupt */
if(!bQuit)
{
bQuit = QueryUserForDisplayingData(&inputInterruptConfig.displayData);
}
/* Query user for location interrupt will be sent out to */
if(!bQuit)
{
bQuit = GetIntSteeringTypeFromUser(&inputInterruptConfig.steering);
}
if (!bQuit)
{
if(inputInterruptConfig.displayData)
fifoDataFile = stdout;
else
fifoDataFile = fopen("fifoData.txt","w+");
/**** 2. Implement Bus Interrupt Handling ****/
setIRQ(inputInterruptConfig.steering,&inputInterruptConfig.irq);
inputInterruptConfig.cardIndex = inputCANConfig.cardIndex;
if(inputInterruptConfig.bProcessOnboardInterrupts == TRUE)
{
check_status(naibrd_InstallISR(inputInterruptConfig.cardIndex,inputInterruptConfig.irq,(nai_isr_t)IntOnboardIsr,NULL));
}
else
{
check_status(naibrd_InstallISR(inputInterruptConfig.cardIndex,inputInterruptConfig.irq, (nai_isr_t)IntOffboardIsr, (void*)&inputCANConfig.cardIndex));
}
/****3. configure Module to perform interrupts ****/
configureCANToInterruptOnRx(inputInterruptConfig,inputCANConfig);
/****4. Configure Module to cause Interrupts ****/
Cfg_Rx_CAN(inputCANConfig);
/****Initialize Message Queue ****/
InitInterruptAppThread(ONBOARD_INT, 0);
nai_msDelay(10);
UpdateThreadState(RUN);
/****Enable Interrupts****/
enableCANInterrupts(inputCANConfig,TRUE);
/***5. Show Interrupt Handling (contains step 6) ***/
canIntProcessFunc = handleCANInterrupt;
/***Request user triggers interrupt ***/
DisplayMessage_CANInterrupt(MSG_USER_TRIGGER_CAN_INT);
/****Wait on program threads****/
while (!isThreadStateTerminated()){}
bQuit = TRUE;
if(!inputInterruptConfig.displayData)
fclose(fifoDataFile);
/*****7. Clear Module Configurations*****/
enableCANInterrupts(inputCANConfig,FALSE);
/*****8. Clear Board Configurations *****/
check_status(naibrd_UninstallISR(inputInterruptConfig.cardIndex));
}
return bQuit;
}
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 *****/
/**************************************************************************************************************/
#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;
}
Full Source — nai_can_int.c (SSK 1.x)
#include <stdio.h>
#include <stdlib.h>
#include <string.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"
/* Common CAN Sample Program include files */
#include "nai_can_int.h"
#include "nai_can_cfg.h"
/* naibrd include files */
#include "nai.h"
#include "naibrd.h"
#include "naibrd_ether.h"
#include "functions/naibrd_can.h"
#include "maps/nai_map_can.h"
bool_t interruptOccured;
int32_t frameCount;
uint32_t interruptVector;
InterruptConfig inputInterruptConfig;
/* Extern Functions or Variables*/
extern CanConfig inputCANConfig;
extern FILE* fifoDataFile;
/***********************/
/* Function Prototypes */
/***********************/
void getFIFODataFromInterruptChannels(uint8_t status, FIFO* fifoData[NAI_GEN5_CAN_MAX_CHANNEL_COUNT], int32_t minChannel, int32_t maxChannel);
bool_t CAN_checkForInterrupt(CanConfig inputCANConfig) {
bool_t bQuit;
uint8_t status;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
status = 0;
bQuit = FALSE;
interruptOccured = FALSE;
while (!bQuit) {
printf("\nPress enter to check if interrupt Occurred (press Q to quit):");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit) {
if (interruptOccured) {
naibrd_CAN_GetStatusRaw(inputCANConfig.cardIndex, inputCANConfig.module, NAI_CAN_STATUS_RECV_MSG_LATCHED, &status);
printf("\nVector = %#x \n", interruptVector);
printf("Status = %#x \n", status);
interruptOccured = FALSE;
}
else {
printf("\nNo Interrupt Occurred");
}
printf("\n\nWould you like to clear the status register? (default:N):");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (inputBuffer[0] == 'y' || inputBuffer[0] == 'Y')
{
naibrd_CAN_ClearStatusRaw(inputCANConfig.cardIndex, inputCANConfig.module, NAI_CAN_STATUS_RECV_MSG_LATCHED, status);
}
}
}
return bQuit;
}
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));
}
void DisplayMessage_CANInterrupt(int32_t msgId)
{
switch (msgId)
{
case (int32_t)MSG_BANNER_CAN_INT:
{
printf("\n********************************************************************************");
printf("\n****** CAN INTERRUPT ******");
printf("\nAn interrupt will occur when the CAN module receives a message ");
printf("\n********************************************************************************");
}
break;
case (int32_t)MSG_USER_TRIGGER_CAN_INT:
{
printf("\nPress \"Q\" to quit the application.\nPlease trigger CAN interrupt (Recv Msg):");
}
break;
case (int32_t)MSG_USER_CLEAR_CAN_INT:
{
printf("Press \"C\" to clear interrupts... ");
}
break;
}
}
void enableCANInterrupts(CanConfig inputCANConfig, bool_t enable) {
int32_t channel;
int32_t enableRegVal = 0;
int32_t isEnabled = 0;
for (channel = inputCANConfig.minChannel; channel <= inputCANConfig.maxChannel; channel++)
{
isEnabled = 1 << (channel - 1);
if (enable)
enableRegVal = (1 << (channel - 1)) | enableRegVal;
else if (isEnabled & enableRegVal)
enableRegVal = (1 << (channel - 1)) ^ enableRegVal;
}
naibrd_CAN_SetIntEnableRaw(inputCANConfig.cardIndex, inputCANConfig.module, enableRegVal);
}
bool_t GetCANLatchStatusTriggerMode(int32_t* interrupt_Edge_Trigger)
{
bool_t bQuit;
uint32_t temp;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
printf("\nEnter Latched Status Trigger Mode (Edge=0, Level=1) (Default=0): ");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
if (inputResponseCnt > 0)
{
temp = (int32_t)atol((const char*)inputBuffer);
if (temp == 0 || temp == 1)
{
*interrupt_Edge_Trigger = temp;
}
else
{
printf("ERROR: Invalid Interrupt Trigger Mode.\n");
}
}
else
*interrupt_Edge_Trigger = 0;
}
return(bQuit);
}
#if defined (__VXWORKS__)
void basic_ISR_CAN(uint32_t param)
#else
void basic_ISR_CAN(void *param, uint32_t vector)
#endif
{
interruptOccured = TRUE;
#if defined (__VXWORKS__)
interruptVector = nai_Onboard_GetInterruptVector();
nai_Onboard_ClearInterrupt();
#elif defined (WIN32)
UNREFERENCED_PARAMETER(param);
interruptVector = vector;
#else
interruptVector = vector;
#endif
}
void handleCANInterrupt(uint32_t nVector) {
FIFO* fifo[NAI_GEN5_CAN_MAX_CHANNEL_COUNT];
uint8_t status;
int32_t maxChannel;
int32_t channel;
maxChannel = inputCANConfig.maxChannel;
printf("\n\nInterrupt Occurred \n\n");
if (inputInterruptConfig.bPromptForInterruptClear)
{
promptUserToClearInterrupt_CAN();
}
naibrd_CAN_GetStatusRaw(inputCANConfig.cardIndex, inputCANConfig.module, NAI_CAN_STATUS_RECV_MSG_LATCHED, &status);
naibrd_CAN_ClearStatusRaw(inputCANConfig.cardIndex, inputCANConfig.module, NAI_CAN_STATUS_RECV_MSG_LATCHED, status);
for (channel = inputCANConfig.minChannel; channel <= inputCANConfig.maxChannel; channel++)
{
fifo[channel - 1] = allocateSpaceForFIFO(MAX_FIFO_COUNT, inputCANConfig.maxPayload);
}
getFIFODataFromInterruptChannels(status, fifo, inputCANConfig.minChannel, maxChannel);
printInterruptInformation_CAN(inputCANConfig, nVector, status, fifo, FALSE, fifoDataFile);
for (channel = inputCANConfig.minChannel; channel < inputCANConfig.maxChannel; channel++)
{
deallocSpaceForFIFO(fifo[channel - 1]);
fifo[channel - 1] = NULL;
}
}
void getFIFODataFromInterruptChannels(uint8_t status, FIFO* fifoData[NAI_GEN5_CAN_MAX_CHANNEL_COUNT], int32_t minChannel, int32_t maxChannel)
{
int32_t channel = 1;
uint8_t statusBitSet;
int32_t cardIndex = inputCANConfig.cardIndex;
int32_t module = inputCANConfig.module;
int32_t bufferLength;
CanDataFrame* buffer;
uint32_t* fifoStatus;
for (channel = minChannel; channel <= maxChannel; ++channel)
{
statusBitSet = status & (1 << (channel - 1));
if (statusBitSet && fifoData[channel - 1] != NULL)
{
buffer = fifoData[channel - 1]->buffer;
bufferLength = fifoData[channel - 1]->maxFramesOnFifo;
fifoStatus = &fifoData[channel - 1]->fifoStatus;
fifoData[channel - 1]->numOfFramesOnFifo = getFIFOChannelData(cardIndex, module, channel, buffer, bufferLength);
check_status(naibrd_CAN_GetFifoStatusRaw(cardIndex, module, channel, fifoStatus));
frameCount = frameCount + (fifoData[channel - 1]->numOfFramesOnFifo);
}
}
}
void printInterruptInformation_CAN(CanConfig inputCANConfig, int32_t interruptID, uint8_t status, FIFO* fifoData[NAI_GEN5_CAN_MAX_CHANNEL_COUNT], bool_t isEther, FILE* stream)
{
uint8_t statusBitSet;
int32_t channel;
uint32_t fifoStatus;
CanDataFrame* buffer;
int32_t bufferLength;
printf("\n");
fprintf(stream, "Interrupt Information\n");
fprintf(stream, "-----------------------------------\n");
if (isEther)
fprintf(stream, "\nIDR ID = %#x \n", interruptID);
else
fprintf(stream, "\nVector = %#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)
{
fifoStatus = fifoData[channel - 1]->fifoStatus;
buffer = fifoData[channel - 1]->buffer;
bufferLength = fifoData[channel - 1]->numOfFramesOnFifo;
fprintf(stream, "\n");
printFIFOStatusRx(fifoStatus, stream);
fprintf(stream, "\n");
printFIFOChannelData(channel, buffer, bufferLength, stream);
fprintf(stream, "\n\n");
}
}
fprintf(stream, "\nFrames Rx = %d", frameCount);
fprintf(stream, "\n-----------------------------------\n");
}
void promptUserToClearInterrupt_CAN()
{
SetUserRequestClearInt(FALSE);
printf("\n");
DisplayMessage_CANInterrupt(MSG_USER_CLEAR_CAN_INT);
while (!GetUserRequestClearInt())
{
nai_msDelay(10);
}
}
void ClearInterrupt_CAN()
{
nai_status_bit_t status;
if (inputInterruptConfig.bPromptForInterruptClear)
{
promptUserToClearInterrupt_CAN();
}
naibrd_CAN_GetStatusRaw(inputCANConfig.cardIndex, inputCANConfig.module, NAI_CAN_STATUS_RECV_MSG_LATCHED, &status);
naibrd_CAN_ClearStatusRaw(inputCANConfig.cardIndex, inputCANConfig.module, NAI_CAN_STATUS_RECV_MSG_LATCHED, status);
}
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;
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));
}
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;
}
}
}
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++;
}
}
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++;
}
}
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++;
}
}
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);
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++;
}
}
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)
{
case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_4:
break;
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;
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;
}
}
}
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++;
}
}
}
}
}
Full Source — nai_can_int.h (SSK 1.x)
/****** NAI CAN Common Sample Interrupt Program Functions and Definitions ******/
#pragma once
#ifndef NAI_CAN_INT_H
#define NAI_CAN_INT_H
/* Common Sample Program include files */
#include "include/naiapp_interrupt.h"
#include "include/naiapp_interrupt_ether.h"
#include "include/naiapp_boardaccess_query.h"
#include "include/naiapp_boardaccess_access.h"
#include "include/naiapp_boardaccess_display.h"
#include "include/naiapp_boardaccess_utils.h"
#include "nai.h"
#include "functions/naibrd_can.h"
#include "nai_can_cfg.h"
/* MSG IDS for DisplayMessage_CANInterrupt */
#define MSG_BANNER_CAN_INT 1
#define MSG_USER_TRIGGER_CAN_INT 2
#define MSG_USER_CLEAR_CAN_INT 3
bool_t GetCANLatchStatusTriggerMode(int32_t* interrupt_Edge_Trigger);
void DisplayMessage_CANInterrupt(int32_t msgId);
bool_t CAN_checkForInterrupt(CanConfig inputCANConfig);
void enableCANInterrupts(CanConfig inputCANConfig, bool_t enable);
void configureCANToInterruptOnRx(InterruptConfig inputInterruptConfig, CanConfig inputCanConfig);
void handleCANInterrupt(uint32_t nVector);
void promptUserToClearInterrupt_CAN();
void ClearInterrupt_CAN();
void printInterruptInformation_CAN(CanConfig inputCANConfig, int32_t interruptID, uint8_t status, FIFO* fifoData[NAI_GEN5_CAN_MAX_CHANNEL_COUNT], bool_t isEther, FILE* stream);
#if defined (__VXWORKS__)
void basic_ISR_CAN(uint32_t param);
#else
void basic_ISR_CAN(void *param, uint32_t vector);
#endif
#endif
Full Source — nai_can_int_ether.h (SSK 1.x)
/****** NAI CAN Common Sample Program Interrupt Ethernet Functions and Definitions ******/
#ifndef NAI_CAN_ETHER_H
#define NAI_CAN_ETHER_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "nai.h"
#include "nai_can_int.h"
#include "nai_can_cfg.h"
#include "naibrd_ether.h"
#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
void setupIDRConfiguration(CanConfig inputCANConfig, IDRConfig* inputIDRConfig, bool_t bGen4CanIDRCommands);
void InitCANRxIDRCommands(CanConfig inputCANConfig, IDRConfig* inputIDRConfig, bool_t bGen4CanIDRCommands);
void MakeCANRxReadRegsCommand(IDRConfig* inputIDRConfig, uint32_t boardAddress, int32_t moduleOffset, bool_t bGen4Ether, uint16_t startIndex);
void MakeCANRxReadRegsFIFOStatus(CanConfig inputCANConfig, IDRConfig* inputIDRConfig, uint32_t boardAddress, int32_t moduleOffset, bool_t bGen4Ether, uint16_t startIndex);
void MakeCANRxWriteRegsCommand(CanConfig inputCANConfig, IDRConfig* inputIDRConfig, uint32_t boardAddress, int32_t moduleOffset, bool_t bGen4Ether, uint16_t startIndex);
void MakeCANRxReadFIFOCommand(CanConfig inputCANConfig, IDRConfig* inputIDRConfig, uint32_t boardAddress, int32_t moduleOffset, bool_t bGen4Ether, uint16_t startIndex);
void HandleCANEtherInterrupt(uint16_t msglen, uint8_t msg[], uint16_t tdr_idr_id);
void getInterruptDataEthernet(uint8_t msg[], uint16_t msglen, FIFO* fifo);
#endif