AR CAN BasicOps
Edit this on GitLab
AR CAN BasicOps Sample Application (SSK 1.x)
Overview
The AR CAN BasicOps sample application demonstrates how to perform CAN bus operations on ARINC-429/CAN dual-protocol modules using the NAI Software Support Kit (SSK 1.x). The AR-CAN module provides CAN-FD (Flexible Data-rate) bus connectivity alongside ARINC-429, and this sample focuses on the CAN side. It covers the core CAN operations you will need in your own application: selecting the CAN protocol (CAN A/B or CAN FD), configuring baud rates, transmitting single messages (8-byte and 64-byte), receiving messages, scheduling periodic transmissions, configuring acceptance filters and filter masks, managing transmit and receive FIFOs, and monitoring message counts.
This sample supports AR-CAN module types that provide CAN-FD functionality. It serves as a practical API reference — each menu command maps directly to one or more naibrd_AR_CAN_*() API calls that you can lift into your own code. Consult your AR-CAN module’s manual for hardware-specific specifications.
Prerequisites
Before running this sample, make sure you have:
-
An NAI board with an AR-CAN module installed.
-
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.
-
A CAN bus connection (loopback cable or external device) for transmit/receive testing.
How to Run
Launch the AR_CAN_BasicOps executable from your build output directory. On startup the application looks for a configuration file (default_ARCAN.txt). On the first run, this file will not exist — the application will present an interactive board menu where you configure a board connection, card index, and module slot. You can save this configuration so that subsequent runs skip the menu and connect automatically. Once connected and a module is selected, the CAN operations menu appears.
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 AR-CAN. For details on board connection configuration, see the First Time Setup Guide. |
The main() function follows a standard SSK 1.x startup flow:
-
Call
naiapp_RunBoardMenu()to load a saved configuration file (if one exists) or present the interactive board menu. The configuration file (default_ARCAN.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 a card index with
naiapp_query_CardIndex(). -
Query for a module slot with
naiapp_query_ModuleNumber(). -
Enter the CAN command loop via
Run_CAN_DisplayCANChoices().
#if defined (__VXWORKS__)
int32_t AR_CAN_BasicOps(void)
#else
int32_t main(void)
#endif
{
bool_t stop = FALSE;
int32_t cardIndex;
int32_t moduleCnt;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
while (stop != TRUE)
{
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
userInput.nCardIdx = cardIndex;
if (stop != TRUE)
{
check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
g_moduleCount = moduleCnt;
stop = naiapp_query_ModuleNumber(moduleCnt, 1, &userInput.nModuleNumber);
if (stop != TRUE)
{
Run_CAN_DisplayCANChoices();
}
}
/* ... quit/restart prompt ... */
}
}
naiapp_access_CloseAllOpenCards();
return 0;
}
|
Important
|
Common connection errors you may encounter at this stage:
|
Program Structure
Entry Point
On standard platforms the entry point is main(). On VxWorks the entry point is AR_CAN_BasicOps():
#if defined (__VXWORKS__)
int32_t AR_CAN_BasicOps(void)
#else
int32_t main(void)
#endif
Application Parameters
The sample uses a global userInput struct to track the current card, module, channel, protocol, and baud rate settings:
static struct _userInput
{
int32_t nCardIdx;
int32_t nModuleNumber;
int32_t nChannelNumber;
int32_t nProtocolIndex;
int32_t nBaudBaseRate;
int32_t nBaudDataRate;
} userInput;
-
nCardIdx— identifies which board in a multi-board system. -
nModuleNumber— the slot number where the AR-CAN module is installed. -
nChannelNumber— the currently selected CAN channel (defaults to 1). -
nProtocolIndex— 0 for CAN A/B (standard), 1 for CAN FD. -
nBaudBaseRate— the base (arbitration) baud rate index. -
nBaudDataRate— the data phase baud rate index (CAN FD only).
Command Loop
Run_CAN_DisplayCANChoices() drives the interactive command loop. It displays the menu, reads the user’s selection, and dispatches to the matching handler:
| Command | Description |
|---|---|
1 |
Set channel number |
2 |
Set protocol (CAN A/B = 0, CAN FD = 1) |
3 |
Set baud rate (base and data rates) |
A |
Transmit a single 8-byte message |
B |
Transmit a single 64-byte CAN FD message |
C |
Receive a single message |
D |
Receive message pump (continuous receive) |
E |
Schedule a periodic message |
F |
Start the transmit scheduler |
G |
Stop the transmit scheduler |
I |
Set a receive acceptance filter |
J |
Get a receive acceptance filter |
K |
Set a filter mask |
L |
Get a filter mask |
M |
Remove a filter |
N |
Enable acceptance filters |
O |
Disable acceptance filters |
U |
Reset transmit FIFO |
V |
Reset receive FIFO |
W |
Enable transmit |
X |
Disable transmit |
Y |
Reset drop count |
Z |
Display FIFO counts and diagnostics |
The menu-driven structure is a convenience of the sample application. In your own application, you would call the same underlying naibrd_AR_CAN_*() API functions directly.
Channel and Protocol Configuration
Set Channel
To select which CAN channel to operate on in your own application, simply track the channel number as an integer. The sample stores it in userInput.nChannelNumber. Before performing any operation, the sample validates the channel number with naibrd_AR_CAN_GetChannelCountForModule():
if (chan < 1 || chan > naibrd_AR_CAN_GetChannelCountForModule(card, mod))
{
/* Invalid channel */
}
Set Protocol
To select between CAN A/B (standard, 8-byte payload max) and CAN FD (flexible data-rate, 64-byte payload max):
/* Protocol index: 0 = CAN A/B, 1 = CAN FD */
int32_t protocolIndex = 1; /* CAN FD */
The protocol selection affects the maximum payload size and the available data rates. CAN FD supports payloads up to 64 bytes and higher data-phase baud rates.
Set Baud Rate
To configure the base (arbitration) rate and data-phase rate for a channel, call naibrd_AR_CAN_SetBaudRate(). To read the current rates, call naibrd_AR_CAN_GetBaudRate():
naibrd_ar_can_low_rate_type_t origBaseRate;
naibrd_ar_can_high_rate_type_t origDataRate;
/* Read current baud rates */
naibrd_AR_CAN_GetBaudRate(cardIndex, module, channel, &origBaseRate, &origDataRate);
/* Set new baud rates */
naibrd_AR_CAN_SetBaudRate(cardIndex, module, channel,
(naibrd_ar_can_low_rate_type_t)baseRateIndex,
(naibrd_ar_can_high_rate_type_t)dataRateIndex);
Base rate options:
-
0 = 1000K
-
1 = 500K
-
3 = 250K
Data rate options:
-
0 = 2000K (default)
-
6 = 4000K
-
7 = 3000K
-
8 = 2000K
-
9 = 1000K
Both sides of the CAN bus must use the same baud rates. Consult your AR-CAN module’s manual for the complete list of supported rates.
|
Important
|
Common Errors
|
Message Transmit
Transmit a Single 8-Byte Message
To transmit a standard CAN message (up to 8 bytes), call naibrd_AR_CAN_QueueTransmit() to place the message in the transmit FIFO, then call naibrd_AR_CAN_SetTxEnable() to enable the transmitter:
uint8_t payload[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
uint32_t msgId = 0x111444;
bool_t isModeA = FALSE; /* Extended (29-bit) ID */
/* Queue the message */
status = naibrd_AR_CAN_QueueTransmit(cardIndex, module, channel,
(naibrd_ar_can_protocol_type_t)protocolIndex, isModeA,
msgId, &payload[0], STANDARD_CAN_MAX_PAYLOAD);
/* Enable transmit so the message goes out on the bus */
status = naibrd_AR_CAN_SetTxEnable(cardIndex, module, channel, TRUE);
-
protocolIndex— selects CAN A/B or CAN FD. -
isModeA—TRUEfor standard (11-bit) message ID,FALSEfor extended (29-bit) message ID. -
msgId— the CAN message identifier. -
STANDARD_CAN_MAX_PAYLOAD— 8 bytes for standard CAN.
Transmit a Single 64-Byte CAN FD Message
For CAN FD payloads up to 64 bytes, the call is the same but with the CAN FD protocol and a larger payload buffer:
uint8_t payload[64];
uint32_t msgId = 0x17778888;
/* Fill 64-byte payload */
for (i = 0; i < 64; i++)
payload[i] = (uint8_t)(i & 0xFF);
/* Queue with CAN FD protocol forced */
status = naibrd_AR_CAN_QueueTransmit(cardIndex, module, channel,
NAIBRD_AR_CAN_PROTOCOL_FD, FALSE, msgId, &payload[0], CAN_FD_MAX_PAYLOAD);
/* Enable transmit */
status = naibrd_AR_CAN_SetTxEnable(cardIndex, module, channel, TRUE);
|
Note
|
64-byte payloads require CAN FD protocol. If CAN A/B is selected, the maximum payload is 8 bytes. |
Enable/Disable Transmit
By default, each channel’s transmit capability is disabled. You must enable it before any message will leave the FIFO:
/* Enable transmit */
naibrd_AR_CAN_SetTxEnable(cardIndex, module, channel, TRUE);
/* Disable transmit (messages can still be queued but won't send) */
naibrd_AR_CAN_SetTxEnable(cardIndex, module, channel, FALSE);
Disabling transmit does not clear the FIFO — messages remain queued and will be sent when transmit is re-enabled.
|
Important
|
Common Errors
|
Message Receive
Receive a Single Message
To read a single message from the receive FIFO, first check whether a message is waiting, then call naibrd_AR_CAN_Receive():
uint8_t rxBuffer[80];
uint32_t msgId = 0;
uint32_t timestamp;
int32_t rxCount = 0;
bool_t msgWaiting = FALSE;
/* Check if a message is available */
naibrd_AR_CAN_GetRxFIFOMessageWaiting(cardIndex, module, channel, &msgWaiting);
if (msgWaiting)
{
/* Read the message */
naibrd_AR_CAN_Receive(cardIndex, module, channel, sizeof(rxBuffer),
&msgId, ×tamp, &rxBuffer[0], &rxCount);
}
-
msgId— the received message’s CAN identifier. -
timestamp— the hardware timestamp when the message was received. -
rxBuffer— the payload data. -
rxCount— the number of payload bytes received.
Receive Message Pump
The message pump continuously polls the receive FIFO and reads all available messages. In your own application, implement a polling loop or use interrupt-driven receive for better efficiency:
do
{
naibrd_AR_CAN_GetRxFIFOMessageWaiting(cardIndex, module, channel, &msgWaiting);
if (msgWaiting)
{
naibrd_AR_CAN_Receive(cardIndex, module, channel, bufferLen,
&msgId, ×tamp, &rxBuffer[0], &rxCount);
receivedMsgCount++;
}
} while (status == NAI_SUCCESS);
Scheduled Transmissions
Schedule a Periodic Message
To schedule a message for periodic transmission at a fixed interval, call naibrd_AR_CAN_ScheduleMsg():
uint8_t payload[64];
uint32_t msgId = 0x11112222;
int16_t entryIndex = 1; /* Schedule slot (1-based) */
int16_t rate = 5; /* Interval in milliseconds between transmissions */
int16_t skew = 5; /* Delay in milliseconds after scheduler starts */
naibrd_AR_CAN_ScheduleMsg(cardIndex, module, channel,
entryIndex, rate, skew, NAIBRD_AR_CAN_PROTOCOL_FD,
FALSE, msgId, &payload[0], CAN_FD_MAX_PAYLOAD);
-
entryIndex— the schedule slot to populate. Multiple schedules can coexist on one channel. -
rate— the interval in milliseconds between transmissions. -
skew— the delay after the scheduler starts before this schedule begins transmitting. By assigning different skew values to different schedules, you can stagger transmissions to reduce bus collisions.
Start and Stop the Scheduler
The scheduler does not start automatically after scheduling a message. You must explicitly start it:
/* Start the scheduler -- all configured schedules begin transmitting */
naibrd_AR_CAN_StartScheduler(cardIndex, module, channel);
/* Stop the scheduler -- all schedules halt */
naibrd_AR_CAN_StopScheduler(cardIndex, module, channel);
Stopping the scheduler does not remove configured schedules. Restarting the scheduler resumes all configured schedules.
Acceptance Filtering
Acceptance filters control which incoming CAN messages are placed on the receive FIFO. Filters are not enabled by default — all messages are received until filtering is explicitly enabled. Up to 4 filters can be assigned to each channel (zero-based index).
Set a Filter
To define an acceptance filter based on message ID and the first two payload bytes:
int32_t filterIndex = 0;
uint32_t msgId = 0x17778889;
bool_t isModeA = FALSE;
uint8_t firstTwoBytes[2] = {0x00, 0x00};
naibrd_AR_CAN_SetAcceptFilter(cardIndex, module, channel,
filterIndex, msgId, isModeA, &firstTwoBytes[0]);
Set a Filter Mask
The filter mask determines which bits of the incoming message are compared against the filter. A 1 in the mask means the corresponding bit must match; a 0 means that bit is ignored:
int32_t maskIndex = 0;
uint32_t maskMsgId = 0x1FFFFFFF; /* All 29 ID bits must match */
uint8_t maskPayload[2] = {0x00, 0x00}; /* Don't filter on payload */
naibrd_AR_CAN_SetAcceptMask(cardIndex, module, channel,
maskIndex, maskMsgId, FALSE, &maskPayload[0]);
Get Filter and Mask Configuration
To read back the current filter or mask:
uint32_t readMsgId;
bool_t readModeA;
uint8_t readPayload[2];
naibrd_AR_CAN_GetAcceptFilter(cardIndex, module, channel,
filterIndex, &readMsgId, &readModeA, &readPayload[0]);
naibrd_AR_CAN_GetAcceptMask(cardIndex, module, channel,
maskIndex, &readMsgId, &readModeA, &readPayload[0]);
Remove a Filter
To remove a previously configured filter (removes both filter and mask at the specified index):
naibrd_AR_CAN_RemoveAcceptFilter(cardIndex, module, channel, filterIndex);
Enable and Disable Filtering
Filters must be explicitly enabled before they take effect. Disabling filters does not remove them — it just allows all messages through:
/* Enable acceptance filtering */
naibrd_AR_CAN_EnableAcceptFilters(cardIndex, module, channel, TRUE);
/* Disable acceptance filtering (all messages pass through) */
naibrd_AR_CAN_EnableAcceptFilters(cardIndex, module, channel, FALSE);
|
Important
|
Common Errors
|
FIFO Management
Reset FIFOs
To clear all data from the transmit or receive FIFO:
/* Reset transmit FIFO */
naibrd_AR_CAN_ResetTxFifo(cardIndex, module, channel);
/* Reset receive FIFO */
naibrd_AR_CAN_ResetRxFifo(cardIndex, module, channel);
Resetting the FIFO discards all queued messages. This is useful for error recovery without requiring a full channel reset.
Display FIFO Counts
To monitor FIFO utilization and detect overflow conditions:
int32_t txFIFOCount, txFIFOFrameCount;
int32_t rxFIFOCount, rxFIFOFrameCount;
int32_t dropCount;
naibrd_AR_CAN_GetTxFIFOCount(cardIndex, module, channel, &txFIFOCount);
naibrd_AR_CAN_GetTxFIFOFrameCount(cardIndex, module, channel, &txFIFOFrameCount);
naibrd_AR_CAN_GetRxFIFOCount(cardIndex, module, channel, &rxFIFOCount);
naibrd_AR_CAN_GetRxFIFOFrameCount(cardIndex, module, channel, &rxFIFOFrameCount);
naibrd_AR_CAN_GetDropCount(cardIndex, module, channel, &dropCount);
-
txFIFOCount/rxFIFOCount— the number of entries currently in the FIFO. -
txFIFOFrameCount/rxFIFOFrameCount— the number of complete CAN messages in the FIFO. -
dropCount— the number of received messages that were dropped because the receive FIFO was full.
Reset Drop Count
To reset the drop counter after investigating overflow conditions:
naibrd_AR_CAN_ResetDropCount(cardIndex, module, channel);
|
Important
|
Common Errors
|
Troubleshooting Reference
This table summarizes common errors and symptoms covered in the sections above. Consult your AR-CAN module’s manual for hardware-specific diagnostic procedures.
| 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 |
Module not present at selected slot |
No AR-CAN module installed at the specified slot |
Verify hardware configuration and module slot assignment |
Transmit fails silently |
Transmit FIFO not enabled |
Call |
No messages received |
No bus traffic, filters blocking messages, or baud rate mismatch |
Verify CAN bus connection; disable filters for initial testing; ensure all bus devices use the same baud rates |
64-byte payload rejected |
CAN A/B protocol selected (8-byte max) |
Switch to CAN FD protocol (index 1) |
Baud rate error or no communication |
Base or data rate mismatch between devices |
Set matching baud rates on all devices on the CAN bus |
Drop count increasing |
Receive FIFO full; application not reading fast enough |
Increase read frequency or reset the receive FIFO |
Invalid channel number error |
Channel number out of range for the module |
Verify channel count with |
Scheduler not transmitting |
Scheduler not started after scheduling messages |
Call |
Filters not taking effect |
Filters configured but not enabled |
Call |
Full Source
The complete source for this sample is provided below for reference. The sections above explain each part in detail.
Full Source — AR_CAN_BasicOps.c (SSK 1.x)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if defined (LINUX)
#include <ctype.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#endif
#include "AR_CAN_BasicOps.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"
/* naibrd include files */
#include "nai.h"
#include "naibrd.h"
#include "functions/naibrd_ar_can.h"
#include "naibrd_ether.h"
#include "advanced/nai_ether_adv.h"
static const int8_t *CONFIG_FILE = (int8_t *)"default_ARCAN.txt";
#define SINGLE_MSG_BUFFER_LEN 80
#define STANDARD_CAN_MAX_PAYLOAD 8
#define CAN_FD_MAX_PAYLOAD 64
static struct _userInput
{
int32_t nCardIdx;
int32_t nModuleNumber;
int32_t nChannelNumber;
int32_t nProtocolIndex;
int32_t nBaudBaseRate;
int32_t nBaudDataRate;
} userInput;
/* Function prototypes */
static void Run_CAN_DisplayCANChoices();
static void CAN_SetChan();
static void CAN_SetProtocol();
static void CAN_SetBaud();
static void CAN_Single8ByteXmit();
static void CAN_Single64ByteXmit();
static void CAN_ReceiveMsg();
static void CAN_ReceiveMsgPump();
static void CAN_ScheduleMsg();
static void CAN_StartSchedule();
static void CAN_StopSchedule();
static void CAN_SetFilter();
static void CAN_GetFilter();
static void CAN_SetFilterMask();
static void CAN_GetFilterMask();
static void CAN_RemoveFilter();
static void CAN_EnableFilters();
static void CAN_DisableFilters();
static void CAN_ResetTxFIFO();
static void CAN_ResetRxFIFO();
static void CAN_EnableTx();
static void CAN_DisableTx();
static void CAN_ResetDropCount();
static void CAN_DisplayCounts();
static bool_t checkChannelNumber(int32_t card, int32_t mod, int32_t chan);
#if defined (__VXWORKS__)
int32_t AR_CAN_BasicOps(void)
#else
int32_t main(void)
#endif
{
bool_t stop = FALSE;
int32_t cardIndex;
int32_t moduleCnt;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
while (stop != TRUE)
{
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
userInput.nCardIdx = cardIndex;
if (stop != TRUE)
{
check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
g_moduleCount = moduleCnt;
stop = naiapp_query_ModuleNumber(moduleCnt, 1, &userInput.nModuleNumber);
if (stop != TRUE)
{
Run_CAN_DisplayCANChoices();
}
}
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;
}
/* ... remaining functions: Run_CAN_DisplayCANChoices, CAN_SetChan, CAN_SetProtocol, */
/* CAN_SetBaud, CAN_Single8ByteXmit, CAN_Single64ByteXmit, CAN_ReceiveMsg, */
/* CAN_ReceiveMsgPump, CAN_ScheduleMsg, CAN_StartSchedule, CAN_StopSchedule, */
/* CAN_SetFilter, CAN_GetFilter, CAN_SetFilterMask, CAN_GetFilterMask, */
/* CAN_RemoveFilter, CAN_EnableFilters, CAN_DisableFilters, CAN_ResetTxFIFO, */
/* CAN_ResetRxFIFO, CAN_EnableTx, CAN_DisableTx, CAN_ResetDropCount, */
/* CAN_DisplayCounts, checkChannelNumber */
/* See the SSK installation directory for the complete source. */