TTL/CMOS I/O Family Guide
Overview
The TL family is NAI’s line of programmable TTL/CMOS digital I/O smart function modules. A single TL module gives you 24 channels that can each act as a logic-level input (TTL/CMOS sensing, with programmable debounce and a per-channel pull-down/pull-up so you don’t need external resistors) or output (buffered push-pull drive from an internal 3.3 V supply or an external VCC you provide), with continuous background built-in test (BIT) watching every channel.
This page is the starting point if you’re working with any TL module. Use it to understand what the family does, pick TL1 or TL2, get a module wired up, confirm it’s talking, and drive or read your first channel from software. It’s aimed at engineers integrating logic-level digital I/O — interfacing to FPGAs, CPLDs, and other digital logic, sensing switch and relay-contact closures, driving status LEDs and logic-enable lines, and (on TL2) generating PWM or bit patterns and measuring pulse timing.
TL modules handle the 3.3 V / 5 V logic-level discrete signals found throughout embedded and rugged platforms. The channels are single-ended, system-ground referenced (non-isolated), optimized for 3.3 V logic and tolerant of inputs up to 5.5 V, with 24 mA of drive per output. Typical jobs: exchanging discrete state with other on-board logic, reading switch/contact inputs with debounce to reject relay chatter, driving indicators and enables — and, on TL2, generating clocks/PWM/patterns and measuring input pulse width, period, frequency, and edge counts.
TL is the logic-level member of NAI’s discrete/switching I/O families — single-ended TTL/CMOS I/O for talking to digital logic. The DT family covers field-level discrete I/O — higher-voltage relay, lamp, and solenoid loads switched from an external supply, with contact sensing up to ±80 V; the DF family uses differential (RS-422/485) lines for noise-hardened signaling over long, noisy runs; and the RY family uses mechanical relay contacts for galvanic isolation and switching AC, high voltage, or analog signals. Reach for TL when you’re talking to digital logic at TTL/CMOS levels rather than switching field loads.
TTL/CMOS modules at a glance
| Module | Channels | Type | What makes it different | Manual |
|---|---|---|---|---|
| TL1 | 24 | SF | Programmable TTL/CMOS input/output per channel; per-channel debounce, per-bank VCC select (internal 3.3 V or external), 24 mA drive, background BIT | TL1 Manual |
| TL2 | 24 | EF | TL1 features plus a per-channel Mode Select engine — PWM and pattern-generator outputs, pulse/timing measurement (width, period, frequency, edge counts & timestamps), transceiver mode, and synchronous triggering | TL2 Manual |
In the Type column, SF = Standard Function and EF = Enhanced Function. Both TL1 and TL2 give you the same 24-channel programmable TTL/CMOS I/O; an EF module does everything its SF counterpart does and adds a Mode Select engine on top — driving PWM and pattern-generator outputs, measuring input pulse width, period, frequency, edge counts and timestamps, plus transceiver and synchronous-trigger modes. If you only need to read and drive logic states, TL1 is enough; reach for TL2 when you need generated waveforms or timing measurement.
Choosing a member:
- Basic logic-level digital I/O — TL1 (24 channels, each an input or output, with debounce and per-channel status).
- PWM, pattern generation, or pulse/frequency/edge measurement — TL2. It does everything TL1 does and adds the enhanced Mode Select engine.
- Input pull-ups instead of pull-downs, or external VCC strapped to the internal +5 V — pick the matching strapping variant (below). These are factory build options of TL1/TL2, not separate software-selectable members.
Note
TL3/TL5/TL7 (off TL1) and TL4/TL6/TL8 (off TL2) are factory strapping options, not separately programmable members. They change the power-on input pull direction and the external-VCC strap; the channel count, registers, and
naibrd_TTL_*API are identical to TL1/TL2.
| Base | Variant | Strapping difference |
|---|---|---|
| TL1 | TL3 | EXT-VCC strapped to the internal +5 V supply |
| TL1 | TL5 | Inputs default to 100 kΩ pull-ups (vs. pull-downs) |
| TL1 | TL7 | Input pull-ups and EXT-VCC strapped to internal +5 V |
| TL2 | TL4 | EXT-VCC strapped to the internal +5 V supply |
| TL2 | TL6 | Inputs default to 100 kΩ pull-ups |
| TL2 | TL8 | Input pull-ups and EXT-VCC strapped to internal +5 V |
Physical setup
TTL/CMOS wiring differs per module — the specific pinout is the one thing that changes between TL members — so the per-module pinout always comes from that module’s manual. The setup pattern, though, is the same for every TL module:
- Identify the module’s slot number on your NAI motherboard or system. (NAI function modules are COSA modules, factory-installed into the system’s module slots.)
- Bring the I/O out through the breakout board. The slot’s pins appear on the breakout as generic IO# numbers.
- Map IO# pins to the module’s TTL/CMOS channels — either by reading the pinout in the module’s manual or by laying the module’s pinout overlay card over its MOD# pins on the breakout.
- Wire your logic signals (driving logic, switches/contacts, logic loads) to the mapped pins.
A few electrical points hold for every TL module:
- Single-ended and system-ground referenced. All channels share a common ground tied to system ground — these are non-isolated logic-level signals, not isolated field I/O. (For isolated, higher-voltage discrete loads, that’s the DT family.)
- Inputs. Each input channel has an internal 100 kΩ pull-down (pull-up on the TL5/6/7/8 strapping variants), so an open line reads a clean logic 0 without an external resistor. Logic thresholds are V_in_L ≤ 0.8 V = “0” and V_in_H ≥ 2.0 V = “1”, with a 5.5 V absolute input maximum. The thresholds are ratiometric with the channel’s VCC — if you run a bank on an external VCC, the effective threshold scales with it.
- Outputs. Each output is a buffered push-pull driver good for 24 mA sink or source (V_out_L ≤ 0.55 V, V_out_H ≥ 2.4 V). The drive voltage is selected per bank of six channels: the internal 3.3 V supply for 3.3 V logic, or an external VCC you supply (1.2–5.5 V normal range) to match a 5 V logic environment.
- Overcurrent protection. If an output draws too much (e.g., a shorted line), the module resets that channel to input mode and latches an overcurrent status; you clear it with an overcurrent reset (see Features).
- TL2 channels do more than static logic levels. Beyond the input/output behavior above, a TL2 channel’s pins can also carry PWM, clocked bit patterns, and pulse/timing measurements — see Output Functionality Capability for the full enhanced-I/O reference (and Features below for the calls).
Two common cases make this concrete (use your module’s pinout for the actual pin numbers):
- Drive a logic output or indicator. Set the channel’s I/O format to output, choose the VCC source for its bank (internal 3.3 V, or external VCC to drive 5 V logic), then write the channel high or low. The buffered output holds that state and sources/sinks up to 24 mA — enough for a logic input on another device or a small indicator.
- Sense a switch, contact, or logic line as an input. Leave the channel as an input (the power-on default) and let its internal 100 kΩ pull-down hold an open line at logic 0; wire your switch or driving logic so a closed/active state pulls the channel to a valid logic high (≥ 2.0 V). Add debounce to reject contact chatter from mechanical switches and relays (see Features).
The TL module manuals are functional/register references (pinouts included); they have no install steps. Connector and cabling specifics live in Connectors and Cabling Info; powering and reaching the board is covered in Connecting to Boards.
Software
There’s nothing TL-specific about which software you run. The NAI SSK (naibrd library) is identical across every OS and architecture — PetaLinux, VxWorks, DEOS, Windows. What OS you’re on is determined by your motherboard/SBC, not by the TL module. The only family-specific part is which API functions you call: TL modules use the naibrd_TTL_* calls, each taking the cardIndex / module / channel coordinates of the channel you’re driving (a few bank- and module-level calls take bank or just the module instead).
The naibrd_TTL_* API is stateless — every function takes those coordinates and acts immediately on the hardware; there is no Open/Init/Free lifecycle to manage. To adapt code to whichever TL module is installed, read the module type with naibrd_GetModuleName() (SSK 2.x; naibrd_GetModuleID() in 1.x) and its channel count with naibrd_TTL_GetChannelCount().
Where to find what you need:
- Which functions/registers to call — the specific module’s manual (TL1 Manual or TL2 Manual) documents every
naibrd_TTL_*register. - Building and deploying on your platform — Connecting to Boards covers the toolchain, deployment, and terminal access for PetaLinux/ARM Linux, VxWorks, DEOS, and Windows.
- Launching the app on the board itself — Running Applications from the Target walks through loading and launching your executable on the target.
Example — a 68ARM2 SBC with a TL1 module: pull the naibrd_TTL_* calls from the TL1 Manual, set up the ARM Linux toolchain per Connecting to Boards, then load and launch a TL sample on the target per Running Applications from the Target.
Confirm communication
TL gives you an easy first check that needs no external wiring: Built-In Test. Every TL channel runs a continuous background BIT that validates the channel and compares each output’s readback against the state you commanded, so a clean BIT confirms your board connection, the SSK, and the module are all working — before you wire anything up.
Read each channel’s latched BIT status; if none reports a fault, the module is healthy end-to-end:
bool_t pbitComplete;
nai_status_bit_t bitFailed;
/* SSK 2.x: confirm power-on BIT finished, then read a channel's latched BIT status */
naibrd_TTL_CheckPowerOnBITComplete(cardIndex, module, &pbitComplete);
if (pbitComplete)
naibrd_TTL_GetChanMappedStatus(cardIndex, module, channel,
NAIBRD_TTL_STATUS_BIT_LATCHED, &bitFailed);If BIT is complete and no channel reports a fault, your connection, the SSK, and the module are confirmed. (In SSK 1.x, read the same status with naibrd_TTL_GetStatus(..., NAI_TTL_STATUS_BIT_LATCHED, ...); 1.x has no power-on-BIT-complete check.)
To prove the status path end-to-end with no external hardware, you can deliberately trip BIT: set a channel to output and drive it high with no external VCC connected — that open circuit is exactly what BIT flags. (This is what the ttl_summary sample’s “force BIT error” command does.)
/* Force a BIT fault on `channel` — external VCC must be OFF */
naibrd_TTL_SetIOFormat(cardIndex, module, channel, NAIBRD_TTL_IOFORMAT_OUTPUT);
naibrd_TTL_SetOutputState(cardIndex, module, channel, NAIBRD_TTL_STATE_HI);
naibrd_msDelay(300); /* let BIT detect the open circuit */
naibrd_TTL_GetChanMappedStatus(cardIndex, module, channel,
NAIBRD_TTL_STATUS_BIT_LATCHED, &bitFailed); /* now reads 1 */
naibrd_TTL_SetOutputState(cardIndex, module, channel, NAIBRD_TTL_STATE_LO);
naibrd_TTL_SetIOFormat(cardIndex, module, channel, NAIBRD_TTL_IOFORMAT_INPUT); /* restore */Seeing the BIT bit set on demand — and clear again afterward — confirms the whole read/status path is live. To also prove a physical output, set a channel high and read its state back with naibrd_TTL_GetOutputState, or jumper that output to another channel wired as an input and confirm it with naibrd_TTL_GetInputState. The TTL BasicOps and TTL Summary samples exercise all of this interactively.
Features
Each TL operation works per channel through the naibrd_TTL_* API (a few bank- and module-level calls take bank or just the module). The blocks below group the calls by what you’re doing, with the SSK 1.x and 2.x signatures side by side. The 1.x → 2.x change is mostly the enum-type rename nai_ttl_* → naibrd_ttl_*, plus GetStatus/ClearStatus → GetChanMappedStatus/ClearChanMappedStatus, SetOpMode → SetEnhancedMode, ReadFIFORaw → ReadFIFO, and the per-channel interrupt calls picking up the ChanMapped naming. The enhanced blocks at the end apply to TL2 only.
Configure a channel
What it does: set the channel’s I/O format (input or output), choose the output drive supply for its bank (internal 3.3 V or external VCC), and set the input debounce time. You can also read back the measured VCC of a bank.
Applies to: all TL modules.
Also on: VCC source is selected per bank of six channels (bank is 1-based); debounce LSB resolution is set module-wide with naibrd_TTL_SetDebounceLSB.
Relevant APIs:
/* SSK 1.x */
nai_status_t naibrd_TTL_SetIOFormat(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_ioformat_t format);
nai_status_t naibrd_TTL_GetIOFormat(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_ioformat_t* outformat);
nai_status_t naibrd_TTL_SetBankVCCSource(int32_t cardIndex, int32_t module, int32_t bank, nai_ttl_vcc_t source);
nai_status_t naibrd_TTL_GetBankVCCSource(int32_t cardIndex, int32_t module, int32_t bank, nai_ttl_vcc_t* outsource);
nai_status_t naibrd_TTL_GetBankVCCReading(int32_t cardIndex, int32_t module, int32_t bank, float64_t* outvoltage);
nai_status_t naibrd_TTL_SetDebounceTime(int32_t cardIndex, int32_t module, int32_t channel, float64_t debounceTime_mS);
nai_status_t naibrd_TTL_GetDebounceTime(int32_t cardIndex, int32_t module, int32_t channel, float64_t* outdebounceTime_mS);/* SSK 2.x */
nai_status_t naibrd_TTL_SetIOFormat(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_ioformat_t format);
nai_status_t naibrd_TTL_GetIOFormat(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_ioformat_t* p_outformat);
nai_status_t naibrd_TTL_SetBankVCCSource(int32_t cardIndex, int32_t module, int32_t bank, naibrd_ttl_vcc_t source);
nai_status_t naibrd_TTL_GetBankVCCSource(int32_t cardIndex, int32_t module, int32_t bank, naibrd_ttl_vcc_t* p_outsource);
nai_status_t naibrd_TTL_GetBankVCCReading(int32_t cardIndex, int32_t module, int32_t bank, float64_t* p_outvoltage);
nai_status_t naibrd_TTL_SetDebounceTime(int32_t cardIndex, int32_t module, int32_t channel, float64_t debounceTime_mS);
nai_status_t naibrd_TTL_GetDebounceTime(int32_t cardIndex, int32_t module, int32_t channel, float64_t* p_outdebounceTime_mS);Exercise it: TTL BasicOps
Drive outputs & read inputs
What it does: drive an output channel high or low, read an input channel’s logic state, and read back an output channel’s commanded state.
Applies to: all TL modules.
Also on: output state uses NAI[BRD]_TTL_STATE_HI / _LO; a channel must be in output format (SetIOFormat) before SetOutputState takes effect.
Relevant APIs:
/* SSK 1.x */
nai_status_t naibrd_TTL_SetOutputState(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_state_t state);
nai_status_t naibrd_TTL_GetOutputState(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_state_t* outstate);
nai_status_t naibrd_TTL_GetInputState(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_state_t* inputstate);/* SSK 2.x */
nai_status_t naibrd_TTL_SetOutputState(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_state_t state);
nai_status_t naibrd_TTL_GetOutputState(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_state_t* p_outstate);
nai_status_t naibrd_TTL_GetInputState(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_state_t* p_inputstate);Exercise it: TTL BasicOps
Status, BIT & protection
What it does: read per-channel status — BIT, overcurrent, and low→high / high→low transition (each in real-time and latched form; SSK 2.x adds summary, watchdog-timer, and inter-FPGA-fault status) — clear a latched status, and reset a channel after an overcurrent shutdown.
Applies to: all TL modules.
Also on: naibrd_TTL_Reset with NAI[BRD]_TTL_RESET_OVERCURRENT restores a channel that overcurrent shut down to input; _RESET_OVERCURRENT_STATUS clears just the latched status; ResetAll does it module-wide.
Relevant APIs:
/* SSK 1.x */
nai_status_t naibrd_TTL_GetStatus(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_status_type_t type, nai_status_bit_t* outstatusVal);
nai_status_t naibrd_TTL_ClearStatus(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_status_type_t type);
nai_status_t naibrd_TTL_Reset(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_reset_type_t resetType);
nai_status_t naibrd_TTL_ResetAll(int32_t cardIndex, int32_t module, nai_ttl_reset_type_t resetType);/* SSK 2.x */
nai_status_t naibrd_TTL_GetChanMappedStatus(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_status_type_t type, nai_status_bit_t* p_outstatusbit);
nai_status_t naibrd_TTL_ClearChanMappedStatus(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_status_type_t type);
nai_status_t naibrd_TTL_Reset(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_reset_type_t resetType);
nai_status_t naibrd_TTL_ResetAll(int32_t cardIndex, int32_t module, naibrd_ttl_reset_type_t resetType);Exercise it: TTL Summary
Interrupts
What it does: have the module raise an interrupt on a status event (BIT, overcurrent, or a logic transition) instead of polling — enable the interrupt per channel for a status type, choose edge vs. level triggering, and set the delivery vector and steering.
Applies to: all TL modules.
Also on: register your handler once per card with naibrd_ConnectISR(cardIndex, callback); vector and steering are set per group in 1.x and per module in 2.x.
Relevant APIs:
/* SSK 1.x */
nai_status_t naibrd_TTL_SetInterruptEnable(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_status_type_t type, bool_t enable);
nai_status_t naibrd_TTL_SetEdgeLevelInterrupt(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_status_type_t type, nai_ttl_interrupt_t interruptType);
nai_status_t naibrd_TTL_SetGroupInterruptVector(int32_t cardIndex, int32_t module, int32_t group, nai_ttl_status_type_t type, uint32_t vector);
nai_status_t naibrd_TTL_SetGroupInterruptSteering(int32_t cardIndex, int32_t module, int32_t group, nai_ttl_status_type_t type, naibrd_int_steering_t steering);/* SSK 2.x */
nai_status_t naibrd_TTL_SetChanMappedInterruptEnable(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_status_type_t statusType, bool_t enable);
nai_status_t naibrd_TTL_SetChanMappedInterruptTriggerType(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_status_type_t statusType, naibrd_int_trigger_type_t triggerType);
nai_status_t naibrd_TTL_SetChanMappedInterruptVector(int32_t cardIndex, int32_t module, naibrd_ttl_status_type_t statusType, uint32_t vector);
nai_status_t naibrd_TTL_SetChanMappedInterruptSteering(int32_t cardIndex, int32_t module, naibrd_ttl_status_type_t statusType, naibrd_int_steering_t steering);Exercise it: TTL Basic Interrupt · TTL Summary
Enhanced input measurement (TL2 only)
What it does / when: put an input channel into one of TL2’s measurement modes — measure high time or low time, timestamp rising / falling / all edges, count rising / falling / all edges, or measure period or frequency. Measurements stream through a per-channel FIFO (512 deep) that you drain as it fills.
Applies to: TL2 only (Enhanced-Function member).
Also on: select the mode with SetEnhancedMode (2.x) / SetOpMode (1.x) using a NAI[BRD]_TTL_MODE_* constant, set the measurement timebase with SetTimebaseInterval, then enable the channel’s trigger; read accumulated edge counts directly with GetCountData.
Relevant APIs:
/* SSK 1.x */
nai_status_t naibrd_TTL_SetOpMode(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_enhanced_mode_t mode);
nai_status_t naibrd_TTL_SetEnhanceTriggerEnable(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_enable_t enable);
nai_status_t naibrd_TTL_SetTimebaseInterval(int32_t cardIndex, int32_t module, int32_t channel, float64_t interval);
nai_status_t naibrd_TTL_GetFIFOCount(int32_t cardIndex, int32_t module, int32_t channel, uint32_t* outcount);
nai_status_t naibrd_TTL_ReadFIFORaw(int32_t cardIndex, int32_t module, int32_t channel, uint32_t count, uint32_t outdata[], uint32_t* outread);
nai_status_t naibrd_TTL_GetCountData(int32_t cardIndex, int32_t module, int32_t channel, uint32_t* outcount);/* SSK 2.x */
nai_status_t naibrd_TTL_SetEnhancedMode(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_enhanced_mode_t mode);
nai_status_t naibrd_TTL_SetEnhanceTriggerEnable(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_enable_type_t enable);
nai_status_t naibrd_TTL_SetTimebaseInterval(int32_t cardIndex, int32_t module, int32_t channel, float64_t interval);
nai_status_t naibrd_TTL_GetFIFOCount(int32_t cardIndex, int32_t module, int32_t channel, int32_t* p_outcount);
nai_status_t naibrd_TTL_ReadFIFO(int32_t cardIndex, int32_t module, int32_t channel, int32_t count, int32_t timeout, uint32_t outdata[], int32_t* p_outcount, int32_t* p_outcountRemaining);
nai_status_t naibrd_TTL_GetCountData(int32_t cardIndex, int32_t module, int32_t channel, int32_t* p_outcount);Exercise it: TTL Measure.
PWM output (TL2 only)
What it does / when: drive a channel as a pulse-width-modulated output — set the period, pulse width, and polarity, optionally run a fixed burst of cycles instead of running forever, then start and stop it.
Applies to: TL2 only.
Also on: put the channel in a PWM output mode first (SetEnhancedMode / SetOpMode with NAI[BRD]_TTL_MODE_OUTPUT_PWM_FOREVER or _OUTPUT_PWM_CYCLE_NUM_TIMES); SetPWM_BurstNum sets the cycle count for the burst mode.
Relevant APIs:
/* SSK 1.x */
nai_status_t naibrd_TTL_SetPWM_Period(int32_t cardIndex, int32_t module, int32_t channel, float64_t period);
nai_status_t naibrd_TTL_SetPWM_Pulsewidth(int32_t cardIndex, int32_t module, int32_t channel, float64_t pulsewidth);
nai_status_t naibrd_TTL_SetPWM_Polarity(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_pwm_polarity_t polarity);
nai_status_t naibrd_TTL_SetPWM_BurstNum(int32_t cardIndex, int32_t module, int32_t channel, uint32_t burstNum);
nai_status_t naibrd_TTL_StartPWM(int32_t cardIndex, int32_t module, int32_t channel);
nai_status_t naibrd_TTL_StopPWM(int32_t cardIndex, int32_t module, int32_t channel);/* SSK 2.x */
nai_status_t naibrd_TTL_SetPWM_Period(int32_t cardIndex, int32_t module, int32_t channel, float64_t period);
nai_status_t naibrd_TTL_SetPWM_Pulsewidth(int32_t cardIndex, int32_t module, int32_t channel, float64_t pulsewidth);
nai_status_t naibrd_TTL_SetPWM_Polarity(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_pwm_polarity_t polarity);
nai_status_t naibrd_TTL_SetPWM_BurstNum(int32_t cardIndex, int32_t module, int32_t channel, int32_t burstNum);
nai_status_t naibrd_TTL_StartPWM(int32_t cardIndex, int32_t module, int32_t channel);
nai_status_t naibrd_TTL_StopPWM(int32_t cardIndex, int32_t module, int32_t channel);Exercise it: TTL PWM
Pattern generator (TL2 only)
What it does / when: clock out an arbitrary bit pattern across the module’s channels from a RAM buffer — load the pattern, set the start/end address window and the per-step period, then run it continuously or for a set number of bursts.
Applies to: TL2 only. The pattern generator is module-level (it drives the channels together), so most of these calls take the module rather than a single channel.
Also on: SetPatternGenCtrl toggles the control bits (NAI[BRD]_TTL_CTRL_PATTERN_ENABLE, _BURST, _PAUSE, _SYNC_RISING, _SYNC_FALLING); in 2.x, naibrd_TTL_EnablePatternGen is the per-channel enable.
Relevant APIs:
/* SSK 1.x */
nai_status_t naibrd_TTL_SetPatternGenPeriod(int32_t cardIndex, int32_t module, int32_t channel, float64_t period_mS);
nai_status_t naibrd_TTL_SetPatternGenBuf(int32_t cardIndex, int32_t module, int32_t dataPatternLen, uint32_t* dataPattern);
nai_status_t naibrd_TTL_SetPatternGenStartAddr(int32_t cardIndex, int32_t module, uint32_t startAddr);
nai_status_t naibrd_TTL_SetPatternGenEndAddr(int32_t cardIndex, int32_t module, uint32_t EndAddr);
nai_status_t naibrd_TTL_SetPatternGen_BurstNum(int32_t cardIndex, int32_t module, uint32_t burstNum);
nai_status_t naibrd_TTL_SetPatternGenCtrl(int32_t cardIndex, int32_t module, nai_ttl_pattern_ctrl_t controlBit, nai_ttl_enable_t state);/* SSK 2.x */
nai_status_t naibrd_TTL_EnablePatternGen(int32_t cardIndex, int32_t module, int32_t channel, bool_t enable);
nai_status_t naibrd_TTL_SetPatternGenPeriod(int32_t cardIndex, int32_t module, float64_t period_mS);
nai_status_t naibrd_TTL_SetPatternGenBuf(int32_t cardIndex, int32_t module, int32_t dataPatternLen, const uint32_t* dataPattern);
nai_status_t naibrd_TTL_SetPatternGenStartAddr(int32_t cardIndex, int32_t module, uint32_t startAddr);
nai_status_t naibrd_TTL_SetPatternGenEndAddr(int32_t cardIndex, int32_t module, uint32_t EndAddr);
nai_status_t naibrd_TTL_SetPatternGen_BurstNum(int32_t cardIndex, int32_t module, int32_t burstNum);
nai_status_t naibrd_TTL_SetPatternGenCtrl(int32_t cardIndex, int32_t module, naibrd_ttl_pattern_ctrl_t controlBit, naibrd_ttl_enable_type_t state);Exercise it: TTL Pattern Generator
Transceiver mode & synchronous triggering (TL2 only)
What it does / when: Transceiver mode switches multiple channels’ input/output direction together in a single operation; synchronous triggering with preload designates a master trigger channel that releases preloaded output states on the other 23 channels at once (and can extend to additional modules). Both are TL2 enhancements for coordinated, deterministic multi-channel switching.
Applies to: TL2 only.
Also on: these modes are set up through the channel’s enhanced-mode/trigger configuration and the pattern-generator sync control bits — arm a channel’s trigger with naibrd_TTL_SetEnhanceTriggerEnable, and use the _SYNC_RISING / _SYNC_FALLING control bits via SetPatternGenCtrl. The register-level setup (master-trigger selection, preload, transceiver grouping) is detailed in the TL2 Manual.
Relevant APIs:
/* SSK 1.x */
nai_status_t naibrd_TTL_SetEnhanceTriggerEnable(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_enable_t enable);
nai_status_t naibrd_TTL_GetEnhanceTriggerEnable(int32_t cardIndex, int32_t module, int32_t channel, nai_ttl_enable_t* outenable);/* SSK 2.x */
nai_status_t naibrd_TTL_SetEnhanceTriggerEnable(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_enable_type_t enable);
nai_status_t naibrd_TTL_GetEnhanceTriggerEnable(int32_t cardIndex, int32_t module, int32_t channel, naibrd_ttl_enable_type_t* p_outenable);Exercise it: see the pattern-generator and measurement samples above, and the TL2 Manual for the register-level trigger/transceiver setup.
Self-test
To confirm a channel end-to-end with no external hardware, use the background BIT read and the force-BIT-error trick covered under Confirm communication above — then prove a physical output by reading it back or jumpering it to another channel’s input.
Try it
The TTL BasicOps sample application is the fastest way to drive a real TL channel. It exercises the core operations you’ll reuse in your own code: setting a channel’s I/O format, driving and reading state, configuring debounce and the bank VCC source, and reading channel status. It supports TL1 and TL2, so it works for any member of the family.
Getting connected. Every NAI sample app — BasicOps included — starts with the same connection flow before any TL call: naiapp_RunBoardMenu() opens (or loads a saved) board connection, then naiapp_query_CardIndex() and naiapp_query_ModuleNumber() ask which board and which module slot to talk to. Under the hood the connection is opened with naibrd_OpenDevice(). The result is the three coordinates every naibrd_TTL_* call takes: cardIndex (which opened board), module (the slot the TL module sits in), and channel (the channel on that module, numbered from 1). When you write your own program, you do the same — see Opening a Software Handle to Your Board.
Once connected, configuring and driving a channel looks like this:
/* SSK 1.x */
/* Drive channel 1 as an output, then set it high */
naibrd_TTL_SetIOFormat(cardIndex, module, 1, NAI_TTL_IOFORMAT_OUTPUT);
naibrd_TTL_SetOutputState(cardIndex, module, 1, NAI_TTL_STATE_HI);
/* Read channel 2 as an input, with debounce to reject contact chatter */
naibrd_TTL_SetIOFormat(cardIndex, module, 2, NAI_TTL_IOFORMAT_INPUT);
naibrd_TTL_SetDebounceTime(cardIndex, module, 2, 1.0); /* 1 ms */
naibrd_TTL_GetInputState(cardIndex, module, 2, &state);/* SSK 2.x */
/* Drive channel 1 as an output, then set it high */
naibrd_TTL_SetIOFormat(cardIndex, module, 1, NAIBRD_TTL_IOFORMAT_OUTPUT);
naibrd_TTL_SetOutputState(cardIndex, module, 1, NAIBRD_TTL_STATE_HI);
/* Read channel 2 as an input, with debounce to reject contact chatter */
naibrd_TTL_SetIOFormat(cardIndex, module, 2, NAIBRD_TTL_IOFORMAT_INPUT);
naibrd_TTL_SetDebounceTime(cardIndex, module, 2, 1.0); /* 1 ms */
naibrd_TTL_GetInputState(cardIndex, module, 2, &state);Outputs default to the internal 3.3 V supply; to drive a 5 V logic environment, select the external VCC source for that channel’s bank first with naibrd_TTL_SetBankVCCSource(cardIndex, module, bank, NAI[BRD]_TTL_VCC_EXTERNAL) (banks are groups of six channels).
On a TL2, set the channel’s enhanced mode before using PWM, pattern, or measurement features — they’re inactive in standard I/O mode. A continuous PWM output looks like this:
/* SSK 1.x — TL2: continuous 1 kHz PWM at 25% duty on channel 1 */
naibrd_TTL_SetIOFormat(cardIndex, module, 1, NAI_TTL_IOFORMAT_OUTPUT);
naibrd_TTL_SetOpMode(cardIndex, module, 1, NAI_TTL_MODE_OUTPUT_PWM_FOREVER);
naibrd_TTL_SetPWM_Period(cardIndex, module, 1, 1.0); /* period in ms -> 1 kHz */
naibrd_TTL_SetPWM_Pulsewidth(cardIndex, module, 1, 0.25); /* 0.25 ms -> 25% duty */
naibrd_TTL_StartPWM(cardIndex, module, 1);
/* ... later ... */
naibrd_TTL_StopPWM(cardIndex, module, 1);/* SSK 2.x — TL2: continuous 1 kHz PWM at 25% duty on channel 1 */
naibrd_TTL_SetIOFormat(cardIndex, module, 1, NAIBRD_TTL_IOFORMAT_OUTPUT);
naibrd_TTL_SetEnhancedMode(cardIndex, module, 1, NAIBRD_TTL_MODE_OUTPUT_PWM_FOREVER);
naibrd_TTL_SetPWM_Period(cardIndex, module, 1, 1.0); /* period in ms -> 1 kHz */
naibrd_TTL_SetPWM_Pulsewidth(cardIndex, module, 1, 0.25); /* 0.25 ms -> 25% duty */
naibrd_TTL_StartPWM(cardIndex, module, 1);
/* ... later ... */
naibrd_TTL_StopPWM(cardIndex, module, 1);Run it: TTL BasicOps (SSK 2.x) · TTL PWM · TTL Measure · TTL Pattern Generator. For the full set of TL sample apps — and how to build and run them — see the sample-application guides: Using NAI SSK 2.x Sample Applications · Using NAI SSK 1.x Sample Applications.
Hardware capabilities and status monitoring
This section covers what the TL hardware provides and what you can monitor — for how to drive each operation (with the 1.x/2.x APIs), see Features above.
Hardware capabilities. Every TL module gives you 24 channels, each independently an input or output. Inputs have an internal 100 kΩ pull-down (pull-up on the strapping variants), fixed TTL/CMOS thresholds, and a programmable debounce filter. Outputs are buffered push-pull drivers good for 24 mA, drawing from a per-bank-of-six VCC source (internal 3.3 V or an external 1.2–5.5 V supply you provide, with VCC readback), and protected by overcurrent shutdown that latches the channel back to input until reset. Continuous background BIT validates every channel and checks each output’s readback against the commanded state. TL2 adds the Mode Select engine — PWM and pattern-generator outputs, pulse/timing measurement through a 512-deep per-channel FIFO, transceiver mode, and synchronous triggering. (SSK 2.x also surfaces a user watchdog timer and module power-reset control.)
Statuses you can monitor programmatically. TL modules expose a set of status registers, most available in both a real-time (current value) and latched (sticky until you clear it) form:
| Status | What it tells you | Example constant / call |
|---|---|---|
| Summary (SSK 2.x) | Whether any fault (BIT or overcurrent) is set on a channel — one bit per channel, so a single register reveals if anything is wrong module-wide | NAIBRD_TTL_STATUS_SUMMARY_LATCHED |
| BIT | Built-In Test detected a fault on a channel | NAIBRD_TTL_STATUS_BIT_LATCHED / NAI_TTL_STATUS_BIT_LATCHED |
| Overcurrent | An output exceeded its current limit and was shut down (reset to input) | NAIBRD_TTL_STATUS_OVERCURRENT_LATCHED |
| Logic transition | A low→high or high→low edge was seen on a channel | NAIBRD_TTL_STATUS_LO_HI_TRANS_LATCHED, _HI_LO_TRANS_LATCHED |
| FIFO event (TL2) | Measurement FIFO empty / almost-empty / almost-full / full | naibrd_TTL_GetFIFOEventMappedStatus |
| Watchdog / inter-FPGA fault (SSK 2.x) | Internal module-health faults | NAIBRD_TTL_STATUS_WATCHDOG_TIMER_FAULT_LATCHED, _INTER_FPGA_FAULT_LATCHED |
Read these per channel with naibrd_TTL_GetChanMappedStatus (2.x) / naibrd_TTL_GetStatus (1.x), or poll the bit-mapped summary register to check every channel at once. The TTL Summary sample app is built around exactly this — displaying latched BIT, overcurrent, and summary status per channel, clearing latched status, and forcing a BIT error for diagnostic testing. NAI’s ESP2 Embedded Soft Panel can also show module status interactively if you’d rather watch it in a GUI first.
Common pitfalls
- An output won’t drive, or drives the wrong logic level. Outputs need the channel in output format and a VCC source set for its bank. By default a bank uses the internal 3.3 V supply; to drive a 5 V environment, select external VCC for that bank (
naibrd_TTL_SetBankVCCSource) and supply it — VCC is chosen per bank of six channels, not per channel. - An input reads stuck high or low. The internal 100 kΩ pull-down holds an open line at logic 0 (pull-up variants hold it at 1). Make sure your source actually reaches a valid threshold — ≥ 2.0 V for a high, ≤ 0.8 V for a low — and remember the thresholds are ratiometric with the bank’s VCC, so an external VCC shifts them.
- A switch or relay input flickers or double-triggers. That’s contact bounce. Set a debounce time (
naibrd_TTL_SetDebounceTime) to filter it — but don’t set it so long that it swallows the real fast pulses you care about. - A channel stopped driving and became an input on its own. That’s overcurrent protection latching the channel to input. Read overcurrent status, clear the fault condition, then call
naibrd_TTL_ResetwithNAI[BRD]_TTL_RESET_OVERCURRENTto restore drive — it won’t recover by itself. It’s good practice to issue this reset whenever you clear status. - Enhanced features do nothing. PWM, pattern generation, and measurement are TL2 only — TL1 returns
NAI_ERROR_NOT_SUPPORTED. Even on TL2, you must set the channel’s enhanced mode (SetEnhancedMode/SetOpMode) before those calls take effect. - The summary bit reasserts right after you clear it. Clear the underlying BIT and overcurrent status too, not just the summary bit — if the source fault is still latched, summary comes right back.
- The force-BIT-error self-test doesn’t trip. External VCC must be disconnected from the test channel — the trick relies on the open-circuit condition, so a connected VCC lets the output drive normally and no BIT error occurs.
- An input above 5.5 V. The absolute input maximum is 5.5 V; higher risks damaging the channel.
Related resources
- DT Family Guide — field-level single-ended discrete I/O (relay/lamp/solenoid loads, contact sensing, isolated options)
- DF Family Guide — differential RS-422/485 I/O (noise-hardened over long runs)
- RY Family Guide — mechanical relay contacts (galvanic isolation; AC, high-voltage, analog switching)
- Download the SSK — get the library and sample apps
- Connecting to Boards — power, network, terminal, and file transfer to your board
- Opening a Software Handle to Your Board — establish the connection your
naibrd_TTL_*calls run against - Running Applications from the Target — load and launch a built sample on the board
- TL1 Manual — TL1 (Standard Function) registers and the full
naibrd_TTL_*API - TL2 Manual — TL2 (Enhanced Function) registers, enhanced modes, PWM/pattern/measurement
- Using NAI SSK 2.x Sample Applications · Using NAI SSK 1.x Sample Applications — build and run the TL samples across platforms
- ESP2 Quick Start — exercise TTL channels with no code via the Embedded Soft Panel
