Integrator Resources

The official home for NAI Support

Not sure where to start? Try Quick Start Guide or ask a question below!

Toggle Components with Visual Button
JavaScript Form Processing

CAN Receive

CAN Receive Sample Application (SSK 1.x)

Overview

This sample application demonstrates how to configure one or more CAN channels for reception and read incoming CAN frames from the receive FIFO using the NAI Software Support Kit (SSK 1.x). It serves as a practical API reference for building your own CAN receive functionality with NAI embedded function modules.

Unlike the CAN Transmit sample, which operates on a single channel, this application supports multi-channel reception. You select a contiguous range of channels, and the application configures all of them for receive, allocates a FIFO buffer for each, and reads incoming frames from every channel in the range on each polling cycle. This makes it a useful starting point for applications that need to monitor multiple CAN buses simultaneously.

Both CAN A/B (standard/extended) and SAE J1939 protocols are supported. The protocol determines the frame structure, maximum payload size, and whether address claiming is required before reception.

Supported modules: CB1, CB2, CB3, CB4, CB5, CB6

Related samples:

Prerequisites:

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

  • SSK 1.x built and ready to run

  • A CAN bus with at least one transmitting node and proper 120-ohm termination at each end of the bus

How to run:

Launch the CAN_Receive executable from your build output directory. On the first run the board menu appears so you can configure your connection. Subsequent runs skip the menu if a saved configuration file exists. Once connected, the application prompts you for a channel range, protocol (on dual-protocol modules), and baud rate. It then configures the channels for receive and enters a polling loop where you press Enter to read all accumulated FIFO data from the selected channels.

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.

When the application starts, it calls naiapp_RunBoardMenu() with the configuration file default_CANRx.txt. This file does not ship with the SSK — it is created when you save your connection settings from the board menu. On the first run the board menu always appears. On subsequent runs, if the saved configuration file exists, the menu is skipped and the saved settings are loaded automatically.

After the board connection is established, you are prompted to select a card index and module number. The application verifies that the selected module is a CAN module by calling naibrd_CAN_GetChannelCount(). If the channel count is zero, the module is rejected.

inputCANConfig.modid = naibrd_GetModuleID(inputCANConfig.cardIndex, inputCANConfig.module);
channelCount = naibrd_CAN_GetChannelCount(inputCANConfig.modid);

if ((channelCount != 0))
{
   Run_CAN_Receive();
}

In your own application, use naibrd_GetModuleID() and naibrd_CAN_GetChannelCount() to confirm that a module slot contains a supported CAN module before attempting any CAN operations.

Important

Common Connection Errors

  • No board found — verify physical connection and that the correct connection type (PCI, Ethernet, etc.) is configured in the board menu.

  • Connection timeout — check network settings and confirm the board IP address is reachable.

  • Invalid card/module index — the card index is zero-based; the module number is one-based. Ensure your values match the hardware layout.

  • Module not present — naibrd_CAN_GetChannelCount() returns 0 if the slot does not contain a CAN module.

Program Structure

Entry Point

The application entry point is main() (or Run_CAN_Receive_Main() on VxWorks). It initializes the CAN configuration defaults and enters the board selection loop.

initializeCANConfigurations(0, 0, 0, 0, 0, 0, MAX_CAN_PAYLOAD_AB, 8, NAI_CAN_500K_BAUD);

initializeCANConfigurations() populates the global inputCANConfig structure with safe defaults: CAN A/B maximum payload (MAX_CAN_PAYLOAD_AB), minimum payload of 8 bytes, and a 500 Kbaud bit rate. In your own code, set these values to match your system requirements before calling any channel configuration functions.

Application Parameters

The application tracks all configuration state in a global CanConfig structure:

Field Description

cardIndex

Zero-based index of the connected board.

module

One-based module slot number.

modid

Module ID returned by naibrd_GetModuleID(). Used to determine protocol support and channel count.

minChannel / maxChannel

Channel range for receive operations. Unlike the transmit sample, this application operates on a contiguous range of channels.

baudRate

Bit timing selection (NAI_CAN_250K_BAUD, NAI_CAN_500K_BAUD, or NAI_CAN_1M_BAUD).

Your application will need to track the same values. The menu system is a sample convenience — in your own code, set these fields directly and call the API functions.

Channel Range and Protocol Configuration

After board and module selection, you choose which CAN channels to receive on. Unlike the transmit sample, which operates on a single channel, this application prompts for a channel range.

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

bQuit = naiapp_query_ForChannelRange(&inputCANConfig.minChannel,
                                      &inputCANConfig.maxChannel,
                                      minChannel, maxChannel);

To receive on a single channel, specify the same value for both the minimum and maximum channel. To monitor all channels, select the full range from 1 to the module’s channel count.

Protocol Selection

The protocol depends on the module type:

Module Protocol Behavior

CB1

CAN A/B only. No protocol prompt.

CB2

J1939 only. No protocol prompt.

CB3

Dual-protocol. The application prompts you to select J1939 or CAN A/B, then calls the appropriate configuration utility for the entire channel range.

CB4, CB5

Protocol determined by module firmware configuration.

CB6

Dual-protocol with physical line break detection. Same behavior as CB3.

On CB3 modules, the protocol is applied to the entire channel range using shared utility functions:

if (inputCANConfig.modid == NAI_MODULE_ID_CB3)
{
   bQuit = QueryForChannelProtocol(&isJ1939);
   if (isJ1939 && !bQuit)
   {
      setChannelToCANJ1939_R(inputCANConfig.cardIndex, inputCANConfig.module,
                              inputCANConfig.modid,
                              inputCANConfig.minChannel, inputCANConfig.maxChannel);
   }
   else if (!bQuit)
   {
      setChannelToCANAB_R(inputCANConfig.cardIndex, inputCANConfig.module,
                           inputCANConfig.modid,
                           inputCANConfig.minChannel, inputCANConfig.maxChannel);
   }
}

setChannelToCANJ1939_R() and setChannelToCANAB_R() are shared CAN utilities that call naibrd_CAN_SetProtocol() for each channel in the range, configuring them for receive in the selected protocol mode. In your own application, call naibrd_CAN_SetProtocol() directly for each channel you want to configure.

Important

Common Errors

  • NAI_ERROR_NOT_SUPPORTED — the selected module does not support the requested protocol. Verify the module type before calling naibrd_CAN_SetProtocol().

  • Channel count is zero — the module in the selected slot is not a CAN module. Re-check your module number.

Baud Rate Configuration

After protocol selection, the application prompts for a baud rate via QueryUserForCANTiming(). The supported rates are:

  • NAI_CAN_250K_BAUD — 250 Kbaud

  • NAI_CAN_500K_BAUD — 500 Kbaud (default)

  • NAI_CAN_1M_BAUD — 1 Mbaud

The baud rate is stored in inputCANConfig.baudRate and applied when Cfg_Rx_CAN() configures the channels. Internally, Cfg_Rx_CAN() translates the baud rate selection into bit timing parameters (prescaler, SJW, TSEG1, TSEG2) and calls naibrd_CAN_SetBitTiming() for each channel in the range:

getCANTimingParameters(inputCANConfig.baudRate, &prescaler, &sjw, &tseg1, &tseg2);
naibrd_CAN_SetBitTiming(cardIndex, module, channel, prescaler, sjw, tseg1, tseg2);
naibrd_CAN_SetRxEnable(cardIndex, module, channel, TRUE);

To configure CAN bit timing in your own application, call naibrd_CAN_SetBitTiming() directly with the appropriate timing values for your target baud rate. All nodes on the CAN bus must use the same baud rate. Consult your module’s manual for supported baud rates and timing parameter details.

Important

Common Errors

  • No frames received — both ends of the CAN bus must be configured to the same baud rate. Mismatched timing causes all frames to be rejected at the physical layer.

  • Bus error indicators — incorrect timing parameters can put the CAN controller into an error state. Verify prescaler, SJW, TSEG1, and TSEG2 values against your module’s manual.

Receive Channel Configuration

Once all parameters are collected, the application configures the selected channels for reception.

Configuring Channels for Receive

Cfg_Rx_CAN(inputCANConfig);

Cfg_Rx_CAN() is a shared utility that configures bit timing and enables the receive path for every channel in the minChannel to maxChannel range. Internally, it calls:

  • naibrd_CAN_SetBitTiming() — sets the baud rate timing parameters for each channel

  • naibrd_CAN_SetRxEnable() — enables the receive FIFO for each channel

In your own application, call these two functions for each channel you want to receive on.

J1939 Address Claiming

If the channels are configured for J1939, the application performs the address claiming procedure before entering the receive loop:

if (IsCAN_J1939(inputCANConfig.cardIndex, inputCANConfig.module, inputCANConfig.minChannel))
{
   nai_msDelay(250);
   claimAddresses(inputCANConfig.cardIndex, inputCANConfig.module,
                   inputCANConfig.minChannel, inputCANConfig.maxChannel);
}

claimAddresses() calls naibrd_CAN_SetAddress() for each channel in the range to initiate the J1939 address claim process. It waits up to several seconds for each address to be successfully claimed, polling with naibrd_CAN_GetAddress().

Address claiming is mandatory for J1939 communication. Without a claimed source address, the CAN controller will not participate in J1939 bus traffic.

Enabling Transmit

Even though this is a receive application, the transmit path is also enabled:

setTxEnable(inputCANConfig.cardIndex, inputCANConfig.module,
            inputCANConfig.minChannel, inputCANConfig.maxChannel, TRUE);

This is required for J1939 address claiming, which involves transmitting address claim messages onto the bus. For CAN A/B, enabling transmit is not strictly necessary for reception, but the sample enables it so that address claiming works correctly on dual-protocol modules.

Receive Loop and FIFO Reading

This is the core of the application. After channel configuration, the application allocates FIFO buffers, then enters a polling loop that reads received frames on demand.

FIFO Allocation

The application allocates a FIFO buffer for each channel in the range:

for (channel = inputCANConfig.minChannel; channel <= inputCANConfig.maxChannel; channel++)
{
   fifoData[channel - 1] = allocateSpaceForFIFO(1, 1785);
}

allocateSpaceForFIFO() dynamically allocates a FIFO structure containing an array of CanDataFrame buffers. Each buffer is allocated with a maximum data size of 1785 bytes, which accommodates the largest possible J1939 transport protocol message. For CAN A/B applications where the maximum payload is 8 bytes, this allocation is larger than necessary but harmless.

In your own application, size the FIFO allocation to match the protocol you are using. For CAN A/B, MAX_CAN_PAYLOAD_AB (8 bytes) is sufficient.

Polling for Received Data

The application enters a loop that reads all channel FIFOs each time you press Enter:

while (bContinue)
{
   printf("\nPress Enter to Read Channel Fifos...Or Press Q to quit:\n");
   bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR,
                                         inputBuffer, &inputResponseCnt);
   if (!bQuit)
   {
      getAllFIFOData(inputCANConfig.cardIndex, inputCANConfig.module, fifoData,
                     inputCANConfig.minChannel, inputCANConfig.maxChannel);
      printAllFIFOData(fifoData, stdout,
                       inputCANConfig.minChannel, inputCANConfig.maxChannel);
   }
   else
   {
      bContinue = FALSE;
   }
}

On each iteration:

  1. getAllFIFOData() reads all pending frames from the receive FIFO for every channel in the range. Internally, it calls naibrd_CAN_ReceiveFIFO() (or the J1939 equivalent) for each channel, copying received frame data into the corresponding fifoData[] buffer.

  2. printAllFIFOData() displays the received data for all channels to the console, showing the frame contents and payload length for each received frame.

This is a polling model — the application only reads the FIFO when you press Enter. Frames that arrive between polling cycles accumulate in the hardware FIFO. If you need event-driven reception (reading frames as soon as they arrive), see the CAN Interrupt Basic sample, which uses hardware interrupts to trigger reads.

Cleanup

When you quit the receive loop, the application deallocates the FIFO buffers:

for (channel = inputCANConfig.minChannel; channel <= inputCANConfig.maxChannel; channel++)
{
   deallocSpaceForFIFO(fifoData[channel - 1]);
}

In your own application, always free FIFO buffers when you are done receiving to avoid memory leaks.

Important

Common Errors

  • No data received — verify that the transmitting node is sending on the same bus, at the same baud rate, and using the same protocol (CAN A/B or J1939). Check bus wiring and termination.

  • FIFO overflow — if the transmitter sends frames faster than the application polls, the hardware FIFO can overflow and frames will be lost. Increase your polling frequency or use interrupt-driven reception.

  • J1939 address claim failure — if claimAddresses() times out, the source address was not successfully claimed. Another device on the bus may already hold that address, or the bus may not be properly terminated. Verify bus wiring, termination resistors, and that no address conflicts exist.

  • Stale data in FIFO — frames from a previous session may remain in the FIFO if the channel was not reset. The first read after configuration may include old data. Read and discard the initial FIFO contents if your application requires a clean start.

Troubleshooting Reference

Note

This section summarizes errors covered in the preceding sections. Consult your module’s manual for hardware-specific diagnostics and error register definitions.

Error / Symptom Possible Causes Suggested Resolution

No board found

Physical connection issue; incorrect connection type in board menu.

Verify cabling and connection type (PCI, Ethernet, etc.). Re-run the board menu to reconfigure.

Module not recognized

Selected slot does not contain a CAN module; wrong module number.

Verify hardware layout and use the correct one-based module number.

No frames received

Baud rate mismatch; transmitter not active; bus not terminated; protocol mismatch (CAN A/B vs. J1939).

Ensure all nodes use the same baud rate and protocol. Verify 120-ohm termination at each end of the bus. Confirm the transmitter is sending.

J1939 address claim timeout

Address conflict on the bus; bus not terminated; no other J1939 nodes responding.

Verify bus termination (120 ohm at each end). Check for address conflicts. Ensure at least one other J1939 node is on the bus.

FIFO overflow / missing frames

Transmit rate exceeds polling rate; application not reading FIFO frequently enough.

Increase polling frequency. Consider using interrupt-driven reception (see CAN Interrupt Basic).

Stale data on first read

Hardware FIFO contains frames from a previous session.

Read and discard the first FIFO read after channel configuration, or reset the channel before enabling receive.

NAI_ERROR_NOT_SUPPORTED

Protocol not supported by the installed module type.

Verify the module type supports the requested protocol (CAN A/B or J1939). See the protocol selection table above.

CAN controller in error state

Incorrect bit timing parameters; bus wiring issues; excessive noise.

Verify bit timing values against the module manual. Check bus wiring and termination. Inspect error counters.

Full Source

Full Source — CAN_Receive.c (SSK 1.x)
/**************************************************************************************************************/
/**
<summary>
This sample application lets you receive CAN messages on a range of channels.

</summary>
*/
/**************************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* Common Sample Program include files */
#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_cfg.h"

/* naibrd include files */
#include "nai.h"
#include "naibrd.h"
#include "functions/naibrd_can.h"
#include "naibrd_ether.h"
#include "advanced/nai_ether_adv.h"

/* Extern Functions or Variables*/
extern CanConfig inputCANConfig;

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

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

/********************************/
/* Internal Function Prototypes */
/********************************/

static bool_t Run_CAN_Receive(void);
int32_t Run_CAN_Receive_Main(void);

/**************************************************************************************************************/
/**
<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 Run_CAN_Receive_Main(void)
#else
int32_t main(void)
#endif
{
   bool_t stop = FALSE;
   int32_t moduleCnt;
   int32_t channelCount;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   initializeCANConfigurations(0, 0, 0, 0, 0, 0, MAX_CAN_PAYLOAD_AB, 8, NAI_CAN_500K_BAUD);

   if (naiapp_RunBoardMenu(CONFIG_FILE) == (bool_t)TRUE)
   {
      while (stop != TRUE)
      {
         /* Select Card Index */
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &inputCANConfig.cardIndex);
         if (stop != TRUE)
         {
            check_status(naibrd_GetModuleCount(inputCANConfig.cardIndex, &moduleCnt));

            /* Select Module */
            stop = naiapp_query_ModuleNumber(moduleCnt, 1, &inputCANConfig.module);
            if (stop != TRUE)
            {
               inputCANConfig.modid = naibrd_GetModuleID(inputCANConfig.cardIndex, inputCANConfig.module);
               channelCount = naibrd_CAN_GetChannelCount(inputCANConfig.modid);

               if ((channelCount != 0))
               {
                  Run_CAN_Receive();
               }
               else
               {
                  printf(" *** Module selection not recognized as valid module type for this application. ***\n\n");
               }
            }

            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>
CAN_Receive lets you receive CAN messages on a range of channels.

</summary>
*/
/**************************************************************************************************************/
static bool_t Run_CAN_Receive(void)
{
   FIFO* fifoData[NAI_GEN5_CAN_MAX_CHANNEL_COUNT];
   bool_t bContinue;
   int32_t maxChannel;
   int32_t minChannel;
   bool_t isJ1939 = TRUE;
   bool_t bQuit = FALSE;
   int32_t channel;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

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

   bQuit = naiapp_query_ForChannelRange(&inputCANConfig.minChannel ,&inputCANConfig.maxChannel, minChannel, maxChannel);

   if(!bQuit)
   {
      if( inputCANConfig.modid == NAI_MODULE_ID_CB3 )
      {
         bQuit = QueryForChannelProtocol(&isJ1939);
         if(isJ1939 && !bQuit)
         {
            setChannelToCANJ1939_R(inputCANConfig.cardIndex,inputCANConfig.module,inputCANConfig.modid,inputCANConfig.minChannel, inputCANConfig.maxChannel);
         }
         else if(!bQuit)
         {
            setChannelToCANAB_R(inputCANConfig.cardIndex,inputCANConfig.module,inputCANConfig.modid,inputCANConfig.minChannel, inputCANConfig.maxChannel);
         }
      }
      else if(inputCANConfig.modid == NAI_MODULE_ID_CB2)
         isJ1939 = TRUE;
      else if(inputCANConfig.modid == NAI_MODULE_ID_CB1)
         isJ1939 = FALSE;

   }

   if(!bQuit)
   {
      bQuit = QueryUserForCANTiming(&inputCANConfig.baudRate,isJ1939);
   }

   if (!bQuit)
   {
      bContinue = TRUE;

      /* Configure channel to Rx */

      Cfg_Rx_CAN(inputCANConfig);

      if(IsCAN_J1939(inputCANConfig.cardIndex, inputCANConfig.module, inputCANConfig.minChannel))
      {
         printf("\n");
         nai_msDelay(250);
         claimAddresses(inputCANConfig.cardIndex,inputCANConfig.module, inputCANConfig.minChannel, inputCANConfig.maxChannel);
      }

      /* Configure channel to Tx (Needed by other devices wishing to claim addresses) */
      setTxEnable(inputCANConfig.cardIndex,inputCANConfig.module,inputCANConfig.minChannel , inputCANConfig.maxChannel ,TRUE);

      /* allocate buffer to hold fifo data received */
      for(channel = inputCANConfig.minChannel; channel <= inputCANConfig.maxChannel;channel++)
      {
         fifoData[channel - 1] = allocateSpaceForFIFO(1,1785);
      }

      while (bContinue)
      {

         printf("\nPress Enter to Read Channel Fifos...Or Press Q to quit:\n");
         bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
         if (!bQuit)
         {
            getAllFIFOData(inputCANConfig.cardIndex,inputCANConfig.module,fifoData,inputCANConfig.minChannel,inputCANConfig.maxChannel);
            printf("fifo DATA Received\n");
            printf("-----------------------");
            printf("\n");
            printAllFIFOData(fifoData,stdout,inputCANConfig.minChannel,inputCANConfig.maxChannel);
            printf("\n");
            printf("\n");
         }
         else
         {
            bContinue = FALSE;
         }
      }

      for(channel = inputCANConfig.minChannel; channel <= inputCANConfig.maxChannel;channel++)
      {
         deallocSpaceForFIFO(fifoData[channel -1]);
      }

   }

   return bQuit;
}

Help Bot

X