The following Python for Windows example demonstrates device discovery and the ability for the USB-CTR04 to sample the digital port at the same rate as the counter channels.
This is a one-shot acquisition not continuous that uses ul.daq_in_scan() instead of ul.c_in_scan(). Using One-shot or finite mode (default), the ul.daq_in_scan() function will block until the data has been capture to the buffer.
The program configures two counter channels, the first counter is set to range counting. The output line is enabled and is control by the output registers. The second counter is configured to measure period. The idea is that the first counter divides the incoming frequency by 100 and the second counter measures it. For a test signal, the TMR0 line (frequency pulse output) is use. Recommended connection are TMR0-->C0IN and C0O-->C1IN
To install the Python for Windows support, copy the following URL into your browser and follow the instructions provided.
This example was built using Python 3.8.5 and PyCharm. The code module is provided via a download link at the end of this article.
"""
File: daq_in_scan_usb_1800.py
Library Call Demonstrated: mcculw.ul.daq_in_scan()
Purpose: Synchronously scans digital port and counters
in the foreground using USB-CTR04 64-bit support.
Demonstration: Collects data from digital port, and two counter inputs.
Other Library Calls: mcculw.ul.ignore_instacal()
mcculw.ul.get_daq_device_inventory()
mcculw.ul.create_daq_device()
mcculw.ul.win_buf_alloc_64()
mcculw.ul.c_config_scan()
mcculw.ul.pulse_out_start()
mcculw.ul.daq_in_scan()
mcculw.ul.pulse_out_stop()
mcculw.ul.win_buf_free()
mcculw.ul.release_daq_device()
Special Requirements: This examples filters on the USB-1808 Series.
"""
from __future__ import absolute_import, division, print_function
from builtins import * # @UnusedWildImport
from ctypes import cast, POINTER, c_double, c_ulonglong, c_ubyte, c_ushort
from mcculw import ul
from mcculw.enums import ScanOptions, ChannelType, ULRange, DigitalPortType
from mcculw.enums import InterfaceType, FunctionType
from mcculw.enums import CounterMode, CounterDebounceMode, CounterDebounceTime
from mcculw.enums import CounterEdgeDetection, CounterTickSize, CounterRegister
import ctypes
def run_example():
use_device_detection = True
memhandle = None
try:
# **********Begin Device Discovery************
board_num = 0
board_index = 0
find_device = "USB-CTR04"
if use_device_detection:
board_num = -1
ul.ignore_instacal()
dev_list = ul.get_daq_device_inventory(InterfaceType.USB)
if len(dev_list) > 0:
for device in dev_list:
if str(device) == find_device:
print(f"Found {find_device} board number = {board_index}")
print(f"Serial number: {device.unique_id}")
print(f"Product type: {hex(device.product_id)}")
board_num = board_index
ul.create_daq_device(board_num, device)
break
board_index = board_index + 1
if board_num == -1:
print(f"Device {find_device} not found")
else:
print("No devices detected")
# **********End of Device Discovery************
# configure counter to count from x to x+n with active output
ctr0_mode = CounterMode.TOTALIZE | CounterMode.RANGE_LIMIT_ON | CounterMode.OUTPUT_ON
if board_num > -1:
ul.c_config_scan(board_num,
0,
ctr0_mode,
CounterDebounceTime.DEBOUNCE_NONE,
CounterDebounceMode.TRIGGER_AFTER_STABLE,
CounterEdgeDetection.RISING_EDGE,
CounterTickSize.TICK20PT83ns,
0)
# divide by 100
ul.c_load_32(board_num, CounterRegister.MINLIMITREG0, 0)
ul.c_load_32(board_num, CounterRegister.MAXLIMITREG0, 99)
# Output will toggle high at zero and low at 49 50% duty cycle
ul.c_load_32(board_num, CounterRegister.OUTPUTVAL0REG0, 0)
ul.c_load_32(board_num, CounterRegister.OUTPUTVAL1REG0, 49)
# measure period of counter 0 output; connect C0O to C1IN.
ul.c_config_scan(board_num,
1,
CounterMode.PERIOD,
CounterDebounceTime.DEBOUNCE500ns,
CounterDebounceMode.TRIGGER_AFTER_STABLE,
CounterEdgeDetection.RISING_EDGE,
CounterTickSize.TICK20PT83ns,
0)
# tick_size is the amount of time assigned to single count
tick_size = 0.000000020830
# Create the daq_in_scan channel configuration lists
chan_list = []
chan_type_list = []
gain_list = []
chan_list.append(0)
chan_type_list.append(ChannelType.CTRBANK0)
gain_list.append(ULRange.NOTUSED)
chan_list.append(0)
chan_type_list.append(ChannelType.CTRBANK1)
gain_list.append(ULRange.NOTUSED)
chan_list.append(0)
chan_type_list.append(ChannelType.CTRBANK2)
gain_list.append(ULRange.NOTUSED)
chan_list.append(0)
chan_type_list.append(ChannelType.CTRBANK3)
gain_list.append(ULRange.NOTUSED)
chan_list.append(1)
chan_type_list.append(ChannelType.CTRBANK0)
gain_list.append(ULRange.NOTUSED)
chan_list.append(1)
chan_type_list.append(ChannelType.CTRBANK1)
gain_list.append(ULRange.NOTUSED)
chan_list.append(1)
chan_type_list.append(ChannelType.CTRBANK2)
gain_list.append(ULRange.NOTUSED)
chan_list.append(1)
chan_type_list.append(ChannelType.CTRBANK3)
gain_list.append(ULRange.NOTUSED)
# The digital port is 8-bits but it's
# treated as a 16-bit register; use PADZERO as a
# dummy place holder to for the other 48-bits
chan_list.append(DigitalPortType.AUXPORT)
chan_type_list.append(ChannelType.DIGITAL8)
gain_list.append(ULRange.NOTUSED)
chan_list.append(DigitalPortType.AUXPORT)
chan_type_list.append(ChannelType.PADZERO)
gain_list.append(ULRange.NOTUSED)
chan_list.append(DigitalPortType.AUXPORT)
chan_type_list.append(ChannelType.PADZERO)
gain_list.append(ULRange.NOTUSED)
chan_list.append(DigitalPortType.AUXPORT)
chan_type_list.append(ChannelType.PADZERO)
gain_list.append(ULRange.NOTUSED)
timer_num0 = 0
frequency = 20000
duty_cycle = 0.5
# Start the pulse timer output (optional parameters omitted)
# this is used as a test signal
# connect TMR0 to the C0IN
# counter 0 will divide this by its range limit amount
actual_frequency, actual_duty_cycle, _ = ul.pulse_out_start(
board_num, timer_num0, frequency, duty_cycle)
num_channels = len(chan_list) # num of 16 bit locations
num_64_bit_channels = 3 # num of 64-bit channel
rate = 2000
num_rows = 384 # Scans, must be multiple of num_64_bit_channels
total_count = num_channels * num_rows # 12 * 96
# Allocate memory for the scan and cast it to a ctypes array pointer
# The default register size is 64 bits
memhandle = ul.win_buf_alloc_64(total_count)
# Check if the buffer was successfully allocated
if not memhandle:
raise Exception('Error: Failed to allocate memory')
buffer = cast(memhandle, POINTER(c_ulonglong))
try:
# Start the scan
ul.daq_in_scan(board_num,
chan_list,
chan_type_list,
gain_list,
num_channels,
rate,
0,
total_count,
memhandle,
ScanOptions.DEFAULTIO)
except Exception as e:
print('daq_in_scan error >>>', e)
print('Scan completed successfully.')
# Start updating the displayed values
status, curr_count, curr_index = ul.get_status(
board_num, FunctionType.DAQIFUNCTION)
print(f"Status {status} Count {curr_count} Index {curr_index}")
# stop TMR0 and TMR1
ul.pulse_out_stop(board_num, timer_num0)
print("Index\tCOIN\tC1IN\tDI")
# Print the data loop - digital port is only 8-bit so use mask to zero out
# upper bits
index = 0
mask = 0xFF
for i in range(num_rows):
if buffer[index + 1] > 0:
# if count is not zero calculate frequency
f = 1 / (buffer[index + 1] * tick_size)
else:
f = 0.01
print(f"{i:d}\t{buffer[index]:d}\t{f:5.2f} Hz \t{(buffer[index+2] & mask):d}")
index += num_64_bit_channels
except Exception as e:
print('\n', e)
finally:
if memhandle:
# Free the buffer in a finally block to prevent a memory leak.
ul.win_buf_free(memhandle)
if use_device_detection:
ul.release_daq_device(board_num)
if __name__ == '__main__':
run_example()