TTL Interrupt Basic
Edit this on GitLab
TTL Interrupt Basic Sample Application (SSK 1.x)
Overview
The TTL Interrupt Basic sample application demonstrates how to configure and handle hardware interrupts on TTL (Transistor-Transistor Logic) I/O modules using the NAI Software Support Kit (SSK 1.x). TTL channels are bidirectional digital I/O lines — each channel can be independently configured as an input or an output. This sample shows how to monitor a single channel for interrupt events across three status types: BIT (Built-In Test) failures, low-to-high signal transitions, and high-to-low signal transitions. The overcurrent status type is also available on TTL modules but is handled through the same group status mechanism demonstrated here.
Transition interrupts detect edge events on input channels — useful for capturing switch closures, signal assertions, and state changes without polling. BIT interrupts verify the internal logic path of each channel, providing continuous hardware health monitoring. Overcurrent interrupts detect output faults when a channel configured as an output is sourcing or sinking excessive current.
This sample supports the following TTL module types: D7 (Gen3), TL1, TL2, TL3, TL4, TL5, TL6, TL7, and TL8 (Gen5).
For background on I/O direction configuration, output modes, and channel format setup, see the TTL BasicOps guide. For general interrupt concepts — including edge vs. level triggering, interrupt vector numbering, steering architecture, and latency measurement — see the Interrupts API Guide. This guide focuses on how those concepts apply specifically to TTL modules and walks through the practical implementation using the SSK 1.x API.
Prerequisites
Before running this sample, make sure you have:
-
An NAI board with a TTL module installed (D7, TL1-TL8).
-
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 signal source or external connection to trigger transition interrupts on the monitored channel, or an overcurrent load condition to trigger overcurrent interrupts on an output channel.
How to Run
Launch the TTL_Interrupt_Basic executable from your build output directory. On startup the application looks for a configuration file (default_TTL_Interrupt_Basic.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, the application prompts you for a channel number and interrupt steering mode, then begins waiting for interrupt events.
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 TTL. For details on board connection configuration, see the First Time Setup Guide. |
The main() function (named TTL_Interrupt_Basic() on VxWorks) follows a standard SSK 1.x startup flow:
-
Initialize default TTL and interrupt configuration structures with
initializeTTLConfigurations()andinitializeInterruptConfigurations(). -
Call
naiapp_RunBoardMenu()to load a saved configuration file (if one exists) or present the interactive board menu. The configuration file (default_TTL_Interrupt_Basic.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 the card index and module number. The application validates the module by calling
naibrd_GetModuleID()and proceeds only if a valid TTL module is detected. -
Call
Run_TTL_Interrupt_Basic()to configure and monitor interrupts on the selected module.
#if defined (__VXWORKS__)
int32_t TTL_Interrupt_Basic(void)
#else
int32_t main(void)
#endif
{
bool_t stop = FALSE;
int32_t cardIndex;
int32_t moduleCnt;
int32_t module;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
initializeTTLConfigurations(0, 0, 0, 0, 0, 0);
initializeInterruptConfigurations(FALSE, FALSE, FALSE, 0, 0, 0, 0);
if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
while (stop != TRUE)
{
/* Query the user for the card index */
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
inputTTLConfig.cardIndex = cardIndex;
if (stop != TRUE)
{
check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
/* Query the user for the module number */
stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
inputTTLConfig.module = module;
if (stop != TRUE)
{
inputTTLConfig.modid = naibrd_GetModuleID(cardIndex, module);
if ((inputTTLConfig.modid != 0))
{
Run_TTL_Interrupt_Basic();
}
}
}
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;
}
|
Important
|
Common connection errors you may encounter at this stage:
|
Program Structure
The TTL Interrupt Basic sample is split across two source files:
-
TTL_Interrupt_Basic.c— contains themain()entry point, board connection logic, user prompts for channel and steering selection, and the top-level interrupt lifecycle (install ISR, configure, monitor, clean up). -
nai_ttl_int.c— shared TTL interrupt utilities used by all TTL interrupt samples. Contains the interrupt configuration function (configureTTLToInterrupt()), enable/disable logic (enableTTLInterrupts()), the ISR callback (basic_ISR_TTL()), and the interrupt polling/reporting function (checkForTTLInterrupt()).
The entry point is main() (or TTL_Interrupt_Basic() on VxWorks). After establishing the board connection, the application follows a linear flow:
-
Query the user for the channel number to monitor. The selected channel is stored in
inputTTLConfig.channel, and bothminChannelandmaxChannelare set to the same value so that only the selected channel is configured for interrupts. -
Query the user for interrupt steering mode (onboard, offboard PCI, offboard PCIe).
-
Install the ISR with
naibrd_InstallISR(). -
Configure the module for interrupts with
configureTTLToInterrupt(). -
Enable interrupts with
enableTTLInterrupts(). -
Enter the polling loop with
checkForTTLInterrupt(), which waits for the user to press Enter to check for events or Q to quit. -
On exit, disable interrupts and uninstall the ISR.
The menu system is a sample convenience — in your own code, call these API functions directly.
Shared Data Structures
The sample uses two structures to track user selections and board state:
-
TtlConfig(defined innai_ttl_cfg.h) — holdscardIndex,module,modid,channel,minChannel, andmaxChannel. These identify the target hardware and the range of channels to configure. -
InterruptConfig(defined innaiapp_interrupt.h) — holdssteering,irq, andinterrupt_Edge_Trigger. These control how interrupts are routed and triggered.
Both structures are declared as globals (inputTTLConfig and inputInterruptConfig) so the main file and the shared interrupt utilities can reference them.
Interrupt Status Types
The TTL module exposes four distinct interrupt status types. Each type monitors a different hardware condition and latches when that condition is detected. Understanding what each type reports — and the underlying hardware detection mechanism — will help you decide which ones to enable in your application.
BIT (Built-In Test)
NAI_TTL_STATUS_BIT_LATCHED
The module’s internal self-test continuously verifies the read/write logic path on each TTL channel. The BIT circuitry writes a known value through the channel’s data path and reads it back, confirming that the internal FPGA logic, register interface, and driver circuitry are functioning correctly. When the read-back does not match the expected value, BIT latches the corresponding status bit and, if enabled, raises an interrupt. Detection occurs within 10 ms of the fault condition.
Enable BIT interrupts for health monitoring, safety-critical applications, or continuous hardware validation. BIT detects data path faults — not external signal issues — so a BIT failure indicates an internal hardware problem that requires attention. In systems where a failed channel must be flagged immediately rather than discovered during a periodic status poll, BIT interrupts provide the fastest notification path.
Overcurrent
NAI_TTL_STATUS_OVERCURRENT_LATCHED
An output channel detected an overcurrent condition. When a TTL channel is configured as an output, the module’s current-sensing circuitry monitors the current being sourced or sunk by the output driver. The detection mechanism compares the commanded output value against the actual readback from the output pin. If the readback does not match the commanded state — indicating the output driver cannot maintain the correct voltage level due to excessive current draw — the module flags an overcurrent condition. An 80 ms stabilization period is applied before the fault is confirmed, preventing transient load spikes from generating false alarms. If the overcurrent condition persists after stabilization, the channel is automatically reset to input mode to protect the output driver.
Enable overcurrent interrupts for output protection, fault detection, and load monitoring. In applications that drive external loads through TTL output channels, an overcurrent interrupt lets you react immediately — disabling the output, alerting the operator, or switching to a backup channel — before damage occurs. Common causes include wiring shorts, unexpectedly heavy loads, or back-driving from external circuitry.
|
Note
|
Overcurrent detection applies only to channels configured as outputs. Channels in input mode do not generate overcurrent interrupts. |
Low-to-High Transition
NAI_TTL_STATUS_LO_HI_TRANS_LATCHED
A TTL input channel detected a rising edge — the signal transitioned from a logic low to a logic high. The module’s input detection circuitry captures this edge with a detection time of approximately 100 ns and latches the status bit so the event is not lost even if the signal changes again before software reads the register.
Enable low-to-high transition interrupts for rising-edge detection: switch closures, signal assertion, event counting, or detecting when an external condition becomes active. This is the most common trigger type for applications that need to respond the moment an input asserts. The 100 ns detection time means the input pulse must remain high for at least 100 ns to be reliably captured.
High-to-Low Transition
NAI_TTL_STATUS_HI_LO_TRANS_LATCHED
A TTL input channel detected a falling edge — the signal transitioned from a logic high to a logic low. The detection circuitry captures this edge with a detection time of approximately 40 ns and latches the status bit for reliable software consumption.
Enable high-to-low transition interrupts for falling-edge detection: switch openings, signal de-assertion, or detecting when an external condition clears. Used together with low-to-high interrupts, the two transition types give full visibility into both edges of a signal, which is useful for measuring pulse widths or tracking state changes. The faster detection time (40 ns vs. 100 ns for low-to-high) reflects the asymmetric switching characteristics of TTL input circuitry.
|
Note
|
Transition interrupts apply to channels configured as inputs. Channels configured as outputs will not detect external transitions because the output driver is actively driving the pin. Consult the TL1/TL2 module manual for detailed detection timing specifications and voltage threshold values. |
The sample enables BIT, low-to-high, and high-to-low status types for the selected channel. In your own application, enable only the status types relevant to your use case to reduce interrupt traffic and simplify your handler logic. For I/O direction configuration, see the TTL BasicOps guide.
Interrupt Configuration
The configureTTLToInterrupt() function in nai_ttl_int.c performs the complete interrupt setup sequence. The same function is shared by all TTL interrupt samples. It takes the user’s interrupt settings (InterruptConfig) and the target module information (TtlConfig) and configures the module registers in five steps.
Step 1: Disable Interrupts Before Reconfiguring
Always disable interrupts before changing configuration to avoid spurious interrupts during setup. The function begins by calling enableTTLInterrupts(inputTTLConfig, FALSE), which iterates over the configured channel range and disables all three interrupt status types.
enableTTLInterrupts(inputTTLConfig, FALSE);
Step 2: Clear All Status Registers
Stale latched status from a previous run will trigger an immediate interrupt if not cleared. The pattern is: read the current raw status, then write back the value to clear it. The sample repeats this read-and-clear sequence for all three status types — low-to-high transition, high-to-low transition, and BIT.
/* Clear the Interrupt Status (Read the status and write back "1" to statuses which are set to clear the status) */
check_status(naibrd_TTL_GetGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, &rawstatus));
check_status(naibrd_TTL_ClearGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, rawstatus));
check_status(naibrd_TTL_GetGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, &rawstatus));
check_status(naibrd_TTL_ClearGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, rawstatus));
check_status(naibrd_TTL_GetGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_BIT_LATCHED, &rawstatus));
check_status(naibrd_TTL_ClearGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_BIT_LATCHED, rawstatus));
The naibrd_TTL_GetGroupStatusRaw() call reads all channel statuses for the specified type as a bitmask. Writing that same bitmask back with naibrd_TTL_ClearGroupStatusRaw() clears exactly the bits that were set. The group number 1 refers to the first (and typically only) channel group on TTL modules.
Step 3: Set Interrupt Vector
The vector identifies which interrupt source generated the event. Unlike the DT interrupt sample which uses a single shared vector, the TTL sample assigns a distinct vector to each status type — NAI_TTL_LOHI_INTERRUPT_VECTOR, NAI_TTL_HILO_INTERRUPT_VECTOR, and NAI_TTL_BIT_INTERRUPT_VECTOR. This allows the ISR and polling handler to determine which status type fired by examining the vector value alone.
/* Setup the Interrupt Vector - map to the same vector */
check_status(naibrd_TTL_SetGroupInterruptVector(cardIndex, module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, NAI_TTL_LOHI_INTERRUPT_VECTOR));
check_status(naibrd_TTL_SetGroupInterruptVector(cardIndex, module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, NAI_TTL_HILO_INTERRUPT_VECTOR));
check_status(naibrd_TTL_SetGroupInterruptVector(cardIndex, module, 1, NAI_TTL_STATUS_BIT_LATCHED, NAI_TTL_BIT_INTERRUPT_VECTOR));
Step 4: Set Trigger Mode
The trigger mode is set per-channel for each status type. The user’s selection (interrupt_Edge_Trigger) is cast to nai_ttl_interrupt_t and passed to naibrd_TTL_SetEdgeLevelInterrupt(). A value of 0 selects edge-triggered mode, where the interrupt fires once when the condition is first detected. A value of 1 selects level-triggered mode, where the interrupt continues to assert as long as the condition remains active. For detailed theory on edge vs. level triggering and when to use each mode, see the Interrupts API Guide.
for (chan = 1; chan <= inputTTLConfig.maxChannel; chan++)
{
check_status(naibrd_TTL_SetEdgeLevelInterrupt(cardIndex, module, chan, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, (nai_ttl_interrupt_t)interrupt_Edge_Trigger));
check_status(naibrd_TTL_SetEdgeLevelInterrupt(cardIndex, module, chan, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, (nai_ttl_interrupt_t)interrupt_Edge_Trigger));
check_status(naibrd_TTL_SetEdgeLevelInterrupt(cardIndex, module, chan, NAI_TTL_STATUS_BIT_LATCHED, (nai_ttl_interrupt_t)interrupt_Edge_Trigger));
}
Step 5: Set Interrupt Steering
Interrupt steering determines how the interrupt signal is delivered from the module to your application. The sample applies the user-selected steering mode to all three status types:
check_status(naibrd_TTL_SetGroupInterruptSteering(cardIndex, module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, steering));
check_status(naibrd_TTL_SetGroupInterruptSteering(cardIndex, module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, steering));
check_status(naibrd_TTL_SetGroupInterruptSteering(cardIndex, module, 1, NAI_TTL_STATUS_BIT_LATCHED, steering));
The steering options are:
-
Onboard (
NAIBRD_INT_STEERING_ON_BOARD_0) — handled locally on the board’s processor. The ISR runs directly on the board without traversing a bus. -
cPCI offboard (
NAIBRD_INT_STEERING_CPCI_APP) — routes the interrupt to the CompactPCI backplane host. -
PCIe offboard (
NAIBRD_INT_STEERING_PCIE_APP) — routes the interrupt to the PCIe host. Standard choice for desktop or rack-mount systems with a PCIe backplane connection.
For a full description of each steering mode and the hardware paths involved, refer to your module manual and the Interrupts API Guide.
Enable Interrupts Per Channel
After configureTTLToInterrupt() returns, the main application calls enableTTLInterrupts() to turn on interrupt generation for the selected channel. This function iterates from minChannel to maxChannel (both set to the user’s selected channel in this sample) and enables all three status types:
void enableTTLInterrupts(TtlConfig inputTTLConfig, bool_t enable) {
int32_t channel;
for (channel = inputTTLConfig.minChannel; channel <= inputTTLConfig.maxChannel; channel++)
{
check_status(naibrd_TTL_SetInterruptEnable(inputTTLConfig.cardIndex, inputTTLConfig.module, channel, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, enable));
check_status(naibrd_TTL_SetInterruptEnable(inputTTLConfig.cardIndex, inputTTLConfig.module, channel, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, enable));
check_status(naibrd_TTL_SetInterruptEnable(inputTTLConfig.cardIndex, inputTTLConfig.module, channel, NAI_TTL_STATUS_BIT_LATCHED, enable));
}
}
In your own application, you can selectively enable only the status types and channels you need rather than enabling everything. For example, if you only need to detect rising edges on channel 3, call naibrd_TTL_SetInterruptEnable() once with NAI_TTL_STATUS_LO_HI_TRANS_LATCHED for channel 3 only.
|
Important
|
Common interrupt configuration errors:
|
Interrupt Handler and Polling
This sample uses two complementary interrupt handling mechanisms: the ISR callback that fires when the hardware interrupt arrives, and a user-driven polling loop that checks and reports the interrupt state.
ISR Callback
The basic_ISR_TTL() function is the interrupt service routine installed with naibrd_InstallISR(). It is intentionally minimal — ISRs must execute quickly and avoid blocking operations.
#if defined (__VXWORKS__)
void basic_ISR_TTL(uint32_t param)
#else
void basic_ISR_TTL(void *param, uint32_t vector)
#endif
{
interruptOccured = TRUE;
#if defined (__VXWORKS__)
interruptVector = nai_Onboard_GetInterruptVector();
nai_Onboard_ClearInterrupt();
#elif defined (WIN32)
UNREFERENCED_PARAMETER(param);
interruptVector = vector;
#else
interruptVector = vector;
#endif
}
The ISR sets two global variables: interruptOccured (a boolean flag) and interruptVector (the vector value that identifies which status type fired). On VxWorks, the vector must be retrieved explicitly via nai_Onboard_GetInterruptVector() and the interrupt cleared with nai_Onboard_ClearInterrupt(). On Windows and Linux, the vector is passed directly as a parameter.
Polling and Reporting
The checkForTTLInterrupt() function runs in a loop, prompting the user to press Enter to check whether an interrupt has occurred. When the ISR has set interruptOccured to TRUE, the function examines interruptVector to determine which status type fired, reads the raw group status for that type, and reports the vector and status values.
bool_t checkForTTLInterrupt(TtlConfig inputTTLConfig) {
bool_t bQuit;
int32_t cardIndex;
int32_t module;
uint32_t rawstatus = 0;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
cardIndex = inputTTLConfig.cardIndex;
module = inputTTLConfig.module;
bQuit = FALSE;
interruptOccured = FALSE;
while (!bQuit) {
printf("\nPress enter to check if Lo-Hi or Hi-Lo interrupt Occurred (press Q to quit):");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit) {
if (interruptOccured) {
switch (interruptVector)
{
case NAI_TTL_LOHI_INTERRUPT_VECTOR:
printf("\nLo-Hi Interrupt Occurred");
check_status(naibrd_TTL_GetGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, &rawstatus));
break;
case NAI_TTL_HILO_INTERRUPT_VECTOR:
printf("\nHi-Lo Interrupt Occurred");
check_status(naibrd_TTL_GetGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, &rawstatus));
break;
}
printf("\nVector = %#x \n", interruptVector);
printf("Status = %#x \n", rawstatus);
interruptOccured = FALSE;
}
else {
printf("\nNo Interrupt Occurred");
}
printf("\n\nWould you like to clear the status register? (default:N):");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (inputBuffer[0] == 'y' || inputBuffer[0] == 'Y')
{
switch (interruptVector)
{
case NAI_TTL_LOHI_INTERRUPT_VECTOR:
check_status(naibrd_TTL_ClearGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, rawstatus));
break;
case NAI_TTL_HILO_INTERRUPT_VECTOR:
check_status(naibrd_TTL_ClearGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, rawstatus));
break;
}
}
}
}
return bQuit;
}
The polling handler works as follows:
-
Check the ISR flag — if
interruptOccuredisTRUE, the ISR has fired since the last check. -
Identify the source — the
interruptVectorvalue distinguishes between low-to-high (NAI_TTL_LOHI_INTERRUPT_VECTOR), high-to-low (NAI_TTL_HILO_INTERRUPT_VECTOR), and BIT (NAI_TTL_BIT_INTERRUPT_VECTOR) events. -
Read the group status —
naibrd_TTL_GetGroupStatusRaw()returns a bitmask indicating which channels in the group triggered. Each set bit corresponds to a channel that latched the status condition. -
Report — the vector and raw status values are printed so you can see which interrupt type fired and which channels are affected.
-
Clear to re-arm — the user is prompted to clear the status register. Answering Y calls
naibrd_TTL_ClearGroupStatusRaw()with the previously read status value, which clears exactly the bits that were set and re-arms the interrupt for the next event. If you skip the clear step with edge-triggered interrupts, no new edge will occur and the interrupt will never fire again.
In your own application, you would typically clear the status immediately in the handler rather than prompting the user. The prompt-to-clear approach used here is a learning tool that lets you observe the latched status before it is cleared.
|
Important
|
Common interrupt handling errors:
|
Troubleshooting Reference
This table summarizes common errors and symptoms covered in the sections above. For detailed context, refer to the relevant section. Consult your module’s manual for hardware-specific diagnostic procedures.
Debugging Interrupts That Are Not Firing
When interrupts are not being delivered, use this two-step approach to isolate the problem:
-
Check whether the status registers are changing. Call
naibrd_TTL_GetGroupStatusRaw()to read the latched status registers for the interrupt type you expect. If the status bits are changing when the hardware condition occurs, the module is detecting the event correctly — the issue is in your interrupt delivery path (steering, ISR installation, or IRQ routing). -
If the status registers are NOT changing, the issue is at the hardware or channel configuration level. For transition interrupts, verify that the channel is configured as an input (output channels cannot detect external transitions). For overcurrent, verify the channel is configured as an output with a load connected. For BIT, check that the module is healthy and no FPGA errors are present. Consult your module’s manual for the specific register addresses for each status type.
| 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 the configuration file exists, check that it lists the correct interface and address. If it does not exist, the board menu will appear — configure and save your connection settings. |
Module not detected or not recognized as TTL |
No TTL module installed at the selected slot, incorrect module number, or a non-TTL module is present |
Verify hardware configuration. |
ISR installation failure |
Steering mode does not match hardware configuration, driver not loaded, or bus access issue |
Verify steering mode matches your bus architecture. On Linux, check that the NAI driver is loaded. On Windows, verify the device appears in Device Manager. |
Interrupts not firing after enable |
Status registers not cleared before enabling, wrong steering mode, ISR not installed |
Follow the full setup sequence: disable, clear status, set vector, set trigger mode, set steering, install ISR, enable. See the debugging technique above. |
Overcurrent interrupts on output channels |
Load impedance too low (excessive current draw), wiring short, or back-driving from external circuitry |
Measure the load current and verify it is within the module’s rated output current. Check wiring for shorts. The module applies an 80 ms stabilization window before confirming the fault — transient spikes shorter than 80 ms will not trigger the interrupt. |
Transition interrupts not firing on channels configured as output |
Output channels actively drive the pin and cannot detect external transitions |
Reconfigure the channel as an input using the TTL BasicOps API ( |
BIT failures on healthy hardware |
Transient power fluctuation, FPGA initialization not complete, or actual hardware degradation |
Retry after power cycling the board. If BIT failures persist, the channel may have a genuine hardware fault — consult the module manual for diagnostic procedures. |
Wrong steering mode |
Using onboard steering from an external host, or offboard steering from the onboard processor |
Match steering to where your application executes. Onboard: |
ISR fires but polling loop reports "No Interrupt Occurred" |
Multiple interrupts arrived between user checks and |
The polling approach in this sample can miss rapid successive interrupts. In production, use a threaded handler (as shown in the DT Interrupt sample) or read status registers on every ISR invocation. |
Edge vs. level mismatch |
Edge mode selected but the condition is persistent (interrupt fires once then stops); or level mode selected but only a single notification was expected |
Use edge triggering when you want one notification per event and will clear status in the handler. Use level triggering when you need the interrupt to persist until the underlying condition is resolved. See the Interrupts API Guide. |
Status registers not changing (debugging) |
Channel not configured correctly, trigger mode mismatch, no physical signal present, or channel direction wrong |
Verify channel I/O direction, trigger mode, and that the expected signal is present at the input. For transition interrupts, confirm the channel is an input. Read registers directly — consult module manual for addresses. |
Full Source
The complete source for this sample is provided below for reference. The sections above explain each part in detail. This sample consists of two source files: the main application and shared TTL interrupt utilities.
Full Source — TTL_Interrupt_Basic.c (SSK 1.x)
/**************************************************************************************************************/
/**
<summary>
The TTL_Interrupt_Basic program demonstrates how to perform an interrupt when a single channel receives
a ttl message. The purpose of this program is to demonstrate the method calls in the naibrd library for performing
the interrupt. More information on this process can be found in the naibrd SSK Quick Guide(Interrupts) file.
</summary>
*/
/**************************************************************************************************************/
/************************/
/* Include Declarations */
/************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/*Common Module Specific Sample Program include files*/
#include "nai_ttl_int.h"
#include "nai_ttl_cfg.h"
/* Common Sample Program include files */
#include "include/naiapp_interrupt.h"
#include "include/naiapp_interrupt_ether.h"
#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"
/* Module Specific NAI Board Library files */
#include "functions/naibrd_ttl.h"
InterruptConfig inputInterruptConfig;
/* Extern Functions or Variables*/
extern TtlConfig inputTTLConfig;
/*********************************************/
/* Application Name and Revision Declaration */
/*********************************************/
static const int8_t *CONFIG_FILE = (int8_t *)"default_TTL_Interrupt_Basic.txt";
/********************************/
/* Internal Function Prototypes */
/********************************/
static bool_t Run_TTL_Interrupt_Basic();
/**************************************************************************************************************/
/***** Main Routine *****/
/**************************************************************************************************************/
/**************************************************************************************************************/
/**
<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 TTL_Interrupt_Basic(void)
#else
int32_t main(void)
#endif
{
bool_t stop = FALSE;
int32_t cardIndex;
int32_t moduleCnt;
int32_t module;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
initializeTTLConfigurations(0, 0, 0, 0, 0, 0);
initializeInterruptConfigurations(FALSE, FALSE, FALSE, 0, 0, 0, 0);
if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
while (stop != TRUE)
{
/* Query the user for the card index */
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
inputTTLConfig.cardIndex = cardIndex;
if (stop != TRUE)
{
check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
/* Query the user for the module number */
stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
inputTTLConfig.module = module;
if (stop != TRUE)
{
inputTTLConfig.modid = naibrd_GetModuleID(cardIndex, module);
if ((inputTTLConfig.modid != 0))
{
Run_TTL_Interrupt_Basic();
}
}
}
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>
This function is broken into the following major steps. These steps correspond with the steps provided
in the naibrd SSK Quick Guide(Interrupts) file.
2. Bus Interrupt Handling - Install ISR
API CALLS - naibrd_InstallISR
3. Enable Module Interrupts- Configures module to interrupt when channel receives TTL message.
API CALLS - naibrd_TTL_SetInterruptEdgeLevel, naibrd_TTL_SetIntVector, naibrd_TTL_SetInterruptSteering, naibrd_TTL_SetIntEnable
4. Not applicable to this module
5. Show Interrupt Handling - Check if any interrupt has occurred and report back the status and vector
6. Re-arming Interrupts - Clear the status register to allow interrupts to occur again. This is done by writing to the status register.
In this program, we use an API call to do this.
API CALLS - naibrd_TTL_ClearStatusRaw
7. Clear Module Configurations
8. Clear Board Configurations
API CALLS - naibrd_UninstallISR
</summary>
*/
/**************************************************************************************************************/
static bool_t Run_TTL_Interrupt_Basic()
{
bool_t bQuit = FALSE;
int32_t maxChannel = naibrd_TTL_GetChannelCount(inputTTLConfig.modid);
int32_t minChannel = 1;
/*Query user for RX channel*/
if(!bQuit)
{
bQuit = naiapp_query_ChannelNumber(maxChannel,minChannel,&inputTTLConfig.channel);
inputTTLConfig.minChannel = inputTTLConfig.channel;
inputTTLConfig.maxChannel = inputTTLConfig.channel;
}
/*Query user for location interrupt will be sent out to*/
if(!bQuit)
{
bQuit = GetIntSteeringTypeFromUser(&inputInterruptConfig.steering);
}
if (!bQuit)
{
/**** 2. Implement Bus Interrupt Handling****/
setIRQ(inputInterruptConfig.steering,&inputInterruptConfig.irq);
naibrd_InstallISR(inputTTLConfig.cardIndex,inputInterruptConfig.irq, basic_ISR_TTL,NULL);
/****3. configure Module to perform interrupts****/
configureTTLToInterrupt(inputInterruptConfig,inputTTLConfig);
enableTTLInterrupts(inputTTLConfig,TRUE);
/****5. Show Interrupt Handling (contains step 6)****/
bQuit = checkForTTLInterrupt(inputTTLConfig);
/*****7. Clear Module Configurations*****/
enableTTLInterrupts(inputTTLConfig,FALSE);
/*****8. Clear Board Configurations *****/
check_status(naibrd_UninstallISR(inputTTLConfig.cardIndex));
}
return bQuit;
}
Full Source — nai_ttl_int.c (SSK 1.x)
#include <stdio.h>
#include <stdlib.h>
/* Common Sample Program include files */
#include "include/naiapp_interrupt.h"
#include "include/naiapp_interrupt_ether.h"
#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 TTL Sample Program include files */
#include "nai_ttl_int.h"
#include "nai_ttl_cfg.h"
/* naibrd include files */
#include "nai.h"
#include "naibrd.h"
#include "naibrd_ether.h"
#include "functions/naibrd_ttl.h"
#include "maps/nai_map_ttl.h"
bool_t interruptOccured; /* used by checkForInterrupt to see if interrupt occurred */
int32_t frameCount; /* counter increment when frames are found */
uint32_t interruptVector; /* used by checkForInterrupt to get vector of the interrupt that occurred in myIsr */
/* Extern Functions or Variables*/
extern InterruptConfig inputInterruptConfig;
extern TtlConfig inputTTLConfig;
/**************************************************************************************************************/
/**
<summary>
This function configures an interrupt to occur when a message is received on any of the ttl channels.
</summary>
*/
/**************************************************************************************************************/
void configureTTLToInterrupt(InterruptConfig inputInterruptConfig, TtlConfig inputTTLConfig)
{
int32_t cardIndex = inputTTLConfig.cardIndex;
int32_t module = inputTTLConfig.module;
int32_t interrupt_Edge_Trigger = inputInterruptConfig.interrupt_Edge_Trigger;
int32_t steering = inputInterruptConfig.steering;
uint32_t rawstatus = 0;
int32_t chan;
enableTTLInterrupts(inputTTLConfig, FALSE);
/* Clear the Interrupt Status (Read the status and write back "1" to statuses which are set to clear the status) */
check_status(naibrd_TTL_GetGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, &rawstatus));
check_status(naibrd_TTL_ClearGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, rawstatus));
check_status(naibrd_TTL_GetGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, &rawstatus));
check_status(naibrd_TTL_ClearGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, rawstatus));
check_status(naibrd_TTL_GetGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_BIT_LATCHED, &rawstatus));
check_status(naibrd_TTL_ClearGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_BIT_LATCHED, rawstatus));
/* Setup the Interrupt Vector - map to the same vector */
check_status(naibrd_TTL_SetGroupInterruptVector(cardIndex, module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, NAI_TTL_LOHI_INTERRUPT_VECTOR));
check_status(naibrd_TTL_SetGroupInterruptVector(cardIndex, module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, NAI_TTL_HILO_INTERRUPT_VECTOR));
check_status(naibrd_TTL_SetGroupInterruptVector(cardIndex, module, 1, NAI_TTL_STATUS_BIT_LATCHED, NAI_TTL_BIT_INTERRUPT_VECTOR));
/* Setup the Latched Status Mode */
for (chan = 1; chan <= inputTTLConfig.maxChannel; chan++)
{
check_status(naibrd_TTL_SetEdgeLevelInterrupt(cardIndex, module, chan, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, (nai_ttl_interrupt_t)interrupt_Edge_Trigger));
check_status(naibrd_TTL_SetEdgeLevelInterrupt(cardIndex, module, chan, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, (nai_ttl_interrupt_t)interrupt_Edge_Trigger));
check_status(naibrd_TTL_SetEdgeLevelInterrupt(cardIndex, module, chan, NAI_TTL_STATUS_BIT_LATCHED, (nai_ttl_interrupt_t)interrupt_Edge_Trigger));
}
check_status(naibrd_TTL_SetGroupInterruptSteering(cardIndex, module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, steering));
check_status(naibrd_TTL_SetGroupInterruptSteering(cardIndex, module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, steering));
check_status(naibrd_TTL_SetGroupInterruptSteering(cardIndex, module, 1, NAI_TTL_STATUS_BIT_LATCHED, steering));
}
/**************************************************************************************************************/
/**
<summary>
Enables the channels within the (minChannel,maxChannel) range to interrupt
if enable is true and disables them otherwise.
</summary>
*/
/**************************************************************************************************************/
void enableTTLInterrupts(TtlConfig inputTTLConfig, bool_t enable) {
int32_t channel;
for (channel = inputTTLConfig.minChannel; channel <= inputTTLConfig.maxChannel; channel++)
{
check_status(naibrd_TTL_SetInterruptEnable(inputTTLConfig.cardIndex, inputTTLConfig.module, channel, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, enable));
check_status(naibrd_TTL_SetInterruptEnable(inputTTLConfig.cardIndex, inputTTLConfig.module, channel, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, enable));
check_status(naibrd_TTL_SetInterruptEnable(inputTTLConfig.cardIndex, inputTTLConfig.module, channel, NAI_TTL_STATUS_BIT_LATCHED, enable));
}
}
/**************************************************************************************************************/
/**
<summary>
The routine is called to handle a interrupt. This function alerts checkForInterrupt that an interrupt has occurred.
In vxWorks you must clear the Interrupt on the board within the ISR.
</summary>
*/
/**************************************************************************************************************/
#if defined (__VXWORKS__)
void basic_ISR_TTL(uint32_t param)
#else
void basic_ISR_TTL(void *param, uint32_t vector)
#endif
{
interruptOccured = TRUE;
#if defined (__VXWORKS__)
interruptVector = nai_Onboard_GetInterruptVector();
nai_Onboard_ClearInterrupt();
#elif defined (WIN32)
UNREFERENCED_PARAMETER(param);
interruptVector = vector;
#else
interruptVector = vector;
#endif
}
/**************************************************************************************************************/
/**
<summary>
Prompts user to check if an interrupt has occurred. MyIsr() should be installed if using this function.
</summary>
*/
/**************************************************************************************************************/
bool_t checkForTTLInterrupt(TtlConfig inputTTLConfig) {
bool_t bQuit;
int32_t cardIndex;
int32_t module;
uint32_t rawstatus = 0;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
cardIndex = inputTTLConfig.cardIndex;
module = inputTTLConfig.module;
bQuit = FALSE;
interruptOccured = FALSE;
while (!bQuit) {
printf("\nPress enter to check if Lo-Hi or Hi-Lo interrupt Occurred (press Q to quit):");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit) {
if (interruptOccured) {
switch (interruptVector)
{
case NAI_TTL_LOHI_INTERRUPT_VECTOR:
printf("\nLo-Hi Interrupt Occurred");
check_status(naibrd_TTL_GetGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, &rawstatus));
break;
case NAI_TTL_HILO_INTERRUPT_VECTOR:
printf("\nHi-Lo Interrupt Occurred");
check_status(naibrd_TTL_GetGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, &rawstatus));
break;
}
printf("\nVector = %#x \n", interruptVector);
printf("Status = %#x \n", rawstatus);
interruptOccured = FALSE;
}
else {
printf("\nNo Interrupt Occurred");
}
printf("\n\nWould you like to clear the status register? (default:N):");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (inputBuffer[0] == 'y' || inputBuffer[0] == 'Y')
{
switch (interruptVector)
{
case NAI_TTL_LOHI_INTERRUPT_VECTOR:
check_status(naibrd_TTL_ClearGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, rawstatus));
break;
case NAI_TTL_HILO_INTERRUPT_VECTOR:
check_status(naibrd_TTL_ClearGroupStatusRaw(cardIndex, module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, rawstatus));
break;
}
}
}
}
return bQuit;
}
/**************************************************************************************************************/
/**
<summary>
GetTTLLatchStatusTriggerMode handles prompting the user for the trigger mode for the latched status register
(Edge Triggered or Level Triggered).
</summary>
*/
/**************************************************************************************************************/
bool_t GetTTLLatchStatusTriggerMode(int32_t* interrupt_Edge_Trigger)
{
bool_t bQuit;
uint32_t temp;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
printf("\nEnter Latched Status Trigger Mode (Edge=0, Level=1) (Default=0): ");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
if (inputResponseCnt > 0)
{
temp = (int32_t)atol((const char*)inputBuffer);
if (temp == 0 || temp == 1)
{
*interrupt_Edge_Trigger = temp;
}
else
{
printf("ERROR: Invalid Interrupt Trigger Mode.\n");
}
}
else
*interrupt_Edge_Trigger = 0;
}
return(bQuit);
}
/**************************************************************************************************************/
/**
<summary>
This routine takes in the vector of the TTL RX interrupt that has just occurred. And gets the status, fifo status,
and fifo data of all channels that interrupted, and prints it.
</summary>
*/
/**************************************************************************************************************/
void handleTTLInterrupt(uint32_t nVector) {
uint32_t rawstatus = 0;
printf("\n\nInterrupt Occurred \n\n");
if (inputInterruptConfig.bPromptForInterruptClear)
{
promptUserToClearInterrupt_TTL();
}
switch (nVector)
{
case NAI_TTL_LOHI_INTERRUPT_VECTOR:
printf("LoHi Transition Interrupt\n");
check_status(naibrd_TTL_GetGroupStatusRaw(inputTTLConfig.cardIndex, inputTTLConfig.module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, &rawstatus));
check_status(naibrd_TTL_ClearGroupStatusRaw(inputTTLConfig.cardIndex, inputTTLConfig.module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, rawstatus));
break;
case NAI_TTL_HILO_INTERRUPT_VECTOR:
printf("HiLo Transition Interrupt\n");
check_status(naibrd_TTL_GetGroupStatusRaw(inputTTLConfig.cardIndex, inputTTLConfig.module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, &rawstatus));
check_status(naibrd_TTL_ClearGroupStatusRaw(inputTTLConfig.cardIndex, inputTTLConfig.module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, rawstatus));
break;
case NAI_TTL_BIT_INTERRUPT_VECTOR:
printf("BIT Interrupt\n");
check_status(naibrd_TTL_GetGroupStatusRaw(inputTTLConfig.cardIndex, inputTTLConfig.module, 1, NAI_TTL_STATUS_BIT_LATCHED, &rawstatus));
check_status(naibrd_TTL_ClearGroupStatusRaw(inputTTLConfig.cardIndex, inputTTLConfig.module, 1, NAI_TTL_STATUS_BIT_LATCHED, rawstatus));
break;
}
printInterruptInformation_TTL(nVector, rawstatus, FALSE);
}
void promptUserToClearInterrupt_TTL()
{
/* Prompt the user to clear the interrupt received */
SetUserRequestClearInt(FALSE);
printf("\n");
DisplayMessage_TTLInterrupt(MSG_USER_CLEAR_TTL_INT);
/* Wait for the user to respond */
while (!GetUserRequestClearInt())
{
nai_msDelay(10);
}
}
/**************************************************************************************************************/
/**
<summary>
DisplayMessage_TTLInterrupt handles displaying the messages associated with the msgId passed in.
</summary>
*/
/**************************************************************************************************************/
void DisplayMessage_TTLInterrupt(int32_t msgId)
{
switch (msgId)
{
case (int32_t)MSG_BANNER_TTL_INT:
{
printf("\n********************************************************************************");
printf("\n****** TTL INTERRUPT ******");
printf("\nAn interrupt will occur when the TTL Module receives an interrupt status ");
printf("\n********************************************************************************");
}
break;
case (int32_t)MSG_USER_TRIGGER_TTL_INT:
{
printf("\nPress \"Q\" to quit the application.\nPlease trigger TTL Lo-Hi,Hi-Lo or BIT status interrupt:");
}
break;
case (int32_t)MSG_USER_CLEAR_TTL_INT:
{
printf("Press \"C\" to clear interrupts... ");
}
break;
}
}
/**************************************************************************************************************/
/**
<summary>
Function will print the Data provided for each channel that has been set in the status register.
It will also print the status and idr id or vector.
</summary>
*/
/**************************************************************************************************************/
void printInterruptInformation_TTL(int32_t interruptID, uint32_t status, bool_t isEther)
{
printf("-----------------------------------\n");
if (isEther)
printf("\nIDR ID = %#x \n", interruptID);
else
printf("\nVector = %#x \n", interruptID);
printf("Status = %#x \n", status);
}
void ClearInterrupt_TTL()
{
uint32_t status;
if (inputInterruptConfig.bPromptForInterruptClear)
{
promptUserToClearInterrupt_TTL();
}
naibrd_TTL_GetGroupStatusRaw(inputTTLConfig.cardIndex, inputTTLConfig.module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, &status);
naibrd_TTL_ClearGroupStatusRaw(inputTTLConfig.cardIndex, inputTTLConfig.module, 1, NAI_TTL_STATUS_LO_HI_TRANS_LATCHED, status);
naibrd_TTL_GetGroupStatusRaw(inputTTLConfig.cardIndex, inputTTLConfig.module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, &status);
naibrd_TTL_ClearGroupStatusRaw(inputTTLConfig.cardIndex, inputTTLConfig.module, 1, NAI_TTL_STATUS_HI_LO_TRANS_LATCHED, status);
}