CAN Receive
Edit this on GitLab
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:
-
CAN Transmit Sample Application — configure a channel and transmit CAN frames
-
CAN Interrupt Basic Sample Application — handle CAN receive and error interrupts instead of polling
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
|
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 |
|---|---|
|
Zero-based index of the connected board. |
|
One-based module slot number. |
|
Module ID returned by |
|
Channel range for receive operations. Unlike the transmit sample, this application operates on a contiguous range of channels. |
|
Bit timing selection ( |
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
|
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
|
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:
-
getAllFIFOData()reads all pending frames from the receive FIFO for every channel in the range. Internally, it callsnaibrd_CAN_ReceiveFIFO()(or the J1939 equivalent) for each channel, copying received frame data into the correspondingfifoData[]buffer. -
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
|
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. |
|
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;
}