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 Recv

CAN Recv Sample Application (SSK 2.x)

Overview

The CAN Recv sample application demonstrates how to receive CAN messages on a selected channel using the NAI Software Support Kit (SSK 2.x). It serves as a practical API reference for building your own CAN receive functionality with NAI embedded function modules.

The application supports both CAN A/B (standard CAN 2.0) and SAE J1939 protocols. The protocol determines the frame structure, maximum payload size, and whether address claiming is required before reception. After querying the user for the channel, protocol (on dual-protocol modules), and baud rate timing, it configures the channel for reception and enters a polling loop that reads frames from the hardware FIFO on demand.

Supported modules: CB1, CB2, CB3, CB6, CB8

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.

CB6

Dual-protocol. Same behavior as CB3.

CB8

CAN A/B protocol support. The application uses standard CAN A/B framing on this module type.

For the SSK 1.x version, see CAN Receive (SSK 1.x).

Related samples:

Prerequisites

Before running this sample, make sure you have:

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

  • SSK 2.x installed on your development host.

  • The sample applications built. Refer to the SSK 2.x Software Development Guide for platform-specific build instructions.

  • A CAN bus with at least one transmitting node (use the CAN Xmit sample or external equipment) and proper 120-ohm termination at each end of the bus.

How to Run

Launch the can_recv executable from your build output directory. On startup the application looks for a 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. Once connected, the application queries for a channel number, protocol (on dual-protocol modules), and baud rate timing, then enters the receive loop.

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

#if defined (NAIBSP_CONFIG_SOFTWARE_OS_VXWORKS)
int32_t CAN_Receive(void)
#else
int32_t main(void)
#endif
{
   bool_t stop = NAI_FALSE;
   int32_t moduleCount;
   int32_t channelCount, cardIndex, module;
   uint32_t modId;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   if (naiapp_RunBoardMenu(DEF_CONFIG_FILE) == (bool_t)NAI_TRUE)
   {
      while (stop != NAI_TRUE)
      {
         /* Select Card Index */
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         if (stop != NAI_TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCount));

            /* Select Module */
            stop = naiapp_query_ModuleNumber(moduleCount, 1, &module);
            if (stop != NAI_TRUE)
            {
               check_status(naibrd_GetModuleName(cardIndex, module, &modId));

               channelCount = naibrd_CAN_GetChannelCount(modId);

               if ((channelCount != 0))
               {
                  naiapp_Run_CAN_Receive(cardIndex, module, modId);
               }
               else
               {
                  naiif_printf(" *** Module selection not recognized as valid module type for this application. ***\r\n\r\n");
               }
            }

            naiif_printf("\r\nType Q to quit or Enter key to restart application:\r\n");
            stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
         }
      }
   }

   naiif_printf("\r\nType the Enter key to exit the program: ");
   naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);

   naiapp_access_CloseAllOpenCards();

   return 0;
}

In your own application, use naibrd_GetModuleName() 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() on most platforms, or CAN_Receive() on VxWorks. After the board connection and module selection, it calls naiapp_Run_CAN_Receive(), which handles all CAN-specific configuration and data reception.

Application Flow

  1. Query for a channel number.

  2. Determine the protocol based on module type (J1939, CAN A/B, or user-selected).

  3. Configure the channel for the selected protocol.

  4. Query for baud rate timing.

  5. For J1939, claim addresses if applicable.

  6. Configure the channel for reception.

  7. Enter a receive loop that reads FIFO frames on user command.

Channel and Protocol Configuration

After board and module selection, you choose which CAN channel to receive on. The application queries for a single channel number within the valid range for the installed module.

Protocol Selection by Module Type

The protocol is determined automatically for fixed-protocol modules. On dual-protocol modules (CB3, CB6), the user is prompted to choose.

if (modId == NAIBRD_MODULE_ID_CB3 || modId == NAIBRD_MODULE_ID_CB6)
{
   /* query for protocol - for J1939 capable modules only */
   bQuit = naiapp_QueryForChannelProtocol(modId, &isJ1939);
   if (isJ1939 && !bQuit)
   {
      naiapp_setChannelToCANJ1939_R(cardIndex, module, modId, channel);
   }
   else if (!bQuit)
   {
      naiapp_setChannelToCANAB_R(cardIndex, module, modId, channel);
   }
}
else if (modId == NAIBRD_MODULE_ID_CB2)
{
   isJ1939 = NAI_TRUE;
}
else if (modId == NAIBRD_MODULE_ID_CB1)
{
   isJ1939 = NAI_FALSE;
}

The protocol selection logic works as follows:

  • CB1 — CAN A/B only. The isJ1939 flag is set to NAI_FALSE without prompting the user. CB1 modules support standard CAN 2.0A/2.0B framing with a maximum payload of 8 bytes per frame.

  • CB2 — J1939 only. The isJ1939 flag is set to NAI_TRUE without prompting. CB2 modules use SAE J1939 protocol with extended 29-bit identifiers and transport protocol support for payloads larger than 8 bytes.

  • CB3, CB6 — Dual-protocol modules. naiapp_QueryForChannelProtocol() asks the user to select J1939 or CAN A/B. Based on the selection, either naiapp_setChannelToCANJ1939_R() or naiapp_setChannelToCANAB_R() is called to configure the channel protocol mode via naibrd_CAN_SetProtocol().

  • CB8 — The CB8 module supports CAN A/B protocol. When CB8 is detected, the application does not enter the CB3/CB6 dual-protocol branch, and the default protocol handling applies for standard CAN framing.

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 naiapp_QueryUserForCANTiming(). The baud rate is stored and applied when naiapp_Cfg_Rx_CAN() configures the channel. Internally, the configuration utility translates the baud rate selection into bit timing parameters (prescaler, SJW, TSEG1, TSEG2) and calls naibrd_CAN_SetBitTiming() for the channel:

bQuit = naiapp_QueryUserForCANTiming(&baudRate, isJ1939);

The supported rates depend on the protocol:

  • CAN A/B — 250 Kbaud, 500 Kbaud (default), or 1 Mbaud.

  • J1939 — 250 Kbaud (standard J1939 rate) or 500 Kbaud.

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.

J1939 Address Claiming

If the channel is configured for J1939, the application performs the address claiming procedure before entering the receive loop. This is done inside a conditional compilation block that is active when J1939-capable module support is compiled into the build:

#if defined (NAIBRD_SSK_MODULE_ID_CB2) || defined (NAIBRD_SSK_MODULE_ID_CB3) || defined (NAIBRD_SSK_MODULE_ID_CB6)
   if (naiapp_IsCAN_J1939(cardIndex, module, channel))
   {
      naiif_printf("\r\n");
      naibrd_msDelay(250);
      naiapp_claimAddresses(cardIndex, module, channel);
   }
#endif

naiapp_claimAddresses() calls naibrd_CAN_SetAddress() for the channel to initiate the J1939 address claim process. It then polls with naibrd_CAN_GetAddress() waiting for the address to be successfully claimed. The 250 ms delay before the call gives the CAN controller time to complete its initialization.

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

Note
The #if defined guard means this code is only compiled when the build includes support for CB2, CB3, or CB6 modules. If your build does not define these macros, J1939 address claiming will be omitted. Refer to the SSK 2.x Software Development Guide for build configuration details.

Receive Channel Configuration

After all parameters are collected and J1939 addressing is complete (if applicable), the application configures the channel for reception:

naiapp_Cfg_Rx_CAN(cardIndex, module, channel, baudRate);

naiapp_Cfg_Rx_CAN() is a shared utility that configures bit timing and enables the receive path for the selected channel. Internally, it calls:

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

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

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

Receive Loop and FIFO Reading

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

FIFO Data Structure

The application uses a fifoData array to hold received frame data. Each element corresponds to a channel and contains:

  • numOfFramesOnFifo — the count of frames currently in the hardware FIFO.

  • data[] — an array of frame buffers. Each frame buffer has separate fields for J1939 data (dataJ1939) and CAN A/B data (dataAB), reflecting the different payload structures of the two protocols.

Frame Buffer Clearing with memset()

Before reading new data, the application clears the frame buffers to ensure no stale data remains from a previous read:

for (frmIdx = 0; frmIdx < fifoData[channel - 1].numOfFramesOnFifo; frmIdx++)
{
   if (isJ1939)
      memset(fifoData[channel - 1].data[frmIdx].dataJ1939, 0, sizeof(fifoData[channel - 1].data[frmIdx].dataJ1939));
   else
      memset(fifoData[channel - 1].data[frmIdx].dataAB, 0, sizeof(fifoData[channel - 1].data[frmIdx].dataAB));
}

The memset() call zeros out the protocol-specific portion of each frame buffer. This is important because:

  • The number of frames in the current read may differ from the previous read. Without clearing, residual data from a longer previous read could appear as valid frame content.

  • J1939 and CAN A/B frames have different payload sizes. Clearing the correct field (dataJ1939 vs. dataAB) based on the active protocol prevents misinterpretation.

Polling for Received Data

The application enters a loop that reads the channel FIFO each time you press Enter:

while (bContinue)
{
   naiif_printf("\r\nPress Enter to Read Channel Fifos...Or Press Q to quit:\r\n");
   bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
   if (!bQuit)
   {
      int frmIdx;
      check_status(naibrd_CAN_GetRxFIFOFrameCount(cardIndex, module, channel, &outcount));

      fifoData[channel - 1].numOfFramesOnFifo = outcount;

      /* Clear frame buffers before reading */
      for (frmIdx = 0; frmIdx < fifoData[channel - 1].numOfFramesOnFifo; frmIdx++)
      {
         if (isJ1939)
            memset(fifoData[channel - 1].data[frmIdx].dataJ1939, 0, sizeof(fifoData[channel - 1].data[frmIdx].dataJ1939));
         else
            memset(fifoData[channel - 1].data[frmIdx].dataAB, 0, sizeof(fifoData[channel - 1].data[frmIdx].dataAB));
      }

      naiapp_getFIFOChannelData(cardIndex, module, channel, fifoData);

      naiif_printf("fifo DATA Received\r\n");
      naiif_printf("-----------------------");
      naiif_printf("\r\n");
      naiapp_printFIFOChannelData(channel, fifoData, isJ1939);

      naiif_printf("\r\n");
      naiif_printf("\r\n");
   }
   else
   {
      bContinue = NAI_FALSE;
   }
}

On each iteration:

  1. naibrd_CAN_GetRxFIFOFrameCount() queries the hardware for the number of frames waiting in the receive FIFO.

  2. The frame buffers are cleared via memset() (as described above).

  3. naiapp_getFIFOChannelData() reads all pending frames from the receive FIFO for the channel. Internally, it calls the appropriate naibrd CAN receive function for each frame, copying received frame data into the fifoData[] buffer.

  4. naiapp_printFIFOChannelData() displays the received data to the console, showing the frame contents and payload in the appropriate format for J1939 or CAN A/B.

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 an interrupt-based CAN sample.

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

Troubleshooting Reference

Error / Symptom Possible Causes Suggested Resolution

No board found or connection timeout

Board not powered, incorrect configuration file

Verify hardware is powered and connected.

Module selection not recognized

Not a CAN module (CB1-CB3, CB6, CB8)

Verify the module type supports CAN.

No frames received

Transmitter not running, cable not connected, baud rate mismatch

Ensure the CAN Xmit sample is running. Verify cabling and baud rate.

Protocol mismatch

Transmitter using different protocol (J1939 vs CAN A/B)

Ensure both endpoints use the same protocol.

J1939 address claim fails

No other J1939 nodes on the bus, address conflict

J1939 address claiming requires proper bus termination. Check for address conflicts.

FIFO overflow / missing frames

Transmit rate exceeds polling rate

Increase polling frequency or use interrupt-driven reception.

NAI_ERROR_NOT_SUPPORTED

Protocol not supported by the installed module type

Verify the module type supports the requested protocol. See the protocol selection table above.

CAN controller in error state

Incorrect bit timing parameters, bus wiring issues

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

Full Source

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

/* nailib include files */
#include "nai_libs/nailib/include/naitypes.h"
#include "nai_libs/nailib/include/nailib.h"
#include "nai_libs/nailib/include/nailib_utils.h"

/* naibrd include files */
#include "nai_libs/naibrd/include/naibrd.h"
#include "nai_libs/naibrd/include/functions/naibrd_can.h"

/* naiif include files */
#include "nai_libs/naiif/include/naiif_stdio.h"

/* Common Sample Program include files */
#include "nai_sample_apps/naiapp_common/include/naiapp_boardaccess_menu.h"
#include "nai_sample_apps/naiapp_common/include/naiapp_boardaccess_query.h"
#include "nai_sample_apps/naiapp_common/include/naiapp_boardaccess_access.h"
#include "nai_sample_apps/naiapp_common/include/naiapp_boardaccess_display.h"
#include "nai_sample_apps/naiapp_common/include/naiapp_boardaccess_utils.h"

/* Common CAN Sample Program include files */
#include "../../can_common_utils/can_common_utils.h"

/**********************/
/* Application Name   */
/**********************/
static const int8_t *DEF_CONFIG_FILE = (int8_t *)"default_CANRx.txt";

/********************************/
/* Internal Function Prototypes */
/********************************/
static bool_t naiapp_Run_CAN_Receive(int32_t cardIndex, int32_t module, uint32_t modId);

/**************************************************************************************************************/
/**
* <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 (NAIBSP_CONFIG_SOFTWARE_OS_VXWORKS)
int32_t CAN_Receive(void)
#else
int32_t main(void)
#endif
{
   bool_t stop = NAI_FALSE;
   int32_t moduleCount;
   int32_t channelCount, cardIndex, module;
   uint32_t modId;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   if (naiapp_RunBoardMenu(DEF_CONFIG_FILE) == (bool_t)NAI_TRUE)
   {
      while (stop != NAI_TRUE)
      {
         /* Select Card Index */
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         if (stop != NAI_TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCount));

            /* Select Module */
            stop = naiapp_query_ModuleNumber(moduleCount, 1, &module);
            if (stop != NAI_TRUE)
            {
               check_status(naibrd_GetModuleName(cardIndex, module, &modId));

               channelCount = naibrd_CAN_GetChannelCount(modId);

               if ((channelCount != 0))
               {
                  naiapp_Run_CAN_Receive(cardIndex, module, modId);
               }
               else
               {
                  naiif_printf(" *** Module selection not recognized as valid module type for this application. ***\r\n\r\n");
               }
            }

            naiif_printf("\r\nType Q to quit or Enter key to restart application:\r\n");
            stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
         }
      }
   }

   naiif_printf("\r\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 naiapp_Run_CAN_Receive(int32_t cardIndex, int32_t module, uint32_t modId)
{
   int32_t maxChannel;
   int32_t minChannel;
   bool_t bContinue;
   bool_t isJ1939 = NAI_TRUE;
   bool_t bQuit = NAI_FALSE;
   int32_t channel;
   int32_t outcount;
   naibrd_can_baud_rate_type_t baudRate = NAIBRD_CAN_BAUD_UNASSIGNED;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   minChannel = 1;
   maxChannel = naibrd_CAN_GetChannelCount(modId);

   bQuit = naiapp_query_ChannelNumber(maxChannel, minChannel, &channel);

   if (!bQuit)
   {
      if (modId == NAIBRD_MODULE_ID_CB3 || modId == NAIBRD_MODULE_ID_CB6)
      {
         /* query for protocol - for J1939 capable modules only, otherwise no query will occur, and isJ1939 will be set to NAI_FALSE */
         bQuit = naiapp_QueryForChannelProtocol(modId, &isJ1939);
         if (isJ1939 && !bQuit)
         {
            naiapp_setChannelToCANJ1939_R(cardIndex, module, modId, channel);
         }
         else if (!bQuit)
         {
            naiapp_setChannelToCANAB_R(cardIndex, module, modId, channel);
         }
      }
      else if (modId == NAIBRD_MODULE_ID_CB2)
      {
         isJ1939 = NAI_TRUE;
      }
      else if (modId == NAIBRD_MODULE_ID_CB1)
      {
         isJ1939 = NAI_FALSE;
      }
   }

   if (!bQuit)
   {
      bQuit = naiapp_QueryUserForCANTiming(&baudRate, isJ1939);
   }

   if (!bQuit)
   {
      bContinue = NAI_TRUE;

#if defined (NAIBRD_SSK_MODULE_ID_CB2) || defined (NAIBRD_SSK_MODULE_ID_CB3) || defined (NAIBRD_SSK_MODULE_ID_CB6)
      if (naiapp_IsCAN_J1939(cardIndex, module, channel))
      {
         naiif_printf("\r\n");
         naibrd_msDelay(250);
         naiapp_claimAddresses(cardIndex, module, channel);
      }
#endif
      naiapp_Cfg_Rx_CAN(cardIndex, module, channel, baudRate);

      while (bContinue)
      {
         naiif_printf("\r\nPress Enter to Read Channel Fifos...Or Press Q to quit:\r\n");
         bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
         if (!bQuit)
         {
            int frmIdx;
            check_status(naibrd_CAN_GetRxFIFOFrameCount(cardIndex, module, channel, &outcount));

            fifoData[channel - 1].numOfFramesOnFifo = outcount;

            for (frmIdx = 0; frmIdx < fifoData[channel - 1].numOfFramesOnFifo; frmIdx++)
            {
               if (isJ1939)
                  memset(fifoData[channel - 1].data[frmIdx].dataJ1939, 0, sizeof(fifoData[channel - 1].data[frmIdx].dataJ1939));
               else
                  memset(fifoData[channel - 1].data[frmIdx].dataAB, 0, sizeof(fifoData[channel - 1].data[frmIdx].dataAB));
            }

            naiapp_getFIFOChannelData(cardIndex, module, channel, fifoData);

            naiif_printf("fifo DATA Received\r\n");
            naiif_printf("-----------------------");
            naiif_printf("\r\n");
            naiapp_printFIFOChannelData(channel, fifoData, isJ1939);

            naiif_printf("\r\n");
            naiif_printf("\r\n");
         }
         else
         {
            bContinue = NAI_FALSE;
         }
      }
   }

   return bQuit;
}

Help Bot

X