SER HDLC Rx
Edit this on GitLab
SER HDLC Rx Sample Application (SSK 1.x)
Overview
The SER HDLC Rx sample application demonstrates how to configure a serial channel for HDLC (High-Level Data Link Control) synchronous reception using the NAI SSK 1.x library. This is a receive-only application — it sets up the channel, enables the receiver, and reads incoming HDLC packets on demand. It serves as a practical API reference for implementing HDLC receive functionality in your own application.
This sample is designed to work with the following serial module types:
-
P8 — Gen 2 Serial
-
PC — Gen 2 Serial
-
PD — Gen 2 Serial
-
Px — Gen 3 Serial
-
KB — Gen 3 Serial
-
Gen 5 Serial modules
-
Combination (CM) modules that include serial functionality (e.g., CMH)
To transmit HDLC data to this receiver, run the SER HDLC Tx sample on a paired channel or a separate board. For a combined transmit-and-receive example on a single channel using loopback, see the SER HDLC Loopback sample.
Prerequisites
-
NAI board with a supported serial module installed
-
SSK 1.x built and ready to run (see First Time Setup Guide)
-
A transmitting source — either the SER HDLC Tx sample, external HDLC equipment, or loopback wiring
How to Run
Launch the compiled SER_HDLC_Rx executable. The board connection menu appears on first run. After connecting, select the card index, module number, and channel number when prompted. Choose an interface level (Loopback, RS-232, RS-422, or RS-485), then press Enter to begin reading HDLC packets.
Board Connection and Module Selection
This startup sequence is common to all NAI sample applications. The board connection and module selection code shown here is not specific to serial modules. For details on board connection configuration, see the First Time Setup Guide.
On launch, main() calls naiapp_RunBoardMenu() with the configuration file name default_SerHDLC_Rx.txt. This file does not ship with the SSK. It is created automatically when you save your connection settings from the board menu. On the first run the board menu always appears and prompts you for connection parameters (IP address, bus type, etc.). On subsequent runs the menu is skipped if the saved configuration file exists.
After the board connection is established, the application queries you for the card index and module number, retrieves the module ID, and passes all three to the receive function Run_SER_HDLC_Rx().
static const int8_t *CONFIG_FILE = (const int8_t *)"default_SerHDLC_Rx.txt";
/* ... */
if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
while (stop != TRUE)
{
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
if (stop != TRUE)
{
check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
if (stop != TRUE)
{
moduleID = naibrd_GetModuleID(cardIndex, module);
if ((moduleID != 0))
{
Run_SER_HDLC_Rx(cardIndex, module, moduleID);
}
}
}
printf("\nType Q to quit or Enter key to restart application:\n");
stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR,
inputBuffer, &inputResponseCnt);
}
}
After Run_SER_HDLC_Rx() returns, you can press Enter to select a different card/module or type Q to exit. The application calls naiapp_access_CloseAllOpenCards() before terminating.
|
Important
|
Common Connection Errors
|
Program Structure
Entry Point
The application uses the standard SSK dual entry point pattern. On VxWorks the entry point is SER_HDLC_Rx_Sample(); on all other platforms it is main(). Both follow the same logic.
#if defined (__VXWORKS__)
int32_t SER_HDLC_Rx_Sample(void)
#else
int32_t main(void)
#endif
Application Constants
The sample defines three constants that control receive behavior:
| Constant | Value | Purpose |
|---|---|---|
|
20 |
Maximum number of 32-bit words to read per |
|
10 |
Timeout in milliseconds for the packet read function. |
|
1000 |
Maximum number of milliseconds to wait for FIFO clear to complete. |
In your own application, adjust MAX_DATA_RX to match the largest HDLC frame size you expect. Set MAX_TIMEOUT based on your system’s latency tolerance — a longer timeout blocks longer but reduces polling overhead.
Interface Selection
Before configuring the channel, the application queries you for the physical interface level:
static void serial_query_interface(nai_ser_interface_t* interfaceLevel)
{
/* ... */
printf("1 - Loop-back\n");
printf("2 - RS232\n");
printf("3 - RS422\n");
printf("4 - RS485\n");
/* ... */
}
The menu system is a sample convenience. In your own code, set the interface level directly by passing the appropriate nai_ser_interface_t enum value to naibrd_SER_SetInterfaceLevel():
-
NAI_SER_INTF_LOOPBACK— Internal loopback (no external wiring needed; useful for testing). -
NAI_SER_INTF_RS232— RS-232 single-ended signaling. -
NAI_SER_INTF_RS422— RS-422 differential signaling. -
NAI_SER_INTF_RS485— RS-485 half-duplex differential signaling.
When using this sample with the SER HDLC Tx sample, both sides must be configured to the same interface level.
Channel Reset and FIFO Clear
Before configuring the channel, the application resets it to a known state and clears both the receive and transmit FIFOs. This ensures no stale data remains from a previous session.
naibrd_SER_ChannelReset(cardIndex, modNum, chanNum);
naibrd_SER_ClearRxFifo(cardIndex, modNum, chanNum);
naibrd_SER_ClearTxFifo(cardIndex, modNum, chanNum);
The FIFO clear operation is not instantaneous. The application polls the channel control register to confirm the clear bits have been deasserted by the hardware:
nCntlValueLo = NAI_SER_CTRLLO_CLEAR_RX_FIFO | NAI_SER_CTRLLO_CLEAR_TX_FIFO;
for (i = 0; i < CLEAR_FIFO_TIMEOUT &&
(nCntlValueLo & (NAI_SER_CTRLLO_CLEAR_RX_FIFO | NAI_SER_CTRLLO_CLEAR_TX_FIFO)); i++)
{
nai_ser_chanctrl chanCtrlRaw;
naibrd_SER_GetChannelControlRaw(cardIndex, modNum, chanNum, &chanCtrlRaw);
nCntlValueLo = chanCtrlRaw & 0x0000FFFF;
nai_msDelay(1);
}
if (i == CLEAR_FIFO_TIMEOUT)
{
printf("Unable to clear FIFOs %d\n", chanNum);
return;
}
The loop checks once per millisecond for up to CLEAR_FIFO_TIMEOUT (1000) iterations. If the FIFOs have not cleared after one second, the channel may be in an error state. In your own code, handle this condition by logging the failure and optionally retrying the channel reset.
|
Important
|
Common Errors
|
HDLC Channel Configuration
After the FIFOs are cleared, the application configures the channel for HDLC reception. Each call sets one parameter of the serial channel.
/* Set HDLC protocol mode */
check_status(naibrd_SER_SetProtocol(cardIndex, modNum, chanNum, NAI_SER_PROTOCOL_HDLC));
/* Set physical interface level */
check_status(naibrd_SER_SetInterfaceLevel(cardIndex, modNum, chanNum, interfaceLevel));
/* Set clock to internal Tx and Rx */
check_status(naibrd_SER_SetClockMode(cardIndex, modNum, chanNum, TXINT_RXINT));
/* Set baud rate */
check_status(naibrd_SER_SetBaudrate(cardIndex, modNum, chanNum, 115200));
/* Set receive HDLC address / sync character */
check_status(naibrd_SER_SetRxHdlcAddrsSyncChar(cardIndex, modNum, chanNum, 0x247A));
/* Enable 16-bit HDLC address recognition */
nConfigWordLo = (NAI_SER_CFGLO_ADDR_LEN_16 | NAI_SER_CFGLO_ADDR_REC);
check_status(naibrd_SER_SetChannelConfigRaw(cardIndex, modNum, chanNum, nConfigWordLo));
Configuration Parameters
| API Call | Value Used | Notes |
|---|---|---|
|
|
Selects HDLC synchronous framing. The channel will expect HDLC flag sequences (0x7E) to delimit frames. |
|
User-selected |
Must match the transmitter’s interface level. For loopback testing, use |
|
|
Both transmit and receive clocks are generated internally. For external clock sources, consult your module’s manual. |
|
115200 |
Must match the transmitter’s baud rate. Consult your module’s manual for the supported baud rate range. |
|
|
The 16-bit HDLC address the receiver will recognize. This must match the address used by the transmitter. In the SER HDLC Tx sample, the same value |
|
|
Enables 16-bit address recognition. The hardware will filter incoming frames and only accept those whose address field matches the configured value. |
Gen 2/3 Configuration Delay
After writing the configuration, Gen 2 and Gen 3 modules (P8, PC, PD, Px, KB) require a short delay for the hardware to acknowledge the new settings:
if (NAI_MODULE_ID_P8 == modid || NAI_MODULE_ID_PC == modid || NAI_MODULE_ID_PD == modid ||
NAI_MODULE_ID_Px == modid || NAI_MODULE_ID_KB == modid)
{
nai_msDelay(20); /* Allow 20ms for the HW to acknowledge the configuration (GEN 2/3 only) */
}
Gen 5 modules do not require this delay. In your own application, include this delay when targeting Gen 2/3 modules to avoid reading stale configuration state.
Enabling the Receiver
After configuration is complete, enable the receiver and wait briefly for it to become active:
check_status(naibrd_SER_SetReceiverEnable(cardIndex, modNum, chanNum, 1));
nai_msDelay(100); /* 100ms wait to ensure the receiver is on */
Pass 1 to enable the receiver, 0 to disable it. The 100-millisecond delay gives the hardware time to synchronize before you begin reading. In your own code, you can reduce this delay if your application has other initialization work to perform before reading.
|
Important
|
Common Errors
|
Receiving HDLC Packets
The core of this application is the receive loop, which calls naibrd_SER_GetHDLCPacket32() to read one HDLC frame at a time and then displays the data and status bytes.
do
{
check_status(naibrd_SER_GetHDLCPacket32(cardIndex, modNum, chanNum, RecvDataStatus,
MAX_DATA_RX, (uint32_t*)&nNumWordsRecv, MAX_TIMEOUT));
printf(" %d words read\n", nNumWordsRecv);
for (i = 0; i < nNumWordsRecv; i++)
printf("Recd 0x%02X, Status= %02X\n",
(RecvDataStatus[i] & 0x00FF), (RecvDataStatus[i] >> 8) & 0x00FF);
printf("Press ENTER to receive again, or 'Q' to exit program : ");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR,
inputBuffer, &inputResponseCnt);
} while (TRUE != bQuit);
GetHDLCPacket32 vs. ReceiveBuffer32
The SSK provides two functions for reading serial data in HDLC mode, and the correct choice depends on your module generation:
-
naibrd_SER_GetHDLCPacket32()— Used by this sample. This function reads a complete HDLC packet and includes a timeout parameter. It is the correct function for Gen 2/3 modules (P8, PC, PD, Px, KB). The timeout (set here toMAX_TIMEOUT= 10 ms) tells the function how long to wait for a complete packet before returning. If no complete packet arrives within the timeout, the function returns withnNumWordsRecvset to 0. -
naibrd_SER_ReceiveBuffer32()— The alternative for Gen 5 modules. This function reads raw data from the receive FIFO without HDLC-specific packet framing awareness at the API level. On Gen 5 modules, the hardware itself handles HDLC frame detection, soReceiveBuffer32returns frame data with embedded status. See the SER HDLC Loopback sample for an example usingReceiveBuffer32.
In your own application, select the appropriate receive function based on the module generation you are targeting. If you need to support both generations, check the module ID at runtime (as the configuration delay section above demonstrates) and branch accordingly.
Understanding the Receive Data Format
Each element of the RecvDataStatus[] array is a 32-bit word that packs both a data byte and a status byte:
| Bits | Mask | Content |
|---|---|---|
[7:0] |
|
The received data byte. |
[15:8] |
|
The status byte for this position in the frame. |
BOF and EOF Status Bytes
The status byte indicates the position of each data byte within the HDLC frame:
-
BOF (Beginning of Frame) — The status byte on the first data word in a frame will have the BOF indicator set. This marks the start of a new HDLC frame following the opening flag sequence (0x7E).
-
EOF (End of Frame) — The status byte on the last data word will have the EOF indicator set. This marks the end of the frame, corresponding to the closing flag sequence.
-
Mid-frame bytes — Data bytes between BOF and EOF will have a neutral status value.
When processing received data in your own application, check the status byte of each word to determine frame boundaries. This is essential when multiple frames arrive back-to-back in a single read, as each frame’s BOF and EOF markers let you separate them.
CRC Validation
HDLC frames include a CRC (Cyclic Redundancy Check) appended by the transmitter before the closing flag. The serial module hardware automatically validates the CRC on each received frame.
-
If the CRC is valid, the frame data is placed in the receive FIFO with normal status bytes.
-
If the CRC is invalid, the status byte on the EOF word will indicate a CRC error.
Check the EOF status byte after reading each frame. A CRC error indicates data corruption during transmission — verify your wiring, baud rate match, and signal integrity. The SER HDLC Tx sample shows how the CRC is automatically appended on the transmit side.
|
Important
|
Common Errors
|
Troubleshooting Reference
The following table summarizes errors and symptoms covered in the preceding sections. Consult your module’s manual for hardware-specific diagnostics.
| Error / Symptom | Possible Causes | Suggested Resolution |
|---|---|---|
FIFO clear timeout |
Module firmware is unresponsive or channel is in an error state. |
Reset the module or power-cycle the board. Verify the module firmware revision meets minimum requirements. |
|
The selected protocol, interface level, or API function is not supported by the installed module or firmware revision. |
Verify the module type supports HDLC. Check your module’s manual for minimum FPGA revision requirements. |
No board found / connection timeout |
Board is powered off, network misconfigured, or bus driver not loaded. |
Verify power, network connectivity (Ethernet), or bus driver installation (PCI/cPCI). |
0 words received |
No complete HDLC packet arrived before the timeout expired. Transmitter may not be running, or configuration mismatch. |
Confirm the transmitter is active. Verify baud rate, interface level, HDLC address, and address length all match between transmitter and receiver. |
CRC error in status byte |
Data corruption on the physical link due to wiring issues, baud rate mismatch, or electrical noise. |
Check cable connections and termination. Ensure baud rates match. For long cable runs, verify signal integrity. |
Garbled or misaligned data |
HDLC address or address length mismatch between transmitter and receiver. |
Ensure both sides use the same HDLC address value (e.g., |
Receiver does not start |
|
Call |
Configuration not taking effect on Gen 2/3 |
Missing 20 ms hardware acknowledgment delay after writing configuration registers. |
Add |
Full Source
Full Source — SER_HDLC_Rx.c (SSK 1.x)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.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_ser.h"
#include "advanced/nai_ether_adv.h"
static const int8_t *CONFIG_FILE = (const int8_t *)"default_SerHDLC_Rx.txt";
/* Function prototypes */
void Run_SER_HDLC_Rx(int32_t cardIndex, int32_t module, uint32_t modid);
static void serial_query_interface(nai_ser_interface_t* interfaceLevel);
#define MAX_DATA_RX 20
#define MAX_TIMEOUT 10 /* 10ms timeout */
#define CLEAR_FIFO_TIMEOUT 1000 /* 1 second */
/**************************************************************************************************************/
/** \defgroup SERHDLCRx Serial Synchronous Receive
The purpose of the Serial Synchronous Receive sample application is to illustrate the methods to call in the
naibrd library to configure a given serial channel for receiving. The SER_HDLC_Tx application can be run in
unison to this application to receive serial data.
*/
/**************************************************************************************************************/
#if defined (__VXWORKS__)
int32_t SER_HDLC_Rx_Sample(void)
#else
int32_t main(void)
#endif
{
bool_t stop = FALSE;
int32_t cardIndex;
int32_t moduleCnt;
int32_t module;
uint32_t moduleID = 0;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
while (stop != TRUE)
{
/* Query the user for the card index */
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
if (stop != TRUE)
{
check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
/* Query the user for the module number */
stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
if (stop != TRUE)
{
moduleID = naibrd_GetModuleID(cardIndex, module);
if ((moduleID != 0))
{
Run_SER_HDLC_Rx(cardIndex, module, moduleID);
}
}
}
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;
}
/**************************************************************************************************************/
/** \ingroup SERHDLCRx
Configures a serial module for synchronous receiving. The user is queried for the serial channel to receive on.
\param cardIndex (Input) Logical Card Index assigned to connection with the NAI_BOARD (0 - NAI_MAX_CARDS-1).
\param modNum (Input) Module Number of the module to access (1 - [max modules for board]).
\param modid (Input) The ID of the module.
*/
/**************************************************************************************************************/
void Run_SER_HDLC_Rx(int32_t cardIndex, int32_t modNum, uint32_t modid)
{
int32_t ch, i;
int32_t channelCount;
int32_t chanNum;
int32_t nCntlValueLo, nConfigWordLo;
int32_t nNumWordsRecv = 0;
uint32_t RecvDataStatus[255];
bool_t bQuit = FALSE;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
nai_ser_interface_t interfaceLevel = NAI_SER_INTF_LOOPBACK;
channelCount = naibrd_SER_GetChannelCount(modid);
naiapp_query_ChannelNumber(channelCount, 1, &chanNum);
serial_query_interface(&interfaceLevel);
naibrd_SER_ChannelReset(cardIndex, modNum, chanNum);
naibrd_SER_ClearRxFifo(cardIndex, modNum, chanNum);
naibrd_SER_ClearTxFifo(cardIndex, modNum, chanNum);
nCntlValueLo = NAI_SER_CTRLLO_CLEAR_RX_FIFO | NAI_SER_CTRLLO_CLEAR_TX_FIFO;
for (i = 0; i < CLEAR_FIFO_TIMEOUT && (nCntlValueLo & (NAI_SER_CTRLLO_CLEAR_RX_FIFO | NAI_SER_CTRLLO_CLEAR_TX_FIFO)); i++)
{
nai_ser_chanctrl chanCtrlRaw;
naibrd_SER_GetChannelControlRaw(cardIndex, modNum, chanNum, &chanCtrlRaw);
nCntlValueLo = chanCtrlRaw & 0x0000FFFF;
nai_msDelay(1);
}
if (i == CLEAR_FIFO_TIMEOUT)
{
printf("Unable to clear FIFOs %d\n", chanNum);
printf("Please press Enter to exit...");
while ((ch = getchar()) != 0x0A);
return;
}
printf("\nSerial Channel # %d\n", chanNum);
/* Configure for HDLC on the channel selected. */
check_status(naibrd_SER_SetProtocol(cardIndex, modNum, chanNum, NAI_SER_PROTOCOL_HDLC)); /* HDLC mode */
check_status(naibrd_SER_SetInterfaceLevel(cardIndex, modNum, chanNum, interfaceLevel)); /* Loopback, RS232, RS422, or RS485 */
check_status(naibrd_SER_SetClockMode(cardIndex, modNum, chanNum, TXINT_RXINT)); /* Tx and Rx internal */
check_status(naibrd_SER_SetBaudrate(cardIndex, modNum, chanNum, 115200)); /* 115,200 baud */
check_status(naibrd_SER_SetRxHdlcAddrsSyncChar(cardIndex, modNum, chanNum, 0x247A)); /* recv sync char - must be same as xmit for loopback */
nConfigWordLo = (NAI_SER_CFGLO_ADDR_LEN_16 | NAI_SER_CFGLO_ADDR_REC); /* 16-bit HDLC addr recognition */
check_status(naibrd_SER_SetChannelConfigRaw(cardIndex, modNum, chanNum, nConfigWordLo));
if (NAI_MODULE_ID_P8 == modid || NAI_MODULE_ID_PC == modid || NAI_MODULE_ID_PD == modid ||
NAI_MODULE_ID_Px == modid || NAI_MODULE_ID_KB == modid)
{
nai_msDelay(20); /* Allow 20ms for the HW to acknowledge the configuration (GEN 2/3 only)*/
}
check_status(naibrd_SER_SetReceiverEnable(cardIndex, modNum, chanNum, 1)); /* Enable the Receiver */
/* 100 millisecond wait to ensure the receiver is on. */
nai_msDelay(100);
printf("Please press Enter to read back data...");
while ((ch = getchar()) != 0x0A);
do
{
check_status(naibrd_SER_GetHDLCPacket32(cardIndex, modNum, chanNum, RecvDataStatus, MAX_DATA_RX,
(uint32_t*)&nNumWordsRecv, MAX_TIMEOUT));
printf(" %d words read\n", nNumWordsRecv);
for (i = 0; i < nNumWordsRecv; i++)
printf("Recd 0x%02X, Status= %02X\n",
(RecvDataStatus[i] & 0x00FF), (RecvDataStatus[i] >> 8) & 0x00FF);
printf("Press ENTER to receive again, or '%c' to exit program : ", NAI_QUIT_CHAR);
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
} while (TRUE != bQuit);
return;
}
/**************************************************************************************************************/
/** \ingroup SERHDLCRx
Queries the user for the interface type.
\param interfaceLevel (Output) The selected interface level.
*/
/**************************************************************************************************************/
static void serial_query_interface(nai_ser_interface_t* interfaceLevel)
{
int8_t inputBuffer[80];
int32_t choice;
printf("Please select an interface type [default=1]:\n");
printf("1 - Loop-back\n");
printf("2 - RS232\n");
printf("3 - RS422\n");
printf("4 - RS485\n");
printf("Enter choice: ");
fgets((char*)inputBuffer, sizeof(inputBuffer), stdin);
choice = atoi((char*)inputBuffer);
switch (choice)
{
case 1:
*interfaceLevel = NAI_SER_INTF_LOOPBACK;
break;
case 2:
*interfaceLevel = NAI_SER_INTF_RS232;
break;
case 3:
*interfaceLevel = NAI_SER_INTF_RS422;
break;
case 4:
*interfaceLevel = NAI_SER_INTF_RS485;
break;
default:
printf("ERROR: Invalid choice. Defaulting to Loop-back.\n");
*interfaceLevel = NAI_SER_INTF_LOOPBACK;
break;
}
return;
}